Important 1: ShelveAlarmAsync Timed branch now multiplies shelvingTimeSeconds × 1000.0
before passing to CallMethodAsync — OPC UA Part 9 TimedShelve ShelvingTime is a Duration
in milliseconds, not seconds. IOpcUaClientService XML doc and ShelveCommand --duration
description updated to document the seconds-in / ms-out contract.
Important 2: ShelveAlarmAsync builds shelvingStateNodeId with the same
EndsWith(".ShelvingState") guard already used by the .Condition suffix in
AcknowledgeAlarmAsync / ConfirmAlarmAsync, preventing double-append.
Important 3: Add 6 service-layer tests to OpcUaClientServiceTests —
ConfirmAlarmAsync_OnSuccess_ReturnsGood
ConfirmAlarmAsync_OnServiceResultException_ReturnsBadStatusCode
ShelveAlarmAsync_OneShot_CallsMethodWithNoArgs
ShelveAlarmAsync_Timed_PassesDurationInMilliseconds (regression guard for Important 1)
ShelveAlarmAsync_Unshelve_CallsMethodWithNoArgs
ShelveAlarmAsync_OnServiceResultException_ReturnsBadStatusCode
FakeSessionAdapter extended with CallMethodInputArgs list to record per-call input
arguments so the Timed test can assert the ms value.
Minor 4: ShelveCommand output changed from "Shelve (OneShot) successful" to
"{shelveKind} successful/failed" so Unshelve reads "Unshelve successful: …".
Minor 6: ShelveCommand --duration description updated to "(must be > 0; in seconds,
converted to milliseconds for the OPC UA call; required for --kind Timed)".
Adds ConfirmAlarmAsync and ShelveAlarmAsync to IOpcUaClientService and
OpcUaClientService (mirroring the AcknowledgeAlarmAsync pattern: same
CallMethodAsync/ServiceResultException/StatusCode contract). Adds ShelveKind
enum (OneShot/Timed/Unshelve). Adds three new CLI commands — ack, confirm,
shelve — with hex EventId input and per-command argument validation.
Updates both fakes (CLI + UI) to implement the new interface members and
record calls. Adds 16 unit tests covering argument mapping, invalid-input
CommandException paths, bad-status output, and disconnect-in-finally.
- Client.Shared-003: DefaultSessionAdapter.WriteValueAsync / CallMethodAsync
guard against null/empty Results and throw ServiceResultException with
the response's ServiceResult code instead of indexing into a missing
list.
- Client.Shared-004: DefaultSessionAdapter.CloseAsync / HistoryReadRawAsync
/ HistoryReadAggregateAsync use the Session.*Async overloads and honour
the caller's CancellationToken.
- Client.Shared-009: AcknowledgeAlarmAsync returns the underlying
ServiceResultException.StatusCode on failure instead of always Good;
IOpcUaClientService doc updated to describe the new contract.
- Client.Shared-010: ConnectionSettings.CertificateStorePath defaults to
empty; DefaultApplicationConfigurationFactory resolves the canonical
PKI path lazily, so per-failover ConnectionSettings copies don't hit
the filesystem.
- Client.Shared-011: added the alarm-fallback regression test, extracted
EndpointSelector as a pure static, and added EndpointSelectorTests
covering security-mode match, Basic256Sha256 preference, fallback,
diagnostics, hostname rewrite, and null/empty guards.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Group all 69 projects into category subfolders under src/ and tests/ so the
Rider Solution Explorer mirrors the module structure. Folders: Core, Server,
Drivers (with a nested Driver CLIs subfolder), Client, Tooling.
- Move every project folder on disk with git mv (history preserved as renames).
- Recompute relative paths in 57 .csproj files: cross-category ProjectReferences,
the lib/ HintPath+None refs in Driver.Historian.Wonderware, and the external
mxaccessgw refs in Driver.Galaxy and its test project.
- Rebuild ZB.MOM.WW.OtOpcUa.slnx with nested solution folders.
- Re-prefix project paths in functional scripts (e2e, compliance, smoke SQL,
integration, install).
Build green (0 errors); unit tests pass. Docs left for a separate pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>