Task #253 follow-up — bidirectional + subscribe-sees-change e2e stages #208

Merged
dohertj2 merged 1 commits from task-253b-e2e-bidirectional into v2 2026-04-21 10:11:10 -04:00
Owner

Summary

The original three-stage design in PR #207 (probe / driver-loopback / forward-bridge) only verified driver-write → server-read. User called this out — nothing tested the reverse direction, nor asserted that OPC UA subscriptions actually deliver data-change notifications for driver-originated changes.

New stages

  • Test-OpcUaWriteBridgeotopcua-cli write the NodeId → wait → driver CLI reads the PLC side. Covers client → server → driver → PLC.
  • Test-SubscribeSeesChangeotopcua-cli subscribe --duration N in the background, settle 2s, driver-side write, wait for the window to close, assert captured stdout mentions the value. Covers server-side monitored-item + data-change delivery end-to-end.

Wired into all six per-driver scripts (modbus / abcip / ablegacy / s7 / focas / twincat). README updated to describe the five-stage design + note that the published tag must be writable for stages 4 and 5.

Ancillary

Prepended UTF-8 BOM to every script so the Windows PowerShell 5.1 parser accepts the em-dashes PowerShell 7 already handled fine. Scripts still #Requires -Version 7.0; the BOM is purely defensive for IDE / CI step parsers that don't respect the shebang.

Test plan

  • [System.Management.Automation.Language.Parser]::ParseFile clean on all scripts (test-all.ps1's ?? operators are expected PS7-only, not errors)
  • Live run against Modbus + AB CIP simulators (manual — dev-box-local)
## Summary The original three-stage design in PR #207 (probe / driver-loopback / forward-bridge) only verified `driver-write → server-read`. User called this out — nothing tested the reverse direction, nor asserted that OPC UA subscriptions actually deliver data-change notifications for driver-originated changes. ## New stages - **Test-OpcUaWriteBridge** — `otopcua-cli write` the NodeId → wait → driver CLI reads the PLC side. Covers `client → server → driver → PLC`. - **Test-SubscribeSeesChange** — `otopcua-cli subscribe --duration N` in the background, settle 2s, driver-side write, wait for the window to close, assert captured stdout mentions the value. Covers server-side monitored-item + data-change delivery end-to-end. Wired into all six per-driver scripts (modbus / abcip / ablegacy / s7 / focas / twincat). README updated to describe the five-stage design + note that the published tag must be writable for stages 4 and 5. ## Ancillary Prepended UTF-8 BOM to every script so the Windows PowerShell 5.1 parser accepts the em-dashes PowerShell 7 already handled fine. Scripts still `#Requires -Version 7.0`; the BOM is purely defensive for IDE / CI step parsers that don't respect the shebang. ## Test plan - [x] `[System.Management.Automation.Language.Parser]::ParseFile` clean on all scripts (test-all.ps1's `??` operators are expected PS7-only, not errors) - [ ] Live run against Modbus + AB CIP simulators (manual — dev-box-local)
dohertj2 added 1 commit 2026-04-21 10:10:54 -04:00
The original three-stage design (probe / driver-loopback / forward-
bridge) only proved driver-write → server-read. It missed:

 - OPC UA write → server → driver → PLC (the reverse direction)
 - server-side data-change notifications actually firing (a stale
   subscription can still let a read-after-the-fact return the new
   value and look fine)

Extend _common.ps1 with two helpers:

 - Test-OpcUaWriteBridge: otopcua-cli write the NodeId -> wait 3s ->
   driver CLI read the PLC side, assert equality.
 - Test-SubscribeSeesChange: Start-Process otopcua-cli subscribe in the
   background with --duration N, settle 2s, driver-side write, wait for
   the subscription window to close, assert captured stdout contains
   the new value.

Wire both into test-modbus / test-abcip / test-ablegacy / test-s7 /
test-focas / test-twincat after the existing forward-bridge stage.
Update README to describe the five-stage design + note that the
published NodeId must be writable for stages 4 + 5.

Also prepend UTF-8 BOM to every script in scripts/e2e so Windows
PowerShell 5.1 parsers agree on em-dash byte sequences the way
PowerShell 7 already does. The scripts still #Requires -Version 7.0 —
the BOM is purely defensive for IDE / CI step parsers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dohertj2 merged commit 7b1c910806 into v2 2026-04-21 10:11:10 -04:00
dohertj2 deleted branch task-253b-e2e-bidirectional 2026-04-21 10:11:10 -04:00
dohertj2 referenced this issue from a commit 2026-04-30 08:21:26 -04:00
Client rename residuals: lmxopcua-cli → otopcua-cli + LmxOpcUaClient → OtOpcUaClient with migration shim. Closes task #208 (the executable-name + LocalAppData-folder slice that was called out in Client.CLI.md / Client.UI.md as a deliberately-deferred residual of the Phase 0 rename). Six source references flipped to the canonical OtOpcUaClient spelling: Program.cs CliFx executable name + description (lmxopcua-cli → otopcua-cli), DefaultApplicationConfigurationFactory.cs ApplicationName + ApplicationUri (LmxOpcUaClient + urn:localhost:LmxOpcUaClient → OtOpcUaClient + urn:localhost:OtOpcUaClient), OpcUaClientService.CreateSessionAsync session-name arg, ConnectionSettings.CertificateStorePath default, MainWindowViewModel.CertificateStorePath default, JsonSettingsService.SettingsDir. Two consuming tests (ConnectionSettingsTests + MainWindowViewModelTests) updated to assert the new canonical name. New ClientStoragePaths static helper at src/ZB.MOM.WW.OtOpcUa.Client.Shared/ClientStoragePaths.cs is the migration shim — single entry point for the PKI root + pki subpath, runs a one-shot legacy-folder probe on first resolution: if {LocalAppData}/LmxOpcUaClient/ exists + {LocalAppData}/OtOpcUaClient/ does not, Directory.Move renames it in place (atomic on NTFS within the same volume) so trusted server certs + saved connection settings persist across the rename without operator action. Idempotent per-process via a Lock-guarded _migrationChecked flag so repeated CertificateStorePath getter calls on the hot path pay no IO cost beyond the first. Fresh-install path (neither folder exists) + already-migrated path (only canonical exists) + manual-override path (both exist — developer has set up something explicit) are all no-ops that leave state alone. IOException on the Directory.Move is swallowed + logged as a false return so a concurrent peer process losing the race doesn't crash the consumer; the losing process falls back to whatever state exists. Five new ClientStoragePathsTests assert: GetRoot ends with canonical name under LocalAppData, GetPkiPath nests pki under root, CanonicalFolderName is OtOpcUaClient, LegacyFolderName is LmxOpcUaClient (the migration contract — a typo here would leak the legacy folder past the shim), repeat invocation returns false after first-touch arms the in-process guard. Doc-side residual-explanation notes in docs/Client.CLI.md + docs/Client.UI.md are dropped now that the rename is real; replaced with a short "pre-#208 dev boxes migrate automatically on first launch" note that points at ClientStoragePaths. Sample CLI invocations in Client.CLI.md updated via sed from lmxopcua-cli to otopcua-cli across every command block (14 replacements). Pre-existing staleness in SubscribeCommandTests.Execute_PrintsSubscriptionMessage surfaced during the test run — the CLI's subscribe command has long since switched to an aggregate "Subscribed to {count}/{total} nodes (interval: ...)" output format but the test still asserted the original single-node form. Updated the assertion to match current output + added a comment explaining the change; this is unrelated to the rename but was blocking a green Client.CLI.Tests run. Full solution build 0 errors; Client.Shared.Tests 136/136 + 5 new shim tests passing; Client.UI.Tests 98/98; Client.CLI.Tests 52/52 (was 51/52 before the subscribe-test fix). No Admin/Core/Server changes — this touches only the client layer.
Sign in to join this conversation.