fix(driver-twincat): resolve High code-review findings (Driver.TwinCAT-001, -002, -007, -008, -013)
Driver.TwinCAT-001 — InitializeAsync/ReinitializeAsync ignored driverConfigJson. Extracted the DTO-to-options parse into a shared TwinCATDriverFactoryExtensions.ParseOptions; InitializeAsync now re-parses driverConfigJson into a mutable _options field, so a config generation pushed via ReinitializeAsync (added/removed devices, tags, probe settings) is actually applied at runtime. Driver.TwinCAT-002 — LInt/ULInt narrowed to Int32. ToDriverDataType now maps LInt to Int64, ULInt to UInt64, UDInt to UInt32, UInt/USInt to UInt16, Int/SInt to Int16, and the IEC TIME/DATE/DT/TOD types to UInt32 (their raw UDINT counter). Removed the stale "Int64 gap" comment — no truncation or sign flips at the OPC UA encode layer. Driver.TwinCAT-007 — EnsureConnectedAsync was not thread-safe. Connect/reconnect is now serialized per device by a SemaphoreSlim (DeviceState.ConnectGate) with a double-checked connect, mirroring the S7 driver. Concurrent read/write/probe callers can no longer leak a client or race a create-vs-dispose. Driver.TwinCAT-008 — native ADS notification callbacks ran driver logic on the AMS router thread. AdsTwinCATClient now enqueues AdsNotificationEx callbacks onto a bounded Channel drained by a dedicated managed task; the router-thread callback only does a non-blocking TryWrite, so a slow consumer cannot stall ADS notification delivery process-wide. Driver.TwinCAT-013 — TwinCATDriver did not implement IRediscoverable. The driver now implements IRediscoverable; AdsTwinCATClient detects ADS 0x0702 (symbol-version-changed) on read/write paths and raises OnSymbolVersionChanged, which the driver forwards as OnRediscoveryNeeded so Core rebuilds the address space after a PLC program re-download. Adds TwinCATHighFindingsRegressionTests covering all five fixes; updates the data-type mapping assertion in TwinCATDriverTests. TwinCAT driver builds clean; 119 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,13 +22,25 @@ public static class TwinCATDriverFactoryExtensions
|
||||
internal static TwinCATDriver CreateInstance(string driverInstanceId, string driverConfigJson)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(driverInstanceId);
|
||||
var options = ParseOptions(driverConfigJson, driverInstanceId);
|
||||
return new TwinCATDriver(options, driverInstanceId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a TwinCAT driver-config JSON document into a <see cref="TwinCATDriverOptions"/>.
|
||||
/// Shared by <see cref="CreateInstance"/> (constructor-time) and
|
||||
/// <see cref="TwinCATDriver.InitializeAsync"/> / <see cref="TwinCATDriver.ReinitializeAsync"/>
|
||||
/// so a config generation pushed via Reinitialize is actually applied (Driver.TwinCAT-001).
|
||||
/// </summary>
|
||||
internal static TwinCATDriverOptions ParseOptions(string driverConfigJson, string driverInstanceId)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(driverConfigJson);
|
||||
|
||||
var dto = JsonSerializer.Deserialize<TwinCATDriverConfigDto>(driverConfigJson, JsonOptions)
|
||||
?? throw new InvalidOperationException(
|
||||
$"TwinCAT driver config for '{driverInstanceId}' deserialised to null");
|
||||
|
||||
var options = new TwinCATDriverOptions
|
||||
return new TwinCATDriverOptions
|
||||
{
|
||||
Devices = dto.Devices is { Count: > 0 }
|
||||
? [.. dto.Devices.Select(d => new TwinCATDeviceOptions(
|
||||
@@ -49,8 +61,6 @@ public static class TwinCATDriverFactoryExtensions
|
||||
UseNativeNotifications = dto.UseNativeNotifications ?? true,
|
||||
EnableControllerBrowse = dto.EnableControllerBrowse ?? false,
|
||||
};
|
||||
|
||||
return new TwinCATDriver(options, driverInstanceId);
|
||||
}
|
||||
|
||||
private static TwinCATTagDefinition BuildTag(TwinCATTagDto t, string driverInstanceId) =>
|
||||
|
||||
Reference in New Issue
Block a user