fix(write): re-gate UInt1 — historian creates degenerate UInt1 analog tags
Live evidence: EnsureTags(UInt1) returns success but the server stores a degenerate tag (descriptor type byte 0x00, no GUID/name), so GetTagInfo truncates and writes fail. Not client-fixable on the analog path. Int8/UInt8 stay GREEN (live-proven). UInt1 reverts to ProtocolEvidenceMissingException (fail-closed); golden rows removed; negative-gate tests added. Removed the one-off capture diagnostic. Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii
This commit is contained in:
@@ -32,8 +32,9 @@ namespace AVEVA.Historian.Client.Wcf;
|
||||
/// Int8 → Int64(8) · UInt8 → UInt64(8)
|
||||
/// </code>
|
||||
///
|
||||
/// Live-captured for Float/Double/Int2/Int4/UInt4; UInt1/Int8/UInt8 mirror the captured
|
||||
/// Double value layout (8 LE bytes for Int8/UInt8, 1 byte for UInt1) pending live round-trip.
|
||||
/// Live-captured for Float/Double/Int2/Int4/UInt4; Int8/UInt8 mirror the captured
|
||||
/// Double value layout (8 LE bytes) pending live round-trip. UInt1 is re-gated: the
|
||||
/// historian accepts EnsureTags(UInt1) but stores a degenerate tag — writes would fail.
|
||||
/// Other tag types have no captured value encoding and are rejected.
|
||||
/// </remarks>
|
||||
internal static class HistorianHistoricalWriteProtocol
|
||||
@@ -54,7 +55,8 @@ internal static class HistorianHistoricalWriteProtocol
|
||||
/// <paramref name="tagGuid"/> is the per-tag GUID (from the gRPC tag-info read),
|
||||
/// <paramref name="dataType"/> is the tag's declared analog type (selects the value width), and
|
||||
/// <paramref name="receivedTimeUtc"/> is the storage/received timestamp the orchestrator stamps.
|
||||
/// Throws <see cref="ProtocolEvidenceMissingException"/> for tag types without a captured encoding.
|
||||
/// Throws <see cref="ProtocolEvidenceMissingException"/> for tag types without a captured encoding
|
||||
/// (including UInt1, which is re-gated — the historian creates a degenerate UInt1 analog tag).
|
||||
/// Int8/UInt8 carry exact magnitude only up to 2^53 — the value API is <see langword="double"/>;
|
||||
/// full 64-bit range is a separate follow-on.
|
||||
/// </summary>
|
||||
@@ -121,10 +123,6 @@ internal static class HistorianHistoricalWriteProtocol
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(b, checked((uint)value));
|
||||
return b;
|
||||
}
|
||||
case HistorianDataType.UInt1:
|
||||
{
|
||||
return [checked((byte)value)];
|
||||
}
|
||||
case HistorianDataType.Int8:
|
||||
{
|
||||
byte[] b = new byte[8];
|
||||
@@ -140,7 +138,7 @@ internal static class HistorianHistoricalWriteProtocol
|
||||
default:
|
||||
throw new ProtocolEvidenceMissingException(
|
||||
$"AddHistoricalValuesAsync has no captured value encoding for tag data type '{dataType}'. " +
|
||||
"Captured types: Float, Double, Int2, Int4, UInt4, UInt1, Int8, UInt8.");
|
||||
"Captured types: Float, Double, Int2, Int4, UInt4, Int8, UInt8.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user