fix(driver-twincat): resolve Medium code-review finding (Driver.TwinCAT-011)

Confirm AdsErrorCode values from Beckhoff.TwinCAT.Ads 7.0.172 and rewrite
MapAdsError with 20 explicit cases. Fix critical bug: AdsSymbolVersionChanged
was 0x0702 (DeviceInvalidGroup) but DeviceSymbolVersionInvalid is 1809
(0x0711); correct constant and all comments. Add BadOutOfService for
DeviceNotReady and BadInvalidState for DeviceInvalidState/PLC-in-Config.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-22 10:49:38 -04:00
parent f7d6bd12b9
commit 40b28e8820
5 changed files with 72 additions and 44 deletions
@@ -1,56 +1,82 @@
namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT;
/// <summary>
/// Maps AMS / ADS error codes to OPC UA StatusCodes. ADS error codes are defined in
/// <c>AdsErrorCode</c> from <c>Beckhoff.TwinCAT.Ads</c> — 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
/// <c>AdsErrorCode</c> enum in <c>Beckhoff.TwinCAT.Ads</c> 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).
/// </summary>
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) ----
/// <summary>
/// ADS <c>ADSERR_DEVICE_SYMBOLVERSIONINVALID</c> — error <c>0x0702</c> (1794 decimal).
/// ADS <c>DeviceSymbolVersionInvalid</c> — 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
/// <see cref="Core.Abstractions.IRediscoverable"/> trigger, not a connection error
/// (docs/v2/driver-specs.md §6, Driver.TwinCAT-013).
/// <para>
/// Note: legacy Beckhoff InfoSys documentation sometimes cites this as "0x0702"; that
/// is a transcription error — 0x0702 is <c>DeviceInvalidGroup</c> (1794). The SDK enum
/// value 1809 (0x0711) is authoritative (Driver.TwinCAT-011).
/// </para>
/// </summary>
public const uint AdsSymbolVersionChanged = 0x0702u;
public const uint AdsSymbolVersionChanged = 1809u; // DeviceSymbolVersionInvalid = 0x0711
/// <summary>True when <paramref name="adsError"/> is the symbol-version-changed code.</summary>
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;
/// <summary>
/// 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 <c>AdsErrorCode</c> enum) to an OPC UA
/// StatusCode. 0 = success. Device-layer codes (0x07000x073F) cover the operations a
/// driver actually encounters during normal runtime.
/// </summary>
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,
};
}