M3 R3.2: HistorianHistoricalWriteProtocol — the "ON" AddStreamValues serializer (golden-validated)
Managed serializer for the historical-write "ON" buffer, byte-for-byte matching the live capture (WcfHistoricalWriteProtocolTests golden-tests the exact 56-byte native buffer). Layout: "ON"(0x4E4F) + count + lengths + 16B tag GUID + sample FILETIME + u16 quality(192) + 4B descriptor + received FILETIME + value. Value encoding (Float, captured): an 8-byte slot = u32(0) + float32(value) — the 4-byte float in the high dword, NOT a double. The 16B tag GUID is the per-tag GUID the SDK already parses as ParseTagInfoRecord's "typeId" (confirmed: it appears at offset 8 of GetTagInfosFromName's response = where typeId is read, and in EnsureTags' response + the "ON" buffer). Only the Float encoding is captured; other types rejected until captured. Next: gRPC orchestrator (write-enabled session -> EnsureTags -> resolve tag GUID -> AddStreamValues) + public AddHistoricalValuesAsync + live write/read-back. 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:
@@ -0,0 +1,44 @@
|
||||
using AVEVA.Historian.Client.Wcf;
|
||||
|
||||
namespace AVEVA.Historian.Client.Tests;
|
||||
|
||||
public sealed class WcfHistoricalWriteProtocolTests
|
||||
{
|
||||
// The exact 56-byte HistoryService.AddStreamValues "values" buffer captured live from the native
|
||||
// 2023 R2 client writing a historical Float sample (124.5) to a sandbox tag — see
|
||||
// docs/plans/revision-write-path.md §"R3.1 CAPTURED".
|
||||
private const string CapturedBufferHex =
|
||||
"4f4e0100380000002e00" + // "ON" + count(1) + totalLen(56) + payloadLen(46)
|
||||
"d51d3107e9f8664793b155d3d1aef544" + // tag GUID 07311dd5-f8e9-4766-93b1-55d3d1aef544
|
||||
"70df38b1d101dd01" + // sample FILETIME
|
||||
"c000" + // OpcQuality = 192
|
||||
"c0100100" + // analog double descriptor
|
||||
"e64bcb74e201dd01" + // received/version FILETIME
|
||||
"000000000000f942"; // double 124.5
|
||||
|
||||
[Fact]
|
||||
public void SerializeAddStreamValuesBuffer_MatchesCapturedNativeBuffer()
|
||||
{
|
||||
var tagGuid = new Guid("07311dd5-f8e9-4766-93b1-55d3d1aef544");
|
||||
DateTime sampleTime = DateTime.FromFileTimeUtc(0x01dd01d1b138df70);
|
||||
DateTime receivedTime = DateTime.FromFileTimeUtc(0x01dd01e274cb4be6);
|
||||
|
||||
byte[] actual = HistorianHistoricalWriteProtocol.SerializeAddStreamValuesBuffer(
|
||||
tagGuid, sampleTime, value: 124.5, receivedTime, quality: 192);
|
||||
|
||||
Assert.Equal(Convert.FromHexString(CapturedBufferHex), actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SerializeAddStreamValuesBuffer_HeaderDeclaresLengths()
|
||||
{
|
||||
byte[] buffer = HistorianHistoricalWriteProtocol.SerializeAddStreamValuesBuffer(
|
||||
Guid.NewGuid(), DateTime.UtcNow, value: 1.0, DateTime.UtcNow);
|
||||
|
||||
Assert.Equal(56, buffer.Length);
|
||||
Assert.Equal(0x4E4F, BitConverter.ToUInt16(buffer, 0)); // "ON"
|
||||
Assert.Equal(1, BitConverter.ToUInt16(buffer, 2)); // sampleCount
|
||||
Assert.Equal((uint)buffer.Length, BitConverter.ToUInt32(buffer, 4));
|
||||
Assert.Equal(buffer.Length - 10, BitConverter.ToUInt16(buffer, 8));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user