feat(cli): historyread --events reads event history over the wire (lmxopcua#776)
v2-ci / unit-tests (push) Has been cancelled

Item 1 of #776 (the B2 over-the-wire ask): Client.CLI had no way to issue a
HistoryReadEvents. `historyread --events` now sends ReadEventDetails to an
event notifier and prints one line per event in select-clause order.
--fields picks the select clauses (default EventId, EventType, Time,
SourceName, Severity, Message, ConditionName (on ConditionType), SourceNode);
--of-type adds an OfType where-clause. --events is exclusive with --modified /
--aggregate, and --fields / --of-type require it.

Client.Shared gains IOpcUaClientService/ISessionAdapter.HistoryReadEventsAsync
(default-implemented as NotSupported for other implementations) backed by the
real session adapter through HistoryReadPaging, so continuation points are
followed up to --max and a Bad node status is surfaced, not printed as empty.

Tests: CLI tests over the fake service (default filter, --fields/--of-type
filter, option conflicts); an over-the-wire test drives the shared client's
event read against the in-process server — the default fields come back
non-null, OfType filters, an invalid where-clause is BadEventFilterInvalid.
The other #776 items are decisions and are left open.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
Joseph Doherty
2026-11-09 16:07:11 -05:00
parent e790938f94
commit 380ec541c2
11 changed files with 369 additions and 4 deletions
@@ -207,6 +207,21 @@ public interface IOpcUaClientService : IDisposable
DateTime endTime, int maxValues = 1000, CancellationToken ct = default) =>
throw new NotSupportedException("This client service does not support modified-value history reads.");
/// <summary>
/// Reads event history (Part 11 HistoryReadEvents) from an event notifier: the events in the window that pass the
/// filter's where-clause, each projected onto its select clauses.
/// </summary>
/// <param name="nodeId">The event notifier node (an equipment/device folder, or the Server object).</param>
/// <param name="startTime">Window start.</param>
/// <param name="endTime">Window end.</param>
/// <param name="filter">The event filter: the select clauses to project and an optional where-clause.</param>
/// <param name="maxEvents">The maximum number of events to return.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>The events in server order.</returns>
Task<IReadOnlyList<HistoryEventEntry>> HistoryReadEventsAsync(NodeId nodeId, DateTime startTime, DateTime endTime,
EventFilter filter, int maxEvents = 1000, CancellationToken ct = default) =>
throw new NotSupportedException("This client service does not support event history reads.");
/// <summary>
/// Sends a HistoryUpdate request (insert/replace/update or delete) for the supplied details.
/// </summary>