Files
lmxopcua/docs/v3/twincat-eventlogger-spike.md

5.1 KiB

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.