fix(abcip,adminui): ParseEnum device wording + libplctag probe assertion (#574); AB forms preserve unknown keys + MaxConcurrentReads (#576); probe off for new AbCip driver (#577); docs T4:0.DN (#584)

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
Joseph Doherty
2026-10-07 22:53:04 -04:00
parent 04b6563286
commit e588443ced
9 changed files with 251 additions and 15 deletions
@@ -84,7 +84,7 @@ public static class AbCipDriverFactoryExtensions
? [.. dto.Devices.Select(d => new AbCipDeviceOptions(
HostAddress: d.HostAddress ?? throw new InvalidOperationException(
$"AB CIP config for '{driverInstanceId}' has a device missing HostAddress"),
PlcFamily: ParseEnum<AbCipPlcFamily>(d.PlcFamily, "device", driverInstanceId, "PlcFamily",
PlcFamily: ParseEnum<AbCipPlcFamily>(d.PlcFamily, tagName: null, driverInstanceId, "PlcFamily",
fallback: AbCipPlcFamily.ControlLogix),
DeviceName: d.DeviceName,
AllowPacking: d.AllowPacking,
@@ -130,7 +130,8 @@ public static class AbCipDriverFactoryExtensions
private static TimeSpan PositiveTimeoutOrDefault(int? ms, int defaultMs) =>
TimeSpan.FromMilliseconds(ms is int v and > 0 ? v : defaultMs);
// tagName null = a driver-level field: the error text names the driver config, not a tag.
// tagName null = a driver-level (or device-level) config field: the error text names the driver config,
// not a tag. Never pass a placeholder like "device" as tagName — it renders as "tag 'device'" (#574).
private static T ParseEnum<T>(string? raw, string? tagName, string driverInstanceId, string field,
T? fallback = null) where T : struct, Enum
{