fix(driver-s7): resolve High code-review findings (Driver.S7-001, -006, -007, -011)
Driver.S7-001: Timer (T{n}) / Counter (C{n}) addresses parsed cleanly but
the read path had no S7DataType or decode case for them, so a Timer/Counter
tag passed fail-fast init and then threw a misleading type-mismatch on every
read. InitializeAsync now runs RejectUnsupportedTagAddresses, throwing a clear
NotSupportedException ("not yet supported", echoing tag name + address) so the
config error fails fast at init.
Driver.S7-006: ShutdownAsync cancelled the probe/poll CTSs but did not await
the fire-and-forget loop tasks before DisposeAsync disposed _gate, letting a
loop iteration mid-semaphore race a disposed object. The probe task is now
tracked in _probeTask and each poll task in SubscriptionState.PollTask;
ShutdownAsync cancels every CTS, awaits Task.WhenAll of those handles with a
bounded 5 s DrainTimeout, then disposes the CTSs and gate. Task.Run is passed
CancellationToken.None so the handle is always awaitable.
Driver.S7-007: a PUT/GET-disabled fault (permanent misconfiguration) was
mapped identically to a transient PlcException — both BadDeviceFailure +
Degraded. ReadAsync/WriteAsync now split the catch via an IsAccessDenied
filter (S7.Net exposes no typed code for AccessingObjectNotAllowed, so the
inner-exception chain is inspected for the "not allowed" marker). Access-denied
now maps to BadNotSupported and Faulted with a config-alert message pointing
at the TIA Portal PUT/GET toggle; genuine device faults stay BadDeviceFailure.
Driver.S7-011: S7Driver ignored driverConfigJson on Initialize/Reinitialize,
so a config change delivered through ReinitializeAsync (the only Core-initiated
in-process recovery path) was silently discarded. Config parsing was factored
into S7DriverFactoryExtensions.ParseOptions; InitializeAsync now re-parses
driverConfigJson and rebuilds _options whenever the document has a real body.
An empty / placeholder document keeps the constructor options.
Adds S7DriverCodeReviewFixTests covering Timer/Counter rejection, config-json
application on Initialize/Reinitialize, and shutdown-drain with active
subscriptions. All 68 S7 driver tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,19 @@ public static class S7DriverFactoryExtensions
|
||||
}
|
||||
|
||||
internal static S7Driver CreateInstance(string driverInstanceId, string driverConfigJson)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(driverInstanceId);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(driverConfigJson);
|
||||
return new S7Driver(ParseOptions(driverInstanceId, driverConfigJson), driverInstanceId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a driver-config JSON document into a strongly-typed <see cref="S7DriverOptions"/>.
|
||||
/// Shared by the factory (instance creation) and by <see cref="S7Driver.InitializeAsync"/>
|
||||
/// / <see cref="S7Driver.ReinitializeAsync"/> so a config change delivered through the
|
||||
/// <c>IDriver</c> contract is actually applied — see code-review finding Driver.S7-011.
|
||||
/// </summary>
|
||||
internal static S7DriverOptions ParseOptions(string driverInstanceId, string driverConfigJson)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(driverInstanceId);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(driverConfigJson);
|
||||
@@ -34,7 +47,7 @@ public static class S7DriverFactoryExtensions
|
||||
throw new InvalidOperationException(
|
||||
$"S7 driver config for '{driverInstanceId}' missing required Host");
|
||||
|
||||
var options = new S7DriverOptions
|
||||
return new S7DriverOptions
|
||||
{
|
||||
Host = dto.Host!,
|
||||
Port = dto.Port ?? 102,
|
||||
@@ -54,8 +67,6 @@ public static class S7DriverFactoryExtensions
|
||||
ProbeAddress = dto.Probe?.ProbeAddress ?? "MW0",
|
||||
},
|
||||
};
|
||||
|
||||
return new S7Driver(options, driverInstanceId);
|
||||
}
|
||||
|
||||
private static S7TagDefinition BuildTag(S7TagDto t, string driverInstanceId) =>
|
||||
|
||||
Reference in New Issue
Block a user