fix: FASS2 状态机与 UDP 枢纽审查项

等锁超时、全路径签名、无锁窗口不下发、Fault 终态可等待、Pass+机构不再短路、Alarm 不杀单、catch-up 停在未完成站;VehicleCode=0/重复车号拒绝注册,Hub 已运行时不抢监听口;FileLogger 空目录可写。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
黄兆尉
2026-08-26 23:04:58 +08:00
co-authored by Cursor
parent 29a06f004f
commit e4a245e644
13 changed files with 465 additions and 48 deletions
@@ -57,6 +57,20 @@ namespace StandardScene.Magnetic.Tests.Protocol
Assert.Equal((ulong)202, _car2.Reports[0].Task);
}
[Fact]
public void EnsureStarted_NewPort_ReceivesOnNewPort()
{
var newPort = AllocateUdpPort();
Fass2UdpHub.EnsureStarted(newPort);
Thread.Sleep(150);
var frame = Fass2TestFrameBuilder.BuildStateFrame(1, 1, 88, 301);
using var sender = new UdpClient();
sender.Send(frame, frame.Length, new IPEndPoint(IPAddress.Loopback, newPort));
Assert.NotNull(_car1.WaitForAck());
Assert.Equal((ushort)88, _car1.Reports[_car1.Reports.Count - 1].Node.Node);
}
[Fact]
public void Hub_IgnoresUnregisteredCarCode()
{
@@ -66,6 +80,31 @@ namespace StandardScene.Magnetic.Tests.Protocol
Assert.Empty(_car2.Reports);
}
[Fact]
public void Register_VehicleCodeZero_DoesNotReceiveState()
{
var zeroPort = AllocateUdpPort();
using var zero = new TestUdpCar(0, zeroPort);
Assert.False(Fass2UdpHub.Register(zero));
SendState(0, 2, 40, 0);
Thread.Sleep(200);
Assert.Empty(zero.Reports);
}
[Fact]
public void Register_DuplicateVehicleCode_KeepsFirstCar()
{
var dupPort = AllocateUdpPort();
using var dup = new TestUdpCar(1, dupPort);
Assert.False(Fass2UdpHub.Register(dup));
SendState(1, 2, 41, 401);
Assert.NotNull(_car1.WaitForAck());
Assert.Equal((ushort)41, _car1.Reports[_car1.Reports.Count - 1].Node.Node);
Assert.Empty(dup.Reports);
}
public void Dispose()
{
Fass2UdpHub.Unregister(_car1);