# TC3 EventLogger spike — managed-wrapper investigation **Question (b) from the PR 5.1 / #316 plan**: Does Beckhoff publish a managed `TcEventLogger` wrapper that lets the driver subscribe to alarms via `EventLogger.AlarmRaised` instead of decoding AMS port 110 notifications by hand? ## TL;DR **No managed wrapper.** The `Beckhoff.TwinCAT.Ads` v6 NuGet (the regular managed SDK the driver already takes a dependency on) ships only the ADS read/write/notification surface — it does not surface `TcEventLogger` on the .NET side. The C++ TcCOM headers (`TcEventLogger.h` etc.) exist in the on-box TwinCAT install (`%TC_INSTALLPATH%\Components\TcEventLogger\`) but there is no managed projection of those COM interfaces in any official Beckhoff NuGet as of TC3 build 4024.x. Decision: **ship a binary-protocol decode against AMS port 110** (`AMSPORT_EVENTLOG`) with index group `ADSIGRP_TCEVENTLOG_ALARMS`. The decoder lands in `AdsTwinCATAlarmGate` (production) and `NullTwinCATAlarmGate` (default / no-op). Best-effort field decoding — fields the protocol analyzer hasn't yet identified surface as `"Unknown"`. ## What was checked | Source | Result | | --- | --- | | `Beckhoff.TwinCAT.Ads` v6.x NuGet, namespace inventory | `TwinCAT.Ads`, `TwinCAT.Ads.SumCommand`, `TwinCAT.Ads.TypeSystem`, `TwinCAT.TypeSystem`. **No** `TcEventLogger` namespace. | | `Beckhoff.TwinCAT.Ads.TcpRouter` v6.x NuGet | Router only; no EventLogger surface. | | Beckhoff Information System (Infosys) → TwinCAT 3 → EventLogger → API reference | Documents only the C++ TcCOM API + the PLC-side `Tc3_EventLogger` library. No managed-language section. | | TwinCAT install on dev box → `Components\TcEventLogger\` | C++ headers + DLL only; the `.tlb` could be COM-imported via `tlbimp` but that creates a brittle install-path-coupled binding. | | Public Beckhoff GitHub orgs | `Beckhoff/TwinCAT-Tools-Library` etc. — no managed EventLogger wrapper. | ## Why decode at the wire? A `tlbimp` projection of the on-box TcCOM `.tlb` would technically work but introduces three problems: 1. **Install-path coupling** — the `.tlb` lives under `%TC_INSTALLPATH%`; the driver would need to find / load it at runtime + ship a per-build interop assembly. 2. **Bitness lock-in** — TcCOM is x86; the driver builds AnyCPU. 3. **No upgrade path** — Beckhoff makes no API-stability guarantees on the TcCOM surface across TC3 builds. Direct AMS-port-110 notifications keep the driver coupled to **only** the `Beckhoff.TwinCAT.Ads` v6 NuGet's stable wire surface. Trade-off: the binary protocol is undocumented in managed-code form; we work around that by: - Writing a permissive decoder that surfaces unrecognised fields as `"Unknown"` rather than throwing. - Gating the entire bridge behind `EnableAlarms=false` so deployments that don't run TcEventLogger pay no cost. - Logging the raw payload at TRACE level when a decode partially succeeds, so operators can hand the bytes to the integration team for follow-up decoding. ## What ships in PR 5.1 - `ITwinCATAlarmGate` interface — driver-internal seam. - `NullTwinCATAlarmGate` — default no-op implementation, used when `EnableAlarms=false` and as the unit-test substitute base. - `TwinCATAlarmSource` — projects `TwinCATAlarmEvent` onto the driver's `IAlarmSource` surface; handles subscription bookkeeping + source-id filtering. - `TwinCATDriver` declares `IAlarmSource`; methods short-circuit when the gate is null (default). - Production `AdsTwinCATAlarmGate` (with the binary decoder) is scaffolded — the wire path is best-effort and can be tightened in a follow-up PR without touching the driver's public surface. ## Open questions for the follow-up PR 1. **Exact byte layout** of the alarm-list notification payload — needs a wire trace from a known-good TC3 EventLogger configuration compared against the C++ `TcEventLogger.h` struct definitions. 2. **Acknowledge wire format** — the `AcknowledgeAsync` path writes to the EventLogger ack index group; the operand layout (event-id vs. condition-id mapping) is best-effort in PR 5.1. 3. **Multi-language alarm text** — TC3 EventLogger supports localized message texts. The decoder should pick the runtime's configured language; PR 5.1 falls back to the first text it finds. 4. **Active-alarm refresh on subscribe** — TC3's `RefreshActive` semantic is documented in C++ but not exposed through AMS port 110 notifications directly. The follow-up PR should investigate whether a separate `Read` against the active-alarm-list index group can backfill the snapshot at subscribe time. ## Why land PR 5.1 anyway The driver's public `IAlarmSource` surface, the options knob, the unit tests, the CLI verb, and the integration-test scaffold are all independent of the wire decoder's completeness. Deferring the entire PR until decode coverage is 100 % blocks every consumer that just needs the capability negotiation contract (the OPC UA server's `DriverNodeManager` checks `driver is IAlarmSource` to decide whether to expose the alarm subtree). Shipping the gated scaffold now lets those consumers light up without committing to a specific decoder quality bar.