Files
histsdk/tests/AVEVA.Historian.Client.Tests/GrpcEventSendProtocolTests.cs
T
Joseph Doherty afc7c4bf96 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
2026-06-23 15:37:22 -04:00

54 lines
2.6 KiB
C#

using System;
using System.Buffers.Binary;
using AVEVA.Historian.Client.Wcf;
using Xunit;
namespace AVEVA.Historian.Client.Tests;
/// <summary>
/// Golden-byte coverage for the 2023 R2 gRPC event-SEND registration buffers, pinned against a live
/// native capture (<c>capture-send-event</c> scenario, 2026-06-23). The send itself rides
/// <c>HistoryService.AddStreamValues</c> with the same "OS" buffer the WCF path uses
/// (<see cref="HistorianEventWriteProtocol"/>, already golden-tested in
/// <c>WcfEventWriteProtocolTests</c>); what is gRPC-specific is the CM_EVENT registration the event
/// connection performs first (RegisterTags + the 86-byte gRPC EnsureTags). These fixtures are the raw
/// bytes the native client sent on the wire — they carry no identity (CM_EVENT / "AnE Event" /
/// constant tag + event-type GUIDs / a registration FILETIME).
/// </summary>
public class GrpcEventSendProtocolTests
{
// GrpcHistoryClient.RegisterTags.tagInfos captured from the native event connection: the packet
// header 50 67 02 00 + count(1) + the 16-byte CM_EVENT tag GUID.
private const string CapturedRegisterTagsHex =
"506702000100000045813b35f05d464da253871aef49b321";
// GrpcHistoryClient.EnsureTags.tagInfos captured from the native event connection (86 bytes): the
// 8-byte EnsureTags header + CM_EVENT CTagMetadata + a registration FILETIME + the …e01f2f27
// event-type GUID.
private const string CapturedEnsureTagsHex =
"4e670300010000000386000545813b35f05d464da253871aef49b321090800434d5f4556454e54090900416e45204576656e7402020100000001000000004e18d6bd4503dd0142ae595fb63b604791a5ab0be01f2f27";
[Fact]
public void BuildRegisterCmEventInputBuffer_MatchesNativeGrpcCapture()
{
byte[] expected = Convert.FromHexString(CapturedRegisterTagsHex);
byte[] actual = HistorianEventRegistrationProtocol.BuildRegisterCmEventInputBuffer();
Assert.Equal(expected, actual);
}
[Fact]
public void SerializeCmEventEnsureTagsGrpc_MatchesNativeGrpcCapture()
{
byte[] expected = Convert.FromHexString(CapturedEnsureTagsHex);
Assert.Equal(86, expected.Length);
// The only run-varying field is the registration FILETIME (the 8 bytes immediately before the
// trailing 16-byte event-type GUID). Feed the captured time back so the comparison is exact.
long filetime = BinaryPrimitives.ReadInt64LittleEndian(expected.AsSpan(expected.Length - 24, 8));
DateTime createdUtc = DateTime.FromFileTimeUtc(filetime);
byte[] actual = HistorianAddTagsProtocol.SerializeCmEventEnsureTagsGrpc(createdUtc);
Assert.Equal(expected, actual);
}
}