190 lines
6.8 KiB
C#
190 lines
6.8 KiB
C#
using StandardScene.Magnetic.Protocol;
|
|
using StandardScene.Magnetic.Tasking;
|
|
using StandardScene.Magnetic.Tests.TestHelpers;
|
|
using System;
|
|
using Xunit;
|
|
|
|
namespace StandardScene.Magnetic.Tests.Protocol
|
|
{
|
|
public sealed class Fass2ProtocolGoldenTests
|
|
{
|
|
[Fact]
|
|
public void BuildControl_Start_Car1_Param0_MatchesGoldenLayout()
|
|
{
|
|
var frame = Fass2Protocol.BuildControl(Fass2Protocol.CmdStart, 1, 0);
|
|
|
|
Assert.Equal(Fass2Protocol.ControlFrameLength, frame.Length);
|
|
Assert.Equal(Fass2Protocol.Begin, frame[0]);
|
|
Assert.Equal(Fass2Protocol.CmdStart, frame[1]);
|
|
Assert.Equal(0x01, frame[2]);
|
|
Assert.Equal(0x00, frame[3]);
|
|
Assert.Equal(0x00, frame[4]);
|
|
Assert.Equal(0x00, frame[5]);
|
|
Assert.Equal(Fass2Protocol.Xor(frame, 1, 47), frame[48]);
|
|
Assert.Equal(Fass2Protocol.End, frame[49]);
|
|
|
|
const string goldenPrefix = "BB 01 01 00 00 00";
|
|
Assert.StartsWith(goldenPrefix, Fass2TestFrameBuilder.ToHex(frame));
|
|
}
|
|
|
|
[Fact]
|
|
public void BuildNodes_TwoNodes_LittleEndianAndXor()
|
|
{
|
|
var nodes = new[]
|
|
{
|
|
new Fass2NodeMessage
|
|
{
|
|
Node = 1,
|
|
Distance = 500,
|
|
StartStop = 1,
|
|
Speed = 120
|
|
},
|
|
new Fass2NodeMessage
|
|
{
|
|
Node = 2,
|
|
StartStop = 2,
|
|
Lift = 1
|
|
}
|
|
};
|
|
|
|
var frame = Fass2Protocol.BuildNodes(1, 0x0102030405060708UL, nodes);
|
|
|
|
Assert.Equal(Fass2Protocol.NodesFrameLength, frame.Length);
|
|
Assert.Equal(Fass2Protocol.CmdNodes, frame[1]);
|
|
Assert.Equal(0x08, frame[4]);
|
|
Assert.Equal(0x01, frame[11]);
|
|
Assert.Equal(0x02, frame[12]);
|
|
Assert.Equal(0x00, frame[13]);
|
|
Assert.Equal(Fass2Protocol.Xor(frame, 1, 297), frame[298]);
|
|
Assert.Equal(Fass2Protocol.End, frame[299]);
|
|
|
|
var parsedNode0 = Fass2NodeMessage.FromBytes(CopyRange(frame, 14, 25));
|
|
Assert.Equal((ushort)1, parsedNode0.Node);
|
|
Assert.Equal((ushort)500, parsedNode0.Distance);
|
|
Assert.Equal((byte)1, parsedNode0.StartStop);
|
|
Assert.Equal((ushort)120, parsedNode0.Speed);
|
|
|
|
var parsedNode1 = Fass2NodeMessage.FromBytes(CopyRange(frame, 39, 25));
|
|
Assert.Equal((ushort)2, parsedNode1.Node);
|
|
Assert.Equal((byte)2, parsedNode1.StartStop);
|
|
Assert.Equal((byte)1, parsedNode1.Lift);
|
|
}
|
|
|
|
[Fact]
|
|
public void BuildAction_RoundTripsNodeBlock()
|
|
{
|
|
var node = new Fass2NodeMessage
|
|
{
|
|
Node = 3,
|
|
StartStop = 22,
|
|
Lift = 2,
|
|
Speed = 120
|
|
};
|
|
|
|
var frame = Fass2Protocol.BuildAction(2, 999, node);
|
|
|
|
Assert.Equal(Fass2Protocol.ActionFrameLength, frame.Length);
|
|
Assert.Equal(Fass2Protocol.CmdAction, frame[1]);
|
|
Assert.Equal(0x02, frame[2]);
|
|
Assert.Equal(0x00, frame[3]);
|
|
Assert.Equal(Fass2Protocol.Xor(frame, 1, 97), frame[98]);
|
|
|
|
var parsed = Fass2NodeMessage.FromBytes(CopyRange(frame, 14, 25));
|
|
Assert.Equal(node.Node, parsed.Node);
|
|
Assert.Equal(node.StartStop, parsed.StartStop);
|
|
Assert.Equal(node.Lift, parsed.Lift);
|
|
Assert.Equal(node.Speed, parsed.Speed);
|
|
}
|
|
|
|
[Fact]
|
|
public void BuildStateResponse_FixedTimestamp_EncodesBeijingCalendar()
|
|
{
|
|
var frame = Fass2Protocol.BuildStateResponse(1, 1_704_067_200_000UL);
|
|
|
|
Assert.Equal(Fass2Protocol.CmdStateResponse, frame[1]);
|
|
Assert.Equal(0x01, frame[2]);
|
|
Assert.Equal(0x00, frame[3]);
|
|
Assert.Equal(Fass2Protocol.Xor(frame, 1, 47), frame[48]);
|
|
Assert.Equal(Fass2Protocol.End, frame[49]);
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseState_RoundTripsMinimalFrame()
|
|
{
|
|
var frame = Fass2TestFrameBuilder.BuildStateFrame(5, 2, 7, 42);
|
|
|
|
Assert.True(Fass2Protocol.TryExtractStateFrame(frame, frame.Length, out var extracted));
|
|
Assert.Equal(frame, extracted);
|
|
|
|
var report = Fass2Protocol.ParseState(frame);
|
|
Assert.Equal((ushort)5, report.Car);
|
|
Assert.Equal((byte)2, report.State);
|
|
Assert.Equal((ulong)42, report.Task);
|
|
Assert.Equal((ushort)7, report.Node.Node);
|
|
Assert.Equal((ushort)500, report.Node.Distance);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryExtractStateFrame_RejectsBadXor()
|
|
{
|
|
var frame = Fass2TestFrameBuilder.BuildStateFrame(5, 2, 7, 42);
|
|
frame[98] ^= 0xFF;
|
|
|
|
Assert.False(Fass2Protocol.TryExtractStateFrame(frame, frame.Length, out _));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseState_RejectsBadXor()
|
|
{
|
|
var frame = Fass2TestFrameBuilder.BuildStateFrame(5, 2, 7, 42);
|
|
frame[98] ^= 0xFF;
|
|
|
|
Assert.Throws<ArgumentException>(() => Fass2Protocol.ParseState(frame));
|
|
}
|
|
|
|
[Fact]
|
|
public void NodeMessage_ToBytes_FromBytes_RoundTrip()
|
|
{
|
|
var source = new Fass2NodeMessage
|
|
{
|
|
Node = 11,
|
|
Distance = 1234,
|
|
StartStop = 2,
|
|
Direction = 3,
|
|
Orientation = 64,
|
|
Byroad = 2,
|
|
Speed = 120,
|
|
Lift = 1,
|
|
Roll = 2,
|
|
Shutdown = 1
|
|
};
|
|
|
|
var roundTrip = Fass2NodeMessage.FromBytes(source.ToBytes());
|
|
Assert.Equal(source.Node, roundTrip.Node);
|
|
Assert.Equal(source.Distance, roundTrip.Distance);
|
|
Assert.Equal(source.StartStop, roundTrip.StartStop);
|
|
Assert.Equal(source.Direction, roundTrip.Direction);
|
|
Assert.Equal(source.Orientation, roundTrip.Orientation);
|
|
Assert.Equal(source.Byroad, roundTrip.Byroad);
|
|
Assert.Equal(source.Speed, roundTrip.Speed);
|
|
Assert.Equal(source.Lift, roundTrip.Lift);
|
|
Assert.Equal(source.Roll, roundTrip.Roll);
|
|
Assert.Equal(source.Shutdown, roundTrip.Shutdown);
|
|
}
|
|
|
|
[Fact]
|
|
public void SpeedToProtocol_UsesMPerMinTimesTen()
|
|
{
|
|
Assert.Equal((ushort)120, Fass2TaskBuilder.SpeedToProtocol(12));
|
|
Assert.Equal((ushort)120, Fass2TaskBuilder.SpeedToProtocol(0.2));
|
|
}
|
|
|
|
private static byte[] CopyRange(byte[] source, int offset, int length)
|
|
{
|
|
var copy = new byte[length];
|
|
Buffer.BlockCopy(source, offset, copy, 0, length);
|
|
return copy;
|
|
}
|
|
}
|
|
}
|