120 lines
6.0 KiB
Markdown
120 lines
6.0 KiB
Markdown
# Wonderware Historian Backend
|
|
|
|
The Wonderware Historian backend is **not a tag driver** — it has no address
|
|
space, no `IDriver` lifecycle, and exposes no PLC. It is a **server-side
|
|
historian sink**: an optional sidecar that gives OtOpcUa read access to AVEVA
|
|
System Platform (Wonderware) Historian history and a write-back path for alarm
|
|
events. It runs only when `Historian:Wonderware:Enabled=true`.
|
|
|
|
For the sidecar's place in a deployment, see
|
|
[ServiceHosting.md](../ServiceHosting.md). For the alarm-history store-and-forward
|
|
flow that drains into it, see [AlarmHistorian.md](../AlarmHistorian.md).
|
|
|
|
## Architecture
|
|
|
|
```
|
|
+-------------------------------------------+
|
|
| OtOpcUa Host (.NET 10 AnyCPU) |
|
|
| Server.History.IHistoryRouter --read--+--+
|
|
| Core.AlarmHistorian.SqliteStore | |
|
|
| AndForwardSink --write----+--+
|
|
| WonderwareHistorianClient (.NET 10) | |
|
|
+-------------------------------------------+ |
|
|
| named pipe
|
|
MessagePack frames | (shared secret + allowed-SID)
|
|
v
|
|
+-------------------------------------------+
|
|
| OtOpcUaWonderwareHistorian (sidecar) |
|
|
| net48 / x64 |
|
|
| PipeServer + HistorianFrameHandler |
|
|
| HistorianDataSource (reads) |
|
|
| SdkAlarmHistorianWriteBackend (writes) |
|
|
| aahClientManaged / HistorianAccess |
|
|
+-------------------------------------------+
|
|
```
|
|
|
|
The split exists because the AVEVA Historian SDK (`aahClientManaged` +
|
|
native `aahClient.dll`) is .NET Framework 4.8 / x64 — so it lives out-of-process
|
|
in the sidecar, and everything in the OtOpcUa host stays .NET 10 AnyCPU. The
|
|
host never references the SDK; it speaks the pipe contract only.
|
|
|
|
## Project split
|
|
|
|
| Project | Target | Role |
|
|
|---------|--------|------|
|
|
| `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/` | net48 / x64 | The **sidecar** (`OutputType=Exe`). Hosts the named-pipe server, the historian reader, and the alarm-write backend bound to the AVEVA SDK |
|
|
| `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client/` | net10.0 | `WonderwareHistorianClient` — the in-host pipe client consumed by the history router and the alarm sink |
|
|
| `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client.Contracts/` | net10.0 | `WonderwareHistorianClientOptions` (pipe name, shared secret, timeouts) |
|
|
|
|
> The csproj targets **net48 / x64** (`PlatformTarget=x64`) — the AVEVA Historian
|
|
> 2020 SDK ships an x64 `aahClientManaged` build; the earlier x86 default was an
|
|
> inherited v1 artifact, not a constraint of the Historian SDK.
|
|
|
|
## What it does
|
|
|
|
The sidecar exposes two surfaces, both over the same named pipe:
|
|
|
|
### Read path — `IHistorianDataSource`
|
|
|
|
`HistorianDataSource` (in the sidecar) reads history through the
|
|
`aahClientManaged` SDK; `WonderwareHistorianClient` (in the host) implements
|
|
`IHistorianDataSource` and maps returned samples back to OPC UA `DataValue`s for
|
|
`Server.History.IHistoryRouter`. The read surface is:
|
|
|
|
| Call | Maps to |
|
|
|------|---------|
|
|
| `ReadRawAsync` | Raw historical samples for a tag over a time range |
|
|
| `ReadProcessedAsync` / `ReadAggregateAsync` | Aggregated samples at an interval |
|
|
| `ReadAtTimeAsync` | Samples at specific timestamps |
|
|
| `ReadEventsAsync` | Historical events for a source |
|
|
| `GetHealthSnapshot` | Connection health for the host-side health surface |
|
|
|
|
### Write path — alarm-historian write-back
|
|
|
|
`WonderwareHistorianClient` also implements `IAlarmHistorianWriter`. Alarm events
|
|
are drained into the sidecar from `Core.AlarmHistorian.SqliteStoreAndForwardSink`
|
|
and persisted by `SdkAlarmHistorianWriteBackend` via
|
|
`HistorianAccess.AddStreamedValue(HistorianEvent, out HistorianAccessError)`. The
|
|
production writer is wrapped by `AahClientManagedAlarmEventWriter`, which handles
|
|
batch orchestration and per-event `HistorianAccessError` outcome classification
|
|
(connection-class errors are retryable; malformed-argument errors are not).
|
|
|
|
The alarm write path can be disabled independently of reads by setting
|
|
`OTOPCUA_HISTORIAN_ALARM_WRITE_ENABLED=false` — the sidecar then rejects
|
|
`WriteAlarmEvents` frames while still serving history reads.
|
|
|
|
## Hosting and IPC
|
|
|
|
- **Process**: `OtOpcUaWonderwareHistorian`, installed/managed by
|
|
`scripts/install/` (`Install-Services.ps1 -InstallWonderwareHistorian`).
|
|
- **Spawn config**: the supervisor passes the pipe name, the allowed server
|
|
principal SID, and a per-process shared secret via environment
|
|
(`OTOPCUA_HISTORIAN_PIPE`, `OTOPCUA_ALLOWED_SID`, `OTOPCUA_HISTORIAN_SECRET`);
|
|
Historian connection settings come from `OTOPCUA_HISTORIAN_SERVER` /
|
|
`_PORT` / `_INTEGRATED` / `_USER` / `_PASS` etc. (see
|
|
`src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Program.cs`).
|
|
- **Pipe-only mode**: with `OTOPCUA_HISTORIAN_ENABLED!=true` the sidecar boots
|
|
without loading the SDK at all — used for smoke and IPC tests.
|
|
- **Wire**: MessagePack-framed request/reply; the named-pipe ACL restricts the
|
|
pipe to the allowed SID and the client proves the shared secret in a Hello
|
|
frame. The client owns a single channel with one in-flight call at a time and
|
|
retries a transport failure once before propagating — broader backoff is the
|
|
caller's responsibility.
|
|
|
|
## Testing
|
|
|
|
- **Sidecar unit tests** —
|
|
`tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/` cover the
|
|
reader, the alarm-write backend outcome classification, and the pipe-frame
|
|
handler with a faked SDK seam.
|
|
- **Client unit tests** —
|
|
`tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client.Tests/`
|
|
cover the pipe client + framing against an in-process duplex pipe pair.
|
|
|
|
## Further reading
|
|
|
|
- [ServiceHosting.md](../ServiceHosting.md) — where the sidecar fits in a
|
|
deployment and how it's installed
|
|
- [AlarmHistorian.md](../AlarmHistorian.md) — the alarm store-and-forward flow
|
|
that feeds the write-back path
|