M3 R3.2 SHIPPED: AddHistoricalValuesAsync — historical backfill writes over gRPC (live-validated)
Public HistorianClient.AddHistoricalValuesAsync(tag, values) inserts non-streamed original
(backfill) values for an existing tag over the 2023 R2 gRPC front door. The pure-managed SDK
wrote a value and read it back live (gated test AddHistoricalValuesAsync_OverGrpc_WritesAndReadsBack
PASSED against the real server).
- HistorianGrpcHistoricalWriteOrchestrator: write-enabled (0x401) session ->
RetrievalService.GetTagInfosFromName (resolves the per-tag GUID = the tag-info TypeId, and
registers the tag on the session) -> HistoryService.AddStreamValues("ON" buffer) per sample.
- HistorianHistoricalValue (public record: TimestampUtc, Value, OpcQuality=192).
- gRPC-only: non-RemoteGrpc transports throw ProtocolEvidenceMissingException (the 2020 WCF
non-streamed write is architecturally blocked, D2).
- Float value encoding only (the captured type); other types rejected by the serializer.
275 unit tests pass; the new gated live write/read-back test is green against the 2023 R2 server.
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:
@@ -173,6 +173,40 @@ public sealed class HistorianGrpcIntegrationTests
|
||||
Assert.All(result.Attempts, a => Assert.False(a.Succeeded));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddHistoricalValuesAsync_OverGrpc_WritesAndReadsBack()
|
||||
{
|
||||
string? host = Environment.GetEnvironmentVariable("HISTORIAN_GRPC_HOST");
|
||||
// Gated additionally on a dedicated sandbox-tag env var so this WRITE test never runs by
|
||||
// accident — set HISTORIAN_WRITE_SANDBOX_TAG to an existing Float tag you are happy to write
|
||||
// backfill samples to. M3 R3.2: HistoryService.AddStreamValues ("ON" buffer).
|
||||
string? sandboxTag = Environment.GetEnvironmentVariable("HISTORIAN_WRITE_SANDBOX_TAG");
|
||||
if (string.IsNullOrWhiteSpace(host) || string.IsNullOrWhiteSpace(sandboxTag)
|
||||
|| string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HISTORIAN_USER")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HistorianClient client = new(BuildOptions(host));
|
||||
|
||||
// A backfill sample at a fixed historical second, with a distinctive value.
|
||||
DateTime stamp = new DateTime(DateTime.UtcNow.Year, 1, 2, 3, 4, 5, DateTimeKind.Utc);
|
||||
const double expected = 222.5;
|
||||
bool wrote = await client.AddHistoricalValuesAsync(
|
||||
sandboxTag!,
|
||||
[new HistorianHistoricalValue(stamp, expected)],
|
||||
CancellationToken.None);
|
||||
Assert.True(wrote);
|
||||
|
||||
// Read the window around the sample back and confirm it landed.
|
||||
List<HistorianSample> samples = [];
|
||||
await foreach (HistorianSample s in client.ReadRawAsync(sandboxTag!, stamp.AddMinutes(-1), stamp.AddMinutes(1), maxValues: 16, CancellationToken.None))
|
||||
{
|
||||
samples.Add(s);
|
||||
}
|
||||
Assert.Contains(samples, s => s.NumericValue is { } v && Math.Abs(v - expected) < 0.01);
|
||||
}
|
||||
|
||||
private static HistorianClientOptions BuildOptions(string host)
|
||||
{
|
||||
string? user = Environment.GetEnvironmentVariable("HISTORIAN_USER");
|
||||
|
||||
Reference in New Issue
Block a user