LoggingSession mixed two things: the contest, which one station has one of, and what the operator is typing, which belongs to a radio. It is now ContestSession and RadioPosition. Two positions share one log, one score and one run of serial numbers, so a station worked on radio 1 is a dupe on radio 2. Frequency, mode, split, what is typed and whether you are running belong to each radio on its own. A second radio opens a second entry window. It has no menu of its own: it is another view of the same contest, not another program. Only the window for radio 1 opens the database and the connections. Ctrl+Tab moves the operator to the other radio and the keyboard follows. Ctrl+Shift+Tab puts both radios in the headphones. OtrspBox speaks OTRSP to an SO2R box over a serial port: TX1/TX2 for the key, RX1/RX2 and RX1S for the headphones, AUXnnn for an output line. It follows the active radio, and every message points the box at this radio before keying, so a message cannot go out of the radio the operator has just left. A command that would change nothing is not sent, because the box works relays; N1MM does the same. The box takes a Stream, so what it sends is tested without a serial port. Still missing: alternating CQ, and voice keying on the second radio. Looked at under Xvfb with two fake radios, one of them split: both windows up, radio 1 showing 14008.00 into 14020.0, radio 2 on 14030.00, Ctrl+Tab moving the keyboard between them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
55 lines
1.6 KiB
C#
55 lines
1.6 KiB
C#
using Nonemm.Contests;
|
|
using Nonemm.Contests.Rules;
|
|
using Nonemm.Core;
|
|
using Nonemm.Storage;
|
|
|
|
namespace Nonemm.Session.Tests;
|
|
|
|
public class MessageExpanderTests
|
|
{
|
|
private static RadioPosition Session()
|
|
{
|
|
FakeLogStore store = new();
|
|
ContestInstance instance = store.AddContest(new ContestInstance
|
|
{
|
|
ContestNumber = 0,
|
|
ContestName = "CQWW",
|
|
SentExchange = "14",
|
|
});
|
|
return new RadioPosition(new ContestSession(
|
|
store,
|
|
new CqWorldWide(ModeCategory.Cw),
|
|
instance,
|
|
new StationInfo { Callsign = "DL1ABC", CqZone = 14, Name = "Erik" },
|
|
null));
|
|
}
|
|
|
|
[Fact]
|
|
public void MyCallAndTheirCallAreFilledIn()
|
|
{
|
|
RadioPosition session = Session();
|
|
session.Entry.Call = "JA1XYZ";
|
|
Assert.Equal("JA1XYZ DE DL1ABC", MessageExpander.Expand("{CALL} DE {MYCALL}", session));
|
|
}
|
|
|
|
[Fact]
|
|
public void TheSentExchangeComesFromTheContestSetup() =>
|
|
Assert.Equal("599 14", MessageExpander.Expand("{SENTRST} {EXCH}", Session()));
|
|
|
|
[Fact]
|
|
public void HashIsTheSerialNumber() =>
|
|
Assert.Equal("NR 1", MessageExpander.Expand("NR #", Session()));
|
|
|
|
[Fact]
|
|
public void CutNumbersReplaceZeroAndNine() =>
|
|
Assert.Equal("5NN", MessageExpander.Expand("{SENTRSTCUT}", Session()));
|
|
|
|
[Fact]
|
|
public void AMacroThatIsNotKnownDisappears() =>
|
|
Assert.Equal("TU ", MessageExpander.Expand("TU {NOTAMACRO}", Session()));
|
|
|
|
[Fact]
|
|
public void AnUnclosedMacroIsLeftAsTyped() =>
|
|
Assert.Equal("TU {MYCALL", MessageExpander.Expand("TU {MYCALL", Session()));
|
|
}
|