Pace the pump on what MMTTY says it has left
The engine probe answered two questions on the air, and they point opposite
ways.
TxBufLen is the number of characters left to transmit. Seventeen characters
pushed, five decoded back, and it read 12, counting down to 0 as the message
went out. So the pump no longer counts character times off the clock: it asks
the engine, feeds while the answer is under Lead, and asks again. The baud rate
is now only the fallback for an engine that will not answer.
A backspace is not an edit. Pushed in as a character it made the count go up by
four and the text went out unchanged, so text the engine has been given cannot
be taken back, only aborted. EngineTypeAhead was built on the opposite belief
and is gone, along with the setting that chose it and the interface that existed
to switch between the two.
A count that stops going down means the engine is holding what it has: MMTTY set
to Word out keeps a word until the space after it. The pump feeds one character
per look while that lasts, so the space arrives and the word goes out rather
than the message sitting there.
The probe kept two bugs of its own that this run showed: it read the count a
millisecond after pushing, saw 0 and called the message finished, and it started
a step while the previous unkey was still in flight, so that step ran with the
engine down. It now needs two empty answers in a row and waits for the engine to
say it has stopped. It also asks what {RX} does to a word the engine is holding,
which decides whether a macro without a trailing space loses its last word.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RtspmWmS7f8kUvcyaHpRWZ
This commit is contained in:
@@ -84,7 +84,7 @@ public class TypeAheadTests
|
||||
buffer.Append("OM5X 599 001");
|
||||
|
||||
await WaitForAsync(() => buffer.Sent.Length >= 5);
|
||||
buffer.Rewrite("599 002", TransmitBuffer.NoCursor);
|
||||
buffer.Rewrite("599 002", TypeAhead.NoCursor);
|
||||
|
||||
await WaitForAsync(() => buffer.Pending.Length == 0);
|
||||
Assert.EndsWith("599 002", Sent, StringComparison.Ordinal);
|
||||
@@ -111,7 +111,7 @@ public class TypeAheadTests
|
||||
buffer.Rewrite("CQ TEST", 2);
|
||||
await WaitForAsync(() => Sent.Length == 2);
|
||||
|
||||
buffer.Cursor = TransmitBuffer.NoCursor;
|
||||
buffer.Cursor = TypeAhead.NoCursor;
|
||||
|
||||
await WaitForAsync(() => Sent == "CQ TEST");
|
||||
Assert.Equal("CQ TEST", Sent);
|
||||
@@ -212,21 +212,53 @@ public class TypeAheadTests
|
||||
Assert.Equal(2, Sent.Length);
|
||||
}
|
||||
|
||||
/// The estimate of what the engine still holds is only an estimate. An
|
||||
/// engine that says it has stopped transmitting has an empty buffer, and
|
||||
/// the next character goes to it at once rather than a character time
|
||||
/// later.
|
||||
/// An engine that says how much it holds is asked rather than timed, so
|
||||
/// nothing depends on the baud rate being right.
|
||||
[Fact]
|
||||
public async Task AnIdleEngineIsFedWithoutWaiting()
|
||||
public async Task ACountingEngineIsAskedHowMuchItHolds()
|
||||
{
|
||||
using TypeAhead buffer = Buffer(baud: Slow);
|
||||
FakeEngine engine = new();
|
||||
using TypeAhead buffer = new(engine, baud: Slow);
|
||||
|
||||
buffer.Append("CQ TEST");
|
||||
await WaitForAsync(() => Sent.Length >= 2);
|
||||
|
||||
buffer.EngineIdle();
|
||||
await WaitForAsync(() => engine.Waiting.Length >= 2);
|
||||
await Task.Delay(30);
|
||||
Assert.Equal("CQ", engine.Waiting);
|
||||
|
||||
await WaitForAsync(() => Sent.Length >= 4);
|
||||
Assert.Equal("CQ T", Sent);
|
||||
engine.Transmit(2);
|
||||
|
||||
await WaitForAsync(() => engine.Transmitted.Length + engine.Waiting.Length >= 4);
|
||||
Assert.Equal("CQ T", engine.Transmitted + engine.Waiting);
|
||||
}
|
||||
|
||||
/// MMTTY set to Word out holds a word until the space after it, so the
|
||||
/// count stops going down. Waiting for it to move would leave the message
|
||||
/// sitting there, so the pump feeds on.
|
||||
[Fact]
|
||||
public async Task AnEngineHoldingWhatItHasIsFedAnyway()
|
||||
{
|
||||
FakeEngine engine = new();
|
||||
using TypeAhead buffer = new(engine, baud: Slow);
|
||||
|
||||
buffer.Append("CQ TEST");
|
||||
|
||||
await WaitForAsync(() => engine.Waiting == "CQ TEST");
|
||||
Assert.Equal("CQ TEST", engine.Waiting);
|
||||
}
|
||||
|
||||
/// The clock is the fallback for an engine that will not answer.
|
||||
[Fact]
|
||||
public async Task AnEngineThatWillNotCountIsPacedByTheClock()
|
||||
{
|
||||
FakeEngine engine = new() { Counts = false };
|
||||
using TypeAhead buffer = new(engine, baud: Fast);
|
||||
|
||||
buffer.Append("CQ TEST");
|
||||
|
||||
await WaitForAsync(() => engine.Waiting == "CQ TEST");
|
||||
Assert.False(buffer.Counts);
|
||||
Assert.Equal("CQ TEST", engine.Waiting);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -236,4 +268,70 @@ public class TypeAheadTests
|
||||
|
||||
Assert.Equal(165, buffer.CharacterTime.TotalMilliseconds, 0.5);
|
||||
}
|
||||
|
||||
/// An engine with a buffer of its own: it takes characters, holds them
|
||||
/// until the test says they have been transmitted, and says how many it
|
||||
/// has. `Counts` false is the engine that will not answer.
|
||||
private sealed class FakeEngine : EngineBuffer
|
||||
{
|
||||
private readonly Lock gate = new();
|
||||
private readonly StringBuilder waiting = new();
|
||||
private readonly StringBuilder transmitted = new();
|
||||
|
||||
public bool Counts { get; init; } = true;
|
||||
|
||||
public event EventHandler<int>? Buffered;
|
||||
|
||||
public string Waiting
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (gate)
|
||||
{
|
||||
return waiting.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string Transmitted
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (gate)
|
||||
{
|
||||
return transmitted.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Task TypeAsync(char character, CancellationToken cancellation = default)
|
||||
{
|
||||
lock (gate)
|
||||
{
|
||||
waiting.Append(character);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task AskBufferedAsync(string property = "", CancellationToken cancellation = default)
|
||||
{
|
||||
int left;
|
||||
lock (gate)
|
||||
{
|
||||
left = Counts ? waiting.Length : -1;
|
||||
}
|
||||
Buffered?.Invoke(this, left);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void Transmit(int count)
|
||||
{
|
||||
lock (gate)
|
||||
{
|
||||
int going = Math.Min(count, waiting.Length);
|
||||
transmitted.Append(waiting.ToString(0, going));
|
||||
waiting.Remove(0, going);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user