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:
@@ -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
|
explicit case for symbol-version-changed routed to rediscovery, and for PLC-in-Config mapped
|
||||||
to `BadOutOfService`/`BadInvalidState`.
|
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
|
### Driver.TwinCAT-012
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ internal sealed class AdsTwinCATClient : ITwinCATClient
|
|||||||
|
|
||||||
public event EventHandler? OnSymbolVersionChanged;
|
public event EventHandler? OnSymbolVersionChanged;
|
||||||
|
|
||||||
/// <summary>Raise <see cref="OnSymbolVersionChanged"/> when <paramref name="adsError"/> is 0x0702.</summary>
|
/// <summary>Raise <see cref="OnSymbolVersionChanged"/> when <paramref name="adsError"/> is <c>DeviceSymbolVersionInvalid</c> (1809 / 0x0711).</summary>
|
||||||
private uint MapAndSignal(uint adsError)
|
private uint MapAndSignal(uint adsError)
|
||||||
{
|
{
|
||||||
if (TwinCATStatusMapper.IsSymbolVersionChanged(adsError))
|
if (TwinCATStatusMapper.IsSymbolVersionChanged(adsError))
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ public interface ITwinCATClient : IDisposable
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Raised when the client observes the ADS symbol-version-changed code
|
/// Raised when the client observes the ADS symbol-version-changed code
|
||||||
/// (<see cref="TwinCATStatusMapper.AdsSymbolVersionChanged"/>) on any read / write /
|
/// (<see cref="TwinCATStatusMapper.AdsSymbolVersionChanged"/> — <c>DeviceSymbolVersionInvalid</c>
|
||||||
/// notification — the signal that a PLC program re-download has invalidated every
|
/// 1809 / 0x0711) on any read / write / notification — the signal that a PLC program
|
||||||
/// symbol + notification handle. The driver forwards this to
|
/// re-download has invalidated every symbol + notification handle. The driver forwards
|
||||||
/// <see cref="Core.Abstractions.IRediscoverable.OnRediscoveryNeeded"/> so Core rebuilds
|
/// this to <see cref="Core.Abstractions.IRediscoverable.OnRediscoveryNeeded"/> so Core
|
||||||
/// the address space subtree (docs/v2/driver-specs.md §6, Driver.TwinCAT-013).
|
/// rebuilds the address space subtree (docs/v2/driver-specs.md §6, Driver.TwinCAT-013).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event EventHandler? OnSymbolVersionChanged;
|
event EventHandler? OnSymbolVersionChanged;
|
||||||
|
|
||||||
|
|||||||
@@ -516,14 +516,16 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Routes a wire-detected ADS symbol-version-changed (0x0702) to Core as an
|
/// Routes a wire-detected ADS symbol-version-changed (DeviceSymbolVersionInvalid 1809 /
|
||||||
/// <see cref="IRediscoverable"/> invocation (Driver.TwinCAT-013). A PLC re-download
|
/// 0x0711) to Core as an <see cref="IRediscoverable"/> invocation (Driver.TwinCAT-013).
|
||||||
/// invalidates every symbol + notification handle, so the address space must be rebuilt
|
/// A PLC re-download invalidates every symbol + notification handle, so the address
|
||||||
/// — this is the documented TwinCAT failure mode, not a transient connection error.
|
/// space must be rebuilt — this is the documented TwinCAT failure mode, not a transient
|
||||||
|
/// connection error.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void HandleSymbolVersionChanged(object? sender, EventArgs e) =>
|
private void HandleSymbolVersionChanged(object? sender, EventArgs e) =>
|
||||||
OnRediscoveryNeeded?.Invoke(this, new RediscoveryEventArgs(
|
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 void Dispose() => DisposeAsync().AsTask().GetAwaiter().GetResult();
|
||||||
public async ValueTask DisposeAsync() => await ShutdownAsync(CancellationToken.None).ConfigureAwait(false);
|
public async ValueTask DisposeAsync() => await ShutdownAsync(CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|||||||
@@ -1,56 +1,82 @@
|
|||||||
namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT;
|
namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maps AMS / ADS error codes to OPC UA StatusCodes. ADS error codes are defined in
|
/// Maps AMS / ADS error codes to OPC UA StatusCodes. ADS error codes are taken from the
|
||||||
/// <c>AdsErrorCode</c> from <c>Beckhoff.TwinCAT.Ads</c> — this mapper covers the ones a
|
/// <c>AdsErrorCode</c> enum in <c>Beckhoff.TwinCAT.Ads</c> 7.x — numeric values are the
|
||||||
/// driver actually encounters during normal operation (symbol-not-found, access-denied,
|
/// authoritative source (not the hex shorthand in older Beckhoff InfoSys pages, which has
|
||||||
/// timeout, router-not-initialized, invalid-group/offset, etc.).
|
/// known transcription errors). Key device-layer codes start at 0x0700 (1792 decimal).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class TwinCATStatusMapper
|
public static class TwinCATStatusMapper
|
||||||
{
|
{
|
||||||
public const uint Good = 0u;
|
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>
|
/// <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
|
/// 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
|
/// notification handle the driver holds is now stale. The driver treats this as an
|
||||||
/// <see cref="Core.Abstractions.IRediscoverable"/> trigger, not a connection error
|
/// <see cref="Core.Abstractions.IRediscoverable"/> trigger, not a connection error
|
||||||
/// (docs/v2/driver-specs.md §6, Driver.TwinCAT-013).
|
/// (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>
|
/// </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>
|
/// <summary>True when <paramref name="adsError"/> is the symbol-version-changed code.</summary>
|
||||||
public static bool IsSymbolVersionChanged(uint adsError) => adsError == AdsSymbolVersionChanged;
|
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>
|
/// <summary>
|
||||||
/// Map an AMS / ADS error code (uint from AdsErrorCode enum). 0 = success; non-zero
|
/// Map an AMS / ADS error code (uint cast from <c>AdsErrorCode</c> enum) to an OPC UA
|
||||||
/// codes follow Beckhoff's AMS error table (7 = target port not found, 1792 =
|
/// StatusCode. 0 = success. Device-layer codes (0x0700–0x073F) cover the operations a
|
||||||
/// ADSERR_DEVICE_SRVNOTSUPP, 1793 = ADSERR_DEVICE_INVALIDGRP, 1794 =
|
/// driver actually encounters during normal runtime.
|
||||||
/// ADSERR_DEVICE_INVALIDOFFSET, 1798 = ADSERR_DEVICE_SYMBOLNOTFOUND, 1808 =
|
|
||||||
/// ADSERR_DEVICE_ACCESSDENIED, 1861 = ADSERR_CLIENT_SYNCTIMEOUT).
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static uint MapAdsError(uint adsError) => adsError switch
|
public static uint MapAdsError(uint adsError) => adsError switch
|
||||||
{
|
{
|
||||||
0 => Good,
|
0 => Good,
|
||||||
6 or 7 => BadCommunicationError, // target port unreachable
|
|
||||||
1792 => BadNotSupported, // service not supported
|
// AMS router / transport errors
|
||||||
1793 => BadOutOfRange, // invalid index group
|
6 or 7 => BadCommunicationError, // TargetPortNotFound / TargetMachineNotFound
|
||||||
1794 => BadOutOfRange, // invalid index offset
|
1285 or 1290 => BadCommunicationError, // RouterNotInitialized / RouterNotActive
|
||||||
1798 => BadNodeIdUnknown, // symbol not found
|
|
||||||
1807 => BadDeviceFailure, // device in invalid state
|
// Device-layer codes (Beckhoff.TwinCAT.Ads AdsErrorCode enum, confirmed 7.0.172)
|
||||||
1808 => BadNotWritable, // access denied
|
1792 => BadDeviceFailure, // DeviceError (generic device error)
|
||||||
1811 or 1812 => BadOutOfRange, // size mismatch
|
1793 => BadNotSupported, // DeviceServiceNotSupported
|
||||||
1861 => BadTimeout, // sync timeout
|
1794 => BadOutOfRange, // DeviceInvalidGroup (ADS index-group error)
|
||||||
_ => BadCommunicationError,
|
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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user