SendEvent over gRPC: implement + live-validate (was capture-gated)
Captured the native 2023 R2 client's gRPC event send (new capture-send-event harness scenario): it rides HistoryService.AddStreamValues with the SAME "OS" (0x534F) storage-sample buffer the WCF path already uses (HistorianEventWrite- Protocol) — confirming "no distinct RPC", and that it is NOT the historical write's "ON" buffer. Diffed the write-enabled vs read-only v8 Event open: byte- identical apart from per-session crypto, so the existing OpenSession event path is reused unchanged. So SendEvent-over-gRPC was pure assembly of proven parts: - HistorianGrpcEventWriteOrchestrator = v8 Event open + CM_EVENT registration (UpdC3/RegisterTags/EnsureTags) + AddStreamValues(OS buffer). - HistorianClient.SendEventAsync now routes to it for RemoteGrpc (WCF otherwise). Live-validated end-to-end against the 2023 R2 server: pure-managed SDK send → AddStreamValues BSuccess=true → the event reads back from the server (markers confirmed in returned event rows). The native gRPC RegisterTags(24B) + EnsureTags(86B) byte-match our serializers (new GrpcEventSendProtocolTests golden, closing the 83-vs-86 EnsureTags question). Gated live test SendEventAsync_OverGrpc_AcceptsEvent (opt-in HISTORIAN_GRPC_EVENT_SEND=1). 331 offline tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B6mcaT2PjRFKcogzp9UkfC
This commit is contained in:
@@ -114,18 +114,24 @@ public sealed class HistorianClient : IAsyncDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a single <see cref="HistorianEvent"/> to the Historian's built-in CM_EVENT tag
|
||||
/// over the WCF event pipeline (Open2 event mode → CM_EVENT registration → AddS2). The
|
||||
/// event is appended to the historian's event history and is readable back via
|
||||
/// <see cref="ReadEventsAsync"/> / the <c>v_AlarmEventHistory2</c> view. Only original
|
||||
/// events (<see cref="HistorianEvent.RevisionVersion"/> = 0) with string-valued properties
|
||||
/// are supported; other property value types and revision/update/delete events throw
|
||||
/// Sends a single <see cref="HistorianEvent"/> to the Historian's built-in CM_EVENT tag.
|
||||
/// Over WCF this runs Open2 event mode → CM_EVENT registration → AddS2; over the 2023 R2
|
||||
/// <see cref="HistorianTransport.RemoteGrpc"/> transport it runs the captured-equivalent
|
||||
/// v8 Event OpenConnection → CM_EVENT registration → <c>HistoryService.AddStreamValues</c>
|
||||
/// with the same "OS" event buffer (live-captured 2026-06-23 — the send rides the same RPC
|
||||
/// and buffer as the WCF path, not a distinct event RPC). The event is appended to the
|
||||
/// historian's event history and is readable back via <see cref="ReadEventsAsync"/> /
|
||||
/// the <c>v_AlarmEventHistory2</c> view. Only original events
|
||||
/// (<see cref="HistorianEvent.RevisionVersion"/> = 0) with string-valued properties are
|
||||
/// supported; other property value types and revision/update/delete events throw
|
||||
/// <see cref="ProtocolEvidenceMissingException"/> until their wire encoding is captured.
|
||||
/// </summary>
|
||||
public Task<bool> SendEventAsync(HistorianEvent historianEvent, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(historianEvent);
|
||||
return new HistorianWcfEventOrchestrator(_options).SendEventAsync(historianEvent, cancellationToken);
|
||||
return _options.Transport == HistorianTransport.RemoteGrpc
|
||||
? new Grpc.HistorianGrpcEventWriteOrchestrator(_options).SendEventAsync(historianEvent, cancellationToken)
|
||||
: new HistorianWcfEventOrchestrator(_options).SendEventAsync(historianEvent, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user