110 lines
4.5 KiB
C#
110 lines
4.5 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests.DL205;
|
|
|
|
/// <summary>
|
|
/// Verifies DL260 I/O-memory coil mappings against the <c>dl205.json</c> pymodbus profile.
|
|
/// DirectLOGIC Y-outputs and C-relays are exposed to Modbus as FC01/FC05 coils, but at
|
|
/// non-zero base addresses that confuse operators used to "Y0 is the first coil". The sim
|
|
/// seeds Y0 → coil 2048 = ON and C0 → coil 3072 = ON as fixed markers.
|
|
/// </summary>
|
|
[Collection(ModbusSimulatorCollection.Name)]
|
|
[Trait("Category", "Integration")]
|
|
[Trait("Device", "DL205")]
|
|
public sealed class DL205CoilMappingTests(ModbusSimulatorFixture sim)
|
|
{
|
|
[Fact]
|
|
public async Task DL260_Y0_maps_to_coil_2048()
|
|
{
|
|
if (sim.SkipReason is not null) Assert.Skip(sim.SkipReason);
|
|
if (!string.Equals(Environment.GetEnvironmentVariable("MODBUS_SIM_PROFILE"), "dl205",
|
|
StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
Assert.Skip("MODBUS_SIM_PROFILE != dl205 — skipping.");
|
|
}
|
|
|
|
var coil = DirectLogicAddress.YOutputToCoil("Y0");
|
|
coil.ShouldBe((ushort)2048);
|
|
|
|
var options = BuildOptions(sim, [
|
|
new ModbusTagDefinition("DL260_Y0",
|
|
ModbusRegion.Coils, Address: coil,
|
|
DataType: ModbusDataType.Bool, Writable: false),
|
|
]);
|
|
await using var driver = new ModbusDriver(options, driverInstanceId: "dl205-y0");
|
|
await driver.InitializeAsync("{}", TestContext.Current.CancellationToken);
|
|
|
|
var results = await driver.ReadAsync(["DL260_Y0"], TestContext.Current.CancellationToken);
|
|
results[0].StatusCode.ShouldBe(0u);
|
|
results[0].Value.ShouldBe(true, "dl205.json seeds coil 2048 (Y0) = ON");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DL260_C0_maps_to_coil_3072()
|
|
{
|
|
if (sim.SkipReason is not null) Assert.Skip(sim.SkipReason);
|
|
if (!string.Equals(Environment.GetEnvironmentVariable("MODBUS_SIM_PROFILE"), "dl205",
|
|
StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
Assert.Skip("MODBUS_SIM_PROFILE != dl205 — skipping.");
|
|
}
|
|
|
|
var coil = DirectLogicAddress.CRelayToCoil("C0");
|
|
coil.ShouldBe((ushort)3072);
|
|
|
|
var options = BuildOptions(sim, [
|
|
new ModbusTagDefinition("DL260_C0",
|
|
ModbusRegion.Coils, Address: coil,
|
|
DataType: ModbusDataType.Bool, Writable: false),
|
|
]);
|
|
await using var driver = new ModbusDriver(options, driverInstanceId: "dl205-c0");
|
|
await driver.InitializeAsync("{}", TestContext.Current.CancellationToken);
|
|
|
|
var results = await driver.ReadAsync(["DL260_C0"], TestContext.Current.CancellationToken);
|
|
results[0].StatusCode.ShouldBe(0u);
|
|
results[0].Value.ShouldBe(true, "dl205.json seeds coil 3072 (C0) = ON");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DL260_scratch_Crelay_supports_write_then_read()
|
|
{
|
|
if (sim.SkipReason is not null) Assert.Skip(sim.SkipReason);
|
|
if (!string.Equals(Environment.GetEnvironmentVariable("MODBUS_SIM_PROFILE"), "dl205",
|
|
StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
Assert.Skip("MODBUS_SIM_PROFILE != dl205 — skipping.");
|
|
}
|
|
|
|
// Scratch C-relay at coil 4000 (per dl205.json _quirk note) is writable. Write=true then
|
|
// read back to confirm FC05 round-trip works against the DL-mapped coil bank.
|
|
var options = BuildOptions(sim, [
|
|
new ModbusTagDefinition("DL260_C_Scratch",
|
|
ModbusRegion.Coils, Address: 4000,
|
|
DataType: ModbusDataType.Bool, Writable: true),
|
|
]);
|
|
await using var driver = new ModbusDriver(options, driverInstanceId: "dl205-cscratch");
|
|
await driver.InitializeAsync("{}", TestContext.Current.CancellationToken);
|
|
|
|
var writeResults = await driver.WriteAsync(
|
|
[new(FullReference: "DL260_C_Scratch", Value: true)],
|
|
TestContext.Current.CancellationToken);
|
|
writeResults[0].StatusCode.ShouldBe(0u);
|
|
|
|
var readResults = await driver.ReadAsync(["DL260_C_Scratch"], TestContext.Current.CancellationToken);
|
|
readResults[0].StatusCode.ShouldBe(0u);
|
|
readResults[0].Value.ShouldBe(true);
|
|
}
|
|
|
|
private static ModbusDriverOptions BuildOptions(ModbusSimulatorFixture sim, IReadOnlyList<ModbusTagDefinition> tags)
|
|
=> new()
|
|
{
|
|
Host = sim.Host,
|
|
Port = sim.Port,
|
|
UnitId = 1,
|
|
Timeout = TimeSpan.FromSeconds(2),
|
|
Tags = tags,
|
|
Probe = new ModbusProbeOptions { Enabled = false },
|
|
};
|
|
}
|