feat(ab): native managed transport is the default for AbCip and AbLegacy; libplctag remains as fallback
Task 22 (spec section 8 P5), with the controller rulings that exclude PLC-5 and LogixPccc. - Transport is nullable on both option records; null (key absent) means "use the default" and is never serialized. Factory DTO parse: absent -> null; a bad value still fails via ParseEnum. - One resolver per driver (AbCipTransportResolver / AbLegacyTransportResolver), used by the driver constructor, the Test Connect probe and (through the driver) the CLI. AbCip: null -> Native. AbLegacy: null -> Native unless any device is Plc5 or LogixPccc, then the whole driver resolves to Libplctag. Explicit values always win; explicit Native with a Plc5/LogixPccc device logs one Warning at init. - Init log shows configured vs effective, e.g. transport=Native (default). - AdminUI forms: "Default (...)" / "Native (managed)" / "libplctag (fallback)". Default omits the key on save (reverses Task 19's explicit re-emit, which would have pinned every edited driver). - CLIs: --transport default|native|libplctag, default "default" via the resolver, so --plc-type Plc5 with no --transport runs libplctag. - Tests pinned to Libplctag where a default-path runtime was created: found by instrumenting the native factories with the current xUnit test name (one unit test, AbLegacyReinitConfigAdoption; plus the two fixture-gated AbCip probe E2E tests whose exact messages were proven on libplctag). TransportMatrix and the differential harness already pass explicit transports. - Switch/resolver/CLI/form tests are falsified by mutating the resolver (default -> Libplctag, exclusion removed, AbLegacy default -> Libplctag): each mutation turns them red. - Replaces the inline role-name literal in AbCipDriverFactoryExtensions' ParseEnum call with a named constant; RoleAuthorityGuardTests was already red on the base commit for that line. - Docs: AbCip.md / AbLegacy.md default + release note; CLAUDE.md AB section. Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.Cli;
|
||||
|
||||
/// <summary>
|
||||
/// The CLI's <c>--transport</c> choice. <see cref="Default"/> leaves
|
||||
/// <see cref="AbCipDriverOptions.Transport"/> null so the driver resolves it through
|
||||
/// <see cref="AbCipTransportResolver"/> exactly as an absent <c>transport</c> key in a deployed config does.
|
||||
/// </summary>
|
||||
public enum AbCipCliTransport
|
||||
{
|
||||
/// <summary>Use the driver's default (resolved by <see cref="AbCipTransportResolver"/>).</summary>
|
||||
Default,
|
||||
|
||||
/// <summary>The managed native transport.</summary>
|
||||
Native,
|
||||
|
||||
/// <summary>The libplctag fallback transport.</summary>
|
||||
Libplctag,
|
||||
}
|
||||
|
||||
/// <summary>Maps a <see cref="AbCipCliTransport"/> onto the driver option.</summary>
|
||||
public static class AbCipCliTransportExtensions
|
||||
{
|
||||
/// <summary>Returns the configured transport: null for <see cref="AbCipCliTransport.Default"/>.</summary>
|
||||
/// <param name="choice">The CLI choice.</param>
|
||||
/// <returns>The value for <see cref="AbCipDriverOptions.Transport"/>.</returns>
|
||||
public static AbCipTransport? ToConfigured(this AbCipCliTransport choice) => choice switch
|
||||
{
|
||||
AbCipCliTransport.Native => AbCipTransport.Native,
|
||||
AbCipCliTransport.Libplctag => AbCipTransport.Libplctag,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
@@ -28,8 +28,8 @@ public abstract class AbCipCommandBase : DriverCommandBase
|
||||
|
||||
/// <summary>Gets the transport stack used to reach the PLC.</summary>
|
||||
[CommandOption("transport", Description =
|
||||
"Transport stack: libplctag (default) or native (managed EtherNet/IP + CIP).")]
|
||||
public AbCipTransport Transport { get; init; } = AbCipTransport.Libplctag;
|
||||
"Transport stack: default (= native), native (managed EtherNet/IP + CIP) or libplctag (fallback).")]
|
||||
public AbCipCliTransport Transport { get; init; } = AbCipCliTransport.Default;
|
||||
|
||||
/// <summary>Gets the per-operation timeout in milliseconds.</summary>
|
||||
[CommandOption("timeout-ms", Description = "Per-operation timeout in ms (default 5000).")]
|
||||
@@ -73,7 +73,7 @@ public abstract class AbCipCommandBase : DriverCommandBase
|
||||
WriteIdempotent: t.WriteIdempotent,
|
||||
DeviceName: t.DeviceHostAddress))],
|
||||
Timeout = Timeout,
|
||||
Transport = Transport,
|
||||
Transport = Transport.ToConfigured(),
|
||||
Probe = new AbCipProbeOptions { Enabled = false },
|
||||
EnableControllerBrowse = false,
|
||||
EnableAlarmProjection = false,
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Cli;
|
||||
|
||||
/// <summary>
|
||||
/// The CLI's <c>--transport</c> choice. <see cref="Default"/> leaves
|
||||
/// <see cref="AbLegacyDriverOptions.Transport"/> null so the driver resolves it through
|
||||
/// <see cref="AbLegacyTransportResolver"/> exactly as an absent <c>transport</c> key in a deployed config does.
|
||||
/// </summary>
|
||||
public enum AbLegacyCliTransport
|
||||
{
|
||||
/// <summary>Use the driver's default (resolved by <see cref="AbLegacyTransportResolver"/>).</summary>
|
||||
Default,
|
||||
|
||||
/// <summary>The managed native transport.</summary>
|
||||
Native,
|
||||
|
||||
/// <summary>The libplctag fallback transport.</summary>
|
||||
Libplctag,
|
||||
}
|
||||
|
||||
/// <summary>Maps a <see cref="AbLegacyCliTransport"/> onto the driver option.</summary>
|
||||
public static class AbLegacyCliTransportExtensions
|
||||
{
|
||||
/// <summary>Returns the configured transport: null for <see cref="AbLegacyCliTransport.Default"/>.</summary>
|
||||
/// <param name="choice">The CLI choice.</param>
|
||||
/// <returns>The value for <see cref="AbLegacyDriverOptions.Transport"/>.</returns>
|
||||
public static AbLegacyTransport? ToConfigured(this AbLegacyCliTransport choice) => choice switch
|
||||
{
|
||||
AbLegacyCliTransport.Native => AbLegacyTransport.Native,
|
||||
AbLegacyCliTransport.Libplctag => AbLegacyTransport.Libplctag,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
@@ -27,9 +27,9 @@ public abstract class AbLegacyCommandBase : DriverCommandBase
|
||||
|
||||
/// <summary>Gets the transport stack used to reach the PLC.</summary>
|
||||
[CommandOption("transport", Description =
|
||||
"Transport stack: libplctag (default) or native (managed EtherNet/IP + PCCC; PLC-5 N-file reads only, " +
|
||||
"no status-bit writes, LogixPccc unverified).")]
|
||||
public AbLegacyTransport Transport { get; init; } = AbLegacyTransport.Libplctag;
|
||||
"Transport stack: default, native or libplctag. default = native, except libplctag for --plc-type " +
|
||||
"Plc5 / LogixPccc (native there is PLC-5 N-file reads only, no status-bit writes, LogixPccc unverified).")]
|
||||
public AbLegacyCliTransport Transport { get; init; } = AbLegacyCliTransport.Default;
|
||||
|
||||
/// <summary>Gets the per-operation timeout in milliseconds.</summary>
|
||||
[CommandOption("timeout-ms", Description = "Per-operation timeout in ms (default 5000).")]
|
||||
@@ -62,7 +62,7 @@ public abstract class AbLegacyCommandBase : DriverCommandBase
|
||||
RawTags = [.. tags.Select(t => new RawTagEntry(
|
||||
t.Name, AbLegacyTagDefinitionFactory.ToTagConfig(t), t.WriteIdempotent, DeviceName: t.DeviceHostAddress))],
|
||||
Timeout = Timeout,
|
||||
Transport = Transport,
|
||||
Transport = Transport.ToConfigured(),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user