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