b351a81c8f
Capture the original ModbusTagDefinition as _source in ModbusTagRow and
rewrite ToDefinition() to use 'with {}', so StringByteOrder, ArrayCount,
Deadband, UnitId, and CoalesceProhibited survive a load→edit→save cycle.
174 lines
6.3 KiB
C#
174 lines
6.3 KiB
C#
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Pages.Clusters.Drivers;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.Modbus;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests;
|
|
|
|
public sealed class ModbusDriverPageFormSerializationTests
|
|
{
|
|
private static readonly JsonSerializerOptions _opts = new()
|
|
{
|
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
WriteIndented = false,
|
|
};
|
|
|
|
private static readonly JsonSerializerOptions TestJsonOpts = new()
|
|
{
|
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip,
|
|
};
|
|
|
|
[Fact]
|
|
public void RoundTrip_PreservesKnownFields()
|
|
{
|
|
var original = new ModbusDriverOptions
|
|
{
|
|
Host = "10.0.0.42",
|
|
Port = 5020,
|
|
UnitId = 3,
|
|
Timeout = TimeSpan.FromSeconds(5),
|
|
MaxRegistersPerRead = 64,
|
|
MaxRegistersPerWrite = 60,
|
|
MaxCoilsPerRead = 500,
|
|
MaxReadGap = 8,
|
|
UseFC15ForSingleCoilWrites = true,
|
|
UseFC16ForSingleRegisterWrites = true,
|
|
DisableFC23 = true,
|
|
WriteOnChangeOnly = true,
|
|
AutoReconnect = false,
|
|
Family = ModbusFamily.DL205,
|
|
MelsecSubFamily = MelsecFamily.F_iQF,
|
|
Probe = new ModbusProbeOptions
|
|
{
|
|
Enabled = false,
|
|
Interval = TimeSpan.FromSeconds(10),
|
|
Timeout = TimeSpan.FromSeconds(3),
|
|
ProbeAddress = 7,
|
|
},
|
|
KeepAlive = new ModbusKeepAliveOptions
|
|
{
|
|
Enabled = false,
|
|
Time = TimeSpan.FromSeconds(60),
|
|
Interval = TimeSpan.FromSeconds(15),
|
|
RetryCount = 5,
|
|
},
|
|
Reconnect = new ModbusReconnectOptions
|
|
{
|
|
InitialDelay = TimeSpan.FromSeconds(1),
|
|
MaxDelay = TimeSpan.FromSeconds(60),
|
|
BackoffMultiplier = 1.5,
|
|
},
|
|
ProbeTimeoutSeconds = 10,
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(original, _opts);
|
|
var back = JsonSerializer.Deserialize<ModbusDriverOptions>(json, _opts);
|
|
|
|
back.ShouldNotBeNull();
|
|
back.Host.ShouldBe("10.0.0.42");
|
|
back.Port.ShouldBe(5020);
|
|
back.UnitId.ShouldBe((byte)3);
|
|
back.Timeout.ShouldBe(TimeSpan.FromSeconds(5));
|
|
back.MaxRegistersPerRead.ShouldBe((ushort)64);
|
|
back.MaxRegistersPerWrite.ShouldBe((ushort)60);
|
|
back.MaxCoilsPerRead.ShouldBe((ushort)500);
|
|
back.MaxReadGap.ShouldBe((ushort)8);
|
|
back.UseFC15ForSingleCoilWrites.ShouldBeTrue();
|
|
back.UseFC16ForSingleRegisterWrites.ShouldBeTrue();
|
|
back.DisableFC23.ShouldBeTrue();
|
|
back.WriteOnChangeOnly.ShouldBeTrue();
|
|
back.AutoReconnect.ShouldBeFalse();
|
|
back.Family.ShouldBe(ModbusFamily.DL205);
|
|
back.MelsecSubFamily.ShouldBe(MelsecFamily.F_iQF);
|
|
back.Probe.Enabled.ShouldBeFalse();
|
|
back.Probe.Interval.ShouldBe(TimeSpan.FromSeconds(10));
|
|
back.Probe.Timeout.ShouldBe(TimeSpan.FromSeconds(3));
|
|
back.Probe.ProbeAddress.ShouldBe((ushort)7);
|
|
back.KeepAlive.Enabled.ShouldBeFalse();
|
|
back.KeepAlive.Time.ShouldBe(TimeSpan.FromSeconds(60));
|
|
back.KeepAlive.Interval.ShouldBe(TimeSpan.FromSeconds(15));
|
|
back.KeepAlive.RetryCount.ShouldBe(5);
|
|
back.Reconnect.InitialDelay.ShouldBe(TimeSpan.FromSeconds(1));
|
|
back.Reconnect.MaxDelay.ShouldBe(TimeSpan.FromSeconds(60));
|
|
back.Reconnect.BackoffMultiplier.ShouldBe(1.5);
|
|
back.ProbeTimeoutSeconds.ShouldBe(10);
|
|
}
|
|
|
|
[Fact]
|
|
public void Deserialize_DropsUnknownFields()
|
|
{
|
|
var jsonWithExtra = """{"unknownField":"old-value","probeTimeoutSeconds":10}""";
|
|
|
|
var optsWithSkip = new JsonSerializerOptions(_opts)
|
|
{
|
|
UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip,
|
|
};
|
|
|
|
var back = JsonSerializer.Deserialize<ModbusDriverOptions>(jsonWithExtra, optsWithSkip);
|
|
back.ShouldNotBeNull();
|
|
back.ProbeTimeoutSeconds.ShouldBe(10);
|
|
}
|
|
|
|
[Fact]
|
|
public void TagRow_round_trips_through_definition()
|
|
{
|
|
var row = new ModbusDriverPage.ModbusTagRow
|
|
{
|
|
Name = "Pump1_Speed", Region = ModbusRegion.HoldingRegisters, Address = 40001,
|
|
DataType = ModbusDataType.Int16, Writable = true,
|
|
};
|
|
var def = row.ToDefinition();
|
|
var back = ModbusDriverPage.ModbusTagRow.FromDefinition(def);
|
|
|
|
back.Name.ShouldBe("Pump1_Speed");
|
|
back.Address.ShouldBe(40001);
|
|
back.DataType.ShouldBe(ModbusDataType.Int16);
|
|
back.Writable.ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void Tag_list_survives_options_serialize_round_trip()
|
|
{
|
|
var tags = new List<ModbusTagDefinition>
|
|
{
|
|
new("A", ModbusRegion.HoldingRegisters, 1, ModbusDataType.Int16),
|
|
new("B", ModbusRegion.Coils, 2, ModbusDataType.Bool),
|
|
};
|
|
var opts = new ModbusDriverPage.FormModel().ToOptions(tags);
|
|
var json = JsonSerializer.Serialize(opts, TestJsonOpts);
|
|
var back = JsonSerializer.Deserialize<ModbusDriverOptions>(json, TestJsonOpts)!;
|
|
back.Tags.Count.ShouldBe(2);
|
|
back.Tags[0].Name.ShouldBe("A");
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidateRow_rejects_duplicate_name()
|
|
{
|
|
var rows = new List<ModbusDriverPage.ModbusTagRow> { new() { Name = "A" } };
|
|
ModbusDriverPage.ModbusTagRow.ValidateRow(new() { Name = "A" }, rows, null)
|
|
.ShouldNotBeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void ToDefinition_preserves_unedited_fields()
|
|
{
|
|
var original = new ModbusTagDefinition(
|
|
"T", ModbusRegion.HoldingRegisters, 5, ModbusDataType.Int16,
|
|
StringByteOrder: ModbusStringByteOrder.LowByteFirst,
|
|
ArrayCount: 10, Deadband: 0.5, UnitId: 3, CoalesceProhibited: true);
|
|
var row = ModbusDriverPage.ModbusTagRow.FromDefinition(original);
|
|
row.Name = "Renamed";
|
|
|
|
var back = row.ToDefinition();
|
|
back.Name.ShouldBe("Renamed");
|
|
back.UnitId.ShouldBe((byte)3);
|
|
back.ArrayCount.ShouldBe(10);
|
|
back.Deadband.ShouldBe(0.5);
|
|
back.StringByteOrder.ShouldBe(ModbusStringByteOrder.LowByteFirst);
|
|
back.CoalesceProhibited.ShouldBeTrue();
|
|
}
|
|
}
|