# 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.