Files
scadaproj/ZB.MOM.WW.SPHistorianClient/CLAUDE.md
T

7.3 KiB

ZB.MOM.WW.SPHistorianClient

Pure-managed .NET 10 client for AVEVA System Platform Historian (Wonderware), for the ZB.MOM.WW SCADA family. This is a library, not a service — it is linked directly into the consuming application and runs in-process alongside it.

The wire protocol is reverse-engineered and re-implemented in C#. There is no native AVEVA runtime dependencyaahClientManaged.dll / aahClient.dll are not referenced or loaded. The library runs on any OS for offline/unit testing; live WCF transports require Windows.

Status: ported and rebranded into this repo; builds and 191 tests pass on macOS. Version 0.1.0. NOT yet packed/published to any NuGet feed. NOT yet adopted by any consumer.


Supported operation surface

All operations are exposed via the public façade HistorianClient.

Operation Status
ProbeAsync live-verified
ReadRawAsync live-verified
ReadAggregateAsync live-verified across the RetrievalMode enum (15 modes)
ReadAtTimeAsync live-verified
ReadBlocksAsync block history read
ReadEventsAsync live-verified (typed event + property bag)
BrowseTagNamesAsync live-verified
GetTagMetadataAsync live-verified across many native data-type codes
GetConnectionStatusAsync synthesized from authenticated probe
GetStoreForwardStatusAsync synthesized defaults
GetSystemParameterAsync live-verified
EnsureTagAsync live-verified for analog Float; Double/Int2/Int4/UInt4 supported (optional ApplyScaling persists distinct MinRaw/MaxRaw)
DeleteTagAsync live-verified (known issue: server-side cascade may not always complete; use SMC as fallback to clean up sandbox tags)

Out of scope

  • Writing sample values (AddS2) is architecturally blocked — the server runtime cache only ingests from configured IOServer / Application Server pipelines, not from a standalone AddTag client flow.
  • Store-forward write, historian configuration changes, discrete/string tag creation (native AddTag rejects them).

Transport matrix

Configured via HistorianClientOptions.Transport (HistorianTransport enum).

Transport Protocol Platform Verification
LocalPipe WCF/MDAS over Net.NamedPipe (local) Windows-only live-verified (read / browse / metadata / event / status)
RemoteTcpIntegrated WCF/MDAS over Net.TCP + Windows transport auth Windows-only live-verified (full read / browse / metadata / event / status surface)
RemoteTcpCertificate WCF/MDAS over Net.TCP + server-cert TLS Windows-only ProbeAsync live-verified; deeper coverage pending
RemoteGrpc gRPC (2023 R2), Grpc.Net.Client/.Web cross-platform unit-tested; NOT yet live-verified against a real 2023 R2 server (ExchangeKey auth step is unproven)

DI registration

services.AddZbSpHistorianClient(new HistorianClientOptions
{
    Host = "localhost",
    IntegratedSecurity = true,
    Transport = HistorianTransport.RemoteTcpIntegrated,
});

AddZbSpHistorianClient registers the options instance (singleton) + HistorianClient (transient). Because HistorianClientOptions uses required/init-only properties, the consumer passes a fully-built instance. In a real app, bind it from configuration:

services.AddZbSpHistorianClient(
    config.GetSection("Historian").Get<HistorianClientOptions>()!);

The package depends only on Microsoft.Extensions.DependencyInjection.Abstractions — no ASP.NET Core or framework reference required.


Architecture

Three decoupled subsystems under src/ZB.MOM.WW.SPHistorianClient/:

Subsystem Path Responsibility
Public façade HistorianClient.cs, HistorianClientOptions.cs Entry point; delegates to the transport layer
WCF/MDAS layer Wcf/ Managed WCF transport; custom MdasMessageEncoder, binding factory, versioned service contracts
Binary frame layer Protocol/ Historian2020ProtocolDialect; methods without protocol evidence throw ProtocolEvidenceMissingException
Public models Models/ Public DTOs and enums (HistorianSample, HistorianTagMetadata, RetrievalMode, …)
gRPC transport Grpc/ 2023 R2 gRPC transport; recovered .proto compiled by Grpc.Tools at build; wire contracts keep AVEVA's ArchestrA.Grpc.Contract.* namespaces

Build, test, and pack commands

# From ZB.MOM.WW.SPHistorianClient/

# Build
dotnet build ZB.MOM.WW.SPHistorianClient.slnx
dotnet build ZB.MOM.WW.SPHistorianClient.slnx -c Release

# Test (offline unit/golden-byte tests run on any OS;
# Windows-only WCF tests no-op off Windows;
# live integration tests skip when env vars are unset — see below)
dotnet test ZB.MOM.WW.SPHistorianClient.slnx

# Run a single test class
dotnet test ZB.MOM.WW.SPHistorianClient.slnx --filter "FullyQualifiedName~WcfDataQueryProtocolTests"

# Pack (one .nupkg lands in artifacts/)
dotnet pack ZB.MOM.WW.SPHistorianClient.slnx -c Release -o ./artifacts

Test posture

All tests run offline by default; live integration tests are gated by environment variables and skip cleanly when unset.

Test type Count
Offline unit / golden-byte tests the bulk of the 191 total
WCF live integration (gated) skipped off Windows or when HISTORIAN_HOST is unset
gRPC live integration (gated) skipped when HISTORIAN_GRPC_HOST is unset
Total 191

GeneratePackageOnBuild is off — pack explicitly with the command above.

Live integration test environment variables

WCF transports:

Variable Required Notes
HISTORIAN_HOST yes (gates WCF tests) Historian server hostname or IP
HISTORIAN_TEST_TAG yes A historized tag that exists on the server (use a system tag such as SysTimeSec for safe testing)
HISTORIAN_USER optional Omit to use Windows integrated security
HISTORIAN_PASSWORD optional Only used when HISTORIAN_USER is set
HISTORIAN_TAG_FILTER optional Browse filter pattern passed to BrowseTagNamesAsync

gRPC transport:

Variable Required Notes
HISTORIAN_GRPC_HOST yes (gates gRPC tests) 2023 R2 gRPC endpoint host
HISTORIAN_GRPC_PORT optional Default 32565
HISTORIAN_GRPC_TLS optional Set true to enable TLS
HISTORIAN_GRPC_DNSID optional Override DNS identity for certificate validation

Status and provenance

Version 0.1.0. Ported from a reverse-engineering migration bundle and rebranded into this repo. Builds and all 191 tests pass on macOS. NOT yet packed/published to the Gitea NuGet feed. NOT yet adopted by any consumer (OtOpcUa, MxAccessGateway, ScadaBridge).

Production code is pure-managed .NET 10 with no native AVEVA reference. Reverse-engineering tooling and proprietary decompilations from the source bundle were intentionally excluded from this repo.

Safety rules for this library (hard — never violate):

  • Never commit real server hostnames, IP addresses, or credentials.
  • Never commit customer tag names or live capture data (.gitignore blocks *.ndjson and similar raw-capture extensions).
  • Use only generic placeholders (localhost, <your-historized-tag>) and built-in AVEVA system tags (e.g., SysTimeSec) in all documentation and test defaults.