From bbb79427888db01999dacfdeb6c48bdc000b91e6 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Fri, 19 Jun 2026 05:29:51 -0400 Subject: [PATCH] docs(sphistorianclient): approved design for ZB.MOM.WW.SPHistorianClient port --- .../2026-06-19-sphistorianclient-design.md | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 docs/plans/2026-06-19-sphistorianclient-design.md diff --git a/docs/plans/2026-06-19-sphistorianclient-design.md b/docs/plans/2026-06-19-sphistorianclient-design.md new file mode 100644 index 0000000..c2e5050 --- /dev/null +++ b/docs/plans/2026-06-19-sphistorianclient-design.md @@ -0,0 +1,122 @@ +# ZB.MOM.WW.SPHistorianClient — Design + +**Date:** 2026-06-19 +**Status:** Approved — proceeding to implementation plan. + +## Goal + +Repackage the proven, pure-managed .NET 10 `AVEVA.Historian.Client` SDK (delivered in +`HistorianSDK_2023R2/histsdk-migration.zip` from `10.100.0.48`) as the family-branded shared +library **`ZB.MOM.WW.SPHistorianClient`** (System Platform Historian Client), following the same +conventions as the other `ZB.MOM.WW.*` shared libraries in this repo. + +## Context — what the source bundle contains + +`histsdk-migration.zip` → `histsdk-migration/`: + +- `histsdk/` — the SDK git repo. `src/AVEVA.Historian.Client/` is a **pure-managed .NET 10** client + for AVEVA Historian (no `aahClientManaged.dll` / `aahClient.dll` / native AVEVA runtime — the wire + protocol is reverse-engineered and re-implemented in C#). ~165–188 unit + gated-live tests pass. +- `analysis-2023r2/` — reverse-engineering analysis (recovered protos, decompiled stock contract, + transport writeup). **Kept separate from the repo on purpose.** + +Two transport families exist in the SDK: + +| Transport | Protocol | Platform | Verification | +|---|---|---|---| +| `LocalPipe`, `RemoteTcpIntegrated`, `RemoteTcpCertificate` | WCF/MDAS (2020) | **Windows-only** | **live-verified**: raw/aggregate(16 modes)/at-time/event reads, browse, metadata, status, `EnsureTag`/`DeleteTag` | +| `RemoteGrpc` | gRPC (2023 R2) | cross-platform (Grpc.Net.Client/.Web) | unit-tested; **not yet live-verified** against a real 2023 R2 server (`ExchangeKey` auth step unproven) | + +## Decisions (locked) + +1. **Approach: port + rebrand.** Copy the SDK source into `ZB.MOM.WW.SPHistorianClient`, rename the + root namespace, adopt ZB conventions, bring the unit tests, drop non-shippable artifacts. One + coherent shared library — a published package should not ship a third-party (AVEVA) namespace or + non-redistributable reverse-engineering artifacts. +2. **Transports: both WCF + gRPC.** Ship everything that works. WCF members keep + `[SupportedOSPlatform("windows")]`; the gRPC path runs anywhere. No working code discarded. +3. **Not a "component normalization."** There is no duplicated historian code across the three apps + to converge — this is a net-new shared library that simply follows ZB packaging conventions. + +## Repository layout + +Plain files committed into this repo (NOT a nested git repo — see the +`shared-libs-are-plain-files-not-nested-repos` convention): + +``` +ZB.MOM.WW.SPHistorianClient/ + Directory.Build.props # net10.0, Nullable, ImplicitUsings, LangVersion latest, Version 0.1.0, central pkg mgmt + Directory.Packages.props # central PackageVersion entries + ZB.MOM.WW.SPHistorianClient.slnx + CLAUDE.md README.md .gitignore + src/ZB.MOM.WW.SPHistorianClient/ # the single package + HistorianClient.cs, HistorianClientOptions.cs, HistorianTransport.cs + Models/ Protocol/ Transport/ Wcf/ Wcf/Contracts/ Grpc/ Grpc/Protos/*.proto + DependencyInjection/AddZbSpHistorianClient (ZB-idiomatic DI extension) + tests/ZB.MOM.WW.SPHistorianClient.Tests/ # offline unit/golden-byte + gated-live integration + artifacts/ # dotnet pack output +``` + +## Port mechanics + +- Copy `src/AVEVA.Historian.Client/` and `tests/AVEVA.Historian.Client.Tests/` from the bundle. +- Rename the C# root namespace `AVEVA.Historian.Client` → `ZB.MOM.WW.SPHistorianClient` across all + files: 74 `namespace` declarations spanning the root + 6 sub-namespaces + (`.Models`, `.Wcf`, `.Wcf.Contracts`, `.Protocol`, `.Transport`, `.Grpc`), all `using` directives, + and the `InternalsVisibleTo` to the test assembly. Drop the `InternalsVisibleTo` to + `AVEVA.Historian.ReverseEngineering` (tool not shipped). +- **Leave the proto wire contracts untouched:** the 6 `Grpc/Protos/*.proto` keep + `option csharp_namespace = "ArchestrA.Grpc.Contract.*"` — that is AVEVA's wire contract, not ours. + `Grpc.Tools` keeps generating the client stubs at build. +- Convert inline `PackageReference` versions to central management in `Directory.Packages.props`, + matching the `ZB.MOM.WW.Telemetry` template. + +## Dependencies + +- **Library:** `Google.Protobuf`, `Grpc.Net.Client`, `Grpc.Net.Client.Web`, `Grpc.Tools` (build-only, + `PrivateAssets=all`), `System.ServiceModel.NetNamedPipe`, `System.ServiceModel.NetTcp`, + `System.Security.Cryptography.Xml`. Add `Microsoft.Extensions.DependencyInjection.Abstractions` + + `Microsoft.Extensions.Options` for the DI extension. +- **Tests:** `xunit`, `xunit.runner.visualstudio`, `Microsoft.NET.Test.Sdk`, `coverlet.collector`, + `Microsoft.Data.SqlClient` (SQL post-check tests). + +## Excluded (safety / non-redistributable / Windows-native) + +- `tools/` reverse-engineering harnesses (.NET Framework, reference native AVEVA binaries). +- `analysis-2023r2/decompiled/` — proprietary AVEVA decompilations (not redistributable). +- `scripts/` — Frida / PowerShell / Python capture tooling. +- `docs/reverse-engineering/` — identity-bearing `.ndjson` / capture evidence. + +**Kept:** the recovered `.proto` files (needed to build), the offline unit tests, and a sanitized +architecture/surface summary folded into `CLAUDE.md` / `README.md`. `.gitignore` blocks the +identity-bearing patterns (`*.ndjson`, `current/`, `aveva-install-*/`, `artifacts/`-raw, etc.). + +## Public surface (preserved 1:1) + +`HistorianClient` + `HistorianClientOptions` façade; `Models/*`; `HistorianTransport` enum +(`LocalPipe` / `RemoteTcpIntegrated` / `RemoteTcpCertificate` / `RemoteGrpc`); operations: +`ProbeAsync`, `ReadRawAsync` / `ReadAggregateAsync` / `ReadAtTimeAsync`, `ReadEventsAsync`, +`BrowseTagNamesAsync`, `GetTagMetadataAsync`, status calls, `EnsureTagAsync` / `DeleteTagAsync`. + +**One ZB-idiomatic addition:** `AddZbSpHistorianClient(...)` DI extension mirroring `AddZbTelemetry` +— thin: binds `HistorianClientOptions` and registers `HistorianClient`. Optional to consumers. + +## Cross-platform & testing posture + +- WCF members already carry `[SupportedOSPlatform("windows")]`; the library builds and unit-tests on + macOS/Linux. gRPC path is portable. +- Offline unit/golden-byte tests run anywhere. Live integration tests stay gated by `HISTORIAN_*` + env vars and skip cleanly when unset. +- Verify `dotnet build` + `dotnet test` pass locally (macOS) before finishing. + +## Packaging + +`dotnet pack -c Release -o ./artifacts` → `ZB.MOM.WW.SPHistorianClient.0.1.0.nupkg`. Gitea URLs in +package metadata. **Not pushed/published** to any feed unless explicitly requested. + +## Out of scope (this pass) + +- Wiring `ZB.MOM.WW.SPHistorianClient` into any consumer (e.g. OtOpcUa Phase C HistoryRead) — a + separate follow-on. +- Live-verifying the gRPC `RemoteGrpc` path against a real 2023 R2 server. +- Writing samples (`AddS2`) — architecturally blocked in the source SDK; remains out of scope.