Per-tag opt-in for write-retry per docs/v2/plan.md decisions #44, #45, #143. Default is false — writes never auto-retry unless the driver author has marked the tag as safe to replay. Core.Abstractions: - DriverAttributeInfo gains `bool WriteIdempotent = false` at the end of the positional record (back-compatible; every existing call site uses the default). Driver.Modbus: - ModbusTagDefinition gains `bool WriteIdempotent = false`. Safe candidates documented in the param XML: holding-register set-points, configuration registers. Unsafe: edge-triggered coils, counter-increment addresses. - ModbusDriver.DiscoverAsync propagates t.WriteIdempotent into DriverAttributeInfo.WriteIdempotent. Driver.S7: - S7TagDefinition gains `bool WriteIdempotent = false`. Safe candidates: DB word/dword set-points, configuration DBs. Unsafe: M/Q bits that drive edge-triggered program routines. - S7Driver.DiscoverAsync propagates the flag. Stream A.5 integration tests (FlakeyDriverIntegrationTests, 4 new) exercise the invoker + flaky-driver contract the plan enumerates: - Read with 5 transient failures succeeds on the 6th attempt (RetryCount=10). - Non-idempotent write with RetryCount=5 configured still fails on the first failure — no replay (decision #44 guard at the ExecuteWriteAsync surface). - Idempotent write with 2 transient failures succeeds on the 3rd attempt. - Two hosts on the same driver have independent breakers — dead-host trips its breaker but live-host's first call still succeeds. Propagation tests: - ModbusDriverTests: SetPoint WriteIdempotent=true flows into DriverAttributeInfo; PulseCoil default=false. - S7DiscoveryAndSubscribeTests: same pattern for DBx SetPoint vs M-bit. Full solution dotnet test: 947 passing (baseline 906, +41 net across Stream A so far). Pre-existing Client.CLI Subscribe flake unchanged. Stream A's remaining work (wiring CapabilityInvoker into DriverNodeManager's OnReadValue / OnWriteValue / History / Subscribe dispatch paths) is the server-side integration piece + needs DI wiring for the pipeline builder — lands in the next PR on this branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
45 lines
2.4 KiB
C#
45 lines
2.4 KiB
C#
namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
|
|
/// <summary>
|
|
/// Driver-agnostic per-attribute (tag) descriptor used by the generic node-manager
|
|
/// to build OPC UA address-space variables. Every driver maps its native attribute
|
|
/// metadata into this DTO during discovery.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Per <c>docs/v2/plan.md</c> §5a (LmxNodeManager reusability) — <c>DriverAttributeInfo</c>
|
|
/// replaces the v1 Galaxy-specific <c>GalaxyAttributeInfo</c> in the generic node-manager
|
|
/// so the same node-manager class works against every driver.
|
|
/// </remarks>
|
|
/// <param name="FullName">
|
|
/// Driver-side full reference for read/write addressing
|
|
/// (e.g. for Galaxy: <c>"DelmiaReceiver_001.DownloadPath"</c>).
|
|
/// </param>
|
|
/// <param name="DriverDataType">Driver-agnostic data type; maps to OPC UA built-in type at build time.</param>
|
|
/// <param name="IsArray">True when this attribute is a 1-D array.</param>
|
|
/// <param name="ArrayDim">Declared array length when <see cref="IsArray"/> is true; null otherwise.</param>
|
|
/// <param name="SecurityClass">Write-authorization tier for this attribute.</param>
|
|
/// <param name="IsHistorized">True when this attribute is expected to feed historian / HistoryRead.</param>
|
|
/// <param name="IsAlarm">
|
|
/// True when this attribute represents an alarm condition (Galaxy: has an
|
|
/// <c>AlarmExtension</c> primitive). The generic node-manager enriches the variable with an
|
|
/// OPC UA <c>AlarmConditionState</c> when true. Defaults to false so existing non-Galaxy
|
|
/// drivers aren't forced to flow a flag they don't produce.
|
|
/// </param>
|
|
/// <param name="WriteIdempotent">
|
|
/// True when a timed-out or failed write to this attribute is safe to replay. Per
|
|
/// <c>docs/v2/plan.md</c> decisions #44, #45, #143 — writes are NOT auto-retried by default
|
|
/// because replaying a pulse / alarm-ack / counter-increment / recipe-step advance can
|
|
/// duplicate field actions. Drivers flag only tags whose semantics make retry safe
|
|
/// (holding registers with level-set values, set-point writes to analog tags) — the
|
|
/// capability invoker respects this flag when deciding whether to apply Polly retry.
|
|
/// </param>
|
|
public sealed record DriverAttributeInfo(
|
|
string FullName,
|
|
DriverDataType DriverDataType,
|
|
bool IsArray,
|
|
uint? ArrayDim,
|
|
SecurityClassification SecurityClass,
|
|
bool IsHistorized,
|
|
bool IsAlarm = false,
|
|
bool WriteIdempotent = false);
|