Files
lmxopcua/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Cip/ConnectionManagerServicesTests.cs
T

115 lines
5.3 KiB
C#

using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Cip;
using ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests.Captures;
namespace ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests.Cip;
// CAPTURE DECISION: the brief gave timeout ticks 0x0E. Every libplctag capture (logix-forward-open,
// logix-forward-close) carries priority/tick 0x0A, timeout ticks 0x05, so ConnectionManagerServices
// uses 0x05 (brief: 0x0E -> capture: 0x05). The golden-byte tests below use 0x05 accordingly.
public sealed class ConnectionManagerServicesTests
{
private static byte[] CapturedRequest(string file) =>
CaptureFile.CipPayload(CaptureFile.Load(file).First(f => f.FromClient));
private static CipReply CapturedReply(string file) =>
CipReply.Parse(CaptureFile.CipPayload(CaptureFile.Load(file).First(f => !f.FromClient)));
[Fact]
public void Forward_close_frame()
{
var bytes = ConnectionManagerServices.ForwardClose(0x1234, 0x1337, 0xCAFEBABE, [0x01, 0x00]);
Convert.ToHexString(bytes).ShouldBe("4E02200624010A05" + "3412" + "3713" + "BEBAFECA" + "03" + "00" + "0100" + "20022401");
}
[Fact]
public void Forward_close_matches_capture()
{
var cap = CapturedRequest("logix-forward-close.hex");
var serial = BitConverter.ToUInt16(cap, 8);
var vendor = BitConverter.ToUInt16(cap, 10);
var orig = BitConverter.ToUInt32(cap, 12);
vendor.ShouldBe((ushort)0xF33D);
ConnectionManagerServices.ForwardClose(serial, vendor, orig, [0x01, 0x00]).ShouldBe(cap);
}
[Fact]
public void Large_forward_open_matches_capture()
{
var cap = CapturedRequest("logix-forward-open.hex");
// Values read out of the capture: T->O id @12, serial @16, vendor @18, originator serial @20,
// timeout multiplier @24, RPI @28, network params @32 (0x42000FA2 -> size 4002).
var p = new ForwardOpenParams(
Large: true, TOConnectionId: BitConverter.ToUInt32(cap, 12), ConnectionSerial: BitConverter.ToUInt16(cap, 16),
VendorId: BitConverter.ToUInt16(cap, 18), OriginatorSerial: BitConverter.ToUInt32(cap, 20),
TimeoutMultiplier: cap[24], RpiMicros: BitConverter.ToUInt32(cap, 28), ConnectionSize: 4002, Route: [0x01, 0x00]);
p.RpiMicros.ShouldBe(1_000_000u);
BitConverter.ToUInt32(cap, 32).ShouldBe(0x42000FA2u);
ConnectionManagerServices.ForwardOpen(p).ShouldBe(cap);
}
[Fact]
public void Large_forward_open_reply_parses_from_capture()
{
var reply = ConnectionManagerServices.ParseForwardOpenReply(CapturedReply("logix-forward-open.hex"));
reply.OTConnectionId.ShouldBe(0x2CDF633Fu);
reply.TOConnectionId.ShouldBe(0x073F9E60u);
reply.OTApiMicros.ShouldBe(1_000_000u);
reply.TOApiMicros.ShouldBe(1_000_000u);
}
// No small Forward Open 0x54 was captured (the Large FO succeeded), so this layout is checked against
// the CIP spec / the brief rather than a capture.
[Fact]
public void Regular_forward_open_layout_per_spec()
{
var bytes = ConnectionManagerServices.ForwardOpen(new ForwardOpenParams(
false, 0x11223344, 0x5566, 0x7788, 0x99AABBCC, 0x01, 1_000_000, 504, [0x01, 0x00]));
Convert.ToHexString(bytes).ShouldBe(
"540220062401" + "0A05" + "00000000" + "44332211" + "6655" + "8877" + "CCBBAA99" + "01000000"
+ "40420F00" + "F843" + "40420F00" + "F843" + "A3" + "03" + "0100" + "20022401");
}
[Fact]
public void Regular_forward_open_network_params_encoding()
{
var bytes = ConnectionManagerServices.ForwardOpen(new ForwardOpenParams(false, 1, 2, 3, 4, 1, 1_000_000, 504, []));
// 504 = 0x1F8; 0x4200 | 0x1F8 = 0x43F8 -> LE F8 43
Convert.ToHexString(bytes).ShouldContain("40420F00" + "F843");
bytes[0].ShouldBe((byte)0x54);
}
[Fact]
public void Unconnected_send_even_embedded()
{
byte[] embedded = [0x4C, 0x02, 0x91, 0x01, 0x41, 0x00, 0x01, 0x00]; // 8 bytes
Convert.ToHexString(ConnectionManagerServices.UnconnectedSend(embedded, [0x01, 0x00])).ShouldBe(
"520220062401" + "0A05" + "0800" + Convert.ToHexString(embedded) + "01" + "00" + "0100");
}
[Fact]
public void Unconnected_send_pads_odd_embedded()
{
byte[] embedded = [0x01, 0x02, 0x03];
Convert.ToHexString(ConnectionManagerServices.UnconnectedSend(embedded, [0x01, 0x00])).ShouldBe(
"520220062401" + "0A05" + "0300" + "010203" + "00" + "01" + "00" + "0100");
}
[Fact]
public void Refused_forward_open_surfaces_statuses()
{
var reply = CipReply.Parse(Convert.FromHexString("DB000101" + "0901"));
var ex = Should.Throw<AbTransportException>(() => ConnectionManagerServices.ParseForwardOpenReply(reply));
ex.Message.ShouldContain("0x01");
ex.Message.ShouldContain("0x0109");
}
[Theory]
[InlineData("DB000800", true)] // Service Not Supported
[InlineData("DB0001010901", true)] // general 1 / extended 0x0109
[InlineData("DB0001010001", false)] // general 1 / extended 0x0100 (connection in use): no fallback
[InlineData("DB000000", false)]
public void Fallback_rules(string hex, bool expected) =>
ConnectionManagerServices.ShouldFallBackToRegularOpen(CipReply.Parse(Convert.FromHexString(hex))).ShouldBe(expected);
}