feat(adminui): Transport dropdown on AbCip/AbLegacy driver forms (live-verified on docker-dev)
- AbCip/AbLegacy driver forms gain a Transport InputSelect; FromOptions/ToOptions now carry Transport, so saving any other field no longer silently reverts Native to libplctag. - Both drivers log the constructed transport once per init. - AbCip factory now takes the host ILoggerFactory (it ran on NullLogger in production, hiding every AbCip log line); DriverFactoryBootstrap passes it. - Gate results: AdminUI /run (T19) native, inverted (dead port) and libplctag legs.
This commit is contained in:
@@ -100,4 +100,58 @@ public sealed class AbCipDriverPageFormSerializationTests
|
||||
back.Devices[0].PlcFamily.ShouldBe(AbCipPlcFamily.ControlLogix);
|
||||
back.Devices[1].PlcFamily.ShouldBe(AbCipPlcFamily.CompactLogix);
|
||||
}
|
||||
|
||||
// ---- Task 19: Transport (native AB transport switch) ----
|
||||
// These drive the form's OWN load/save path (TryDeserialize -> FromOptions -> ToOptions -> Serialize),
|
||||
// which uses the production JsonSerializerOptions, so a numeric enum or a dropped field shows up here.
|
||||
|
||||
[Theory]
|
||||
[InlineData(AbCipTransport.Libplctag)]
|
||||
[InlineData(AbCipTransport.Native)]
|
||||
public void Transport_survives_FromOptions_ToOptions(AbCipTransport transport)
|
||||
{
|
||||
var original = new AbCipDriverOptions { Transport = transport };
|
||||
var back = AbCipDriverForm.FormModel.FromOptions(original).ToOptions([]);
|
||||
back.Transport.ShouldBe(transport);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Native_transport_serializes_as_a_string()
|
||||
{
|
||||
var form = new AbCipDriverForm.FormModel { Transport = AbCipTransport.Native };
|
||||
var json = AbCipDriverForm.Serialize(form.ToOptions([]));
|
||||
json.ShouldContain("\"transport\":\"Native\"");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Editing_an_unrelated_field_preserves_a_saved_Native_transport()
|
||||
{
|
||||
// The regression the review found: FromOptions/ToOptions dropped Transport, so an operator who set
|
||||
// Native was silently reverted to libplctag the next time they saved ANY other edit.
|
||||
const string saved = """{"transport":"Native","timeout":"00:00:02","devices":[]}""";
|
||||
var loaded = AbCipDriverForm.TryDeserialize(saved);
|
||||
loaded.ShouldNotBeNull();
|
||||
loaded.Transport.ShouldBe(AbCipTransport.Native);
|
||||
|
||||
var form = AbCipDriverForm.FormModel.FromOptions(loaded);
|
||||
form.TimeoutSeconds = 7;
|
||||
var json = AbCipDriverForm.Serialize(form.ToOptions(loaded.Devices));
|
||||
|
||||
json.ShouldContain("\"transport\":\"Native\"");
|
||||
json.ShouldContain("\"timeout\":\"00:00:07\"");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_config_without_transport_loads_as_Libplctag_and_re_emits_it_explicitly()
|
||||
{
|
||||
// Chosen behaviour: an absent key round-trips as an explicit "Libplctag" (the form always writes the
|
||||
// field). The dropdown therefore shows "libplctag (default)".
|
||||
var loaded = AbCipDriverForm.TryDeserialize("""{"probeTimeoutSeconds":5}""");
|
||||
loaded.ShouldNotBeNull();
|
||||
var form = AbCipDriverForm.FormModel.FromOptions(loaded);
|
||||
form.Transport.ShouldBe(AbCipTransport.Libplctag);
|
||||
|
||||
var json = AbCipDriverForm.Serialize(form.ToOptions(loaded.Devices));
|
||||
json.ShouldContain("\"transport\":\"Libplctag\"");
|
||||
}
|
||||
}
|
||||
|
||||
+54
@@ -96,4 +96,58 @@ public sealed class AbLegacyDriverPageFormSerializationTests
|
||||
back.Devices[0].PlcFamily.ShouldBe(AbLegacyPlcFamily.Slc500);
|
||||
back.Devices[1].PlcFamily.ShouldBe(AbLegacyPlcFamily.MicroLogix);
|
||||
}
|
||||
|
||||
// ---- Task 19: Transport (native AB transport switch) ----
|
||||
// These drive the form's OWN load/save path (TryDeserialize -> FromOptions -> ToOptions -> Serialize),
|
||||
// which uses the production JsonSerializerOptions, so a numeric enum or a dropped field shows up here.
|
||||
|
||||
[Theory]
|
||||
[InlineData(AbLegacyTransport.Libplctag)]
|
||||
[InlineData(AbLegacyTransport.Native)]
|
||||
public void Transport_survives_FromOptions_ToOptions(AbLegacyTransport transport)
|
||||
{
|
||||
var original = new AbLegacyDriverOptions { Transport = transport };
|
||||
var back = AbLegacyDriverForm.FormModel.FromOptions(original).ToOptions([]);
|
||||
back.Transport.ShouldBe(transport);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Native_transport_serializes_as_a_string()
|
||||
{
|
||||
var form = new AbLegacyDriverForm.FormModel { Transport = AbLegacyTransport.Native };
|
||||
var json = AbLegacyDriverForm.Serialize(form.ToOptions([]));
|
||||
json.ShouldContain("\"transport\":\"Native\"");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Editing_an_unrelated_field_preserves_a_saved_Native_transport()
|
||||
{
|
||||
// The regression the review found: FromOptions/ToOptions dropped Transport, so an operator who set
|
||||
// Native was silently reverted to libplctag the next time they saved ANY other edit.
|
||||
const string saved = """{"transport":"Native","timeout":"00:00:02","devices":[]}""";
|
||||
var loaded = AbLegacyDriverForm.TryDeserialize(saved);
|
||||
loaded.ShouldNotBeNull();
|
||||
loaded.Transport.ShouldBe(AbLegacyTransport.Native);
|
||||
|
||||
var form = AbLegacyDriverForm.FormModel.FromOptions(loaded);
|
||||
form.TimeoutSeconds = 7;
|
||||
var json = AbLegacyDriverForm.Serialize(form.ToOptions(loaded.Devices));
|
||||
|
||||
json.ShouldContain("\"transport\":\"Native\"");
|
||||
json.ShouldContain("\"timeout\":\"00:00:07\"");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_config_without_transport_loads_as_Libplctag_and_re_emits_it_explicitly()
|
||||
{
|
||||
// Chosen behaviour: an absent key round-trips as an explicit "Libplctag" (the form always writes the
|
||||
// field). The dropdown therefore shows "libplctag (default)".
|
||||
var loaded = AbLegacyDriverForm.TryDeserialize("""{"probeTimeoutSeconds":5}""");
|
||||
loaded.ShouldNotBeNull();
|
||||
var form = AbLegacyDriverForm.FormModel.FromOptions(loaded);
|
||||
form.Transport.ShouldBe(AbLegacyTransport.Libplctag);
|
||||
|
||||
var json = AbLegacyDriverForm.Serialize(form.ToOptions(loaded.Devices));
|
||||
json.ShouldContain("\"transport\":\"Libplctag\"");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user