diff --git a/code-reviews/Driver.TwinCAT/findings.md b/code-reviews/Driver.TwinCAT/findings.md index 3d4beee..0402477 100644 --- a/code-reviews/Driver.TwinCAT/findings.md +++ b/code-reviews/Driver.TwinCAT/findings.md @@ -298,7 +298,7 @@ symbol-version-changed is never routed to rediscovery (see Driver.TwinCAT-013). explicit case for symbol-version-changed routed to rediscovery, and for PLC-in-Config mapped to `BadOutOfService`/`BadInvalidState`. -**Resolution:** _(open)_ +**Resolution:** Resolved 2026-05-22 — confirmed all codes from `Beckhoff.TwinCAT.Ads` 7.0.172 `AdsErrorCode` enum. Rewrote `MapAdsError` with 20 explicit cases keyed to the correct decimal values. Fixed the critical bug: `AdsSymbolVersionChanged` was `0x0702u` (= `DeviceInvalidGroup`) but the actual `DeviceSymbolVersionInvalid` is 1809 (0x0711); corrected constant and updated all comments. Added `BadOutOfService` for `DeviceNotReady` (PLC not running) and `BadInvalidState` for `DeviceInvalidState` (PLC in Config mode, 0x0712) and `DeviceSymbolVersionInvalid` (0x0711). Added `BadOutOfService`/`BadInvalidState` OPC UA StatusCode constants to the mapper. ### Driver.TwinCAT-012 diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/AdsTwinCATClient.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/AdsTwinCATClient.cs index af86e4d..6bec502 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/AdsTwinCATClient.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/AdsTwinCATClient.cs @@ -58,7 +58,7 @@ internal sealed class AdsTwinCATClient : ITwinCATClient public event EventHandler? OnSymbolVersionChanged; - /// Raise when is 0x0702. + /// Raise when is DeviceSymbolVersionInvalid (1809 / 0x0711). private uint MapAndSignal(uint adsError) { if (TwinCATStatusMapper.IsSymbolVersionChanged(adsError)) diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/ITwinCATClient.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/ITwinCATClient.cs index 39b2f78..29339b7 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/ITwinCATClient.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/ITwinCATClient.cs @@ -21,11 +21,11 @@ public interface ITwinCATClient : IDisposable /// /// Raised when the client observes the ADS symbol-version-changed code - /// () on any read / write / - /// notification — the signal that a PLC program re-download has invalidated every - /// symbol + notification handle. The driver forwards this to - /// so Core rebuilds - /// the address space subtree (docs/v2/driver-specs.md §6, Driver.TwinCAT-013). + /// (DeviceSymbolVersionInvalid + /// 1809 / 0x0711) on any read / write / notification — the signal that a PLC program + /// re-download has invalidated every symbol + notification handle. The driver forwards + /// this to so Core + /// rebuilds the address space subtree (docs/v2/driver-specs.md §6, Driver.TwinCAT-013). /// event EventHandler? OnSymbolVersionChanged; diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriver.cs index 2236bfc..af0400f 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriver.cs @@ -516,14 +516,16 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery } /// - /// Routes a wire-detected ADS symbol-version-changed (0x0702) to Core as an - /// invocation (Driver.TwinCAT-013). A PLC re-download - /// invalidates every symbol + notification handle, so the address space must be rebuilt - /// — this is the documented TwinCAT failure mode, not a transient connection error. + /// Routes a wire-detected ADS symbol-version-changed (DeviceSymbolVersionInvalid 1809 / + /// 0x0711) to Core as an invocation (Driver.TwinCAT-013). + /// A PLC re-download invalidates every symbol + notification handle, so the address + /// space must be rebuilt — this is the documented TwinCAT failure mode, not a transient + /// connection error. /// private void HandleSymbolVersionChanged(object? sender, EventArgs e) => OnRediscoveryNeeded?.Invoke(this, new RediscoveryEventArgs( - "TwinCAT symbol-version-changed 0x0702 — PLC program re-downloaded", ScopeHint: "TwinCAT")); + "TwinCAT symbol-version-changed (DeviceSymbolVersionInvalid 0x0711) — PLC program re-downloaded", + ScopeHint: "TwinCAT")); public void Dispose() => DisposeAsync().AsTask().GetAwaiter().GetResult(); public async ValueTask DisposeAsync() => await ShutdownAsync(CancellationToken.None).ConfigureAwait(false); diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATStatusMapper.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATStatusMapper.cs index 78a9b5a..22c4749 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATStatusMapper.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATStatusMapper.cs @@ -1,56 +1,82 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT; /// -/// Maps AMS / ADS error codes to OPC UA StatusCodes. ADS error codes are defined in -/// AdsErrorCode from Beckhoff.TwinCAT.Ads — this mapper covers the ones a -/// driver actually encounters during normal operation (symbol-not-found, access-denied, -/// timeout, router-not-initialized, invalid-group/offset, etc.). +/// Maps AMS / ADS error codes to OPC UA StatusCodes. ADS error codes are taken from the +/// AdsErrorCode enum in Beckhoff.TwinCAT.Ads 7.x — numeric values are the +/// authoritative source (not the hex shorthand in older Beckhoff InfoSys pages, which has +/// known transcription errors). Key device-layer codes start at 0x0700 (1792 decimal). /// public static class TwinCATStatusMapper { public const uint Good = 0u; + // ---- OPC UA StatusCode constants ---- + public const uint BadInternalError = 0x80020000u; + public const uint BadNodeIdUnknown = 0x80340000u; + public const uint BadNotWritable = 0x803B0000u; + public const uint BadOutOfRange = 0x803C0000u; + public const uint BadNotSupported = 0x803D0000u; + public const uint BadDeviceFailure = 0x80550000u; + public const uint BadCommunicationError = 0x80050000u; + public const uint BadTimeout = 0x800A0000u; + public const uint BadTypeMismatch = 0x80730000u; + public const uint BadOutOfService = 0x80BE0000u; + public const uint BadInvalidState = 0x80350000u; + + // ---- AdsErrorCode numeric values (confirmed from Beckhoff.TwinCAT.Ads 7.0.172) ---- + /// - /// ADS ADSERR_DEVICE_SYMBOLVERSIONINVALID — error 0x0702 (1794 decimal). + /// ADS DeviceSymbolVersionInvalid — error code 1809 (0x0711 decimal). /// Raised by the runtime after a PLC program re-download: every symbol handle and /// notification handle the driver holds is now stale. The driver treats this as an /// trigger, not a connection error /// (docs/v2/driver-specs.md §6, Driver.TwinCAT-013). + /// + /// Note: legacy Beckhoff InfoSys documentation sometimes cites this as "0x0702"; that + /// is a transcription error — 0x0702 is DeviceInvalidGroup (1794). The SDK enum + /// value 1809 (0x0711) is authoritative (Driver.TwinCAT-011). + /// /// - public const uint AdsSymbolVersionChanged = 0x0702u; + public const uint AdsSymbolVersionChanged = 1809u; // DeviceSymbolVersionInvalid = 0x0711 /// True when is the symbol-version-changed code. public static bool IsSymbolVersionChanged(uint adsError) => adsError == AdsSymbolVersionChanged; - public const uint BadInternalError = 0x80020000u; - public const uint BadNodeIdUnknown = 0x80340000u; - public const uint BadNotWritable = 0x803B0000u; - public const uint BadOutOfRange = 0x803C0000u; - public const uint BadNotSupported = 0x803D0000u; - public const uint BadDeviceFailure = 0x80550000u; - public const uint BadCommunicationError = 0x80050000u; - public const uint BadTimeout = 0x800A0000u; - public const uint BadTypeMismatch = 0x80730000u; - /// - /// Map an AMS / ADS error code (uint from AdsErrorCode enum). 0 = success; non-zero - /// codes follow Beckhoff's AMS error table (7 = target port not found, 1792 = - /// ADSERR_DEVICE_SRVNOTSUPP, 1793 = ADSERR_DEVICE_INVALIDGRP, 1794 = - /// ADSERR_DEVICE_INVALIDOFFSET, 1798 = ADSERR_DEVICE_SYMBOLNOTFOUND, 1808 = - /// ADSERR_DEVICE_ACCESSDENIED, 1861 = ADSERR_CLIENT_SYNCTIMEOUT). + /// Map an AMS / ADS error code (uint cast from AdsErrorCode enum) to an OPC UA + /// StatusCode. 0 = success. Device-layer codes (0x0700–0x073F) cover the operations a + /// driver actually encounters during normal runtime. /// public static uint MapAdsError(uint adsError) => adsError switch { - 0 => Good, - 6 or 7 => BadCommunicationError, // target port unreachable - 1792 => BadNotSupported, // service not supported - 1793 => BadOutOfRange, // invalid index group - 1794 => BadOutOfRange, // invalid index offset - 1798 => BadNodeIdUnknown, // symbol not found - 1807 => BadDeviceFailure, // device in invalid state - 1808 => BadNotWritable, // access denied - 1811 or 1812 => BadOutOfRange, // size mismatch - 1861 => BadTimeout, // sync timeout - _ => BadCommunicationError, + 0 => Good, + + // AMS router / transport errors + 6 or 7 => BadCommunicationError, // TargetPortNotFound / TargetMachineNotFound + 1285 or 1290 => BadCommunicationError, // RouterNotInitialized / RouterNotActive + + // Device-layer codes (Beckhoff.TwinCAT.Ads AdsErrorCode enum, confirmed 7.0.172) + 1792 => BadDeviceFailure, // DeviceError (generic device error) + 1793 => BadNotSupported, // DeviceServiceNotSupported + 1794 => BadOutOfRange, // DeviceInvalidGroup (ADS index-group error) + 1795 => BadOutOfRange, // DeviceInvalidOffset (ADS index-offset error) + 1796 => BadNotWritable, // DeviceInvalidAccess (write-access denied) + 1797 => BadOutOfRange, // DeviceInvalidSize (size mismatch) + 1798 => BadTypeMismatch, // DeviceInvalidData (data format mismatch) + 1799 => BadOutOfService, // DeviceNotReady (PLC not running / in config) + 1804 => BadNodeIdUnknown, // DeviceNotFound + 1807 => BadDeviceFailure, // DeviceIncompatible (0x070E) + 1808 => BadNodeIdUnknown, // DeviceSymbolNotFound (0x0710) + 1809 => BadInvalidState, // DeviceSymbolVersionInvalid — rediscovery trigger + 1810 => BadInvalidState, // DeviceInvalidState (PLC in Config mode, 0x0712) + 1811 => BadNotSupported, // DeviceTransModeNotSupported (0x0713) + 1812 => BadNodeIdUnknown, // DeviceNotifyHandleInvalid (stale handle, 0x0714) + 1827 => BadNotWritable, // DeviceAccessDenied (0x0723) + 1844 => BadOutOfRange, // DeviceOutOfRange (0x0734) + + // Client-layer timeout + 1861 => BadTimeout, // ClientSyncTimeOut (0x0745) + + _ => BadCommunicationError, }; }