using StandardScene.Magnetic.Protocol; using StandardScene.Magnetic.Tasking; using System.Collections.Generic; using Xunit; namespace StandardScene.Magnetic.Tests.Tasking { public class Fass2ControlStopStateMachineTests { [Fact] public void ControlStop_TruncatesWindow_WaitsOccupancy_ThenReleases() { Fass2NodeMessage[] lastWindow = null; Fass2NodeMessage lastPatch = null; var canRelease = false; var nodes = new List { new Fass2NodeMessage { Node = 1, StartStop = Fass2TaskBuilder.StartStopPass, Distance = 100 }, new Fass2NodeMessage { Node = 2, StartStop = Fass2TaskBuilder.StartStopControlStop }, new Fass2NodeMessage { Node = 3, StartStop = Fass2TaskBuilder.StartStopPass } }; var plan = new Fass2TaskPlan { StartSiteId = 10, GoalSiteId = 12, SiteIds = new List(), Nodes = nodes, FieldsSignature = "keep" }; var machine = new Fass2TaskStateMachine(new Fass2TaskCallbacks { BuildPlan = (_, __, ___) => plan, SendNodes = (window, _) => lastWindow = window, SendAction = (patch, _) => lastPatch = patch, SendControl = (_, __) => { }, ResolveNodeId = id => (ushort)id, AllocateTaskId = () => 1UL, AllocateActionId = () => 9UL, CheckControlRelease = _ => new Fass2ControlReleaseCheck { CanRelease = canRelease, AreaId = "DockA", Capacity = 1, OthersInArea = canRelease ? 0 : 1, ReleaseStartStop = Fass2TaskBuilder.StartStopControlStart, Reason = canRelease ? "area clear" : "area occupied" } }) { StartBeforeMove = false, LockCount = 4, ResendIntervalMs = 60_000, ActionRetryIntervalMs = 0 }; machine.Begin(10, 12); machine.Tick(Report(1, moving: true, startStop: 1)); Assert.Equal(Fass2TaskPhase.Moving, machine.Context.Phase); Assert.NotNull(lastWindow); Assert.Equal(2, lastWindow.Length); Assert.Equal(Fass2TaskBuilder.StartStopControlStop, lastWindow[1].StartStop); machine.Tick(Report(2, moving: false, startStop: 12)); Assert.Equal(Fass2TaskPhase.AtStation, machine.Context.Phase); Assert.Equal(1, machine.Context.CurrentIndex); lastPatch = null; machine.Tick(Report(2, moving: false, startStop: 12)); Assert.True(machine.Context.WaitingForRelease); Assert.Null(lastPatch); Assert.Equal(-1, machine.Context.ControlReleasedIndex); canRelease = true; machine.Tick(Report(2, moving: false, startStop: 12)); Assert.NotNull(lastPatch); Assert.Equal(Fass2TaskBuilder.StartStopControlStart, lastPatch.StartStop); Assert.Equal(1, machine.Context.ControlReleasedIndex); Assert.Equal(Fass2TaskBuilder.StartStopControlStart, nodes[1].StartStop); Assert.True(lastWindow.Length >= 2); Assert.Equal((ushort)3, lastWindow[lastWindow.Length - 1].Node); machine.Tick(Report(2, moving: false, startStop: 11)); Assert.Equal(Fass2TaskPhase.AtStation, machine.Context.Phase); Assert.Equal(1, machine.Context.CurrentIndex); } private static Fass2StateReport Report(ushort node, bool moving, byte startStop) { return new Fass2StateReport { State = (byte)(moving ? 1 : 2), Node = new Fass2NodeMessage { Node = node, StartStop = startStop } }; } } }