198 lines
9.4 KiB
C#
198 lines
9.4 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.AbLegacy;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.PlcFamilies;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests;
|
|
|
|
[Trait("Category", "Unit")]
|
|
public sealed class AbLegacyDriverTests
|
|
{
|
|
[Fact]
|
|
public void DriverType_is_AbLegacy()
|
|
{
|
|
var drv = new AbLegacyDriver(new AbLegacyDriverOptions(), "drv-1");
|
|
drv.DriverType.ShouldBe("AbLegacy");
|
|
drv.DriverInstanceId.ShouldBe("drv-1");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_with_devices_assigns_family_profiles()
|
|
{
|
|
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
|
{
|
|
Devices =
|
|
[
|
|
new AbLegacyDeviceOptions("ab://10.0.0.5/1,0", AbLegacyPlcFamily.Slc500),
|
|
new AbLegacyDeviceOptions("ab://10.0.0.6/", AbLegacyPlcFamily.MicroLogix),
|
|
new AbLegacyDeviceOptions("ab://10.0.0.7/1,0", AbLegacyPlcFamily.Plc5),
|
|
],
|
|
}, "drv-1");
|
|
|
|
await drv.InitializeAsync("{}", CancellationToken.None);
|
|
|
|
drv.DeviceCount.ShouldBe(3);
|
|
drv.GetDeviceState("ab://10.0.0.5/1,0")!.Profile.ShouldBe(AbLegacyPlcFamilyProfile.Slc500);
|
|
drv.GetDeviceState("ab://10.0.0.6/")!.Profile.ShouldBe(AbLegacyPlcFamilyProfile.MicroLogix);
|
|
drv.GetDeviceState("ab://10.0.0.7/1,0")!.Profile.ShouldBe(AbLegacyPlcFamilyProfile.Plc5);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_with_malformed_host_address_faults()
|
|
{
|
|
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
|
{
|
|
Devices = [new AbLegacyDeviceOptions("not-a-valid-address")],
|
|
}, "drv-1");
|
|
|
|
await Should.ThrowAsync<InvalidOperationException>(
|
|
() => drv.InitializeAsync("{}", CancellationToken.None));
|
|
drv.GetHealth().State.ShouldBe(DriverState.Faulted);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ShutdownAsync_clears_devices()
|
|
{
|
|
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
|
{
|
|
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
|
}, "drv-1");
|
|
await drv.InitializeAsync("{}", CancellationToken.None);
|
|
|
|
await drv.ShutdownAsync(CancellationToken.None);
|
|
drv.DeviceCount.ShouldBe(0);
|
|
drv.GetHealth().State.ShouldBe(DriverState.Unknown);
|
|
}
|
|
|
|
[Fact]
|
|
public void Family_profiles_expose_expected_defaults()
|
|
{
|
|
AbLegacyPlcFamilyProfile.Slc500.LibplctagPlcAttribute.ShouldBe("slc500");
|
|
AbLegacyPlcFamilyProfile.Slc500.SupportsLongFile.ShouldBeTrue();
|
|
AbLegacyPlcFamilyProfile.Slc500.DefaultCipPath.ShouldBe("1,0");
|
|
|
|
AbLegacyPlcFamilyProfile.MicroLogix.DefaultCipPath.ShouldBe("");
|
|
AbLegacyPlcFamilyProfile.MicroLogix.SupportsLongFile.ShouldBeFalse();
|
|
|
|
AbLegacyPlcFamilyProfile.Plc5.LibplctagPlcAttribute.ShouldBe("plc5");
|
|
AbLegacyPlcFamilyProfile.Plc5.SupportsLongFile.ShouldBeFalse();
|
|
|
|
AbLegacyPlcFamilyProfile.LogixPccc.LibplctagPlcAttribute.ShouldBe("logixpccc");
|
|
AbLegacyPlcFamilyProfile.LogixPccc.SupportsLongFile.ShouldBeTrue();
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(AbLegacyPlcFamily.Slc500, "slc500")]
|
|
[InlineData(AbLegacyPlcFamily.MicroLogix, "micrologix")]
|
|
[InlineData(AbLegacyPlcFamily.Plc5, "plc5")]
|
|
[InlineData(AbLegacyPlcFamily.LogixPccc, "logixpccc")]
|
|
public void ForFamily_dispatches_correctly(AbLegacyPlcFamily family, string expectedAttribute)
|
|
{
|
|
AbLegacyPlcFamilyProfile.ForFamily(family).LibplctagPlcAttribute.ShouldBe(expectedAttribute);
|
|
}
|
|
|
|
[Fact]
|
|
public void DataType_mapping_covers_atomic_pccc_types()
|
|
{
|
|
AbLegacyDataType.Bit.ToDriverDataType().ShouldBe(DriverDataType.Boolean);
|
|
AbLegacyDataType.Int.ToDriverDataType().ShouldBe(DriverDataType.Int32);
|
|
AbLegacyDataType.Long.ToDriverDataType().ShouldBe(DriverDataType.Int32);
|
|
AbLegacyDataType.Float.ToDriverDataType().ShouldBe(DriverDataType.Float32);
|
|
AbLegacyDataType.String.ToDriverDataType().ShouldBe(DriverDataType.String);
|
|
AbLegacyDataType.TimerElement.ToDriverDataType().ShouldBe(DriverDataType.Int32);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(AbLegacyDataType.TimerElement, "EN", DriverDataType.Boolean)]
|
|
[InlineData(AbLegacyDataType.TimerElement, "TT", DriverDataType.Boolean)]
|
|
[InlineData(AbLegacyDataType.TimerElement, "DN", DriverDataType.Boolean)]
|
|
[InlineData(AbLegacyDataType.TimerElement, "PRE", DriverDataType.Int32)]
|
|
[InlineData(AbLegacyDataType.TimerElement, "ACC", DriverDataType.Int32)]
|
|
[InlineData(AbLegacyDataType.CounterElement, "CU", DriverDataType.Boolean)]
|
|
[InlineData(AbLegacyDataType.CounterElement, "CD", DriverDataType.Boolean)]
|
|
[InlineData(AbLegacyDataType.CounterElement, "DN", DriverDataType.Boolean)]
|
|
[InlineData(AbLegacyDataType.CounterElement, "OV", DriverDataType.Boolean)]
|
|
[InlineData(AbLegacyDataType.CounterElement, "UN", DriverDataType.Boolean)]
|
|
[InlineData(AbLegacyDataType.CounterElement, "PRE", DriverDataType.Int32)]
|
|
[InlineData(AbLegacyDataType.CounterElement, "ACC", DriverDataType.Int32)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "EN", DriverDataType.Boolean)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "EU", DriverDataType.Boolean)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "DN", DriverDataType.Boolean)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "EM", DriverDataType.Boolean)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "ER", DriverDataType.Boolean)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "UL", DriverDataType.Boolean)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "IN", DriverDataType.Boolean)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "FD", DriverDataType.Boolean)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "LEN", DriverDataType.Int32)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "POS", DriverDataType.Int32)]
|
|
public void EffectiveDriverDataType_resolves_subelements(
|
|
AbLegacyDataType dataType, string subElement, DriverDataType expected)
|
|
{
|
|
AbLegacyDataTypeExtensions.EffectiveDriverDataType(dataType, subElement).ShouldBe(expected);
|
|
}
|
|
|
|
[Fact]
|
|
public void EffectiveDriverDataType_unknown_subelement_falls_back_to_base()
|
|
{
|
|
// Permissive — keeps the driver from refusing tags whose sub-element we don't catalogue.
|
|
AbLegacyDataTypeExtensions.EffectiveDriverDataType(AbLegacyDataType.TimerElement, "BOGUS")
|
|
.ShouldBe(DriverDataType.Int32);
|
|
AbLegacyDataTypeExtensions.EffectiveDriverDataType(AbLegacyDataType.TimerElement, null)
|
|
.ShouldBe(DriverDataType.Int32);
|
|
AbLegacyDataTypeExtensions.EffectiveDriverDataType(AbLegacyDataType.Int, "DN")
|
|
.ShouldBe(DriverDataType.Int32);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(AbLegacyDataType.TimerElement, "DN", 13)]
|
|
[InlineData(AbLegacyDataType.TimerElement, "TT", 14)]
|
|
[InlineData(AbLegacyDataType.TimerElement, "EN", 15)]
|
|
[InlineData(AbLegacyDataType.CounterElement, "UN", 10)]
|
|
[InlineData(AbLegacyDataType.CounterElement, "OV", 11)]
|
|
[InlineData(AbLegacyDataType.CounterElement, "DN", 12)]
|
|
[InlineData(AbLegacyDataType.CounterElement, "CD", 13)]
|
|
[InlineData(AbLegacyDataType.CounterElement, "CU", 14)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "FD", 8)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "IN", 9)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "UL", 10)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "ER", 11)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "EM", 12)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "DN", 13)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "EU", 14)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "EN", 15)]
|
|
public void StatusBitIndex_maps_to_standard_pccc_positions(
|
|
AbLegacyDataType dataType, string subElement, int expectedBit)
|
|
{
|
|
AbLegacyDataTypeExtensions.StatusBitIndex(dataType, subElement).ShouldBe(expectedBit);
|
|
}
|
|
|
|
[Fact]
|
|
public void StatusBitIndex_for_word_subelements_is_null()
|
|
{
|
|
AbLegacyDataTypeExtensions.StatusBitIndex(AbLegacyDataType.TimerElement, "PRE").ShouldBeNull();
|
|
AbLegacyDataTypeExtensions.StatusBitIndex(AbLegacyDataType.CounterElement, "ACC").ShouldBeNull();
|
|
AbLegacyDataTypeExtensions.StatusBitIndex(AbLegacyDataType.ControlElement, "LEN").ShouldBeNull();
|
|
AbLegacyDataTypeExtensions.StatusBitIndex(AbLegacyDataType.TimerElement, null).ShouldBeNull();
|
|
AbLegacyDataTypeExtensions.StatusBitIndex(AbLegacyDataType.Int, "DN").ShouldBeNull();
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(AbLegacyDataType.TimerElement, "DN", true)]
|
|
[InlineData(AbLegacyDataType.TimerElement, "TT", true)]
|
|
[InlineData(AbLegacyDataType.TimerElement, "EN", false)] // operator-controllable
|
|
[InlineData(AbLegacyDataType.CounterElement, "DN", true)]
|
|
[InlineData(AbLegacyDataType.CounterElement, "OV", true)]
|
|
[InlineData(AbLegacyDataType.CounterElement, "UN", true)]
|
|
[InlineData(AbLegacyDataType.CounterElement, "CU", false)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "DN", true)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "ER", true)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "EM", true)]
|
|
[InlineData(AbLegacyDataType.ControlElement, "EN", false)]
|
|
public void IsPlcSetStatusBit_classifies_writable_vs_status_bits(
|
|
AbLegacyDataType dataType, string subElement, bool expected)
|
|
{
|
|
AbLegacyDataTypeExtensions.IsPlcSetStatusBit(dataType, subElement).ShouldBe(expected);
|
|
}
|
|
}
|