- id: twincat-1.1 driver: twincat phase: 1 plan_pr_id: "1.1" title: "TwinCAT — Int64 fidelity for LINT/ULINT" plan_anchor: "docs/plans/twincat-plan.md" summary: | Map LInt/ULInt to DriverDataType.Int64 instead of silently truncating to Int32. The TwinCATDataType.cs:40 truncation comment "matches Int64 gap" is removed and MapToClrType already returns long/ulong, so the wire-level read returns the correct boxed types. May add Int64 to Core.Abstractions DriverDataType enum if missing. Closes a long-standing fixture caveat noted in the test suite. files: - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDataType.cs" - "src/ZB.MOM.WW.OtOpcUa.Core.Abstractions/DriverDataType.cs" docs: - "docs/Driver.TwinCAT.Cli.md" - "docs/drivers/TwinCAT-Test-Fixture.md" fixture: - "tests/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCatProject/PLC/GVLs/GVL_Primitives.TcGVL" - "tests/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCatProject/README.md" e2e: [] effort: S deps: [] cross_driver: false notes: "Hardware-gated via TWINCAT_TARGET_NETID; no e2e change to test-twincat.ps1." - id: twincat-1.2 driver: twincat phase: 1 plan_pr_id: "1.2" title: "TwinCAT — TIME/DATE/DT/TOD as native UA types" plan_anchor: "docs/plans/twincat-plan.md" summary: | Stop marshalling IEC TIME/DATE/DT/TOD as raw UDINT and convert to native UA Duration/DateTime types via post-processing in ReadValueAsync, ConvertForWrite, and OnAdsNotificationEx. May expose missing Duration in DriverDataType. CLI syntax updates so users write ISO-8601 / IEC literals instead of numeric raw values. files: - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDataType.cs" - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/AdsTwinCATClient.cs" docs: - "docs/Driver.TwinCAT.Cli.md" fixture: - "tests/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCatProject/PLC/GVLs/GVL_Primitives.TcGVL" - "tests/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCatProject/README.md" e2e: [] effort: M deps: [] cross_driver: false notes: "Hardware-gated via TWINCAT_TARGET_NETID. May add Duration to DriverDataType enum." - id: twincat-1.3 driver: twincat phase: 1 plan_pr_id: "1.3" title: "TwinCAT — Bit-indexed BOOL writes (read-modify-write)" plan_anchor: "docs/plans/twincat-plan.md" summary: | Replace the NotSupportedException at AdsTwinCATClient.cs:99 with read-modify-write on the parent word, serializing concurrent bit writes to the same parent via a keyed SemaphoreSlim. Closes referenced task #181. CLI gains an example and the fixture caveat in the bugs-caught list updates to note writes now work. files: - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/AdsTwinCATClient.cs" docs: - "docs/Driver.TwinCAT.Cli.md" - "docs/drivers/TwinCAT-Test-Fixture.md" fixture: [] e2e: [] effort: S deps: [] cross_driver: false notes: "Reuses GVL_Primitives.vWord (0xBEEF) — no fixture schema change." - id: twincat-1.4 driver: twincat phase: 1 plan_pr_id: "1.4" title: "TwinCAT — Multi-dim and whole-array reads" plan_anchor: "docs/plans/twincat-plan.md" summary: | Expand ReadValueAsync/WriteValueAsync to handle whole-array reads in a single AdsClient call rather than element-by-element. Surface IsArray + ArrayDimensions on TwinCATTagDefinition and through DriverAttributeInfo from DiscoverAsync. Sets up the array-shape plumbing the rest of the driver needs. files: - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/AdsTwinCATClient.cs" - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriver.cs" - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriverOptions.cs" docs: - "docs/Driver.TwinCAT.Cli.md" - "docs/drivers/TwinCAT-Test-Fixture.md" fixture: - "tests/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCatProject/PLC/GVLs/GVL_Arrays.TcGVL" - "tests/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCatProject/README.md" e2e: [] effort: M deps: [] cross_driver: false notes: "Hardware-gated via TWINCAT_TARGET_NETID. New 5x5 aReal2D seed with deterministic pattern." - id: twincat-1.5 driver: twincat phase: 1 plan_pr_id: "1.5" title: "TwinCAT — ENUM and ALIAS at discovery" plan_anchor: "docs/plans/twincat-plan.md" summary: | MapSymbolTypeName currently returns null for non-atomic types, dropping ENUM and ALIAS symbols silently. Switch to inspecting symbol.DataType + Category from TwinCAT.TypeSystem so DataTypeCategory.Enum walks EnumValues and Alias resolves to base atomic recursively. Surface enum members for later EnumStrings rendering. POINTER/REFERENCE/INTERFACE/UNION explicitly out of scope. files: - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/AdsTwinCATClient.cs" docs: - "docs/Driver.TwinCAT.Cli.md" - "docs/drivers/TwinCAT-Test-Fixture.md" fixture: - "tests/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCatProject/README.md" e2e: [] effort: M deps: [] cross_driver: false notes: "Reuses existing GVL_Enums + DUTs; only README integration-test contract entry added." - id: twincat-2.1 driver: twincat phase: 2 plan_pr_id: "2.1" title: "TwinCAT — ADS Sum-read / Sum-write" plan_anchor: "docs/plans/twincat-plan.md" summary: | Replace per-tag ReadValueAsync loops with Beckhoff's ADS Sum commands (IndexGroup 0xF080-0xF084) via SumSymbolRead/SumSymbolWrite to batch N reads/writes per AMS request. Bucket fullReferences by DeviceHostAddress and expose a new ReadValuesAsync surface on ITwinCATClient. Targets ~10x throughput on multi-thousand-tag scans; perf-tier test gated behind TWINCAT_PERF=1. files: - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/AdsTwinCATClient.cs" - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriver.cs" - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/ITwinCATClient.cs" docs: - "docs/v3/twincat-backlog.md" - "docs/drivers/TwinCAT-Test-Fixture.md" fixture: - "tests/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCatProject/PLC/GVLs/GVL_Perf.TcGVL" - "tests/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCatProject/README.md" e2e: [] effort: L deps: [] cross_driver: false notes: "Perf test gated behind TWINCAT_PERF=1 plus TWINCAT_TARGET_NETID; new FB_PerfChurn POU." - id: twincat-2.2 driver: twincat phase: 2 plan_pr_id: "2.2" title: "TwinCAT — Handle-based access with caching" plan_anchor: "docs/plans/twincat-plan.md" summary: | Cache CreateVariableHandleAsync results so per-read overhead drops to read-by-handle (4-byte index vs N-byte symbol path). On DeviceSymbolVersionInvalid (0x710) evict and retry once. Clear cache on AdsClient reconnect until the symbol-version listener (PR 2.3) ships. Dispose path calls DeleteVariableHandleAsync for cached handles. files: - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/AdsTwinCATClient.cs" docs: - "docs/Driver.TwinCAT.Cli.md" - "docs/drivers/TwinCAT-Test-Fixture.md" fixture: [] e2e: [] effort: M deps: [] cross_driver: false notes: "Combines with PR 2.1 for sum-read-by-handle. Reuses GVL_Perf.aTags." - id: twincat-2.3 driver: twincat phase: 2 plan_pr_id: "2.3" title: "TwinCAT — Symbol-version invalidation listener" plan_anchor: "docs/plans/twincat-plan.md" summary: | Register an AddDeviceNotificationAsync on the symbol-version index group (AdsReservedIndexGroup.SymbolVersion 0xF008) so the handle cache from PR 2.2 is wiped on online-change bumps. Initial integration test gated as requires-manual-online-change until automation lands. Resolves open question (c) confirming the v6 enum constant. files: - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/AdsTwinCATClient.cs" docs: - "docs/drivers/TwinCAT-Test-Fixture.md" fixture: - "tests/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCatProject/README.md" e2e: [] effort: M deps: ["twincat-2.2"] cross_driver: false notes: "Hardware-gated via TWINCAT_TARGET_NETID; manual online-change drill documented in README." - id: twincat-3.1 driver: twincat phase: 3 plan_pr_id: "3.1" title: "TwinCAT — Per-tag MaxDelay tuning" plan_anchor: "docs/plans/twincat-plan.md" summary: | Surface NotificationSettings MaxDelay as a per-tag option (default 0 to preserve current behavior). Plumb int? MaxDelayMs through TwinCATTagDefinition, SubscribeAsync, and AddNotificationAsync. Coalesces high-frequency PLC signals so the OPC UA subscription queue stops flooding under bursty change rates. files: - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriverOptions.cs" - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriver.cs" - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/AdsTwinCATClient.cs" docs: - "docs/Driver.TwinCAT.Cli.md" - "docs/drivers/TwinCAT-Test-Fixture.md" fixture: - "tests/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCatProject/README.md" e2e: [] effort: S deps: [] cross_driver: false notes: "Reuses GVL_Fixture.nCounter as 100 Hz driver. Hardware-gated via TWINCAT_TARGET_NETID." - id: twincat-3.2 driver: twincat phase: 3 plan_pr_id: "3.2" title: "TwinCAT — Cycle-time / jitter / PLC-state diagnostics" plan_anchor: "docs/plans/twincat-plan.md" summary: | Augment the probe loop to read _AppInfo.OnlineChangeCnt/AppName and _TaskInfo[1].CycleTime/LastExecTime, surface as TwinCATDeviceDiagnostics on DeviceState, and emit through IDriverDiagnostics (cross-driver surface from Modbus task #154). Read system symbols directly without going through the user browse filter. files: - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriver.cs" - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATSystemSymbolFilter.cs" docs: - "docs/drivers/TwinCAT-Test-Fixture.md" - "docs/Driver.TwinCAT.Cli.md" - "docs/v3/twincat-backlog.md" fixture: - "tests/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCatProject/README.md" e2e: [] effort: M deps: [] cross_driver: true notes: "Reuses IDriverDiagnostics from Modbus task #154. Hardware-gated via TWINCAT_TARGET_NETID." - id: twincat-4.1 driver: twincat phase: 4 plan_pr_id: "4.1" title: "TwinCAT — Nested UDT browse via online type walker" plan_anchor: "docs/plans/twincat-plan.md" summary: | Largest single piece of work. Recurse BrowseSymbolsAsync into IStructType.SubItems yielding one TwinCATDiscoveredSymbol per leaf with dotted instance paths. Expand arrays-of-structs up to a configurable bound (default 1024). Add a pure TwinCATTypeWalker helper. Folds recursed structure into Discovered/ folder tree. Online runtime path only — TMC offline parsing deferred per open question (a). files: - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/AdsTwinCATClient.cs" - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriver.cs" - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATTypeWalker.cs" docs: - "docs/Driver.TwinCAT.Cli.md" - "docs/drivers/TwinCAT-Test-Fixture.md" - "docs/v3/twincat-backlog.md" fixture: - "tests/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCatProject/PLC/DUTs/ST_NestedFlags.TcDUT" - "tests/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCatProject/PLC/DUTs/ST_RecursiveCap.TcDUT" - "tests/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCatProject/PLC/DUTs/ST_AlarmRecord.TcDUT" - "tests/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCatProject/README.md" e2e: [] effort: L deps: ["twincat-1.5"] cross_driver: false notes: "Hardware-gated via TWINCAT_TARGET_NETID. PR 1.4 helpful but not blocking." - id: twincat-5.1 driver: twincat phase: 5 plan_pr_id: "5.1" title: "TwinCAT — IAlarmSource via TC3 EventLogger" plan_anchor: "docs/plans/twincat-plan.md" summary: | Implement IAlarmSource over TcEventLogger on AMS port 110 so PLC alarms surface as OPC UA AC events. Begins with a one-day spike (open question (b)) documented in docs/v3/twincat-eventlogger-spike.md to determine if a managed wrapper exists or if we hit AMS port 110 directly via a secondary AdsClient + AddDeviceNotificationAsync on the alarm-list index group. Gated by new EnableAlarms option (default false). files: - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATAlarmSource.cs" - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriver.cs" - "src/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriverOptions.cs" docs: - "docs/drivers/TwinCAT.md" - "docs/v3/twincat-eventlogger-spike.md" - "docs/Driver.TwinCAT.Cli.md" - "docs/drivers/TwinCAT-Test-Fixture.md" fixture: - "tests/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCatProject/PLC/POUs/FB_AlarmHarness.TcPOU" - "tests/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCatProject/PLC/GVLs/GVL_Alarms.TcGVL" - "tests/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCatProject/README.md" e2e: - "scripts/e2e/test-twincat.ps1" effort: L deps: [] cross_driver: false notes: "Hardware-gated via TWINCAT_TARGET_NETID. Spike-first; e2e Test-AlarmRoundTrip likely deferred to follow-up."