86 lines
3.6 KiB
C#
86 lines
3.6 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.S7.SymbolImport;
|
|
using S7NetCpuType = global::S7.Net.CpuType;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.S7.IntegrationTests.S7_1500;
|
|
|
|
/// <summary>
|
|
/// PR-S7-D2 — UDT fan-out integration test against the python-snap7 S7-1500 fixture.
|
|
/// Seeds a 3-member UDT (Real + Int + Bool) into <c>DB1.MyUdt[400]</c> via
|
|
/// <c>Docker/profiles/s7_1500.json</c>'s <c>udt_layout</c> meta-seed, declares the
|
|
/// same layout in driver options, and verifies the fanned-out leaf reads return the
|
|
/// seeded values end-to-end through real S7comm. Build-only by default — the
|
|
/// simulator fixture skips when python-snap7 isn't running, so this test contributes
|
|
/// to the CI matrix without requiring docker locally.
|
|
/// </summary>
|
|
[Collection(Snap7ServerCollection.Name)]
|
|
[Trait("Category", "Integration")]
|
|
[Trait("Device", "S7_1500")]
|
|
public sealed class S7_1500UdtFanOutTests(Snap7ServerFixture sim)
|
|
{
|
|
private const string ParentTagName = "MyUdt";
|
|
|
|
/// <summary>
|
|
/// UDT layout matching the <c>udt_layout</c> meta-seed in the JSON profile.
|
|
/// Pressure (Real) at byte 0, Status (Int16) at byte 4, Enabled (Bool) at byte 6.
|
|
/// </summary>
|
|
private static readonly S7UdtDefinition MyUdt = new(
|
|
Name: "MyUdt",
|
|
Members:
|
|
[
|
|
new S7UdtMember("Pressure", 0, S7DataType.Float32),
|
|
new S7UdtMember("Status", 4, S7DataType.Int16),
|
|
new S7UdtMember("Enabled", 6, S7DataType.Bool),
|
|
],
|
|
SizeBytes: 7);
|
|
|
|
private static S7DriverOptions BuildUdtOptions(string host, int port) => new()
|
|
{
|
|
Host = host,
|
|
Port = port,
|
|
CpuType = S7NetCpuType.S71500,
|
|
Timeout = TimeSpan.FromSeconds(5),
|
|
Probe = new S7ProbeOptions { Enabled = false },
|
|
Tags =
|
|
[
|
|
// Parent UDT tag — base address points at byte 400 in DB1, where the
|
|
// simulator seeded the UDT contents. Fan-out emits three scalar leaves:
|
|
// MyUdt.Pressure -> DB1.DBD400 (Real 12.5)
|
|
// MyUdt.Status -> DB1.DBW404 (Int16 7)
|
|
// MyUdt.Enabled -> DB1.DBX406.0 (Bool true)
|
|
new S7TagDefinition(ParentTagName, "DB1.DBX400.0", S7DataType.Byte, UdtName: "MyUdt"),
|
|
],
|
|
Udts = [MyUdt],
|
|
};
|
|
|
|
[Fact]
|
|
public async Task Driver_fans_out_udt_into_member_tags()
|
|
{
|
|
if (sim.SkipReason is not null) Assert.Skip(sim.SkipReason);
|
|
|
|
var options = BuildUdtOptions(sim.Host, sim.Port);
|
|
await using var drv = new S7Driver(options, driverInstanceId: "s7-udt-fanout");
|
|
await drv.InitializeAsync("{}", TestContext.Current.CancellationToken);
|
|
|
|
// After fan-out the parent UDT name is gone from the tag map; only the leaves
|
|
// are readable. Reading the parent should surface BadNodeIdUnknown.
|
|
var parent = await drv.ReadAsync([ParentTagName], TestContext.Current.CancellationToken);
|
|
parent[0].StatusCode.ShouldNotBe(0u, "parent UDT tag must be replaced by its leaves");
|
|
|
|
// Read the three leaves and assert the seeded values come back.
|
|
var leaves = await drv.ReadAsync(
|
|
["MyUdt.Pressure", "MyUdt.Status", "MyUdt.Enabled"],
|
|
TestContext.Current.CancellationToken);
|
|
|
|
leaves.Count.ShouldBe(3);
|
|
foreach (var s in leaves)
|
|
s.StatusCode.ShouldBe(0u, "every UDT leaf read must succeed end-to-end");
|
|
|
|
Convert.ToSingle(leaves[0].Value).ShouldBe(12.5f, tolerance: 0.0001f);
|
|
Convert.ToInt32(leaves[1].Value).ShouldBe(7);
|
|
Convert.ToBoolean(leaves[2].Value).ShouldBeTrue();
|
|
}
|
|
}
|