[opcuaclient] OpcUaClient — IHistoryProvider.ReadEventsAsync interface fix + impl #402

Merged
dohertj2 merged 1 commits from auto/opcuaclient/12 into auto/driver-gaps 2026-04-26 09:32:24 -04:00
Owner

Summary

Multi-variable PDU packing for S7 — replaces per-tag Plc.ReadAsync loop with Plc.ReadMultipleVarsAsync batching.

  • S7ReadPacker.cs (new) static helper:
    • Classifies tags as packable / fallback / unknown.
    • Builds S7.Net.Types.DataItem with VarType, Count, DB, StartByteAdr, BitAdr.
    • Computes PDU item budget: (pduSize - 18) / 12, capped at 19 items per call.
    • Bin-packs preserving caller order.
  • S7Driver.ReadAsync — classifies inputs into unknown / packable / fallback. Packable batches dispatch via Plc.ReadMultipleVarsAsync; decodes each DataItem.Value back to per-tag snapshots. Falls back to per-tag ReadOneAsync on batch-level failure so one bad tag doesn't poison the rest of the batch.
  • Arrays, strings, dates, 64-bit ints, UDT-shaped types remain on the existing per-tag path (variable-width or special encoding).

Test plan

  • dotnet build src/ZB.MOM.WW.OtOpcUa.Driver.S7 — clean (0 / 0)
  • dotnet test tests/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests163 / 163 passed (14 new in S7ReadPackerTests: item-budget math, bin-packing 100 items into ≤6 batches, packability classification across all S7DataType values, DataItem field mapping (DB / area / VarType / BitAdr), value reinterpret (signed Int16/Int32 from unsigned wire))
  • Integration tests — Snap7-server fixture supports it; not exercised in this PR

🤖 Auto-generated by the Mode-B execution loop. Closes #292.

Closes #284

## Summary Multi-variable PDU packing for S7 — replaces per-tag `Plc.ReadAsync` loop with `Plc.ReadMultipleVarsAsync` batching. - **`S7ReadPacker.cs`** (new) static helper: - Classifies tags as packable / fallback / unknown. - Builds `S7.Net.Types.DataItem` with `VarType`, `Count`, `DB`, `StartByteAdr`, `BitAdr`. - Computes PDU item budget: `(pduSize - 18) / 12`, capped at **19 items** per call. - Bin-packs preserving caller order. - **`S7Driver.ReadAsync`** — classifies inputs into unknown / packable / fallback. Packable batches dispatch via `Plc.ReadMultipleVarsAsync`; decodes each `DataItem.Value` back to per-tag snapshots. Falls back to per-tag `ReadOneAsync` on **batch-level** failure so one bad tag doesn't poison the rest of the batch. - Arrays, strings, dates, 64-bit ints, UDT-shaped types remain on the existing per-tag path (variable-width or special encoding). ## Test plan - [x] `dotnet build src/ZB.MOM.WW.OtOpcUa.Driver.S7` — clean (0 / 0) - [x] `dotnet test tests/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests` — **163 / 163 passed** (14 new in `S7ReadPackerTests`: item-budget math, bin-packing 100 items into ≤6 batches, packability classification across all `S7DataType` values, `DataItem` field mapping (DB / area / VarType / BitAdr), value reinterpret (signed Int16/Int32 from unsigned wire)) - [ ] Integration tests — Snap7-server fixture supports it; not exercised in this PR 🤖 Auto-generated by the Mode-B execution loop. Closes #292. Closes #284
dohertj2 added 1 commit 2026-04-26 09:32:19 -04:00
Adds a filter-aware overload of IHistoryProvider.ReadEventsAsync that carries
EventFilter SelectClauses + WhereClause, and implements it on the OPC UA
Client driver via Session.HistoryReadAsync + ReadEventDetails.

The change is additive (default-impl returns NotSupportedException) so the
existing Galaxy.Proxy.GalaxyProxyDriver implementation keeps compiling
against the fixed-field overload — no cross-driver refactor required.

* Core.Abstractions: new EventHistoryRequest / SimpleAttributeSpec /
  ContentFilterSpec records mirror the OPC UA wire shape transport-neutrally.
  HistoricalEventBatch / HistoricalEventRow carry an open-ended Fields bag
  keyed by SimpleAttributeSpec.FieldName so server-side dispatch can re-align
  with the client's wire-side SelectClause order.
* OpcUaClient driver: new ReadEventsAsync(fullReference, EventHistoryRequest, ct)
  builds an EventFilter, calls Session.HistoryReadAsync, and unwraps
  HistoryEvent.Events into HistoricalEventBatch rows. Default SelectClause
  set matches BuildHistoryEvent on the server side. ContentFilter bytes are
  decoded through the live session's MessageContext (passthrough — the
  driver does not evaluate filters).
* Unit tests: 7 new tests cover SelectClause translation, default-clause
  fallback, malformed where-clause swallowing, uninitialized-driver guard,
  null-request guard, and IHistoryProvider default fallback.
* Integration scaffold: build-only [Fact] gated on opc-plc --alm; flips to
  green when the fixture image is upgraded.
* Docs: HistoryRead Events section in docs/drivers/OpcUaClient.md plus a
  cross-link from Client.CLI.md historyread page.
* E2E: -HistoryEvents switch on scripts/e2e/test-opcuaclient.ps1 confirms
  the gateway round-trips HistoryReadEvents without
  BadHistoryOperationUnsupported (gated; defaults to skip).

Closes #284
dohertj2 merged commit 7cbc566db9 into auto/driver-gaps 2026-04-26 09:32:24 -04:00
dohertj2 deleted branch auto/opcuaclient/12 2026-04-26 09:32:25 -04:00
Sign in to join this conversation.