docs(sphistorianclient): add CLAUDE.md + README.md
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
# 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 dependency** — `aahClientManaged.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 all 16 retrieval 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 (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
|
||||
|
||||
```csharp
|
||||
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:
|
||||
|
||||
```csharp
|
||||
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
|
||||
|
||||
```bash
|
||||
# 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.
|
||||
@@ -0,0 +1,96 @@
|
||||
# ZB.MOM.WW.SPHistorianClient
|
||||
|
||||
Pure-managed .NET 10 client library for **AVEVA System Platform Historian** (Wonderware).
|
||||
Part of the ZB.MOM.WW SCADA family.
|
||||
|
||||
No native AVEVA runtime dependency — `aahClientManaged.dll` / `aahClient.dll` are **not**
|
||||
required. The wire protocol is re-implemented in managed C#. Live WCF transports require
|
||||
Windows; offline tests and gRPC run cross-platform.
|
||||
|
||||
---
|
||||
|
||||
## Quick start
|
||||
|
||||
```csharp
|
||||
using ZB.MOM.WW.SPHistorianClient;
|
||||
using ZB.MOM.WW.SPHistorianClient.Models;
|
||||
|
||||
await using HistorianClient client = new(new HistorianClientOptions
|
||||
{
|
||||
Host = "localhost",
|
||||
IntegratedSecurity = true,
|
||||
Transport = HistorianTransport.LocalPipe,
|
||||
});
|
||||
|
||||
DateTime endUtc = DateTime.UtcNow;
|
||||
DateTime startUtc = endUtc - TimeSpan.FromMinutes(10);
|
||||
|
||||
await foreach (HistorianSample sample in client.ReadRawAsync(
|
||||
"SysTimeSec", startUtc, endUtc, maxValues: 100))
|
||||
{
|
||||
Console.WriteLine($"{sample.TimestampUtc:o} {sample.NumericValue} Q={sample.Quality}");
|
||||
}
|
||||
```
|
||||
|
||||
### DI registration
|
||||
|
||||
```csharp
|
||||
// In Program.cs / Startup
|
||||
services.AddZbSpHistorianClient(
|
||||
config.GetSection("Historian").Get<HistorianClientOptions>()!);
|
||||
|
||||
// Resolves HistorianClient (transient) from the container
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Supported operations
|
||||
|
||||
| Operation | Status |
|
||||
|---|---|
|
||||
| `ProbeAsync` | live-verified |
|
||||
| `ReadRawAsync` | live-verified |
|
||||
| `ReadAggregateAsync` | live-verified across all 16 retrieval 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 |
|
||||
| `DeleteTagAsync` | live-verified (see note below) |
|
||||
|
||||
> **Note:** Writing sample values is architecturally blocked — the Historian server cache only
|
||||
> ingests from configured IOServer / Application Server pipelines. `DeleteTagAsync` server-side
|
||||
> cascade may not always complete; use SMC as a fallback to clean up sandbox tags.
|
||||
|
||||
---
|
||||
|
||||
## Transport matrix
|
||||
|
||||
| Transport | Protocol | Platform | Verification |
|
||||
|---|---|---|---|
|
||||
| `LocalPipe` | WCF/MDAS over Net.NamedPipe | Windows-only | live-verified |
|
||||
| `RemoteTcpIntegrated` | WCF/MDAS over Net.TCP + Windows auth | Windows-only | live-verified |
|
||||
| `RemoteTcpCertificate` | WCF/MDAS over Net.TCP + TLS | Windows-only | `ProbeAsync` live-verified; deeper coverage pending |
|
||||
| `RemoteGrpc` | gRPC (2023 R2) | cross-platform | unit-tested; live verification pending |
|
||||
|
||||
---
|
||||
|
||||
## Build and test
|
||||
|
||||
```bash
|
||||
# From ZB.MOM.WW.SPHistorianClient/
|
||||
|
||||
dotnet build ZB.MOM.WW.SPHistorianClient.slnx
|
||||
dotnet test ZB.MOM.WW.SPHistorianClient.slnx
|
||||
|
||||
# Pack
|
||||
dotnet pack ZB.MOM.WW.SPHistorianClient.slnx -c Release -o ./artifacts
|
||||
```
|
||||
|
||||
Offline unit tests (191 total) run on any OS. Live integration tests are gated by environment
|
||||
variables (`HISTORIAN_HOST` for WCF, `HISTORIAN_GRPC_HOST` for gRPC) and skip cleanly when unset.
|
||||
See `CLAUDE.md` for the full environment variable reference.
|
||||
Reference in New Issue
Block a user