6.0 KiB
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. For the alarm-history store-and-forward flow that drains into it, see 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 x64aahClientManagedbuild; 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 DataValues 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 byscripts/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 fromOTOPCUA_HISTORIAN_SERVER/_PORT/_INTEGRATED/_USER/_PASSetc. (seesrc/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Program.cs). - Pipe-only mode: with
OTOPCUA_HISTORIAN_ENABLED!=truethe 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 — where the sidecar fits in a deployment and how it's installed
- AlarmHistorian.md — the alarm store-and-forward flow that feeds the write-back path