Compare commits

..

18 Commits

Author SHA1 Message Date
Joseph Doherty 74a153121b test: bound and canonicalize lifecycle descriptor probes 2026-11-10 12:28:40 -05:00
Joseph Doherty a21c49c3cd Merge commit '2d75cecb' into codex/opcua2-cross-version 2026-11-10 12:23:07 -05:00
Joseph Doherty f13f4eb86d test: record cross-version wire compatibility and warmed metrics 2026-11-10 12:21:18 -05:00
Joseph Doherty 2d75cecbb9 test: add isolated cross-version application compatibility harness 2026-11-10 12:18:46 -05:00
Joseph Doherty ebdc589206 test: add isolated cross-version application compatibility harness 2026-11-10 12:18:15 -05:00
Joseph Doherty b104ef9a37 fix(opcua): restore alarm component reference after SDK creation 2026-11-10 12:14:53 -05:00
Joseph Doherty 41e65f6a22 fix(opcua): restore alarm component reference after SDK creation 2026-11-10 12:14:35 -05:00
Joseph Doherty 8be3bb1734 test(opcua): measure soak observation after startup 2026-11-10 12:14:35 -05:00
Joseph Doherty a456d598ab test(opcua): retain manager references across soak restart 2026-11-10 12:14:35 -05:00
Joseph Doherty 73ee3ef20d test(opcua): add opt-in lifecycle and resource soak harness 2026-11-10 12:14:35 -05:00
Joseph Doherty 1840b5ccf6 test: verify live history authority and secure peer reactivation 2026-11-10 11:53:34 -05:00
Joseph Doherty da50123a2c test: start service-level fixtures with authenticated SDK server 2026-11-10 11:52:09 -05:00
Joseph Doherty a76fca09f6 test: converge historian and runtime fixtures on SDK 2.0 2026-11-10 11:52:09 -05:00
Joseph Doherty 780f363f16 docs: document OPC UA 2.0 compatibility and rollout 2026-11-10 11:48:19 -05:00
Joseph Doherty 2541ef39f7 test: exercise baseline aggregate vectors through runtime registration 2026-11-10 11:46:29 -05:00
Joseph Doherty 6562022966 build: remove temporary OPC UA migration analyzer 2026-11-10 11:43:59 -05:00
Joseph Doherty dc9e777bff test: prove trusted channels PKI and scoped audit compatibility 2026-11-10 11:43:59 -05:00
Joseph Doherty ab25bd5318 test: preserve historian baseline oracles under SDK 2.0 2026-11-10 11:43:59 -05:00
83 changed files with 16281 additions and 4246 deletions
-1
View File
@@ -126,7 +126,6 @@
provider that breaks the "managed only" rule, which is why it is opt-in per instance. -->
<PackageVersion Include="System.Data.Odbc" Version="10.0.14" />
<PackageVersion Include="Novell.Directory.Ldap.NETStandard" Version="3.6.0" />
<PackageVersion Include="OPCFoundation.NetStandard.Opc.Ua.MigrationAnalyzer" Version="2.0.1" />
<PackageVersion Include="OPCFoundation.NetStandard.Opc.Ua.Client" Version="2.0.1" />
<!-- Core carries Opc.Ua.StatusCodes, the oracle StatusCodeParityTests checks the drivers'
hard-coded uint constants against. Referenced by that TEST project only — the drivers
@@ -0,0 +1,22 @@
#Requires -Version 7.0
<#
.SYNOPSIS
Runs the production application SDK compatibility matrix in isolated processes.
.DESCRIPTION
Uses exact baseline host/client publish artifacts and builds the candidate from a
separate checkout. Both SDK versions remain in separate processes. Evidence,
artifact SHA-256 hashes, negotiated policies and individual results are retained.
The output directory must not exist. No fleet endpoint or store is used.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)][string]$BaselineRoot,
[Parameter(Mandatory)][string]$BaselineArtifacts,
[Parameter(Mandatory)][string]$CandidateRoot,
[Parameter(Mandatory)][string]$OutputDirectory,
[string]$ContainerImage = 'mcr.microsoft.com/dotnet/sdk:10.0.103-noble'
)
$ErrorActionPreference = 'Stop'
$runner = Join-Path $PSScriptRoot '../validation/opcua-sdk-compatibility/run.py'
& python3 $runner --baseline-root $BaselineRoot --baseline-artifacts $BaselineArtifacts --candidate-root $CandidateRoot --output $OutputDirectory --container-image $ContainerImage
exit $LASTEXITCODE
@@ -0,0 +1,374 @@
#pragma warning disable OTOPCUA0001 // This wire harness deliberately exercises public driver capabilities directly.
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text.Json;
using Microsoft.Extensions.Logging.Abstractions;
using Opc.Ua;
using Opc.Ua.Client;
using ZB.MOM.WW.OtOpcUa.Client.Shared;
using ZB.MOM.WW.OtOpcUa.Client.Shared.Models;
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Core.Authorization;
using ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient;
using ZB.MOM.WW.OtOpcUa.OpcUaServer;
using ZB.MOM.WW.OtOpcUa.OpcUaServer.Security;
using AlarmEventArgs = ZB.MOM.WW.OtOpcUa.Client.Shared.Models.AlarmEventArgs;
using WriteRequest = ZB.MOM.WW.OtOpcUa.Core.Abstractions.WriteRequest;
using SharedSecurityMode = ZB.MOM.WW.OtOpcUa.Client.Shared.Models.SecurityMode;
using CoreHistory = ZB.MOM.WW.OtOpcUa.Core.Abstractions.HistoryReadResult;
internal static class ProcessHost
{
static readonly DateTime Epoch = new(2026, 10, 1, 0, 0, 0, DateTimeKind.Utc);
const string RawUri = "https://zb.com/otopcua/raw", UnsUri = "https://zb.com/otopcua/uns";
const string Folder = "Matrix/dev", Scalar = Folder + "/Scalar", Vector = Folder + "/Vector", Alarm = Folder + "/Alarm";
static readonly CancellationToken Ct = new CancellationTokenSource(TimeSpan.FromMinutes(4)).Token;
static string Password => Environment.GetEnvironmentVariable("OPCUA_MATRIX_PASSWORD") ?? throw new InvalidOperationException("Missing ephemeral credential");
static readonly List<object> Cases = [];
static readonly List<object> Observations = [];
static int Failures;
static string Root = "";
static object? Clr(DataValue v)
{
#if SDK2
return ZB.MOM.WW.OtOpcUa.Client.Shared.Helpers.ValueConverter.ToClrObject(v.WrappedValue);
#else
return v.Value;
#endif
}
static DateTime SourceTime(DataValue value)
{
#if SDK2
return value.SourceTimestamp.ToDateTime();
#else
return value.SourceTimestamp;
#endif
}
static DateTime ServerTime(DataValue value)
{
#if SDK2
return value.ServerTimestamp.ToDateTime();
#else
return value.ServerTimestamp;
#endif
}
static void Require(bool yes, string message) { if (!yes) throw new InvalidOperationException(message); }
static async Task Case(string name, Func<Task> action)
{
try { await action(); Cases.Add(new { name, status = "passed" }); }
catch (Exception e) { Failures++; Cases.Add(new { name, status = "failed", error = e.ToString() }); }
}
static async Task Wait(Func<bool> predicate)
{
for (int i = 0; i < 200; i++) { if (predicate()) return; await Task.Delay(50, Ct); }
Require(false, "Notification deadline exceeded");
}
static ConnectionSettings Settings(string endpoint) => new()
{
EndpointUrl = endpoint,
Username = "apikey",
Password = Password,
SecurityMode = SharedSecurityMode.SignAndEncrypt,
AutoAcceptCertificates = false,
CertificateStorePath = Environment.GetEnvironmentVariable("OPCUA_MATRIX_CLIENT_PKI") ?? Path.Combine(Root, "client-pki")
};
public static async Task<int> Main(string[] args)
{
Root = Path.GetFullPath(args[1]); Directory.CreateDirectory(Root);
try
{
switch (args[0])
{
case "local-data-path": Console.WriteLine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)); return 0;
case "prepare":
var type = typeof(OpcUaClientService).Assembly.GetType("ZB.MOM.WW.OtOpcUa.Client.Shared.Adapters.DefaultApplicationConfigurationFactory")!;
var factory = Activator.CreateInstance(type, true)!;
await (Task<ApplicationConfiguration>)type.GetMethod("CreateAsync")!.Invoke(factory, [Settings("opc.tcp://127.0.0.1:1"), Ct])!;
Console.WriteLine("PREPARED"); return 0;
case "prepare-driver": await PrepareDriver(); return 0;
case "driver": await Driver(args[2]); break;
case "server": await Serve(); return 0;
case "client": await Client(args[2]); break;
case "performance": await Performance(args[2]); break;
default: throw new ArgumentException("Expected prepare/server/client");
}
}
catch (Exception e) { Failures++; Cases.Add(new { name = "process", status = "failed", error = e.ToString() }); }
File.WriteAllText(Path.Combine(Root, "result.json"), JsonSerializer.Serialize(new { sdk = typeof(NodeId).Assembly.GetName().Version?.ToString(), process = Environment.ProcessId, cases = Cases, observations = Observations, failures = Failures, loadedSdkAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name!.StartsWith("Opc.Ua", StringComparison.Ordinal)).Select(a => new { name = a.GetName().Name, version = a.GetName().Version?.ToString(), path = a.Location }) }, new JsonSerializerOptions { WriteIndented = true }));
return Failures == 0 ? 0 : 1;
}
static async Task Serve()
{
var options = new OpcUaApplicationHostOptions
{
ApplicationName = "OtOpcUa.Matrix",
ApplicationUri = $"urn:OtOpcUa.Matrix.{Guid.NewGuid():N}",
PublicHostname = "127.0.0.1",
PkiStoreRoot = Path.Combine(Root, "server-pki"),
AllowAnonymous = false,
AutoAcceptUntrustedClientCertificates = false,
EnabledSecurityProfiles = Environment.GetEnvironmentVariable("OPCUA_MATRIX_SERVER_SECURITY") == "none"
? [OpcUaSecurityProfile.None] : [OpcUaSecurityProfile.Basic256Sha256SignAndEncrypt]
};
using (var listener = new TcpListener(IPAddress.Loopback, 0)) { listener.Start(); options.OpcUaPort = ((IPEndPoint)listener.LocalEndpoint).Port; }
var server = new OtOpcUaSdkServer();
await using var host = new OpcUaApplicationHost(options, NullLogger<OpcUaApplicationHost>.Instance, new Auth());
await host.StartAsync(server, Ct);
var nm = server.NodeManager!; var sink = new SdkAddressSpaceSink(nm);
var authority = new DataPlaneAuthorityState();
authority.Apply("matrix", "rev1", [new ApiKey { ApiKeyId = "matrix", Name = "matrix", SecretHash = "fixture", Principal = "matrix", Enabled = true, CreatedUtc = DateTime.UtcNow.AddDays(-1) }],
[new NodeAcl { NodeAclId = "all", ClusterId = "matrix", Principal = "matrix", ScopeKind = NodeAclScopeKind.Cluster, PermissionFlags = (NodePermissions)int.MaxValue }], new Dictionary<AclScopeRef, IReadOnlyList<NodeAclPath>>());
nm.DataPlaneAuthority = authority;
nm.Authorizer = new DataPlaneAuthorizer(authority, new TriePermissionEvaluator(authority.Tries), true, NullLogger.Instance, TimeProvider.System);
var history = new Source(Root); nm.HistorianDataSource = history;
sink.EnsureFolder(Folder, null, "dev", AddressSpaceRealm.Raw);
sink.EnsureFolder("Eq", null, "Eq", AddressSpaceRealm.Uns);
foreach (var realm in new[] { AddressSpaceRealm.Raw, AddressSpaceRealm.Uns })
{
var scalar = realm == AddressSpaceRealm.Raw ? Scalar : "Eq/Scalar";
var vector = realm == AddressSpaceRealm.Raw ? Vector : "Eq/Vector";
var parent = realm == AddressSpaceRealm.Raw ? Folder : "Eq";
sink.EnsureVariable(scalar, parent, "Scalar", "Int32", true, realm, "matrix.scalar");
sink.EnsureVariable(vector, parent, "Vector", "Int32", true, realm, isArray: true, arrayLength: 3);
sink.WriteValue(scalar, 17, 0, Epoch, realm); sink.WriteValue(vector, new[] { 1, 2, 3 }, 0, Epoch, realm);
}
sink.AddReference("Eq/Scalar", AddressSpaceRealm.Uns, Scalar, AddressSpaceRealm.Raw);
sink.MaterialiseAlarmCondition(Alarm, Folder, "Alarm", "OffNormalAlarm", 800, AddressSpaceRealm.Raw, isNative: true);
sink.WireAlarmNotifiers(Alarm, AddressSpaceRealm.Raw, ["Eq"], AddressSpaceRealm.Uns);
sink.EnsureVariable(Folder + "/UnsetTimestamp", Folder, "UnsetTimestamp", "Int32", false, AddressSpaceRealm.Raw);
sink.WriteValue(Folder + "/UnsetTimestamp", 3, 0, DateTime.MinValue, AddressSpaceRealm.Raw);
nm.NodeWriteGateway = new Gateway();
var state = new AlarmConditionSnapshot(true, false, true, true, AlarmShelvingKind.Unshelved, 800, "matrix-alarm");
nm.NativeAlarmAckRouter = _ => Task.Run(() => { state = state with { Acknowledged = true }; sink.WriteAlarmCondition(Alarm, state, DateTime.UtcNow, AddressSpaceRealm.Raw); });
File.WriteAllText(Path.Combine(Root, "endpoint.txt"), $"opc.tcp://127.0.0.1:{options.OpcUaPort}/OtOpcUa");
File.WriteAllText(Path.Combine(Root, "ready.json"), JsonSerializer.Serialize(new { endpoint = $"opc.tcp://127.0.0.1:{options.OpcUaPort}/OtOpcUa", process = Environment.ProcessId, sdk = typeof(NodeId).Assembly.GetName().Version?.ToString(), rawUri = RawUri, unsUri = UnsUri }));
int tick = 0;
while (!File.Exists(Path.Combine(Root, "stop")))
{
sink.WriteAlarmCondition(Alarm, state with { Message = ++tick % 2 == 0 ? "matrix-alarm" : "matrix-tick" }, DateTime.UtcNow, AddressSpaceRealm.Raw);
await Task.Delay(1000, Ct);
}
using var process = System.Diagnostics.Process.GetCurrentProcess();
File.WriteAllText(Path.Combine(Root, "server-resources.json"), JsonSerializer.Serialize(new { process.WorkingSet64, process.PrivateMemorySize64, process.HandleCount, threads=process.Threads.Count, cpuMilliseconds=process.TotalProcessorTime.TotalMilliseconds, allocatedBytes=GC.GetTotalAllocatedBytes(), collections=new[]{GC.CollectionCount(0),GC.CollectionCount(1),GC.CollectionCount(2)} }, new JsonSerializerOptions {WriteIndented=true}));
}
static async Task Client(string endpoint)
{
using var client = new OpcUaClientService();
await Case("trusted-SignAndEncrypt-API-key", async () => { var info = await client.ConnectAsync(Settings(endpoint), Ct); Require(info.SecurityMode == "SignAndEncrypt" && info.SecurityPolicyUri == SecurityPolicies.Basic256Sha256, "Unexpected negotiated policy"); Observations.Add(new { info.SecurityMode, info.SecurityPolicyUri }); });
if (!client.IsConnected) return;
var namespaces = (string[])Clr(await client.ReadValueAsync(VariableIds.Server_NamespaceArray, Ct))!;
var raw = (ushort)Array.IndexOf(namespaces, RawUri); var uns = (ushort)Array.IndexOf(namespaces, UnsUri);
NodeId ScalarId(ushort ns) => new(ns == raw ? Scalar : "Eq/Scalar", ns);
NodeId VectorId(ushort ns) => new(ns == raw ? Vector : "Eq/Vector", ns);
await Case("raw-UNS-URI-browse", async () =>
{
Require(raw > 0 && uns > 0, "Missing namespace URI");
var rawChildren = await client.BrowseAsync(new NodeId(Folder, raw), Ct);
var unsChildren = await client.BrowseAsync(new NodeId("Eq", uns), Ct);
Observations.Add(new { rawChildren, unsChildren });
// Supplement the production Shared results with unfiltered wire-reference metadata.
var adapter = typeof(OpcUaClientService).GetField("_session", BindingFlags.Instance | BindingFlags.NonPublic)!.GetValue(client)!;
var sdkSession = (Opc.Ua.Client.Session)adapter.GetType().GetField("_session", BindingFlags.Instance | BindingFlags.NonPublic)!.GetValue(adapter)!;
var (_, _, rawReferences) = await sdkSession.BrowseAsync(null, null, new NodeId(Folder, raw), 0u,
BrowseDirection.Forward, ReferenceTypeIds.References, true, 0u);
Observations.Add(new { rawReferences = (rawReferences.ToArray() ?? []).Select(reference => new { nodeId = reference.NodeId.ToString(), browseName = reference.BrowseName.ToString(), nodeClass = reference.NodeClass.ToString(), typeDefinition = reference.TypeDefinition.ToString(), referenceType = reference.ReferenceTypeId.ToString(), reference.IsForward }).ToArray() });
var expectedRaw = new[] { Scalar, Vector, Alarm, Folder + "/UnsetTimestamp" }.Select(id => new NodeId(id, raw).ToString());
Require(expectedRaw.All(id => rawChildren.Any(child => child.NodeId == id)), "Missing raw children: " + string.Join(",", expectedRaw.Where(id => !rawChildren.Any(child => child.NodeId == id))));
Require(new[] { "Eq/Scalar", "Eq/Vector" }.Select(id => new NodeId(id, uns).ToString()).All(id => unsChildren.Any(child => child.NodeId == id)), "Missing UNS scalar/array children");
});
await Case("initial-quality-explicit-source-and-server-timestamps", async () => { var initial = await client.ReadValueAsync(ScalarId(raw), Ct); Require(StatusCode.IsGood(initial.StatusCode), "Bad initial quality"); Require(SourceTime(initial) == Epoch && ServerTime(initial) > Epoch, "Lost explicit initial timestamps"); Observations.Add(new { sourceTimestamp = SourceTime(initial).ToString("O"), serverTimestamp = ServerTime(initial).ToString("O"), contract = "Source supplied as 2026-10-01T00:00:00Z; server timestamp observed without normalization or historian algorithm changes" }); });
foreach (var ns in new[] { raw, uns })
{
await Case($"scalar-read-write-ns-{ns}", async () => { Require(Clr(await client.ReadValueAsync(ScalarId(ns), Ct)) is int, "Scalar lost Int32 type"); Require(StatusCode.IsGood(await client.WriteValueAsync(ScalarId(ns), 42, Ct)), "Scalar write failed"); Require((int)Clr(await client.ReadValueAsync(ScalarId(ns), Ct))! == 42, "Scalar round trip failed"); });
await Case($"typed-array-read-write-ns-{ns}", async () => { Require(Clr(await client.ReadValueAsync(VectorId(ns), Ct)) is int[], "Array lost Int32[] type"); Require(StatusCode.IsGood(await client.WriteValueAsync(VectorId(ns), new[] { 7, 8, 9 }, Ct)), "Array write failed"); Require(((int[])Clr(await client.ReadValueAsync(VectorId(ns), Ct))!).SequenceEqual(new[] { 7, 8, 9 }), "Array round trip failed"); });
}
await Case("monitor-quality-timestamps", async () => { var seen = new ConcurrentQueue<DataValue>(); client.DataChanged += (_, e) => seen.Enqueue(e.Value); await client.SubscribeAsync(ScalarId(raw), 100, Ct); await client.WriteValueAsync(ScalarId(raw), 73, Ct); await Wait(() => seen.Any(v => Equals(Clr(v), 73))); var v = seen.Last(v => Equals(Clr(v), 73)); Require(StatusCode.IsGood(v.StatusCode) && SourceTime(v) > Epoch && ServerTime(v) > Epoch, "Bad monitor quality or missing timestamps"); Observations.Add(new { sourceTimestamp = SourceTime(v).ToString("O"), serverTimestamp = ServerTime(v).ToString("O"), status = v.StatusCode.ToString() }); await client.UnsubscribeAsync(ScalarId(raw), Ct); });
await Case("event-filter-and-native-ack", async () => { var alarms = new ConcurrentQueue<AlarmEventArgs>(); client.AlarmEvent += (_, a) => alarms.Enqueue(a); await client.SubscribeAlarmsAsync(ObjectIds.Server, 100, Ct); await Wait(() => alarms.Any(a => a.Message == "matrix-alarm" && a.ActiveState && a.Severity == 800)); var alarm = alarms.Last(a => a.Message == "matrix-alarm"); Require(alarm.EventId?.Length > 0, "Missing event ID"); Require(StatusCode.IsGood(await client.AcknowledgeAlarmAsync(new NodeId(Alarm, raw).ToString(), alarm.EventId!, "matrix-ack", Ct)), "Ack refused"); await Wait(() => alarms.Any(a => a.Message == "matrix-alarm" && a.AckedState)); await client.UnsubscribeAlarmsAsync(Ct); });
await Case("raw-history-CP-paging", async () => { var values = await client.HistoryReadRawAsync(ScalarId(raw), Epoch, Epoch.AddMinutes(1), 100, Ct); Require(values.Count == 25, "Expected 25 raw samples, got " + values.Count); Require(values.Select(v => Convert.ToInt32(Clr(v))).SequenceEqual(Enumerable.Range(0, 25)), "Raw page loss/duplicates/order"); });
await Case("event-history-CP-paging", async () =>
{
var filter = new EventFilter();
#if SDK2
filter.SelectClauses += new SimpleAttributeOperand(ObjectTypeIds.BaseEventType, new QualifiedName("EventId")) { AttributeId = Attributes.Value };
#else
filter.SelectClauses.Add(new SimpleAttributeOperand {TypeDefinitionId=ObjectTypeIds.BaseEventType,BrowsePath=new QualifiedNameCollection {new QualifiedName("EventId")},AttributeId=Attributes.Value});
#endif
var events = await client.HistoryReadEventsAsync(new NodeId(Folder, raw), Epoch, Epoch.AddMinutes(1), filter, 100, Ct); Require(events.Count == 25, "Expected 25 event samples, got " + events.Count);
#if SDK2
var ids = events.Select(e => System.Text.Encoding.UTF8.GetString((byte[])ZB.MOM.WW.OtOpcUa.Client.Shared.Helpers.ValueConverter.ToClrObject(e.Fields[0])!));
#else
var ids=events.Select(e=>System.Text.Encoding.UTF8.GetString((byte[])e.Fields[0].Value));
#endif
Require(ids.SequenceEqual(Enumerable.Range(0, 25).Select(i => $"e{i:00}")), "Event history loss/duplicates/order across timestamp ties");
});
await Case("event-history-where-filter-positive-and-negative", async () =>
{
foreach (var type in new[] { ObjectTypeIds.BaseEventType, ObjectTypeIds.AuditEventType })
{
var filter = new EventFilter();
#if SDK2
filter.SelectClauses += new SimpleAttributeOperand(ObjectTypeIds.BaseEventType, new QualifiedName("EventId")) { AttributeId = Attributes.Value };
filter.WhereClause.Push(FilterOperator.OfType, Variant.From(type));
#else
filter.SelectClauses.Add(new SimpleAttributeOperand(ObjectTypeIds.BaseEventType,new QualifiedName("EventId")){AttributeId=Attributes.Value});
filter.WhereClause.Push(FilterOperator.OfType,new LiteralOperand(new Variant(type)));
#endif
var events = await client.HistoryReadEventsAsync(new NodeId(Folder, raw), Epoch, Epoch.AddMinutes(1), filter, 100, Ct);
Require(events.Count == (type == ObjectTypeIds.BaseEventType ? 25 : 0), $"Where filter for {type} expected {(type == ObjectTypeIds.BaseEventType ? 25 : 0)} got {events.Count}");
}
});
var unset = await client.ReadValueAsync(new NodeId(Folder + "/UnsetTimestamp", raw), Ct);
Observations.Add(new { sourceTimestamp = SourceTime(unset).ToString("O"), serverTimestamp = ServerTime(unset).ToString("O"), timestampObservation = "Fixture source supplied DateTime.MinValue (unset). These are unmodified application/SDK wire observations; no historian algorithm or expected timestamp was changed." });
await Benchmark(client, ScalarId(raw), VectorId(raw));
await client.DisconnectAsync(Ct);
}
static OpcUaClientDriverOptions DriverOptions(string endpoint) => new()
{
EndpointUrl = endpoint,
SecurityPolicy = OpcUaSecurityPolicy.Basic256Sha256,
SecurityMode = OpcUaSecurityMode.SignAndEncrypt,
AuthType = OpcUaAuthType.Username,
Username = "apikey",
Password = Password,
AutoAcceptCertificates = false,
RawTags = [new("scalar",JsonSerializer.Serialize(new {nodeId=$"nsu={RawUri};s={Scalar}"}),true),
new("vector",JsonSerializer.Serialize(new {nodeId=$"nsu={RawUri};s={Vector}"}),true),
new("uns",JsonSerializer.Serialize(new {nodeId=$"nsu={UnsUri};s=Eq/Scalar"}),true)],
SessionName = "MatrixDriver",
ApplicationUri = "urn:localhost:MatrixDriver"
};
static string IsolatedLocalData()
{
var local = Path.GetFullPath(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData));
Require(local.StartsWith(Path.GetFullPath(Root) + Path.DirectorySeparatorChar, StringComparison.Ordinal), "Driver/CLI canonical PKI must be in task-owned root: " + local);
return local;
}
static async Task PrepareDriver()
{
var local = IsolatedLocalData();
await using var driver = new OpcUaClientDriver(DriverOptions("opc.tcp://127.0.0.1:1"), "matrix");
// The production configuration method creates the application's own certificate without
// attempting a session. Reflection is confined to preparation; all wire operations below
// call the unchanged public driver API.
var type = typeof(OpcUaClientDriver);
var hook = Activator.CreateInstance(type.GetNestedType("CertValidationHook", BindingFlags.NonPublic)!, true)!;
await (Task<ApplicationConfiguration>)type.GetMethod("BuildApplicationConfigurationAsync", BindingFlags.Instance | BindingFlags.NonPublic)!.Invoke(driver, [hook, Ct])!;
File.WriteAllText(Path.Combine(Root, "driver-pki.txt"), Path.Combine(local, "OtOpcUa", "pki"));
}
static async Task Driver(string endpoint)
{
IsolatedLocalData();
await using var driver = new OpcUaClientDriver(DriverOptions(endpoint), "matrix");
await Case("driver-trusted-encrypted-API-key-connect", () => driver.InitializeAsync("{}", Ct));
await Case("driver-raw-UNS-URI-scalar-and-array-read", async () =>
{
var values = await driver.ReadAsync(["scalar", "vector", "uns"], Ct);
Require(values.Count == 3 && values.All(x => x.StatusCode == 0), "Read failure");
Require(values[0].Value is int && values[1].Value is int[] && values[2].Value is int, "Lost scalar/array type");
});
await Case("driver-scalar-array-write-roundtrip", async () =>
{
var writes = await driver.WriteAsync([new WriteRequest("scalar", 91), new WriteRequest("vector", new[] { 11, 12, 13 })], Ct);
Require(writes.All(x => x.StatusCode == 0), "Write failed");
var values = await driver.ReadAsync(["scalar", "vector"], Ct);
Require(Equals(values[0].Value, 91) && ((int[])values[1].Value!).SequenceEqual(new[] { 11, 12, 13 }), "Write round trip mismatch");
});
await Case("driver-monitor-quality-timestamps", async () =>
{
var seen = new ConcurrentQueue<DataValueSnapshot>();
driver.OnDataChange += (_, e) => seen.Enqueue(e.Snapshot);
var subscription = await driver.SubscribeAsync(["scalar"], TimeSpan.FromMilliseconds(100), Ct);
await driver.WriteAsync([new WriteRequest("scalar", 92)], Ct);
await Wait(() => seen.Any(x => Equals(x.Value, 92)));
var value = seen.Last(x => Equals(x.Value, 92));
Require(value.StatusCode == 0 && value.SourceTimestampUtc.HasValue && value.ServerTimestampUtc > Epoch, "Missing monitor quality/timestamps");
Observations.Add(new { value.SourceTimestampUtc, value.ServerTimestampUtc, value.StatusCode });
await driver.UnsubscribeAsync(subscription, Ct);
});
Observations.Add(new { capability = "driver-history", status = "not-applicable", evidence = "Driver has no IHistoryProvider/HistoryRead capability; .claude/rules/drivers.md #655. Shared/CLI own history." });
await driver.ShutdownAsync(Ct);
}
static async Task Performance(string endpoint)
{
using var client = new OpcUaClientService();
var info = await client.ConnectAsync(Settings(endpoint), Ct);
Require(info.SecurityMode == "SignAndEncrypt" && info.SecurityPolicyUri == SecurityPolicies.Basic256Sha256, "Unexpected benchmark endpoint policy");
var namespaces = (string[])Clr(await client.ReadValueAsync(VariableIds.Server_NamespaceArray, Ct))!;
var raw = (ushort)Array.IndexOf(namespaces, RawUri);
Require(raw > 0, "Missing benchmark raw namespace");
Observations.Add(new {info.SecurityMode, info.SecurityPolicyUri, tieredCompilation=Environment.GetEnvironmentVariable("DOTNET_TieredCompilation"), scope="Two scalar/Int32[3] variables; zero data monitors during measurement; five fixture variables and one native condition"});
await Case("warmed-production-client-read-write-measurement", () => Benchmark(client, new NodeId(Scalar, raw), new NodeId(Vector, raw)));
await client.DisconnectAsync(Ct);
}
static async Task Benchmark(OpcUaClientService client, NodeId scalar, NodeId vector)
{
// Local diagnostic subset; excludes reconnect, history and load.
var rounds = int.Parse(Environment.GetEnvironmentVariable("OPCUA_MATRIX_PERF_ROUNDS") ?? "3");
var warmups = int.Parse(Environment.GetEnvironmentVariable("OPCUA_MATRIX_PERF_WARMUPS") ?? "10");
var measured = int.Parse(Environment.GetEnvironmentVariable("OPCUA_MATRIX_PERF_MEASURED") ?? "50");
Require(rounds > 0 && warmups >= 0 && measured > 0, "Invalid performance sample counts");
for (int round = 1; round <= rounds; round++)
{
for (int warmup = 0; warmup < warmups; warmup++)
{
await client.ReadValueAsync(scalar, Ct); await client.WriteValueAsync(scalar, warmup, Ct);
await client.ReadValueAsync(vector, Ct); await client.WriteValueAsync(vector, new[] { warmup, warmup + 1, warmup + 2 }, Ct);
}
var samples = new Dictionary<string, List<double>>();
var before = System.Diagnostics.Process.GetCurrentProcess(); var cpu = before.TotalProcessorTime;
var allocated = GC.GetTotalAllocatedBytes(); var gc = new[] { GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2) };
foreach (var operation in new[] { "scalar-read", "scalar-write", "array-read", "array-write" })
{
var times = new List<double>();
for (int i = 0; i < measured; i++)
{
var start = System.Diagnostics.Stopwatch.GetTimestamp();
if (operation == "scalar-read") await client.ReadValueAsync(scalar, Ct);
else if (operation == "scalar-write") Require(StatusCode.IsGood(await client.WriteValueAsync(scalar, i, Ct)), "Benchmark scalar write");
else if (operation == "array-read") await client.ReadValueAsync(vector, Ct);
else Require(StatusCode.IsGood(await client.WriteValueAsync(vector, new[] { i, i + 1, i + 2 }, Ct)), "Benchmark array write");
times.Add(System.Diagnostics.Stopwatch.GetElapsedTime(start).TotalMilliseconds);
}
samples[operation] = times;
}
before.Refresh();
Observations.Add(new
{
benchmarkRound = round,
warmupOperationsPerKind = 10,
operationsPerKind = 50,
latencyMilliseconds = samples.ToDictionary(x => x.Key, x => new { p50 = x.Value.Order().ElementAt((int)Math.Ceiling(measured * 0.50) - 1), p95 = x.Value.Order().ElementAt((int)Math.Ceiling(measured * 0.95) - 1), p99 = x.Value.Order().ElementAt((int)Math.Ceiling(measured * 0.99) - 1), throughputOperationsPerSecond = measured * 1000.0 / x.Value.Sum() }),
clientCpuMilliseconds = (before.TotalProcessorTime - cpu).TotalMilliseconds,
clientAllocatedBytes = GC.GetTotalAllocatedBytes() - allocated, clientAllocatedBytesPerOperation = (GC.GetTotalAllocatedBytes() - allocated) / (4.0 * measured),
clientWorkingSetBytes = before.WorkingSet64,
clientPrivateBytes = before.PrivateMemorySize64,
clientThreads = before.Threads.Count,
clientHandles = before.HandleCount,
collections = new[] { GC.CollectionCount(0) - gc[0], GC.CollectionCount(1) - gc[1], GC.CollectionCount(2) - gc[2] }
});
}
}
sealed class Auth : IOpcUaUserAuthenticator
{
public Task<OpcUaUserAuthResult> AuthenticateUserNameAsync(string username, string password, CancellationToken ct) => Task.FromResult(username == "apikey" && password == Password ? OpcUaUserAuthResult.AllowKey("matrix", [OpcUaDataPlaneRoles.WriteOperate, OpcUaDataPlaneRoles.AlarmAck], "matrix") : OpcUaUserAuthResult.Deny("Invalid ephemeral fixture key"));
}
sealed class Gateway : IOpcUaNodeWriteGateway { public Task<NodeWriteOutcome> WriteAsync(string id, object? value, AddressSpaceRealm realm, CancellationToken ct) => Task.FromResult(new NodeWriteOutcome(true, null)); }
sealed class Source(string root) : IHistorianDataSource
{
void Log(string operation, int rows) => File.AppendAllText(Path.Combine(root, "history-requests.jsonl"), JsonSerializer.Serialize(new { operation, rows }) + "\n");
public int MaxRawValuesPerRead => 7;
public Task<CoreHistory> ReadRawAsync(string r, DateTime s, DateTime e, uint m, CancellationToken ct)
{ var all = Enumerable.Range(0, 25).Select(i => new DataValueSnapshot(i, 0, Epoch.AddSeconds(i), Epoch.AddSeconds(i))).Where(v => v.SourceTimestampUtc >= s && v.SourceTimestampUtc < e).ToList(); var rows = all.Take((int)(m == 0 ? 7 : m)).ToArray(); Log("raw", rows.Length); return Task.FromResult(new CoreHistory(rows, all.Count > rows.Length ? [1] : null)); }
public Task<HistoricalEventsResult> ReadEventsAsync(string? source, DateTime s, DateTime e, int max, CancellationToken ct)
{ var all = Enumerable.Range(0, 25).Select(i => new HistoricalEvent($"e{i:00}", "matrix", Epoch.AddSeconds(i / 2), Epoch, "matrix-history", 500)).Where(v => v.EventTimeUtc >= s && v.EventTimeUtc < e).ToList(); var rows = all.Take(Math.Min(7, max <= 0 ? 7 : max)).ToArray(); Log("events", rows.Length); return Task.FromResult(new HistoricalEventsResult(rows, all.Count > rows.Length ? [1] : null)); }
public Task<CoreHistory> ReadProcessedAsync(string r, DateTime s, DateTime e, TimeSpan i, HistoryAggregateType a, CancellationToken ct) => NullHistorianDataSource.Instance.ReadProcessedAsync(r, s, e, i, a, ct);
public Task<CoreHistory> ReadAtTimeAsync(string r, IReadOnlyList<DateTime> t, CancellationToken ct) => NullHistorianDataSource.Instance.ReadAtTimeAsync(r, t, ct);
public HistorianHealthSnapshot GetHealthSnapshot() => NullHistorianDataSource.Instance.GetHealthSnapshot();
public void Dispose() { }
}
}
@@ -0,0 +1,96 @@
# OPC UA SDK process compatibility
Run from a dedicated candidate checkout with .NET 10, Python 3, PowerShell 7,
and Docker. Supply the unchanged original application's existing `host/` and
`client/` publish directories. The output directory must not already exist.
```powershell
./scripts/e2e/test-opcua-sdk-compatibility.ps1 `
-BaselineRoot /absolute/original-checkout `
-BaselineArtifacts /absolute/original-publishes `
-CandidateRoot /absolute/dedicated-candidate-checkout `
-OutputDirectory /absolute/fresh-evidence-directory
```
The baseline console references the exact published production assemblies;
it does not rebuild or edit the original application. The candidate console
references the production host, Client.Shared and OPC UA driver projects, and
the candidate CLI is separately published. Different SDK versions always run
in different processes. Source revisions, SDK assemblies, SHA-256 hashes,
commands, per-case results, actual namespace URIs, negotiated Shared security,
loaded client SDK assemblies, timestamps and backend request logs are retained.
The required Shared legs are original→candidate, candidate→original and
candidate→candidate. A fourth original→original leg provides a reference for
both the application contract and the small performance sample. Every Shared
leg checks the exact scalar, array and condition browse identities, both
namespaces by URI, typed scalar/Int32[3] read/write, monitored Good quality and
source/server timestamps, the production alarm event filter and native
acknowledgment, and complete raw/event history across continuation points.
Event-history results include timestamp ties, exact IDs/order and positive and
negative `OfType` predicates. The historian is a deterministic fixture behind
the production server's session-bound continuation store; these results do
not certify any SQL Server, QuestDB or AVEVA backend.
Server, Shared client and driver require trusted Basic256Sha256
SignAndEncrypt peers, with certificate autoaccept disabled. Public certificates
are exchanged before sessions open. The test authenticator accepts one exact,
random, API-key-shaped credential from the process environment; production
ACL, write and condition-method checks use the resulting key identity. This
fixture does not replace cryptographic API-key-authenticator coverage.
The driver and actual CLI run in a disposable `--network none` Linux container,
alongside a separate baseline or candidate server process. PKI lives only in
the mounted task directory; no host profile PKI or fleet endpoint is touched.
The driver hardcodes `LocalApplicationData/OtOpcUa/pki`, so Linux's task-local
`XDG_DATA_HOME` supplies isolation. The helper refuses a driver store outside
its task root. CLI uses its own task-local canonical store. CLI currently
forces `AutoAcceptCertificates=true` and exposes no strict-validation flag
(`CommandBase.CreateConnectionSettings`); the exchanged peers and encrypted
server remain in use, but CLI is not counted as strict client validation.
Driver history is N/A because the application removed that capability (#655);
Shared and CLI execute history reads instead.
Three measured rounds follow ten warmup operations per kind in each round:
50 scalar reads, scalar writes, array reads and array writes per kind. The
sample records nearest-rank p50/p95/p99, sequential throughput, client CPU,
allocated bytes, GC collections, working set, private bytes, threads and
handles. This is a small local diagnostic on a shared development host. It
has no production fan-out, reconnect, queue, backpressure, backend or soak
performance claim and does not establish Gate G4.
A separately published `server` helper can be used by other local consumer
checks. Supply a fresh directory and `OPCUA_MATRIX_PASSWORD` in the process
environment, then launch `dotnet ProcessHost.dll server <directory>`.
`ready.json`/`endpoint.txt` identify the ephemeral loopback port; touching
`<directory>/stop` shuts it down. Its default is strict SignAndEncrypt only.
`OPCUA_MATRIX_SERVER_SECURITY=none` is an explicit fixture-only override for
checking an existing unsecured consumer subset; the matrix runner clears that
override. Anonymous is disabled in either mode. Secure clients must place
their public certificates under `server-pki/trusted/certs` before server start
and trust the resulting `server-pki/own/certs` certificate.
Explicitly supplied timestamps are asserted. Unset source timestamps are
recorded separately as observations, with their original application/SDK wire
values. The harness does not change historian algorithms or normalize the
unset-timestamp sentinel to manufacture parity.
For the supplementary sustained warmup experiment, use the built consoles from
one completed matrix run:
```sh
python3 scripts/validation/opcua-sdk-compatibility/performance.py \
--baseline-harness /absolute/run/build-baseline/bin/Release/net10.0/ProcessHost.dll \
--candidate-harness /absolute/run/build-candidate/bin/Release/net10.0/ProcessHost.dll \
--output /absolute/fresh-performance-directory
```
This runs three fresh baseline/candidate process pairs serially, with 1,000
warmup and 2,000 measured operations per kind in each process. Both server and
client receive `DOTNET_TieredCompilation=0` for this experiment only; the
application's shipped configuration is unchanged. The recorded budget is a
15% increase in the median of the three p95 samples for any operation kind.
Exit code 2 means a diagnostic budget regression, while process failures
remain errors. Even an experiment within that budget does not certify the
complete production performance matrix. The allocation-per-operation field
is the average across all four operation kinds in a measured workload.
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
# Runs only inside the disposable, network-isolated task container.
set -euo pipefail
mkdir -p /task/driver/data /task/cli/data /task/server
XDG_DATA_HOME=/task/driver/data dotnet /candidate/ProcessHost.dll prepare-driver /task/driver > /task/driver-prepare.log 2>&1
OPCUA_MATRIX_CLIENT_PKI=/task/cli/data/OtOpcUaClient/pki XDG_DATA_HOME=/task/cli/data dotnet /candidate/ProcessHost.dll prepare /task/cli > /task/cli-prepare.log 2>&1
mkdir -p /task/server/server-pki/trusted/certs
cp /task/driver/data/OtOpcUa/pki/own/certs/* /task/server/server-pki/trusted/certs/
cp /task/cli/data/OtOpcUaClient/pki/own/certs/* /task/server/server-pki/trusted/certs/
dotnet /server/ProcessHost.dll server /task/server > /task/server.log 2>&1 &
server_pid=$!
cleanup() {
touch /task/server/stop
wait "$server_pid" || true
}
trap cleanup EXIT
for attempt in $(seq 1 600); do
test ! -f /task/server/ready.json || break
kill -0 "$server_pid" || exit 1
sleep 0.1
done
test -f /task/server/ready.json
endpoint=$(cat /task/server/endpoint.txt)
mkdir -p /task/driver/data/OtOpcUa/pki/trusted/certs /task/cli/data/OtOpcUaClient/pki/trusted/certs
cp /task/server/server-pki/own/certs/* /task/driver/data/OtOpcUa/pki/trusted/certs/
cp /task/server/server-pki/own/certs/* /task/cli/data/OtOpcUaClient/pki/trusted/certs/
result=0
XDG_DATA_HOME=/task/driver/data dotnet /candidate/ProcessHost.dll driver /task/driver "$endpoint" > /task/driver.log 2>&1 || result=1
# CLI currently autoaccepts by product design; it has no strict-validation flag.
# Its public certificates are nevertheless explicitly exchanged in this fixture.
XDG_DATA_HOME=/task/cli/data dotnet /cli/ZB.MOM.WW.OtOpcUa.Client.CLI.dll read --url "$endpoint" --security signandencrypt --username apikey --password "$OPCUA_MATRIX_PASSWORD" --node 'ns=2;s=Matrix/dev/Scalar' > /task/cli-read.log 2>&1 || result=1
XDG_DATA_HOME=/task/cli/data dotnet /cli/ZB.MOM.WW.OtOpcUa.Client.CLI.dll historyread --url "$endpoint" --security signandencrypt --username apikey --password "$OPCUA_MATRIX_PASSWORD" --node 'ns=2;s=Matrix/dev/Scalar' --start 2026-10-01T00:00:00Z --end 2026-10-01T00:01:00Z --max 100 > /task/cli-raw-history.log 2>&1 || result=1
XDG_DATA_HOME=/task/cli/data dotnet /cli/ZB.MOM.WW.OtOpcUa.Client.CLI.dll historyread --url "$endpoint" --security signandencrypt --username apikey --password "$OPCUA_MATRIX_PASSWORD" --node 'ns=2;s=Matrix/dev' --start 2026-10-01T00:00:00Z --end 2026-10-01T00:01:00Z --max 100 --events --fields EventId,Message > /task/cli-event-history.log 2>&1 || result=1
exit "$result"
@@ -0,0 +1,8 @@
{
"Id": "sha256:b04611ee9e1b7d36797012ed921962c57189cb4dbb3dff12afb8e158110f293b",
"RepoDigests": [
"mcr.microsoft.com/dotnet/sdk@sha256:e362a8dbcd691522456da26a5198b8f3ca1d7641c95624fadc5e3e82678bd08a"
],
"Architecture": "arm64",
"Os": "linux"
}
@@ -0,0 +1,534 @@
{
"scope": "Early short3round,10warmup+50measured/kind, shared-host diagnostic only. This source run had insufficient alarm browse assertions and is superseded as functional evidence by run-10. Preserve these original timing observations without claiming the earlier functional matrix passed the full browse contract.",
"source": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-07/matrix.json",
"measurements": [
{
"name": "baseline-client-to-baseline-server",
"sdk": "1.5.378.0",
"observations": [
{
"benchmarkRound": 1,
"warmupOperationsPerKind": 10,
"operationsPerKind": 50,
"latencyMilliseconds": {
"scalar-read": {
"p50": 0.1949,
"p95": 0.2802,
"p99": 0.3099,
"throughputOperationsPerSecond": 4941.39505465183
},
"scalar-write": {
"p50": 0.2127,
"p95": 0.2761,
"p99": 0.4201,
"throughputOperationsPerSecond": 4527.919149475669
},
"array-read": {
"p50": 0.1511,
"p95": 0.2183,
"p99": 0.3511,
"throughputOperationsPerSecond": 6323.190935073475
},
"array-write": {
"p50": 0.1346,
"p95": 0.2007,
"p99": 0.2762,
"throughputOperationsPerSecond": 6981.777560566919
}
},
"clientCpuMilliseconds": 51.315,
"clientAllocatedBytes": 1317880,
"clientWorkingSetBytes": 134643712,
"clientPrivateBytes": 0,
"clientThreads": 18,
"clientHandles": 0,
"collections": [
0,
0,
0
]
},
{
"benchmarkRound": 2,
"warmupOperationsPerKind": 10,
"operationsPerKind": 50,
"latencyMilliseconds": {
"scalar-read": {
"p50": 0.1667,
"p95": 0.2128,
"p99": 0.2603,
"throughputOperationsPerSecond": 5854.595271828857
},
"scalar-write": {
"p50": 0.1705,
"p95": 0.2398,
"p99": 0.2455,
"throughputOperationsPerSecond": 5681.043494068992
},
"array-read": {
"p50": 0.1645,
"p95": 0.2415,
"p99": 7.8687,
"throughputOperationsPerSecond": 3070.70607815561
},
"array-write": {
"p50": 0.1646,
"p95": 0.2192,
"p99": 0.2517,
"throughputOperationsPerSecond": 5982.650314089142
}
},
"clientCpuMilliseconds": 45.783,
"clientAllocatedBytes": 1318464,
"clientWorkingSetBytes": 136298496,
"clientPrivateBytes": 0,
"clientThreads": 18,
"clientHandles": 0,
"collections": [
0,
0,
0
]
},
{
"benchmarkRound": 3,
"warmupOperationsPerKind": 10,
"operationsPerKind": 50,
"latencyMilliseconds": {
"scalar-read": {
"p50": 0.1606,
"p95": 0.2057,
"p99": 0.2141,
"throughputOperationsPerSecond": 6152.7102688734385
},
"scalar-write": {
"p50": 0.1645,
"p95": 0.1888,
"p99": 0.1986,
"throughputOperationsPerSecond": 6050.338818973862
},
"array-read": {
"p50": 0.1262,
"p95": 0.1857,
"p99": 2.3447,
"throughputOperationsPerSecond": 5717.945199213212
},
"array-write": {
"p50": 0.1297,
"p95": 0.1718,
"p99": 0.1826,
"throughputOperationsPerSecond": 7597.05234369065
}
},
"clientCpuMilliseconds": 43.613,
"clientAllocatedBytes": 1296800,
"clientWorkingSetBytes": 137920512,
"clientPrivateBytes": 0,
"clientThreads": 18,
"clientHandles": 0,
"collections": [
1,
0,
0
]
}
]
},
{
"name": "baseline-client-to-candidate-server",
"sdk": "1.5.378.0",
"observations": [
{
"benchmarkRound": 1,
"warmupOperationsPerKind": 10,
"operationsPerKind": 50,
"latencyMilliseconds": {
"scalar-read": {
"p50": 0.2427,
"p95": 0.2798,
"p99": 0.3337,
"throughputOperationsPerSecond": 4066.528404700906
},
"scalar-write": {
"p50": 0.2384,
"p95": 0.2922,
"p99": 0.2955,
"throughputOperationsPerSecond": 4237.647258242225
},
"array-read": {
"p50": 0.2082,
"p95": 0.2556,
"p99": 0.3692,
"throughputOperationsPerSecond": 4715.602040912563
},
"array-write": {
"p50": 0.1895,
"p95": 0.295,
"p99": 0.3612,
"throughputOperationsPerSecond": 4881.811347282295
}
},
"clientCpuMilliseconds": 55.468,
"clientAllocatedBytes": 1325592,
"clientWorkingSetBytes": 134545408,
"clientPrivateBytes": 0,
"clientThreads": 19,
"clientHandles": 0,
"collections": [
0,
0,
0
]
},
{
"benchmarkRound": 2,
"warmupOperationsPerKind": 10,
"operationsPerKind": 50,
"latencyMilliseconds": {
"scalar-read": {
"p50": 0.2064,
"p95": 0.293,
"p99": 0.3812,
"throughputOperationsPerSecond": 4710.004992605292
},
"scalar-write": {
"p50": 0.2025,
"p95": 0.3077,
"p99": 0.4019,
"throughputOperationsPerSecond": 4707.1671326762125
},
"array-read": {
"p50": 0.1626,
"p95": 0.2309,
"p99": 0.2487,
"throughputOperationsPerSecond": 5908.2100486836525
},
"array-write": {
"p50": 0.1752,
"p95": 0.243,
"p99": 0.2937,
"throughputOperationsPerSecond": 5501.336824848437
}
},
"clientCpuMilliseconds": 45.733,
"clientAllocatedBytes": 1326672,
"clientWorkingSetBytes": 136249344,
"clientPrivateBytes": 0,
"clientThreads": 19,
"clientHandles": 0,
"collections": [
0,
0,
0
]
},
{
"benchmarkRound": 3,
"warmupOperationsPerKind": 10,
"operationsPerKind": 50,
"latencyMilliseconds": {
"scalar-read": {
"p50": 0.1973,
"p95": 0.2547,
"p99": 0.2738,
"throughputOperationsPerSecond": 4988.62593287305
},
"scalar-write": {
"p50": 0.2038,
"p95": 0.2312,
"p99": 0.2664,
"throughputOperationsPerSecond": 4974.035534509858
},
"array-read": {
"p50": 0.16,
"p95": 0.1892,
"p99": 2.4703,
"throughputOperationsPerSecond": 4757.46446174047
},
"array-write": {
"p50": 0.195,
"p95": 0.2245,
"p99": 0.2277,
"throughputOperationsPerSecond": 5199.775369704029
}
},
"clientCpuMilliseconds": 46.975,
"clientAllocatedBytes": 1286632,
"clientWorkingSetBytes": 137936896,
"clientPrivateBytes": 0,
"clientThreads": 19,
"clientHandles": 0,
"collections": [
1,
0,
0
]
}
]
},
{
"name": "candidate-client-to-baseline-server",
"sdk": "2.0.1.0",
"observations": [
{
"benchmarkRound": 1,
"warmupOperationsPerKind": 10,
"operationsPerKind": 50,
"latencyMilliseconds": {
"scalar-read": {
"p50": 0.2241,
"p95": 0.2949,
"p99": 0.4007,
"throughputOperationsPerSecond": 4283.2055510343935
},
"scalar-write": {
"p50": 0.2197,
"p95": 0.2889,
"p99": 0.3367,
"throughputOperationsPerSecond": 4384.887922264707
},
"array-read": {
"p50": 0.1518,
"p95": 0.2545,
"p99": 0.3429,
"throughputOperationsPerSecond": 6052.902366684823
},
"array-write": {
"p50": 0.1445,
"p95": 0.2006,
"p99": 0.2097,
"throughputOperationsPerSecond": 6667.466762678188
}
},
"clientCpuMilliseconds": 63.388,
"clientAllocatedBytes": 673504,
"clientWorkingSetBytes": 128729088,
"clientPrivateBytes": 0,
"clientThreads": 20,
"clientHandles": 0,
"collections": [
0,
0,
0
]
},
{
"benchmarkRound": 2,
"warmupOperationsPerKind": 10,
"operationsPerKind": 50,
"latencyMilliseconds": {
"scalar-read": {
"p50": 0.1828,
"p95": 0.2139,
"p99": 0.2324,
"throughputOperationsPerSecond": 5446.563762921972
},
"scalar-write": {
"p50": 0.1544,
"p95": 0.2086,
"p99": 8.8284,
"throughputOperationsPerSecond": 3001.903206633005
},
"array-read": {
"p50": 0.1853,
"p95": 0.227,
"p99": 0.2436,
"throughputOperationsPerSecond": 5281.448384404939
},
"array-write": {
"p50": 0.19,
"p95": 0.2219,
"p99": 0.2799,
"throughputOperationsPerSecond": 5251.163132633879
}
},
"clientCpuMilliseconds": 63.984,
"clientAllocatedBytes": 674328,
"clientWorkingSetBytes": 129597440,
"clientPrivateBytes": 0,
"clientThreads": 20,
"clientHandles": 0,
"collections": [
0,
0,
0
]
},
{
"benchmarkRound": 3,
"warmupOperationsPerKind": 10,
"operationsPerKind": 50,
"latencyMilliseconds": {
"scalar-read": {
"p50": 0.1501,
"p95": 0.1844,
"p99": 0.196,
"throughputOperationsPerSecond": 6574.016855779218
},
"scalar-write": {
"p50": 0.1424,
"p95": 0.1684,
"p99": 0.1927,
"throughputOperationsPerSecond": 6859.181013786954
},
"array-read": {
"p50": 0.1601,
"p95": 0.1993,
"p99": 0.2257,
"throughputOperationsPerSecond": 6130.155460742485
},
"array-write": {
"p50": 0.1835,
"p95": 0.2002,
"p99": 0.2039,
"throughputOperationsPerSecond": 5506.911173522769
}
},
"clientCpuMilliseconds": 58.499,
"clientAllocatedBytes": 674224,
"clientWorkingSetBytes": 130433024,
"clientPrivateBytes": 0,
"clientThreads": 20,
"clientHandles": 0,
"collections": [
0,
0,
0
]
}
]
},
{
"name": "candidate-client-to-candidate-server",
"sdk": "2.0.1.0",
"observations": [
{
"benchmarkRound": 1,
"warmupOperationsPerKind": 10,
"operationsPerKind": 50,
"latencyMilliseconds": {
"scalar-read": {
"p50": 0.2968,
"p95": 0.3548,
"p99": 0.4229,
"throughputOperationsPerSecond": 3258.4115895183427
},
"scalar-write": {
"p50": 0.2754,
"p95": 0.3423,
"p99": 0.4283,
"throughputOperationsPerSecond": 3531.7718192862994
},
"array-read": {
"p50": 0.2407,
"p95": 0.2708,
"p99": 0.3132,
"throughputOperationsPerSecond": 4129.194229038145
},
"array-write": {
"p50": 0.27,
"p95": 0.3783,
"p99": 0.3875,
"throughputOperationsPerSecond": 3553.1047028893854
}
},
"clientCpuMilliseconds": 73.878,
"clientAllocatedBytes": 685360,
"clientWorkingSetBytes": 128188416,
"clientPrivateBytes": 0,
"clientThreads": 19,
"clientHandles": 0,
"collections": [
0,
0,
0
]
},
{
"benchmarkRound": 2,
"warmupOperationsPerKind": 10,
"operationsPerKind": 50,
"latencyMilliseconds": {
"scalar-read": {
"p50": 0.2477,
"p95": 0.2772,
"p99": 0.2965,
"throughputOperationsPerSecond": 4027.580873823946
},
"scalar-write": {
"p50": 0.2425,
"p95": 0.2885,
"p99": 0.357,
"throughputOperationsPerSecond": 4128.137384412154
},
"array-read": {
"p50": 0.2063,
"p95": 0.2431,
"p99": 0.2776,
"throughputOperationsPerSecond": 4806.07487864661
},
"array-write": {
"p50": 0.261,
"p95": 0.3474,
"p99": 0.3773,
"throughputOperationsPerSecond": 3704.060391000616
}
},
"clientCpuMilliseconds": 68.193,
"clientAllocatedBytes": 674904,
"clientWorkingSetBytes": 129024000,
"clientPrivateBytes": 0,
"clientThreads": 19,
"clientHandles": 0,
"collections": [
0,
0,
0
]
},
{
"benchmarkRound": 3,
"warmupOperationsPerKind": 10,
"operationsPerKind": 50,
"latencyMilliseconds": {
"scalar-read": {
"p50": 0.247,
"p95": 0.3,
"p99": 0.3212,
"throughputOperationsPerSecond": 4017.096763826847
},
"scalar-write": {
"p50": 0.211,
"p95": 0.2719,
"p99": 0.2778,
"throughputOperationsPerSecond": 4647.185664361664
},
"array-read": {
"p50": 0.2318,
"p95": 0.262,
"p99": 0.291,
"throughputOperationsPerSecond": 4246.789427193043
},
"array-write": {
"p50": 0.2428,
"p95": 0.2903,
"p99": 0.3024,
"throughputOperationsPerSecond": 4139.312708517878
}
},
"clientCpuMilliseconds": 67.128,
"clientAllocatedBytes": 682744,
"clientWorkingSetBytes": 129843200,
"clientPrivateBytes": 0,
"clientThreads": 19,
"clientHandles": 0,
"collections": [
0,
0,
0
]
}
]
}
]
}
@@ -0,0 +1,792 @@
[
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/client/Opc.Ua.Client.dll",
"sha256": "fd000b186f4bc5dbd9947e876f96c178e8c4259ee3e8717d672a4ac2f4c3a058",
"bytes": 852096
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/client/Opc.Ua.Configuration.dll",
"sha256": "30fb5142f13a3b3083c0484bcdedaec2efe2f50e57f10ec6a61257ca5359aa7e",
"bytes": 85120
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/client/Opc.Ua.Core.dll",
"sha256": "602e85316943ad7ebae33a23d60a69a6ea50d91250ad0556906b816435daa37c",
"bytes": 7945344
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/client/Opc.Ua.Security.Certificates.dll",
"sha256": "a2a05c64348f6f8aa60de0fe27d8e5e22d12e09b1ba6172ca34b3773620e394c",
"bytes": 79488
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/client/Opc.Ua.Types.dll",
"sha256": "c5c5e36cf6381444b408fe1b51726b17deca74173764cd62ccee954b3c8c2718",
"bytes": 1127552
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/client/ZB.MOM.WW.OtOpcUa.Client.CLI.deps.json",
"sha256": "8756470c547137f40ef9962dfa6f40c6dc2a1801cd397524dce7127fb79b70b9",
"bytes": 12175
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/client/ZB.MOM.WW.OtOpcUa.Client.CLI.dll",
"sha256": "38ff27df4c1389d2cf97a7ec1fe206733f02c3664b29c4fe18586ede225a53ca",
"bytes": 91648
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/client/ZB.MOM.WW.OtOpcUa.Client.CLI.runtimeconfig.json",
"sha256": "9950d4583cfc9a106857c0d5b8a75b53e8774852b57b80bc3823a74d44a4d87f",
"bytes": 330
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/client/ZB.MOM.WW.OtOpcUa.Client.Shared.dll",
"sha256": "326e14df7bf2a7602637fbff36988c3dd4be38de936da24a1b4287c0ab685be6",
"bytes": 111616
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/Opc.Ua.Client.dll",
"sha256": "fd000b186f4bc5dbd9947e876f96c178e8c4259ee3e8717d672a4ac2f4c3a058",
"bytes": 852096
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/Opc.Ua.Configuration.dll",
"sha256": "30fb5142f13a3b3083c0484bcdedaec2efe2f50e57f10ec6a61257ca5359aa7e",
"bytes": 85120
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/Opc.Ua.Core.dll",
"sha256": "602e85316943ad7ebae33a23d60a69a6ea50d91250ad0556906b816435daa37c",
"bytes": 7945344
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/Opc.Ua.Security.Certificates.dll",
"sha256": "a2a05c64348f6f8aa60de0fe27d8e5e22d12e09b1ba6172ca34b3773620e394c",
"bytes": 79488
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/Opc.Ua.Server.dll",
"sha256": "d8d9706dea577858325730cd89896826ed82f7bfcf9503ccd0260311663a69f4",
"bytes": 642176
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/Opc.Ua.Types.dll",
"sha256": "c5c5e36cf6381444b408fe1b51726b17deca74173764cd62ccee954b3c8c2718",
"bytes": 1127552
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/OtOpcUa.Host.deps.json",
"sha256": "f47fb25d83e40a5c1d96c063f1d63b02a04c222a10554220df58cac896654ea9",
"bytes": 153279
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/OtOpcUa.Host.dll",
"sha256": "e88fc71f59e69e15c3191a782c29ce44127e320c7560ddd26ce1759787806f98",
"bytes": 193024
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/OtOpcUa.Host.runtimeconfig.json",
"sha256": "eff38a9750f7cca2407b1d639a963d56ffea92088e6da971bbbfa74e3dcd7224",
"bytes": 540
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.AdminUI.dll",
"sha256": "1ae11959c22bc53a648b91c6c1ae41e4a5d717e396bd645eae4fb08dc1633597",
"bytes": 2152448
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Cluster.dll",
"sha256": "d6bc21641172d832004b62b8bb76aeb40622a943be2172f394e5c6333c04ef57",
"bytes": 67584
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Commons.dll",
"sha256": "be97a0b975afde7ec4ebf0b10de53c26f212ceca1f6a3ae090e40ebfde304413",
"bytes": 231424
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Configuration.dll",
"sha256": "195dab9f7a9f80c45902e653ff361116a307d8110a3f87baad814bea57689b84",
"bytes": 542720
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.ControlPlane.dll",
"sha256": "36954f51b09930df414675e5a0c038933ad2b27cd6560bf797d99801c02b30a1",
"bytes": 179200
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Core.Abstractions.dll",
"sha256": "b0f4e332ced57eae3ab1dcc282c073f51ea0c545a381aef50479ee58f0c23efc",
"bytes": 124416
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian.dll",
"sha256": "a61a6ea68236dd4ed86ab4e74e4d632c5e260103aa5ebd92f4f995eb9f170b22",
"bytes": 56320
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.dll",
"sha256": "f1e429c0b8fcab8216b9107440008b92eea1236ff1225e79580ad70f51d56431",
"bytes": 83968
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions.dll",
"sha256": "ea24898698c8351bc9cf20fcce725dccc93ccf05ef3af7783f73854cc2033c31",
"bytes": 13312
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Core.Scripting.OutOfProcess.dll",
"sha256": "cf44009a720a00bf9192374d371c7c76e75afbe62cc216b5c1be913b92679127",
"bytes": 135168
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Core.Scripting.Protocol.dll",
"sha256": "07800332d0842b93c3987fca1dc10a1390bcb16f44567610d867513ad9fe17e0",
"bytes": 117248
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Core.Scripting.dll",
"sha256": "9637b7ffb4984f54bc54363ff2d81b6a3401f0cb7eeed8694db626ef41d54d5b",
"bytes": 121856
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.dll",
"sha256": "107aaf2ef2d7704e320dabc1072bc50217faabb2d1488e66c91b1f756b440c45",
"bytes": 11264
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Core.dll",
"sha256": "34a4d7c0dddf4efa5fae91a3798c2f1c08b992faf4a827a9e05ed1338c6e4720",
"bytes": 124928
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.dll",
"sha256": "315dcc540d304c9f1d5b48caa25e5f081569f1a41aa8784cf371c1d6cef7322d",
"bytes": 123392
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Contracts.dll",
"sha256": "d7b34798587d0939cc78eb69d9df1d617c1c75a61b8dd11a5308958a43a8c4f1",
"bytes": 24576
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.AbCip.dll",
"sha256": "73073627fcdc8079df645478d6717576975117c3b21cfa7e9422f1e3bc5f4e44",
"bytes": 214016
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Contracts.dll",
"sha256": "5c66d76dd455c42f0c5b0aed11240fa31e1bdbd2f0a8368d8f41976977e93502",
"bytes": 30720
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.dll",
"sha256": "cbf92a935d86596a9d4dbeeaa625e304c2045851a0367c3a6b0de03bf78bd6fa",
"bytes": 136704
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Calculation.Contracts.dll",
"sha256": "29c079414497b30debe8d3ecd7e77bdc4eb16c4fc076e675680a2e880e54b6c2",
"bytes": 10240
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Calculation.dll",
"sha256": "132e36042a4691cc8e252ab9e9d8c90592fcd897959f533aa37e33f193cf00c8",
"bytes": 57344
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Contracts.dll",
"sha256": "2bdbf743194193d5a7bc323912e2c7f740cc21633154a4a3cc8e034fd15bb7e8",
"bytes": 38912
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.dll",
"sha256": "17c28f27fdecd09a46b971b19655bdb933f84990e91bb80c7092c344a0ca974f",
"bytes": 300032
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser.dll",
"sha256": "f2796d323c222708c28cd0a4289c46db31ec3b6537da7f5cd260fc76f8abdc76",
"bytes": 19456
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Contracts.dll",
"sha256": "780a11f69194713e2f87e74277e99882ee3518b2824551b45cd04e3b9bdf6648",
"bytes": 24576
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.dll",
"sha256": "a8e19baa5a736544d914f942d526a5cc038f6b982451bfa82a77365a159434e4",
"bytes": 224768
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.dll",
"sha256": "400b1896e55d08781231efeba302596299df2b00b2f48afd43d46c22d59b515c",
"bytes": 129536
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb.dll",
"sha256": "8e91a8cce804023639a4bdb2dc60f9fc950992882e50786d45ceeb0f38ed1f68",
"bytes": 167424
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Historian.Sql.Common.dll",
"sha256": "2746b8e9c03b5b8c330152803eee91cf2069f0e6c074d70d03017b1f1f1a2398",
"bytes": 51712
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Historian.SqlServer.dll",
"sha256": "94b9bcd4506427a44158e0b299a5ae7bcee14fd40b85cc9f63420e4631aa1a6f",
"bytes": 143872
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Contracts.dll",
"sha256": "6582151085ddea7d319b990c2c58057b6ba5a49860593229b3642dff527ae204",
"bytes": 19456
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.dll",
"sha256": "c9670c5d169056563294673839ca63321a72a438c519263be708b55f3fc818f9",
"bytes": 214016
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing.dll",
"sha256": "ed07d04709372ef804f465995431b15acdef791f66c8130ab7282372728f7cda",
"bytes": 25088
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Contracts.dll",
"sha256": "724873ac2f6c08f307a150c5575cc3c022d7e367124ed924c0c7899b0e9575a5",
"bytes": 39424
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Modbus.dll",
"sha256": "db1e3e7c79dc1a46e1d4ffaa5ea1aac9ed73814d7f06cbfa855ebb3b27a8810d",
"bytes": 131584
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Browser.dll",
"sha256": "ace18db746c30307e67efa3acd68e915cc2b62b0a85bdba833df4ea7c85b5f6c",
"bytes": 48128
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Contracts.dll",
"sha256": "24e795bd8027d7b3ca3d95630b73be6d7cef774243357b01850f145b6066dcb9",
"bytes": 139776
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Transport.dll",
"sha256": "bec0cecc0bbb06b4715d7b909ccf4d357420e120b2bff3c10ee9f59f71044927",
"bytes": 12800
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.dll",
"sha256": "da64eacb80ddca9cd0a40295bba2c8ff81ee9c68ccaf32cbd329ae854375deb7",
"bytes": 195072
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.dll",
"sha256": "7e2ac501136d24a3d0ad6bdae2e4a47460439ad8ab78af538bbccca2e38e5695",
"bytes": 35840
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Contracts.dll",
"sha256": "c15e6db37681cbbef8684d5301e1bba54186ae503aaf55d1c2d06a0567ab28fe",
"bytes": 20480
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Session.dll",
"sha256": "910604abd4daa6196c8fdcd2db085873ee5a4c27b5a67c359590071117c6e4dd",
"bytes": 16384
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.dll",
"sha256": "cd689b0ec777820d91f914abfd9e4a921143a46df84ee43d0214c66f74225ac0",
"bytes": 144384
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.S7.Contracts.dll",
"sha256": "6fe895674318dc42c0c3cc74266831862d3fe2f13098c26e7d826baf79d7fc47",
"bytes": 29696
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.S7.dll",
"sha256": "d23a7ac62d781f0c14dd7c9d4f51b712c13599ee78b9b6d302d30dfd5e77da37",
"bytes": 124928
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Sql.Browser.dll",
"sha256": "c341d49bf100e0617d9c4e7d24f63d8affd1bc0fe2edce15e51cc14366fa2207",
"bytes": 43008
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Sql.Contracts.dll",
"sha256": "f0a0cc852116f739247422a0427550e52c938b27d1a8b4e685f45a19a23635ab",
"bytes": 38912
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Sql.Dialect.dll",
"sha256": "7d78655da50c07ac049cd687c59f2a7b6eb2c491543bd4089ffbe60fb805205f",
"bytes": 40448
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Sql.dll",
"sha256": "6ec1a9bc7443c1d7b8084ee2fc5e67cbfd2a8d61add303a980cef78a46cb5c67",
"bytes": 144384
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Contracts.dll",
"sha256": "96db9e5d162b19914cf0ba55c1408c1c9271c80c1a1a12604e684f5785de4b96",
"bytes": 17408
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.dll",
"sha256": "c24cfc897bde6c0eb65b9958bad952814ed02a27fe25a55c416c1ba61be2c261",
"bytes": 188928
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.OpcUaServer.dll",
"sha256": "ee1f704fa1574b77bf56cef415c47c33a4428c6852db52bb1443d6765d092cb1",
"bytes": 363008
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Runtime.dll",
"sha256": "50e752383ae81ef6bb23e360fafe9162be0d1dcb7dd2d8731f16bce762834dd4",
"bytes": 539648
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Security.dll",
"sha256": "a88b1577d1e4dd580e012e7673e31d5363c3d9a79fb415bef4c3d63849678a4d",
"bytes": 52736
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-baseline-oracle/OtOpcUa/Directory.Packages.props",
"sha256": "7e6459447843b93bc94fbc1f3eeb1c695b5c46c08aeb94debe86a7d419c63221",
"bytes": 18829
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/Directory.Packages.props",
"sha256": "31a4cac1c205f5b9e67119357c77751b8ab999f6c5f350da68784614040c8a93",
"bytes": 19000
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-baseline/bin/Release/net10.0/ProcessHost.dll",
"sha256": "841f83aeb3a32d49c06518a98963743d720015bba41ae3138649126ce3a6af1c",
"bytes": 94720
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/Opc.Ua.Client.dll",
"sha256": "620da406fabdbc75d11f9827ea7e39d2d3c762471bd2264b0d11f1d6cd9247cd",
"bytes": 2442880
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/Opc.Ua.Configuration.dll",
"sha256": "6ed6a594c93c3ff35d1a16539a9fe87c1e2640549cc2f48ed6bae333dc8bf0e7",
"bytes": 117376
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/Opc.Ua.Core.Schema.dll",
"sha256": "51cfad48657d95d38074bb88be29aea6f5b361a07397247d58838d066b99772e",
"bytes": 135168
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/Opc.Ua.Core.Types.dll",
"sha256": "a7dd141b8dabe8848ff28c2fb8e16a85a0508e6a49e6b1a1c1e267175b99af96",
"bytes": 12703872
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/Opc.Ua.Core.dll",
"sha256": "9d0857aa6b7f3c49037d4ea557b4da8edf07e146069464481783e66c5c9cd35a",
"bytes": 2549376
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/Opc.Ua.Security.Certificates.dll",
"sha256": "7f4969aceb2f0db5328c6882cb4b117342f3b18905c38acdba600683860c4fd0",
"bytes": 118400
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/Opc.Ua.Server.dll",
"sha256": "930e714a2f6e15d393840bcdc95e1afa8012bc740c3738a655f958142721864e",
"bytes": 3332224
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/Opc.Ua.Types.dll",
"sha256": "c542f69882f4e8a4ebd5ccc28a3881d6967c7ffb6dcc3c56dc6c776f39c2ede1",
"bytes": 1792640
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/OtOpcUa.Host.deps.json",
"sha256": "c289766446d61a6bf15574fdde982a8c44b6faaabb38ad718522f63987a58d69",
"bytes": 179492
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/OtOpcUa.Host.dll",
"sha256": "a4ac1663a8af08aec150e02c5a9590e6fd346d051256ebbd9ee16019f2ecc6a2",
"bytes": 193024
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/OtOpcUa.Host.runtimeconfig.json",
"sha256": "eff38a9750f7cca2407b1d639a963d56ffea92088e6da971bbbfa74e3dcd7224",
"bytes": 540
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ProcessHost.deps.json",
"sha256": "cb76c2da9266dd11b61483fc9425d8a7ed3e25c83b1179ef60bfbd559b7db77c",
"bytes": 165039
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ProcessHost.dll",
"sha256": "e025fc21ac3559c3796ec639f0ef7b9c3026ccbbdaa4783e14b8930eae3a2043",
"bytes": 95232
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ProcessHost.runtimeconfig.json",
"sha256": "bdbb6b43e509fbbab6c78b7004c4debf1e9791a648a08f730ff5997f71ef7099",
"bytes": 508
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.AdminUI.dll",
"sha256": "0f76616b8ddccdb681a9e576f7d031a58b0f5e257d579f58b2143679c27afbe6",
"bytes": 2152448
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Client.Shared.dll",
"sha256": "49729f0e97e752a65fa2f8debb4ea5505a7ccdfe9ca1321ae6bf8ce5323a7049",
"bytes": 120320
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Cluster.dll",
"sha256": "4ea4a506d94ecd032b41e0e3643f2d407e9904759c892ffa14a28483ad82862e",
"bytes": 67584
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Commons.dll",
"sha256": "b77b9502c43e126cdb730b535dbd01c76f9f4ad4c79765ba6e7ea447938e3834",
"bytes": 231424
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Configuration.dll",
"sha256": "f7f91c501acd81f7bf2d71740f2141d2cc134248388d2f43f78bb16fbf2e7c37",
"bytes": 542720
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.ControlPlane.dll",
"sha256": "54484082b7d785727c1bbb4cd430542e2c101f31801ef3fe113b99efc8e347c2",
"bytes": 179200
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Abstractions.dll",
"sha256": "c4be146d49102962324a2e4af71f17b2804e6003a82954733dd6302632e5e249",
"bytes": 124416
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian.dll",
"sha256": "71d20c01ccafb8327ee474f1133a7b265d4252872ac9f6fb0b78fe2c8d6e636f",
"bytes": 56320
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.dll",
"sha256": "3ad5440cb4c1644cad844c50a28ac9c0f9534795a6a553df446fb701c34c191a",
"bytes": 83968
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions.dll",
"sha256": "88d9ee543a6a8525a4074ecb9524ce5c095e1bbba8017bde980964cb53da447f",
"bytes": 13312
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Scripting.OutOfProcess.dll",
"sha256": "c889dd89aad72e130439e916371ec4e63f0abbf10f7934e42caf504d5227aadb",
"bytes": 135168
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Scripting.Protocol.dll",
"sha256": "6b21c826e93a1f8d942f2ee101d522d2566b8e8fd8b2ef223b4ca75f679d7118",
"bytes": 117248
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Scripting.dll",
"sha256": "f36de3c774ece202f34288c7bee877cbee3d14a688ae8b07aede00ba016f5b57",
"bytes": 121856
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.dll",
"sha256": "21893da38bf83afe3cb90f6a4d8caed6bb5db837fecc0c819f88ea7e17c73a27",
"bytes": 11264
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.dll",
"sha256": "7aec5cb22b6a9939044502eaedf188a5e95887057b6cc7a70b0444ff83f1802b",
"bytes": 124928
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.dll",
"sha256": "86db4c0cbca35c13f9ffaa258ecc54e15fb05c4b5fbda991909f00793a26a1dc",
"bytes": 123392
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Contracts.dll",
"sha256": "862f63ab4499d1e32cd5a3d7245dca4158398a7d546d5a882be37cb9e4738de8",
"bytes": 24576
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.AbCip.dll",
"sha256": "bd5d38bc81fef735143ba58d2158e1cb37b2b9998e32ebb7765a1eea6111c8c5",
"bytes": 214016
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Contracts.dll",
"sha256": "a0b596bcd65014cafac896c17d1247e0c7cfef076952aee7096d90377ee3aedf",
"bytes": 30720
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.dll",
"sha256": "fdf201aaccbaf10b53eb07f0b627e8ffd3955ec78b3e446be85c03e1ad7ff687",
"bytes": 136704
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Calculation.Contracts.dll",
"sha256": "36033badc6dcfa646592dfe00aa184c657b24f7d73f17c4574853727275c320f",
"bytes": 10240
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Calculation.dll",
"sha256": "6124ecc4dae44b0f4012f03d3583c86ce4d0766205386ff7c70f319c77220e87",
"bytes": 57344
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Contracts.dll",
"sha256": "150a79b5d38205d573399e29a2924eeb62c17261729c82bbf6c606a769f03467",
"bytes": 38912
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.dll",
"sha256": "b45e29a1d892ae00e914839dfe1001c6eb15ce14af1f9800a0ff48a5517d8dd6",
"bytes": 300032
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser.dll",
"sha256": "87f8d77edc62c8d1c60e69620b297b1cef75b91355c871aae707e3f05f30e267",
"bytes": 19456
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Contracts.dll",
"sha256": "4eaad55f82495fedcf82a9be3157c748d2fc6b6f59eb7ea41bff88b18332dd51",
"bytes": 24576
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.dll",
"sha256": "e336053cc6fd0cb57615d2333c9ea99a2a9acac9273c8927af82c3fc551157ac",
"bytes": 224768
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.dll",
"sha256": "e6f7ebfb5b8078a336601a7603a094ac743c4adbf02bb311dfef29d2328eeb65",
"bytes": 129536
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb.dll",
"sha256": "55e7cf55c8fece377cdef598f901e4347b2da13ab7feb265623866c08bad477f",
"bytes": 167424
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Historian.Sql.Common.dll",
"sha256": "b033a90e65f7b67a8ab7ef5c614019a54c12dd5590fc9eae17fca4b736918373",
"bytes": 51712
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Historian.SqlServer.dll",
"sha256": "685e8961b23190b745568955a34cca1462dffce15007c25a2265f44fd1224436",
"bytes": 143872
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Contracts.dll",
"sha256": "a592d1481ea662ef368d07f706e70779a1f879126101f17042ce270c5faa66c3",
"bytes": 19456
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.dll",
"sha256": "1ca66a30106c4b9476eed6b766ae78a59cbf6b408814e2cce9f1d7fd68773762",
"bytes": 214016
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing.dll",
"sha256": "2cb7c0cd79d399bce99f66d40fedbc002aad0d1204c252c1f62a7edd83e73b09",
"bytes": 25088
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Contracts.dll",
"sha256": "a8fd87c6a32b45b91d1c6e12c597916a20d5c254d137a7f8d9bb6cfe473ca674",
"bytes": 39424
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Modbus.dll",
"sha256": "84723c305417dd10440345acefb50c33f47d55b34543584abf7d9235ad13df86",
"bytes": 131584
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Browser.dll",
"sha256": "a5c4262f0a0d378d99a89016d6b7fa927632ff5d99bcc5de52a1ceb359ad2d5c",
"bytes": 48128
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Contracts.dll",
"sha256": "f4328140545f1c8e2226865ceac9d309b8f55bbee0ed5e9713836f16bc0aff63",
"bytes": 139776
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Transport.dll",
"sha256": "b592a854bb997781a1f1d316a615f629cb3fb08dad683f2fdbff2867210cc43b",
"bytes": 12800
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.dll",
"sha256": "4d63899c81316a0d9f856b59759e97dd9b52d53b00ae5fd57a2cdfc916fc73ea",
"bytes": 195072
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.dll",
"sha256": "a21d73fbd9dfd60ebf4ac56a961e172473f54e55727dc1ad856991c9e1ba756c",
"bytes": 36352
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Contracts.dll",
"sha256": "2e45346638a6814dc6af3f8c99ea448bd2538267baf645453e818fe702265157",
"bytes": 20480
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Session.dll",
"sha256": "a9052366f2c27e485bf9a1d6c3150459f54aa1c510b63d01d99a9270ff95f3bc",
"bytes": 19968
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.dll",
"sha256": "3bbe213152adee004934b4fbb612957b6891461f69c20e55459b5a03660a2915",
"bytes": 147968
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.S7.Contracts.dll",
"sha256": "0610e0f7ba83fb3c3768e4909fb1d2fa0d698608c58606b31235b782813b65ca",
"bytes": 29696
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.S7.dll",
"sha256": "f1300ad64b7ddfde76078e9f174a10875a6e7f6a5bdf5d20ae3cdf1c94aecee9",
"bytes": 124928
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Sql.Browser.dll",
"sha256": "0965780c6b2c8f14884e98e1e8f89d7d54d3d74473987cf63f5b97f98b147fef",
"bytes": 43008
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Sql.Contracts.dll",
"sha256": "b705643ccd5e85e11bd585d8258c9d4bb78268bd5664ec20a7310cd92a955751",
"bytes": 38912
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Sql.Dialect.dll",
"sha256": "3ba6f0217710d3c69b1cd7baaaa99a1b43fae3b0d27da66efba6f0c58475fd91",
"bytes": 40448
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Sql.dll",
"sha256": "17b472fe49541f794dccdc2490cda075fc746a515195dd5cee9b1250ef7e702c",
"bytes": 144384
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Contracts.dll",
"sha256": "6b0ea052040c218b70cf2d31c437be7dc474ce6e000395259a9ce4055fd1ce0f",
"bytes": 17408
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.dll",
"sha256": "ecdb122f3800af6ebb07421f0987e9e1561645a10bd05c86000974ce6c898060",
"bytes": 188928
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.OpcUaServer.dll",
"sha256": "40a65a2124fb917d6c5fe38e648ff3c5db9fb96672fd9ff1233992bd5cc17abc",
"bytes": 420864
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Runtime.dll",
"sha256": "a66420c048d83ad43f923fb3b97a07078df570d56a1bb5b2ce8406d6f9cbb4a0",
"bytes": 539648
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Security.dll",
"sha256": "daf52ae4d37a05f00819d837456d1db8191cd721794a12363a4424af46f0801e",
"bytes": 52736
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-cli/Opc.Ua.Client.dll",
"sha256": "620da406fabdbc75d11f9827ea7e39d2d3c762471bd2264b0d11f1d6cd9247cd",
"bytes": 2442880
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-cli/Opc.Ua.Configuration.dll",
"sha256": "6ed6a594c93c3ff35d1a16539a9fe87c1e2640549cc2f48ed6bae333dc8bf0e7",
"bytes": 117376
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-cli/Opc.Ua.Core.Schema.dll",
"sha256": "51cfad48657d95d38074bb88be29aea6f5b361a07397247d58838d066b99772e",
"bytes": 135168
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-cli/Opc.Ua.Core.Types.dll",
"sha256": "a7dd141b8dabe8848ff28c2fb8e16a85a0508e6a49e6b1a1c1e267175b99af96",
"bytes": 12703872
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-cli/Opc.Ua.Core.dll",
"sha256": "9d0857aa6b7f3c49037d4ea557b4da8edf07e146069464481783e66c5c9cd35a",
"bytes": 2549376
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-cli/Opc.Ua.Security.Certificates.dll",
"sha256": "7f4969aceb2f0db5328c6882cb4b117342f3b18905c38acdba600683860c4fd0",
"bytes": 118400
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-cli/Opc.Ua.Types.dll",
"sha256": "c542f69882f4e8a4ebd5ccc28a3881d6967c7ffb6dcc3c56dc6c776f39c2ede1",
"bytes": 1792640
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-cli/ZB.MOM.WW.OtOpcUa.Client.CLI.deps.json",
"sha256": "e1a36de862f9d940bfb55e09db02461bc20f2bd12dad30c8a8bca81ac38be40a",
"bytes": 37379
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-cli/ZB.MOM.WW.OtOpcUa.Client.CLI.dll",
"sha256": "bf1029417276643c6e7b632b882d3fc87c611e52f9e2fdd428e94d2e11f0513e",
"bytes": 92160
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-cli/ZB.MOM.WW.OtOpcUa.Client.CLI.runtimeconfig.json",
"sha256": "9950d4583cfc9a106857c0d5b8a75b53e8774852b57b80bc3823a74d44a4d87f",
"bytes": 330
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-cli/ZB.MOM.WW.OtOpcUa.Client.Shared.dll",
"sha256": "49729f0e97e752a65fa2f8debb4ea5505a7ccdfe9ca1321ae6bf8ce5323a7049",
"bytes": 120320
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/scripts/validation/opcua-sdk-compatibility/ProcessHost.cs",
"sha256": "724bca278fadcb44d086888415df7608acc9be56443682d4d5a08377f1aaccef",
"bytes": 30352
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/scripts/validation/opcua-sdk-compatibility/container-leg.sh",
"sha256": "1f5d1158572379b8a6304c3a3134a78c112043940f5bce468621677f7da3ef9e",
"bytes": 2589
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/scripts/validation/opcua-sdk-compatibility/run.py",
"sha256": "b94c880c0828b3bf9b330e142077dd5ef256cf6a27b037fd48a44361975fe5b6",
"bytes": 12587
}
]
@@ -0,0 +1,202 @@
[
{
"argv": [
"dotnet",
"build",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-baseline/ProcessHost.csproj",
"-c",
"Release",
"-m:1",
"--nologo"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-baseline/build.log"
},
{
"argv": [
"dotnet",
"build",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/ProcessHost.csproj",
"-c",
"Release",
"-m:1",
"--nologo"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/build.log"
},
{
"argv": [
"dotnet",
"publish",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI/ZB.MOM.WW.OtOpcUa.Client.CLI.csproj",
"-c",
"Release",
"-o",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-cli",
"-m:1",
"--nologo"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-cli-build.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-baseline/bin/Release/net10.0/ProcessHost.dll",
"prepare",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/baseline-client-to-baseline-server/shared"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/baseline-client-to-baseline-server/prepare.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-baseline/bin/Release/net10.0/ProcessHost.dll",
"server",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/baseline-client-to-baseline-server/server"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/baseline-client-to-baseline-server/server.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-baseline/bin/Release/net10.0/ProcessHost.dll",
"client",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/baseline-client-to-baseline-server/shared",
"opc.tcp://127.0.0.1:55610/OtOpcUa"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/baseline-client-to-baseline-server/shared.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-baseline/bin/Release/net10.0/ProcessHost.dll",
"prepare",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/baseline-client-to-candidate-server/shared"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/baseline-client-to-candidate-server/prepare.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ProcessHost.dll",
"server",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/baseline-client-to-candidate-server/server"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/baseline-client-to-candidate-server/server.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-baseline/bin/Release/net10.0/ProcessHost.dll",
"client",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/baseline-client-to-candidate-server/shared",
"opc.tcp://127.0.0.1:55642/OtOpcUa"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/baseline-client-to-candidate-server/shared.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ProcessHost.dll",
"prepare",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-client-to-baseline-server/shared"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-client-to-baseline-server/prepare.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-baseline/bin/Release/net10.0/ProcessHost.dll",
"server",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-client-to-baseline-server/server"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-client-to-baseline-server/server.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ProcessHost.dll",
"client",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-client-to-baseline-server/shared",
"opc.tcp://127.0.0.1:55671/OtOpcUa"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-client-to-baseline-server/shared.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ProcessHost.dll",
"prepare",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-client-to-candidate-server/shared"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-client-to-candidate-server/prepare.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ProcessHost.dll",
"server",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-client-to-candidate-server/server"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-client-to-candidate-server/server.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0/ProcessHost.dll",
"client",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-client-to-candidate-server/shared",
"opc.tcp://127.0.0.1:55697/OtOpcUa"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-client-to-candidate-server/shared.log"
},
{
"argv": [
"docker",
"run",
"--rm",
"--name",
"otopcua-sdk2-matrix-222d08944aba",
"--network",
"none",
"-e",
"OPCUA_MATRIX_PASSWORD",
"-v",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0:/candidate:ro",
"-v",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-baseline/bin/Release/net10.0:/server:ro",
"-v",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-cli:/cli:ro",
"-v",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-driver-cli-to-baseline-server:/task",
"mcr.microsoft.com/dotnet/sdk:10.0.103-noble",
"bash",
"/task/run.sh"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-driver-cli-to-baseline-server/container.log"
},
{
"argv": [
"docker",
"run",
"--rm",
"--name",
"otopcua-sdk2-matrix-00f1df5c1613",
"--network",
"none",
"-e",
"OPCUA_MATRIX_PASSWORD",
"-v",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0:/candidate:ro",
"-v",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/build-candidate/bin/Release/net10.0:/server:ro",
"-v",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-cli:/cli:ro",
"-v",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-driver-cli-to-candidate-server:/task",
"mcr.microsoft.com/dotnet/sdk:10.0.103-noble",
"bash",
"/task/run.sh"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10/candidate-driver-cli-to-candidate-server/container.log"
}
]
@@ -0,0 +1,17 @@
{"operation":"raw","rows":7}
{"operation":"raw","rows":7}
{"operation":"raw","rows":7}
{"operation":"raw","rows":7}
{"operation":"raw","rows":5}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
@@ -0,0 +1 @@
{"endpoint":"opc.tcp://127.0.0.1:55610/OtOpcUa","process":35791,"sdk":"1.5.378.0","rawUri":"https://zb.com/otopcua/raw","unsUri":"https://zb.com/otopcua/uns"}
@@ -0,0 +1,13 @@
{
"WorkingSet64": 231833600,
"PrivateMemorySize64": 0,
"HandleCount": 0,
"threads": 31,
"cpuMilliseconds": 1531.714,
"allocatedBytes": 156774688,
"collections": [
20,
8,
2
]
}
@@ -0,0 +1,17 @@
{"operation":"raw","rows":7}
{"operation":"raw","rows":7}
{"operation":"raw","rows":7}
{"operation":"raw","rows":7}
{"operation":"raw","rows":5}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
@@ -0,0 +1 @@
{"endpoint":"opc.tcp://127.0.0.1:55642/OtOpcUa","process":35814,"sdk":"2.0.1.0","rawUri":"https://zb.com/otopcua/raw","unsUri":"https://zb.com/otopcua/uns"}
@@ -0,0 +1,13 @@
{
"WorkingSet64": 201850880,
"PrivateMemorySize64": 0,
"HandleCount": 0,
"threads": 31,
"cpuMilliseconds": 2559.57,
"allocatedBytes": 47810656,
"collections": [
5,
1,
0
]
}
@@ -0,0 +1,17 @@
{"operation":"raw","rows":7}
{"operation":"raw","rows":7}
{"operation":"raw","rows":7}
{"operation":"raw","rows":7}
{"operation":"raw","rows":5}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
@@ -0,0 +1 @@
{"endpoint":"opc.tcp://127.0.0.1:55671/OtOpcUa","process":35841,"sdk":"1.5.378.0","rawUri":"https://zb.com/otopcua/raw","unsUri":"https://zb.com/otopcua/uns"}
@@ -0,0 +1,13 @@
{
"WorkingSet64": 229752832,
"PrivateMemorySize64": 0,
"HandleCount": 0,
"threads": 30,
"cpuMilliseconds": 1699.059,
"allocatedBytes": 157134200,
"collections": [
20,
8,
2
]
}
@@ -0,0 +1,17 @@
{"operation":"raw","rows":7}
{"operation":"raw","rows":7}
{"operation":"raw","rows":7}
{"operation":"raw","rows":7}
{"operation":"raw","rows":5}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
@@ -0,0 +1 @@
{"endpoint":"opc.tcp://127.0.0.1:55697/OtOpcUa","process":35866,"sdk":"2.0.1.0","rawUri":"https://zb.com/otopcua/raw","unsUri":"https://zb.com/otopcua/uns"}
@@ -0,0 +1,13 @@
{
"WorkingSet64": 201097216,
"PrivateMemorySize64": 0,
"HandleCount": 0,
"threads": 30,
"cpuMilliseconds": 2626.114,
"allocatedBytes": 47846920,
"collections": [
5,
1,
0
]
}
@@ -0,0 +1,30 @@
Event history for ns=2;s=Matrix/dev (2026-10-01 00:00 -> 2026-10-01 00:01)
EventId | Message
653030 | matrix-history
653031 | matrix-history
653032 | matrix-history
653033 | matrix-history
653034 | matrix-history
653035 | matrix-history
653036 | matrix-history
653037 | matrix-history
653038 | matrix-history
653039 | matrix-history
653130 | matrix-history
653131 | matrix-history
653132 | matrix-history
653133 | matrix-history
653134 | matrix-history
653135 | matrix-history
653136 | matrix-history
653137 | matrix-history
653138 | matrix-history
653139 | matrix-history
653230 | matrix-history
653231 | matrix-history
653232 | matrix-history
653233 | matrix-history
653234 | matrix-history
25 events returned.
@@ -0,0 +1,30 @@
History for ns=2;s=Matrix/dev/Scalar (2026-10-01 00:00 -> 2026-10-01 00:01)
Timestamp Value Status
2026-10-01T00:00:00.0000000Z 0 Good
2026-10-01T00:00:01.0000000Z 1 Good
2026-10-01T00:00:02.0000000Z 2 Good
2026-10-01T00:00:03.0000000Z 3 Good
2026-10-01T00:00:04.0000000Z 4 Good
2026-10-01T00:00:05.0000000Z 5 Good
2026-10-01T00:00:06.0000000Z 6 Good
2026-10-01T00:00:07.0000000Z 7 Good
2026-10-01T00:00:08.0000000Z 8 Good
2026-10-01T00:00:09.0000000Z 9 Good
2026-10-01T00:00:10.0000000Z 10 Good
2026-10-01T00:00:11.0000000Z 11 Good
2026-10-01T00:00:12.0000000Z 12 Good
2026-10-01T00:00:13.0000000Z 13 Good
2026-10-01T00:00:14.0000000Z 14 Good
2026-10-01T00:00:15.0000000Z 15 Good
2026-10-01T00:00:16.0000000Z 16 Good
2026-10-01T00:00:17.0000000Z 17 Good
2026-10-01T00:00:18.0000000Z 18 Good
2026-10-01T00:00:19.0000000Z 19 Good
2026-10-01T00:00:20.0000000Z 20 Good
2026-10-01T00:00:21.0000000Z 21 Good
2026-10-01T00:00:22.0000000Z 22 Good
2026-10-01T00:00:23.0000000Z 23 Good
2026-10-01T00:00:24.0000000Z 24 Good
25 values returned.
@@ -0,0 +1,5 @@
Node: ns=2;s=Matrix/dev/Scalar
Value: 92
Status: Good [0x00000000]
Source Time: 11/10/2026 17:19:14
Server Time: 11/10/2026 17:19:14
@@ -0,0 +1,9 @@
{"operation":"raw","rows":7}
{"operation":"raw","rows":7}
{"operation":"raw","rows":7}
{"operation":"raw","rows":7}
{"operation":"raw","rows":5}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
@@ -0,0 +1 @@
{"endpoint":"opc.tcp://127.0.0.1:44603/OtOpcUa","process":33,"sdk":"1.5.378.0","rawUri":"https://zb.com/otopcua/raw","unsUri":"https://zb.com/otopcua/uns"}
@@ -0,0 +1,13 @@
{
"WorkingSet64": 194039808,
"PrivateMemorySize64": 266993664,
"HandleCount": 141,
"threads": 19,
"cpuMilliseconds": 1705.768,
"allocatedBytes": 149139552,
"collections": [
5,
4,
1
]
}
@@ -0,0 +1,30 @@
Event history for ns=2;s=Matrix/dev (2026-10-01 00:00 -> 2026-10-01 00:01)
EventId | Message
653030 | matrix-history
653031 | matrix-history
653032 | matrix-history
653033 | matrix-history
653034 | matrix-history
653035 | matrix-history
653036 | matrix-history
653037 | matrix-history
653038 | matrix-history
653039 | matrix-history
653130 | matrix-history
653131 | matrix-history
653132 | matrix-history
653133 | matrix-history
653134 | matrix-history
653135 | matrix-history
653136 | matrix-history
653137 | matrix-history
653138 | matrix-history
653139 | matrix-history
653230 | matrix-history
653231 | matrix-history
653232 | matrix-history
653233 | matrix-history
653234 | matrix-history
25 events returned.
@@ -0,0 +1,30 @@
History for ns=2;s=Matrix/dev/Scalar (2026-10-01 00:00 -> 2026-10-01 00:01)
Timestamp Value Status
2026-10-01T00:00:00.0000000Z 0 Good
2026-10-01T00:00:01.0000000Z 1 Good
2026-10-01T00:00:02.0000000Z 2 Good
2026-10-01T00:00:03.0000000Z 3 Good
2026-10-01T00:00:04.0000000Z 4 Good
2026-10-01T00:00:05.0000000Z 5 Good
2026-10-01T00:00:06.0000000Z 6 Good
2026-10-01T00:00:07.0000000Z 7 Good
2026-10-01T00:00:08.0000000Z 8 Good
2026-10-01T00:00:09.0000000Z 9 Good
2026-10-01T00:00:10.0000000Z 10 Good
2026-10-01T00:00:11.0000000Z 11 Good
2026-10-01T00:00:12.0000000Z 12 Good
2026-10-01T00:00:13.0000000Z 13 Good
2026-10-01T00:00:14.0000000Z 14 Good
2026-10-01T00:00:15.0000000Z 15 Good
2026-10-01T00:00:16.0000000Z 16 Good
2026-10-01T00:00:17.0000000Z 17 Good
2026-10-01T00:00:18.0000000Z 18 Good
2026-10-01T00:00:19.0000000Z 19 Good
2026-10-01T00:00:20.0000000Z 20 Good
2026-10-01T00:00:21.0000000Z 21 Good
2026-10-01T00:00:22.0000000Z 22 Good
2026-10-01T00:00:23.0000000Z 23 Good
2026-10-01T00:00:24.0000000Z 24 Good
25 values returned.
@@ -0,0 +1,5 @@
Node: ns=2;s=Matrix/dev/Scalar
Value: 92
Status: Good [0x00000000]
Source Time: 11/10/2026 17:19:21
Server Time: 11/10/2026 17:19:22
@@ -0,0 +1,9 @@
{"operation":"raw","rows":7}
{"operation":"raw","rows":7}
{"operation":"raw","rows":7}
{"operation":"raw","rows":7}
{"operation":"raw","rows":5}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
{"operation":"events","rows":7}
@@ -0,0 +1 @@
{"endpoint":"opc.tcp://127.0.0.1:44545/OtOpcUa","process":33,"sdk":"2.0.1.0","rawUri":"https://zb.com/otopcua/raw","unsUri":"https://zb.com/otopcua/uns"}
@@ -0,0 +1,13 @@
{
"WorkingSet64": 195645440,
"PrivateMemorySize64": 288792576,
"HandleCount": 153,
"threads": 26,
"cpuMilliseconds": 2091.525,
"allocatedBytes": 42602784,
"collections": [
1,
0,
0
]
}
@@ -0,0 +1,757 @@
[
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/client/Opc.Ua.Client.dll",
"sha256": "fd000b186f4bc5dbd9947e876f96c178e8c4259ee3e8717d672a4ac2f4c3a058",
"bytes": 852096
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/client/Opc.Ua.Configuration.dll",
"sha256": "30fb5142f13a3b3083c0484bcdedaec2efe2f50e57f10ec6a61257ca5359aa7e",
"bytes": 85120
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/client/Opc.Ua.Core.dll",
"sha256": "602e85316943ad7ebae33a23d60a69a6ea50d91250ad0556906b816435daa37c",
"bytes": 7945344
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/client/Opc.Ua.Security.Certificates.dll",
"sha256": "a2a05c64348f6f8aa60de0fe27d8e5e22d12e09b1ba6172ca34b3773620e394c",
"bytes": 79488
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/client/Opc.Ua.Types.dll",
"sha256": "c5c5e36cf6381444b408fe1b51726b17deca74173764cd62ccee954b3c8c2718",
"bytes": 1127552
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/client/ZB.MOM.WW.OtOpcUa.Client.CLI.deps.json",
"sha256": "8756470c547137f40ef9962dfa6f40c6dc2a1801cd397524dce7127fb79b70b9",
"bytes": 12175
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/client/ZB.MOM.WW.OtOpcUa.Client.CLI.dll",
"sha256": "38ff27df4c1389d2cf97a7ec1fe206733f02c3664b29c4fe18586ede225a53ca",
"bytes": 91648
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/client/ZB.MOM.WW.OtOpcUa.Client.Shared.dll",
"sha256": "326e14df7bf2a7602637fbff36988c3dd4be38de936da24a1b4287c0ab685be6",
"bytes": 111616
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/Opc.Ua.Client.dll",
"sha256": "fd000b186f4bc5dbd9947e876f96c178e8c4259ee3e8717d672a4ac2f4c3a058",
"bytes": 852096
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/Opc.Ua.Configuration.dll",
"sha256": "30fb5142f13a3b3083c0484bcdedaec2efe2f50e57f10ec6a61257ca5359aa7e",
"bytes": 85120
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/Opc.Ua.Core.dll",
"sha256": "602e85316943ad7ebae33a23d60a69a6ea50d91250ad0556906b816435daa37c",
"bytes": 7945344
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/Opc.Ua.Security.Certificates.dll",
"sha256": "a2a05c64348f6f8aa60de0fe27d8e5e22d12e09b1ba6172ca34b3773620e394c",
"bytes": 79488
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/Opc.Ua.Server.dll",
"sha256": "d8d9706dea577858325730cd89896826ed82f7bfcf9503ccd0260311663a69f4",
"bytes": 642176
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/Opc.Ua.Types.dll",
"sha256": "c5c5e36cf6381444b408fe1b51726b17deca74173764cd62ccee954b3c8c2718",
"bytes": 1127552
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/OtOpcUa.Host.deps.json",
"sha256": "f47fb25d83e40a5c1d96c063f1d63b02a04c222a10554220df58cac896654ea9",
"bytes": 153279
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.AdminUI.dll",
"sha256": "1ae11959c22bc53a648b91c6c1ae41e4a5d717e396bd645eae4fb08dc1633597",
"bytes": 2152448
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Cluster.dll",
"sha256": "d6bc21641172d832004b62b8bb76aeb40622a943be2172f394e5c6333c04ef57",
"bytes": 67584
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Commons.dll",
"sha256": "be97a0b975afde7ec4ebf0b10de53c26f212ceca1f6a3ae090e40ebfde304413",
"bytes": 231424
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Configuration.dll",
"sha256": "195dab9f7a9f80c45902e653ff361116a307d8110a3f87baad814bea57689b84",
"bytes": 542720
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.ControlPlane.dll",
"sha256": "36954f51b09930df414675e5a0c038933ad2b27cd6560bf797d99801c02b30a1",
"bytes": 179200
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Core.Abstractions.dll",
"sha256": "b0f4e332ced57eae3ab1dcc282c073f51ea0c545a381aef50479ee58f0c23efc",
"bytes": 124416
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian.dll",
"sha256": "a61a6ea68236dd4ed86ab4e74e4d632c5e260103aa5ebd92f4f995eb9f170b22",
"bytes": 56320
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.dll",
"sha256": "f1e429c0b8fcab8216b9107440008b92eea1236ff1225e79580ad70f51d56431",
"bytes": 83968
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions.dll",
"sha256": "ea24898698c8351bc9cf20fcce725dccc93ccf05ef3af7783f73854cc2033c31",
"bytes": 13312
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Core.Scripting.OutOfProcess.dll",
"sha256": "cf44009a720a00bf9192374d371c7c76e75afbe62cc216b5c1be913b92679127",
"bytes": 135168
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Core.Scripting.Protocol.dll",
"sha256": "07800332d0842b93c3987fca1dc10a1390bcb16f44567610d867513ad9fe17e0",
"bytes": 117248
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Core.Scripting.dll",
"sha256": "9637b7ffb4984f54bc54363ff2d81b6a3401f0cb7eeed8694db626ef41d54d5b",
"bytes": 121856
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.dll",
"sha256": "107aaf2ef2d7704e320dabc1072bc50217faabb2d1488e66c91b1f756b440c45",
"bytes": 11264
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Core.dll",
"sha256": "34a4d7c0dddf4efa5fae91a3798c2f1c08b992faf4a827a9e05ed1338c6e4720",
"bytes": 124928
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.dll",
"sha256": "315dcc540d304c9f1d5b48caa25e5f081569f1a41aa8784cf371c1d6cef7322d",
"bytes": 123392
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Contracts.dll",
"sha256": "d7b34798587d0939cc78eb69d9df1d617c1c75a61b8dd11a5308958a43a8c4f1",
"bytes": 24576
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.AbCip.dll",
"sha256": "73073627fcdc8079df645478d6717576975117c3b21cfa7e9422f1e3bc5f4e44",
"bytes": 214016
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Contracts.dll",
"sha256": "5c66d76dd455c42f0c5b0aed11240fa31e1bdbd2f0a8368d8f41976977e93502",
"bytes": 30720
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.dll",
"sha256": "cbf92a935d86596a9d4dbeeaa625e304c2045851a0367c3a6b0de03bf78bd6fa",
"bytes": 136704
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Calculation.Contracts.dll",
"sha256": "29c079414497b30debe8d3ecd7e77bdc4eb16c4fc076e675680a2e880e54b6c2",
"bytes": 10240
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Calculation.dll",
"sha256": "132e36042a4691cc8e252ab9e9d8c90592fcd897959f533aa37e33f193cf00c8",
"bytes": 57344
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Contracts.dll",
"sha256": "2bdbf743194193d5a7bc323912e2c7f740cc21633154a4a3cc8e034fd15bb7e8",
"bytes": 38912
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.dll",
"sha256": "17c28f27fdecd09a46b971b19655bdb933f84990e91bb80c7092c344a0ca974f",
"bytes": 300032
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser.dll",
"sha256": "f2796d323c222708c28cd0a4289c46db31ec3b6537da7f5cd260fc76f8abdc76",
"bytes": 19456
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Contracts.dll",
"sha256": "780a11f69194713e2f87e74277e99882ee3518b2824551b45cd04e3b9bdf6648",
"bytes": 24576
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.dll",
"sha256": "a8e19baa5a736544d914f942d526a5cc038f6b982451bfa82a77365a159434e4",
"bytes": 224768
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.dll",
"sha256": "400b1896e55d08781231efeba302596299df2b00b2f48afd43d46c22d59b515c",
"bytes": 129536
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb.dll",
"sha256": "8e91a8cce804023639a4bdb2dc60f9fc950992882e50786d45ceeb0f38ed1f68",
"bytes": 167424
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Historian.Sql.Common.dll",
"sha256": "2746b8e9c03b5b8c330152803eee91cf2069f0e6c074d70d03017b1f1f1a2398",
"bytes": 51712
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Historian.SqlServer.dll",
"sha256": "94b9bcd4506427a44158e0b299a5ae7bcee14fd40b85cc9f63420e4631aa1a6f",
"bytes": 143872
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Contracts.dll",
"sha256": "6582151085ddea7d319b990c2c58057b6ba5a49860593229b3642dff527ae204",
"bytes": 19456
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.dll",
"sha256": "c9670c5d169056563294673839ca63321a72a438c519263be708b55f3fc818f9",
"bytes": 214016
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing.dll",
"sha256": "ed07d04709372ef804f465995431b15acdef791f66c8130ab7282372728f7cda",
"bytes": 25088
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Contracts.dll",
"sha256": "724873ac2f6c08f307a150c5575cc3c022d7e367124ed924c0c7899b0e9575a5",
"bytes": 39424
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Modbus.dll",
"sha256": "db1e3e7c79dc1a46e1d4ffaa5ea1aac9ed73814d7f06cbfa855ebb3b27a8810d",
"bytes": 131584
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Browser.dll",
"sha256": "ace18db746c30307e67efa3acd68e915cc2b62b0a85bdba833df4ea7c85b5f6c",
"bytes": 48128
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Contracts.dll",
"sha256": "24e795bd8027d7b3ca3d95630b73be6d7cef774243357b01850f145b6066dcb9",
"bytes": 139776
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Transport.dll",
"sha256": "bec0cecc0bbb06b4715d7b909ccf4d357420e120b2bff3c10ee9f59f71044927",
"bytes": 12800
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.dll",
"sha256": "da64eacb80ddca9cd0a40295bba2c8ff81ee9c68ccaf32cbd329ae854375deb7",
"bytes": 195072
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.dll",
"sha256": "7e2ac501136d24a3d0ad6bdae2e4a47460439ad8ab78af538bbccca2e38e5695",
"bytes": 35840
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Contracts.dll",
"sha256": "c15e6db37681cbbef8684d5301e1bba54186ae503aaf55d1c2d06a0567ab28fe",
"bytes": 20480
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Session.dll",
"sha256": "910604abd4daa6196c8fdcd2db085873ee5a4c27b5a67c359590071117c6e4dd",
"bytes": 16384
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.dll",
"sha256": "cd689b0ec777820d91f914abfd9e4a921143a46df84ee43d0214c66f74225ac0",
"bytes": 144384
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.S7.Contracts.dll",
"sha256": "6fe895674318dc42c0c3cc74266831862d3fe2f13098c26e7d826baf79d7fc47",
"bytes": 29696
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.S7.dll",
"sha256": "d23a7ac62d781f0c14dd7c9d4f51b712c13599ee78b9b6d302d30dfd5e77da37",
"bytes": 124928
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Sql.Browser.dll",
"sha256": "c341d49bf100e0617d9c4e7d24f63d8affd1bc0fe2edce15e51cc14366fa2207",
"bytes": 43008
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Sql.Contracts.dll",
"sha256": "f0a0cc852116f739247422a0427550e52c938b27d1a8b4e685f45a19a23635ab",
"bytes": 38912
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Sql.Dialect.dll",
"sha256": "7d78655da50c07ac049cd687c59f2a7b6eb2c491543bd4089ffbe60fb805205f",
"bytes": 40448
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.Sql.dll",
"sha256": "6ec1a9bc7443c1d7b8084ee2fc5e67cbfd2a8d61add303a980cef78a46cb5c67",
"bytes": 144384
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Contracts.dll",
"sha256": "96db9e5d162b19914cf0ba55c1408c1c9271c80c1a1a12604e684f5785de4b96",
"bytes": 17408
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.dll",
"sha256": "c24cfc897bde6c0eb65b9958bad952814ed02a27fe25a55c416c1ba61be2c261",
"bytes": 188928
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.OpcUaServer.dll",
"sha256": "ee1f704fa1574b77bf56cef415c47c33a4428c6852db52bb1443d6765d092cb1",
"bytes": 363008
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Runtime.dll",
"sha256": "50e752383ae81ef6bb23e360fafe9162be0d1dcb7dd2d8731f16bce762834dd4",
"bytes": 539648
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua-sdk-2-upgrade/OtOpcUa/artifacts/opcua2/83ed880e/baseline/host/ZB.MOM.WW.OtOpcUa.Security.dll",
"sha256": "a88b1577d1e4dd580e012e7673e31d5363c3d9a79fb415bef4c3d63849678a4d",
"bytes": 52736
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-baseline-oracle/OtOpcUa/Directory.Packages.props",
"sha256": "7e6459447843b93bc94fbc1f3eeb1c695b5c46c08aeb94debe86a7d419c63221",
"bytes": 18829
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/Directory.Packages.props",
"sha256": "31a4cac1c205f5b9e67119357c77751b8ab999f6c5f350da68784614040c8a93",
"bytes": 19000
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-baseline/bin/Release/net10.0/ProcessHost.dll",
"sha256": "13de622509608b80bc7dce3317e030bfea8d1b597ff8abd7b57f026c17a9ede0",
"bytes": 83968
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/Opc.Ua.Client.dll",
"sha256": "620da406fabdbc75d11f9827ea7e39d2d3c762471bd2264b0d11f1d6cd9247cd",
"bytes": 2442880
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/Opc.Ua.Configuration.dll",
"sha256": "6ed6a594c93c3ff35d1a16539a9fe87c1e2640549cc2f48ed6bae333dc8bf0e7",
"bytes": 117376
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/Opc.Ua.Core.Schema.dll",
"sha256": "51cfad48657d95d38074bb88be29aea6f5b361a07397247d58838d066b99772e",
"bytes": 135168
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/Opc.Ua.Core.Types.dll",
"sha256": "a7dd141b8dabe8848ff28c2fb8e16a85a0508e6a49e6b1a1c1e267175b99af96",
"bytes": 12703872
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/Opc.Ua.Core.dll",
"sha256": "9d0857aa6b7f3c49037d4ea557b4da8edf07e146069464481783e66c5c9cd35a",
"bytes": 2549376
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/Opc.Ua.Security.Certificates.dll",
"sha256": "7f4969aceb2f0db5328c6882cb4b117342f3b18905c38acdba600683860c4fd0",
"bytes": 118400
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/Opc.Ua.Server.dll",
"sha256": "930e714a2f6e15d393840bcdc95e1afa8012bc740c3738a655f958142721864e",
"bytes": 3332224
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/Opc.Ua.Types.dll",
"sha256": "c542f69882f4e8a4ebd5ccc28a3881d6967c7ffb6dcc3c56dc6c776f39c2ede1",
"bytes": 1792640
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/OtOpcUa.Host.deps.json",
"sha256": "c289766446d61a6bf15574fdde982a8c44b6faaabb38ad718522f63987a58d69",
"bytes": 179492
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ProcessHost.deps.json",
"sha256": "cb76c2da9266dd11b61483fc9425d8a7ed3e25c83b1179ef60bfbd559b7db77c",
"bytes": 165039
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ProcessHost.dll",
"sha256": "0db8dd8308811ceecd6f6ac468dbf0e5daacf021904cbf741e96a23daf25cfe7",
"bytes": 83968
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.AdminUI.dll",
"sha256": "30a400d0e69bd7a1527e30865cc4fce183bf1fd4b96bea50e7cdde93140b4f03",
"bytes": 2152448
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Client.Shared.dll",
"sha256": "0856bbccac7c59faf95568e67e25af35b9a68c44ffd0eb5d1891869a454a3de0",
"bytes": 120320
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Cluster.dll",
"sha256": "e875e13fe1261963984ac82f7a41da5f83caff28108edaad253641bfe3a7effc",
"bytes": 67584
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Commons.dll",
"sha256": "2d1d97faebfbad03db642b77553110210890de51834e5f4b6965f079f7d044a0",
"bytes": 231424
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Configuration.dll",
"sha256": "afdb724e0bd460f6deeaedb23e78ff5010f12630c3bd8e2986733ee523b9e30c",
"bytes": 542720
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.ControlPlane.dll",
"sha256": "66c93e8d8457f6d842963294c36f5da4261124a4c9ddf486bca09c4c31b39c06",
"bytes": 179200
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Abstractions.dll",
"sha256": "46d4b41bed7f4a6497d401d7925d1bd4d87a2422e0c2835dc06e006c97142150",
"bytes": 124416
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian.dll",
"sha256": "bba5e5c363e910678700630e18e6db0d3f9151665e0e1aa2bab6917e83b0ecbf",
"bytes": 56320
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.dll",
"sha256": "474cd76eab3f2724f73f2cc5024370523e294e012f5ecd884dcff366ceac342e",
"bytes": 83968
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions.dll",
"sha256": "548b2d19b7bc761fedaf42d39045313394e32a0ccf3ad8bd182e3d31762bd7e4",
"bytes": 13312
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Scripting.OutOfProcess.dll",
"sha256": "a1441bac81dd7ac01794a927315bb530d022964be535e543afc3e514df4dd9a3",
"bytes": 135168
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Scripting.Protocol.dll",
"sha256": "18e6dbfec8c646e463a8e268137aa1e266063e91d366a029bee4c810f39b7542",
"bytes": 117248
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Scripting.dll",
"sha256": "13236c602191c7866f219eb946c135cf2532bc075d2b458914abbc18efef8e43",
"bytes": 121856
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.dll",
"sha256": "6a1a9bdd626985dafd483e317ee9906ece7e03d583b44423cc6a95cb21bfdfaf",
"bytes": 11264
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.dll",
"sha256": "59fe6b4b8feebd8d9ec3ff2e3f12a3a8624cdaa68988b230ab7a228cdd246d04",
"bytes": 124928
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.dll",
"sha256": "6178401b851ce3155566e432a376ef2ec983497eccf298ec33d38cd6ed631be7",
"bytes": 123392
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Contracts.dll",
"sha256": "622505ec95e04d7140ea63252b61e01e170947ff88043394e9e7fe2d2ee38e3c",
"bytes": 24576
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.AbCip.dll",
"sha256": "da3bcdd35f632e5f183ce7721fea455d6396f64234f22eea64862057737c40b9",
"bytes": 214016
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Contracts.dll",
"sha256": "8cf0f65f373b410c597cbdba4cce79a73e6388fd13f1f61964f7a78bdc7b67a8",
"bytes": 30720
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.dll",
"sha256": "efa92e068f15126cb5f846e923c72b0c68be9b0158a22da139f4d537f68983f3",
"bytes": 136704
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Calculation.Contracts.dll",
"sha256": "9b2b094906f46660b9cb5a4a0b53f36d206b4cd88f14837a047c7040ec54f24d",
"bytes": 10240
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Calculation.dll",
"sha256": "58c27311d05d2bf262404520fd2d11a0d2edcb4a98974b61532bcf72a680b4cf",
"bytes": 57344
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Contracts.dll",
"sha256": "2358fb9fbcc9475022b190dde05ee6ee712d45c59d041830151a5bcc7418de5f",
"bytes": 38912
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.dll",
"sha256": "ff87272d5e8dae9f1bc1e560e2cd8d82ee279f220f178300ccf534042b1cf2cf",
"bytes": 300032
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser.dll",
"sha256": "b977d56618bfcfeb71789271cefda7443c278e2da75b04fdae49e51da3474586",
"bytes": 19456
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Contracts.dll",
"sha256": "52749ea655601805af505e9aa255fb4d7a3563d58df5f86032aff59fc03e1cc8",
"bytes": 24576
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.dll",
"sha256": "41a8c6ed1fe24a50ad9793d78f0dbacff1cfb0e005c027baf27e6003af99988c",
"bytes": 224768
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.dll",
"sha256": "d642be7f07b7144bb4ef6d6786e73bc2965a1e3189c68b00e5ed4da522eda8df",
"bytes": 129536
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb.dll",
"sha256": "a8d605f278ef8853fc56693d21948cc79c5f07fcd4cb62983f366cd4dcd507d2",
"bytes": 167424
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Historian.Sql.Common.dll",
"sha256": "7e0c5b3e79763ede195f7446216c6ffd7617a66c2ce9b4e1b182b05ce9675f18",
"bytes": 51712
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Historian.SqlServer.dll",
"sha256": "89c1615dbe0911e014facc4452d2140f4471a01dedd50f02eba48123fd81d11a",
"bytes": 143872
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Contracts.dll",
"sha256": "530fdc78903fb7dc6bf635db5f7a61a35feeeee71cca5df32a433cc8a38ab239",
"bytes": 19456
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.dll",
"sha256": "d87cbf2e84042bd259cc5b6390922a84eb11bb7a11c00d2c4b5dbe00a75a441c",
"bytes": 214016
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing.dll",
"sha256": "17148305ad647f1ecc42911c525bbcee43ed759b751563c92ae2e0f502f5491b",
"bytes": 25088
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Contracts.dll",
"sha256": "850cc34a53b154615a5966c5ab9719ffe65823b419a40b16dc83734c2c77766e",
"bytes": 39424
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Modbus.dll",
"sha256": "912f304a47ea3c2a48b9e3ae1a1f9a29cd9577047049a408ab2f54aceb46dd47",
"bytes": 131584
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Browser.dll",
"sha256": "0a6ee903bea8bf1d16a7cbe5e52c80096b84c230d0266969d15004bad9b0f0e4",
"bytes": 48128
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Contracts.dll",
"sha256": "9dbd98038e6fd19f2cf8e28ad081a691ac1df44dcb94597f6ef6f536da9c10f0",
"bytes": 139776
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Transport.dll",
"sha256": "e133940f52ee7b347dd66bb1eea0bb8a5d936e65aad600888eaea48d67f1555b",
"bytes": 12800
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.dll",
"sha256": "a171ce43d2f01230d975f5f9bd943fd50a8a8fe39e735935d0a5b1138e31ca1c",
"bytes": 195072
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.dll",
"sha256": "efb1d122a0aba3c6fd592690143dc99eea608248c0e590a1f264283a040743b2",
"bytes": 36352
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Contracts.dll",
"sha256": "fda75dcf622ebe33dc82dc40a1a362b5a3a10f782bda169ab255d377223a5895",
"bytes": 20480
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Session.dll",
"sha256": "ff3b3fee2c4f5954033a2cb5e2b0f3b9cccebc656c7d169687701bd52e8eb142",
"bytes": 19968
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.dll",
"sha256": "09ecbd0ecf90b658dc83f6f2cb12211715f2c51350e86d963c112c4469549ab5",
"bytes": 147968
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.S7.Contracts.dll",
"sha256": "c9c170f30592fc5698cb256f0f4705ac6fa5ae51a3da89f19ff7753b51ac44e1",
"bytes": 29696
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.S7.dll",
"sha256": "29ec762d9ef4cf9f7e011069604c6bc725aac5c995bd8f7fa72a35f09921beef",
"bytes": 124928
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Sql.Browser.dll",
"sha256": "78e0f7c4da53feefdc76137e014848b26d46fc33bc29aa79e99c43de918e15f9",
"bytes": 43008
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Sql.Contracts.dll",
"sha256": "65c6cd709903aa34be71c62e4c760e0e01a50783497ad644a057ad1a4a179bc4",
"bytes": 38912
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Sql.Dialect.dll",
"sha256": "a6a9d82c5895b411ebdb95eb2a3de719e3f65280fa7163e0d573db1cf886935b",
"bytes": 40448
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Sql.dll",
"sha256": "0beddb7dd322377b2b1ee496ba59c803528668abe9b62c9ac17bfef8f355b112",
"bytes": 144384
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Contracts.dll",
"sha256": "3e37c0328157641e82a99d3d2096a9ab3d826647039da34231f14dea3af0dc43",
"bytes": 17408
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.dll",
"sha256": "fc9babb948338b46b965344796a0a00560ae917a19eb6d301f2cf94234a7fac5",
"bytes": 188928
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.OpcUaServer.dll",
"sha256": "8a885d5a86dd00ed5c920e650852cda5820cb9cce05fb49781b7844680d9353f",
"bytes": 420864
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Runtime.dll",
"sha256": "19111a82fb1582866bf0d019f4daa8a5060536cfa11a5569a7e0aeeffdbecc03",
"bytes": 539648
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Security.dll",
"sha256": "56f587426e81750fa406686aca50deca2ee8e293ac7969f2a0b606e6acfdfff2",
"bytes": 52736
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/candidate-cli/Opc.Ua.Client.dll",
"sha256": "620da406fabdbc75d11f9827ea7e39d2d3c762471bd2264b0d11f1d6cd9247cd",
"bytes": 2442880
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/candidate-cli/Opc.Ua.Configuration.dll",
"sha256": "6ed6a594c93c3ff35d1a16539a9fe87c1e2640549cc2f48ed6bae333dc8bf0e7",
"bytes": 117376
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/candidate-cli/Opc.Ua.Core.Schema.dll",
"sha256": "51cfad48657d95d38074bb88be29aea6f5b361a07397247d58838d066b99772e",
"bytes": 135168
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/candidate-cli/Opc.Ua.Core.Types.dll",
"sha256": "a7dd141b8dabe8848ff28c2fb8e16a85a0508e6a49e6b1a1c1e267175b99af96",
"bytes": 12703872
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/candidate-cli/Opc.Ua.Core.dll",
"sha256": "9d0857aa6b7f3c49037d4ea557b4da8edf07e146069464481783e66c5c9cd35a",
"bytes": 2549376
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/candidate-cli/Opc.Ua.Security.Certificates.dll",
"sha256": "7f4969aceb2f0db5328c6882cb4b117342f3b18905c38acdba600683860c4fd0",
"bytes": 118400
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/candidate-cli/Opc.Ua.Types.dll",
"sha256": "c542f69882f4e8a4ebd5ccc28a3881d6967c7ffb6dcc3c56dc6c776f39c2ede1",
"bytes": 1792640
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/candidate-cli/ZB.MOM.WW.OtOpcUa.Client.CLI.deps.json",
"sha256": "e1a36de862f9d940bfb55e09db02461bc20f2bd12dad30c8a8bca81ac38be40a",
"bytes": 37379
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/candidate-cli/ZB.MOM.WW.OtOpcUa.Client.CLI.dll",
"sha256": "4e86b2f64ed5bb37826977c9104dd370d80d11553084e5570d445cecc6b0aaeb",
"bytes": 92160
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08/candidate-cli/ZB.MOM.WW.OtOpcUa.Client.Shared.dll",
"sha256": "0856bbccac7c59faf95568e67e25af35b9a68c44ffd0eb5d1891869a454a3de0",
"bytes": 120320
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/scripts/validation/opcua-sdk-compatibility/ProcessHost.cs",
"sha256": "f0a7aca11c38cc8c74c26573458405aa14ed10c81a0a56210dfabf42ead45a83",
"bytes": 27089
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/scripts/validation/opcua-sdk-compatibility/container-leg.sh",
"sha256": "1f5d1158572379b8a6304c3a3134a78c112043940f5bce468621677f7da3ef9e",
"bytes": 2589
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/scripts/validation/opcua-sdk-compatibility/run.py",
"sha256": "240d14f71aca358de5107174bbfbef7ddd5b036cd96fe0889e095a8b9698ec6b",
"bytes": 12459
}
]
@@ -0,0 +1,14 @@
{
"baseline": {
"root": "/Users/dohertj2/.codex/worktrees/opcua2-baseline-oracle/OtOpcUa",
"commit": "83ed880e4471344deaf008da9bd028c7e170bdc7",
"sdkPackages": "/Users/dohertj2/.codex/worktrees/opcua2-baseline-oracle/OtOpcUa/Directory.Packages.props"
},
"candidate": {
"root": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa",
"commit": "ebdc5892063502ccd56e136dd9c2acaccf027f20",
"sdkPackages": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/Directory.Packages.props"
},
"scope": "Loopback application contract and 3 warmed 50-operation rounds per scalar/Int32[3] read/write kind; shared development host, no full load/reconnect/failover/backend/soak performance claim.",
"dateUtc": "2026-11-10T17:18:22Z"
}
@@ -0,0 +1,103 @@
{
"date": "2026-11-10",
"harnessCommit": "ebdc5892",
"candidateSourceCommit": "ebdc5892",
"baselineSourceCommit": "83ed880e",
"browseFixOriginalCommit": "fa823378",
"browseFixLocalCherryPick": "b104ef9a",
"baselineSdkPackage": "1.5.378.182",
"candidateSdkPackage": "2.0.1",
"functional": {
"passed": 62,
"failed": 0,
"skipped": 0,
"legs": [
{
"name": "baseline-client-to-baseline-server",
"passed": 12
},
{
"name": "baseline-client-to-candidate-server",
"passed": 12
},
{
"name": "candidate-client-to-baseline-server",
"passed": 12
},
{
"name": "candidate-client-to-candidate-server",
"passed": 12
},
{
"name": "candidate-driver-cli-to-baseline-server",
"passed": 7
},
{
"name": "candidate-driver-cli-to-candidate-server",
"passed": 7
}
],
"requiredThreeSharedLegsPlusDriverCliCases": 50,
"additionalBaselineReferenceCases": 12
},
"security": {
"policy": "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256",
"mode": "SignAndEncrypt",
"anonymous": false,
"aclEnabled": true,
"fixtureAuthenticator": "Exact ephemeral API-key-shaped credential; production ACL/write/method gates",
"serverSharedDriverAutoaccept": false,
"cliAutoaccept": true,
"cliLimitationEvidence": "Client.CLI CommandBase.CreateConnectionSettings sets AutoAcceptCertificates=true; no strict-validation option"
},
"continuationPaging": {
"rawSamplesPerSharedLeg": 25,
"rawBackendCeiling": 7,
"eventSamplesPerSharedLeg": 25,
"eventBackendCeiling": 7,
"eventTimestampTies": true,
"assertions": "Exact raw values and event IDs, no loss/duplicates; production session-bound continuation store"
},
"preFix": {
"passed": 60,
"failed": 2,
"finding": "Candidate alarm missing from raw parent browse; original->candidate and candidate->candidate failed exact identity checks. SDK2 Create reset ReferenceTypeId to null/default. Original->original and candidate->original controls passed. Fixed by restoring HasComponent after Create; unchanged strict assertions then passed."
},
"performance": {
"status": "diagnostic-within-budget",
"warmupOperations": 24000,
"measuredOperations": 48000,
"totalCalls": 72000,
"nativeProcessPairs": 3,
"tieredCompilation": "0 for both clients and servers; supplementary experiment only",
"budgetPercent": 15,
"medianP95ChangesPercent": {
"scalar-read": 14.94885916601103,
"scalar-write": 1.577761081893314,
"array-read": -16.279069767441868,
"array-write": -17.23684210526316
},
"limitation": "Small local subset; scalar-read candidate pair3 p95 .2219 ms remains an outlier, preserved in raw data; no full Gate G4 claim",
"allocationMetric": "Average allocated bytes/op across all four operation kinds"
},
"rawArtifacts": {
"functional": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-10",
"warmPerformance": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01",
"preFix": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-08",
"earlyShortPerformance": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-07"
},
"notCertified": [
"Real SQL Server/QuestDB/AVEVA backend matrix",
"Full production load/fan-out/reconnect/backpressure performance",
"Two-hour soak",
"External ScadaBridge consumer",
"Mixed-version Akka cluster rolling upgrade"
],
"capabilityNA": [
{
"component": "OPC UA driver",
"capability": "HistoryRead",
"evidence": ".claude/rules/drivers.md #655: driver history capability removed; Client.Shared and CLI perform history"
}
]
}
@@ -0,0 +1,637 @@
[
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ProcessHost.dll",
"sha256": "cdb0565a37704509e19fde6eb9a751b7fd770dc92086e7df55da4d9c76712e41",
"bytes": 94720
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Core.dll",
"sha256": "602e85316943ad7ebae33a23d60a69a6ea50d91250ad0556906b816435daa37c",
"bytes": 7945344
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Types.dll",
"sha256": "c5c5e36cf6381444b408fe1b51726b17deca74173764cd62ccee954b3c8c2718",
"bytes": 1127552
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Server.dll",
"sha256": "d8d9706dea577858325730cd89896826ed82f7bfcf9503ccd0260311663a69f4",
"bytes": 642176
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Client.dll",
"sha256": "fd000b186f4bc5dbd9947e876f96c178e8c4259ee3e8717d672a4ac2f4c3a058",
"bytes": 852096
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Security.Certificates.dll",
"sha256": "a2a05c64348f6f8aa60de0fe27d8e5e22d12e09b1ba6172ca34b3773620e394c",
"bytes": 79488
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Configuration.dll",
"sha256": "30fb5142f13a3b3083c0484bcdedaec2efe2f50e57f10ec6a61257ca5359aa7e",
"bytes": 85120
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.dll",
"sha256": "da64eacb80ddca9cd0a40295bba2c8ff81ee9c68ccaf32cbd329ae854375deb7",
"bytes": 195072
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Contracts.dll",
"sha256": "c15e6db37681cbbef8684d5301e1bba54186ae503aaf55d1c2d06a0567ab28fe",
"bytes": 20480
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Abstractions.dll",
"sha256": "b0f4e332ced57eae3ab1dcc282c073f51ea0c545a381aef50479ee58f0c23efc",
"bytes": 124416
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Client.Shared.dll",
"sha256": "326e14df7bf2a7602637fbff36988c3dd4be38de936da24a1b4287c0ab685be6",
"bytes": 111616
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian.dll",
"sha256": "a61a6ea68236dd4ed86ab4e74e4d632c5e260103aa5ebd92f4f995eb9f170b22",
"bytes": 56320
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Contracts.dll",
"sha256": "6582151085ddea7d319b990c2c58057b6ba5a49860593229b3642dff527ae204",
"bytes": 19456
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.dll",
"sha256": "c24cfc897bde6c0eb65b9958bad952814ed02a27fe25a55c416c1ba61be2c261",
"bytes": 188928
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.dll",
"sha256": "107aaf2ef2d7704e320dabc1072bc50217faabb2d1488e66c91b1f756b440c45",
"bytes": 11264
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Transport.dll",
"sha256": "bec0cecc0bbb06b4715d7b909ccf4d357420e120b2bff3c10ee9f59f71044927",
"bytes": 12800
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.AdminUI.dll",
"sha256": "1ae11959c22bc53a648b91c6c1ae41e4a5d717e396bd645eae4fb08dc1633597",
"bytes": 2152448
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Scripting.Protocol.dll",
"sha256": "07800332d0842b93c3987fca1dc10a1390bcb16f44567610d867513ad9fe17e0",
"bytes": 117248
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Configuration.dll",
"sha256": "195dab9f7a9f80c45902e653ff361116a307d8110a3f87baad814bea57689b84",
"bytes": 542720
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Historian.SqlServer.dll",
"sha256": "94b9bcd4506427a44158e0b299a5ae7bcee14fd40b85cc9f63420e4631aa1a6f",
"bytes": 143872
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Scripting.dll",
"sha256": "9637b7ffb4984f54bc54363ff2d81b6a3401f0cb7eeed8694db626ef41d54d5b",
"bytes": 121856
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Session.dll",
"sha256": "910604abd4daa6196c8fdcd2db085873ee5a4c27b5a67c359590071117c6e4dd",
"bytes": 16384
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.dll",
"sha256": "cbf92a935d86596a9d4dbeeaa625e304c2045851a0367c3a6b0de03bf78bd6fa",
"bytes": 136704
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.ControlPlane.dll",
"sha256": "36954f51b09930df414675e5a0c038933ad2b27cd6560bf797d99801c02b30a1",
"bytes": 179200
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Calculation.Contracts.dll",
"sha256": "29c079414497b30debe8d3ecd7e77bdc4eb16c4fc076e675680a2e880e54b6c2",
"bytes": 10240
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.AbCip.dll",
"sha256": "73073627fcdc8079df645478d6717576975117c3b21cfa7e9422f1e3bc5f4e44",
"bytes": 214016
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Contracts.dll",
"sha256": "5c66d76dd455c42f0c5b0aed11240fa31e1bdbd2f0a8368d8f41976977e93502",
"bytes": 30720
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.S7.Contracts.dll",
"sha256": "6fe895674318dc42c0c3cc74266831862d3fe2f13098c26e7d826baf79d7fc47",
"bytes": 29696
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Contracts.dll",
"sha256": "24e795bd8027d7b3ca3d95630b73be6d7cef774243357b01850f145b6066dcb9",
"bytes": 139776
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.dll",
"sha256": "cd689b0ec777820d91f914abfd9e4a921143a46df84ee43d0214c66f74225ac0",
"bytes": 144384
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Browser.dll",
"sha256": "ace18db746c30307e67efa3acd68e915cc2b62b0a85bdba833df4ea7c85b5f6c",
"bytes": 48128
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions.dll",
"sha256": "ea24898698c8351bc9cf20fcce725dccc93ccf05ef3af7783f73854cc2033c31",
"bytes": 13312
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.S7.dll",
"sha256": "d23a7ac62d781f0c14dd7c9d4f51b712c13599ee78b9b6d302d30dfd5e77da37",
"bytes": 124928
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Contracts.dll",
"sha256": "2bdbf743194193d5a7bc323912e2c7f740cc21633154a4a3cc8e034fd15bb7e8",
"bytes": 38912
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Sql.dll",
"sha256": "6ec1a9bc7443c1d7b8084ee2fc5e67cbfd2a8d61add303a980cef78a46cb5c67",
"bytes": 144384
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.dll",
"sha256": "400b1896e55d08781231efeba302596299df2b00b2f48afd43d46c22d59b515c",
"bytes": 129536
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Sql.Dialect.dll",
"sha256": "7d78655da50c07ac049cd687c59f2a7b6eb2c491543bd4089ffbe60fb805205f",
"bytes": 40448
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb.dll",
"sha256": "8e91a8cce804023639a4bdb2dc60f9fc950992882e50786d45ceeb0f38ed1f68",
"bytes": 167424
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Client.CLI.dll",
"sha256": "38ff27df4c1389d2cf97a7ec1fe206733f02c3664b29c4fe18586ede225a53ca",
"bytes": 91648
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Contracts.dll",
"sha256": "96db9e5d162b19914cf0ba55c1408c1c9271c80c1a1a12604e684f5785de4b96",
"bytes": 17408
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Modbus.dll",
"sha256": "db1e3e7c79dc1a46e1d4ffaa5ea1aac9ed73814d7f06cbfa855ebb3b27a8810d",
"bytes": 131584
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Calculation.dll",
"sha256": "132e36042a4691cc8e252ab9e9d8c90592fcd897959f533aa37e33f193cf00c8",
"bytes": 57344
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Contracts.dll",
"sha256": "780a11f69194713e2f87e74277e99882ee3518b2824551b45cd04e3b9bdf6648",
"bytes": 24576
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Sql.Browser.dll",
"sha256": "c341d49bf100e0617d9c4e7d24f63d8affd1bc0fe2edce15e51cc14366fa2207",
"bytes": 43008
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.dll",
"sha256": "315dcc540d304c9f1d5b48caa25e5f081569f1a41aa8784cf371c1d6cef7322d",
"bytes": 123392
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Contracts.dll",
"sha256": "d7b34798587d0939cc78eb69d9df1d617c1c75a61b8dd11a5308958a43a8c4f1",
"bytes": 24576
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Scripting.OutOfProcess.dll",
"sha256": "cf44009a720a00bf9192374d371c7c76e75afbe62cc216b5c1be913b92679127",
"bytes": 135168
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Commons.dll",
"sha256": "be97a0b975afde7ec4ebf0b10de53c26f212ceca1f6a3ae090e40ebfde304413",
"bytes": 231424
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser.dll",
"sha256": "f2796d323c222708c28cd0a4289c46db31ec3b6537da7f5cd260fc76f8abdc76",
"bytes": 19456
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.dll",
"sha256": "7e2ac501136d24a3d0ad6bdae2e4a47460439ad8ab78af538bbccca2e38e5695",
"bytes": 35840
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.dll",
"sha256": "c9670c5d169056563294673839ca63321a72a438c519263be708b55f3fc818f9",
"bytes": 214016
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.dll",
"sha256": "17c28f27fdecd09a46b971b19655bdb933f84990e91bb80c7092c344a0ca974f",
"bytes": 300032
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.dll",
"sha256": "34a4d7c0dddf4efa5fae91a3798c2f1c08b992faf4a827a9e05ed1338c6e4720",
"bytes": 124928
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing.dll",
"sha256": "ed07d04709372ef804f465995431b15acdef791f66c8130ab7282372728f7cda",
"bytes": 25088
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Sql.Contracts.dll",
"sha256": "f0a0cc852116f739247422a0427550e52c938b27d1a8b4e685f45a19a23635ab",
"bytes": 38912
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Contracts.dll",
"sha256": "724873ac2f6c08f307a150c5575cc3c022d7e367124ed924c0c7899b0e9575a5",
"bytes": 39424
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.OpcUaServer.dll",
"sha256": "ee1f704fa1574b77bf56cef415c47c33a4428c6852db52bb1443d6765d092cb1",
"bytes": 363008
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.dll",
"sha256": "a8e19baa5a736544d914f942d526a5cc038f6b982451bfa82a77365a159434e4",
"bytes": 224768
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Cluster.dll",
"sha256": "d6bc21641172d832004b62b8bb76aeb40622a943be2172f394e5c6333c04ef57",
"bytes": 67584
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Security.dll",
"sha256": "a88b1577d1e4dd580e012e7673e31d5363c3d9a79fb415bef4c3d63849678a4d",
"bytes": 52736
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.dll",
"sha256": "f1e429c0b8fcab8216b9107440008b92eea1236ff1225e79580ad70f51d56431",
"bytes": 83968
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Historian.Sql.Common.dll",
"sha256": "2746b8e9c03b5b8c330152803eee91cf2069f0e6c074d70d03017b1f1f1a2398",
"bytes": 51712
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Runtime.dll",
"sha256": "50e752383ae81ef6bb23e360fafe9162be0d1dcb7dd2d8731f16bce762834dd4",
"bytes": 539648
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ProcessHost.dll",
"sha256": "500c0c486bd91e04b97560b2f08c1fc2b7fc5ac5fa79642aa2b9af6e7a812617",
"bytes": 95232
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Core.Schema.dll",
"sha256": "51cfad48657d95d38074bb88be29aea6f5b361a07397247d58838d066b99772e",
"bytes": 135168
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Core.dll",
"sha256": "9d0857aa6b7f3c49037d4ea557b4da8edf07e146069464481783e66c5c9cd35a",
"bytes": 2549376
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Types.dll",
"sha256": "c542f69882f4e8a4ebd5ccc28a3881d6967c7ffb6dcc3c56dc6c776f39c2ede1",
"bytes": 1792640
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Server.dll",
"sha256": "930e714a2f6e15d393840bcdc95e1afa8012bc740c3738a655f958142721864e",
"bytes": 3332224
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Core.Types.dll",
"sha256": "a7dd141b8dabe8848ff28c2fb8e16a85a0508e6a49e6b1a1c1e267175b99af96",
"bytes": 12703872
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Client.dll",
"sha256": "620da406fabdbc75d11f9827ea7e39d2d3c762471bd2264b0d11f1d6cd9247cd",
"bytes": 2442880
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Security.Certificates.dll",
"sha256": "7f4969aceb2f0db5328c6882cb4b117342f3b18905c38acdba600683860c4fd0",
"bytes": 118400
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Configuration.dll",
"sha256": "6ed6a594c93c3ff35d1a16539a9fe87c1e2640549cc2f48ed6bae333dc8bf0e7",
"bytes": 117376
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.dll",
"sha256": "ad2ebe0e8411f8f1b1d51a0b297f145d633fc8decb6c90f35bd84689f1cb3a69",
"bytes": 195072
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Contracts.dll",
"sha256": "0c28890f2a6ddf1ccfa8cdb3b8c95bb2284c3721b53eb2210b1249ef8a9e6638",
"bytes": 20480
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Abstractions.dll",
"sha256": "fbcb699370cfdf2518758ce3979d1af4fa2041660afad1eaacdf66e32615c549",
"bytes": 124416
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Client.Shared.dll",
"sha256": "c2fe235ab38cd6e9f69201cadfd414e065412c6bd272f97850b33d616ce5ad60",
"bytes": 120320
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian.dll",
"sha256": "12f5ed08d11bfd88a3dfe42418b014d6e23a7785202b91ad12c6e1a83d192fc4",
"bytes": 56320
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Contracts.dll",
"sha256": "a8c4a4a2a9ff90d8f00183ab1a67e44e5eb1e957780d1a27100ca0e93cd61f28",
"bytes": 19456
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.dll",
"sha256": "602fc15da19e29198a3cef5d5020ffa6b0bbb446a6484d984bb3f7a69b83a45d",
"bytes": 188928
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.dll",
"sha256": "c03601d921f0d483a18b8766405223c80f639f734118de1da5d189a4ed513715",
"bytes": 11264
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Transport.dll",
"sha256": "4c31fcfd93242aafa13cb4779425ff02909f81c2d2c1cf7e8ceefb6aaaf1a3f9",
"bytes": 12800
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.AdminUI.dll",
"sha256": "0ce4013902b8cd704e7a20afafcbaa5a487cd1bb7c26cd2ac0713dc11c5a0a0d",
"bytes": 2152448
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Scripting.Protocol.dll",
"sha256": "4a50434a1e440608679f353e10d715c633c9e0220905f12e3e289e0224679224",
"bytes": 117248
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Configuration.dll",
"sha256": "fc9ce3d1c11dfceeee597bc5ae203bd5748c0e7f2b130c3cc8176757790d51aa",
"bytes": 542720
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Historian.SqlServer.dll",
"sha256": "d93a430dc6d2faad5834ca0b4cdf172734db51bb6752d0c5a11fbf1d16e4cb2d",
"bytes": 143872
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Scripting.dll",
"sha256": "1a2c6a0695579b2f5f7885ba488d075683fe9929a092c1d42e1db64ebe187d9a",
"bytes": 121856
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Session.dll",
"sha256": "bb99f9e72c8ee6e25d50faeae179ffe243f8f39bb8ef2d7ae761294cf1ddc23c",
"bytes": 19968
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.dll",
"sha256": "84959687ffbff354616472e085785a583a1d7c2548494bb6d8aa771f034e52bf",
"bytes": 136704
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.ControlPlane.dll",
"sha256": "a37b6d6789271d5f95a3663757049765ea19a20235bfca6da3bbe05e2cb0a993",
"bytes": 179200
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Calculation.Contracts.dll",
"sha256": "c4c1bc2ab5c5f4f594e484b84e9cbe16959439221b287d08c99621de8314107f",
"bytes": 10240
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.AbCip.dll",
"sha256": "877a62c49e1c4a829127424270310ad03a9b140ba28ab90cb9f0d26f57bdc23e",
"bytes": 214016
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Contracts.dll",
"sha256": "921cf8b87f0a00007ff3afcb9038539143d9156dc8ca6c106e4736784bafe108",
"bytes": 30720
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.S7.Contracts.dll",
"sha256": "bd6bdfb0a1b477eba5c982d11630a281649a0b7e6eab0dd0e1b6da977b8348a9",
"bytes": 29696
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Contracts.dll",
"sha256": "b3c73be797d483bd0887fb02ea3104323f713e609cf8e91487c44035b64511a1",
"bytes": 139776
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.dll",
"sha256": "e2863d570d0bb8afe59b15f9e3d594dccf41c966e78cd783c53cbbb6f071514b",
"bytes": 147968
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Browser.dll",
"sha256": "47f5fe5968d002e8d608dc0b08305c7a5e8acfedf76ef2524d1c269fbb138ccf",
"bytes": 48128
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions.dll",
"sha256": "22db91e2af8bc93fb050b4c0d1de9fdf2703029939b82ab6d5b95bc39c9b8c8a",
"bytes": 13312
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.S7.dll",
"sha256": "0fdab087e07f2fae6ebfdd3660177ac32abf0e629a62b2405c0b0af07a096302",
"bytes": 124928
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Contracts.dll",
"sha256": "7c0dd5a8a1e41c0b1b535b657ecb3f8bc30706d51a0674e452312099d6815542",
"bytes": 38912
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Sql.dll",
"sha256": "6a35e1840e33acb081425716253025c8cc1d83cea0303fb5c746dc3d5fa5dae4",
"bytes": 144384
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.dll",
"sha256": "a947eba055aa5196a67bd7fd0864e859f3ecd4600f4869e88902e55e64a2d9b0",
"bytes": 129536
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Sql.Dialect.dll",
"sha256": "ffb2018ed15cd44939a8b48cf87147905485ac7381f49011e8ad7496ee290a7f",
"bytes": 40448
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb.dll",
"sha256": "8c58149292eb015a9dfff038dcc2a0b0bfc706a0c9240d1e47b254cdbd422c1e",
"bytes": 167424
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Contracts.dll",
"sha256": "d669e85c94bfc026d41bc8f85ef0400d449fc20c266f1b22080df8bc078930fc",
"bytes": 17408
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Modbus.dll",
"sha256": "54eafb843b8f4eedde13dd5ec97acd6c2f3aacc169979c4e2bed5b89ce996496",
"bytes": 131584
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Calculation.dll",
"sha256": "1038eb45625e93c77e15f1561d5cd0988ea90753769f40b15f84b634624a71e5",
"bytes": 57344
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Contracts.dll",
"sha256": "b205095b406d297009c6027dd89cc3ede058fb47fe1e2ddd73ec7fe6ce669c68",
"bytes": 24576
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Sql.Browser.dll",
"sha256": "4fb7a9ff8938b2e66bc361741e2beff8f1ab489c02ea6391c7a4141851ded4b8",
"bytes": 43008
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.dll",
"sha256": "d0484e3fcb36299fa9eb1d0c83ee6cdb0ca35d748584c72b5db7466fde69716c",
"bytes": 123392
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Contracts.dll",
"sha256": "db0f111f8debd7b97e374a594a94ccf6f73e21791a65c952773f53f3a87c0b8a",
"bytes": 24576
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.Scripting.OutOfProcess.dll",
"sha256": "f4249cb6b89d06becf41e5494c3b36a154042cd96aad8d09c73a73c1b4e2d5f4",
"bytes": 135168
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Commons.dll",
"sha256": "b230c84ffd9eb1ef56d5321cab9dd32859056dbb861f1d8167ba5f9d0da9ea54",
"bytes": 231424
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser.dll",
"sha256": "28755d92235c896d345bccd6eca9b847f784e6c404178e791cc213a95f9b4eb6",
"bytes": 19456
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.dll",
"sha256": "14c57c172cce3d0ae79cbcf23c4f5eef939d02f96f922af98908e33ead2f17b5",
"bytes": 36352
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.dll",
"sha256": "c2789ab6ac08510360f5c376819c77e51002680d5e53b2c83357c9ca06672b8d",
"bytes": 214016
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.dll",
"sha256": "b235e797154aaf86822e575b9ce226ba18002f15be6b6945531e6ba9c059fb98",
"bytes": 300032
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.dll",
"sha256": "9d00ecb44437222ed3aecc42e9b3337ba1d7450a246fa2390841acc7dc58ac13",
"bytes": 124928
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing.dll",
"sha256": "4348dd5ee686c32c27de164d1c1e4dbd40b52d99f52f286270142acf030c16f8",
"bytes": 25088
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Sql.Contracts.dll",
"sha256": "cd17a68345dfb0ead81b045fd6057c18b186b14393fd2e6a979c242b34fc219d",
"bytes": 38912
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Contracts.dll",
"sha256": "4a8aa7eff2d8129f4803de73041c2daae5ff1643d8499fbdec721659abf9f727",
"bytes": 39424
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.OpcUaServer.dll",
"sha256": "9175d2271e271a0052f165f33513d23014d879d46eb5c8922157a80f6381125d",
"bytes": 420864
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.dll",
"sha256": "6c25cabf5039eefd70f762567ab95ca2a0a18b756f9898af20a881e15185c249",
"bytes": 224768
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Cluster.dll",
"sha256": "071e30ca57f8c7baeea75e8f5b2de15092a0f990636b5ebe3aefb7f974b7b155",
"bytes": 67584
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Security.dll",
"sha256": "b169438db06ed0d0ebddae0b188bbcb1b5725953daad741146ebb95a6f943749",
"bytes": 52736
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.dll",
"sha256": "66fedb5dc0905be00a157e0d041068f0eb2edd925eb8e7ce1c58f2c382eb234e",
"bytes": 83968
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Driver.Historian.Sql.Common.dll",
"sha256": "9eb0644fea0d29ea31f4825fb69c8ae81d1207a4210afe716ce17f1db6688b51",
"bytes": 51712
},
{
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ZB.MOM.WW.OtOpcUa.Runtime.dll",
"sha256": "2ab4d24740bb3ad0f94c42043c494a0b37c94faa692a9c29e925267a7ae3c896",
"bytes": 539648
}
]
@@ -0,0 +1,170 @@
[
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ProcessHost.dll",
"prepare",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-1-baseline/client"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-1-baseline/prepare.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ProcessHost.dll",
"server",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-1-baseline/server"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-1-baseline/server.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ProcessHost.dll",
"performance",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-1-baseline/client",
"opc.tcp://127.0.0.1:54954/OtOpcUa"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-1-baseline/client.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ProcessHost.dll",
"prepare",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-1-candidate/client"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-1-candidate/prepare.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ProcessHost.dll",
"server",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-1-candidate/server"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-1-candidate/server.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ProcessHost.dll",
"performance",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-1-candidate/client",
"opc.tcp://127.0.0.1:54976/OtOpcUa"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-1-candidate/client.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ProcessHost.dll",
"prepare",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-2-baseline/client"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-2-baseline/prepare.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ProcessHost.dll",
"server",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-2-baseline/server"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-2-baseline/server.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ProcessHost.dll",
"performance",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-2-baseline/client",
"opc.tcp://127.0.0.1:55036/OtOpcUa"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-2-baseline/client.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ProcessHost.dll",
"prepare",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-2-candidate/client"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-2-candidate/prepare.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ProcessHost.dll",
"server",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-2-candidate/server"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-2-candidate/server.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ProcessHost.dll",
"performance",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-2-candidate/client",
"opc.tcp://127.0.0.1:55057/OtOpcUa"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-2-candidate/client.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ProcessHost.dll",
"prepare",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-3-baseline/client"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-3-baseline/prepare.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ProcessHost.dll",
"server",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-3-baseline/server"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-3-baseline/server.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/ProcessHost.dll",
"performance",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-3-baseline/client",
"opc.tcp://127.0.0.1:55141/OtOpcUa"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-3-baseline/client.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ProcessHost.dll",
"prepare",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-3-candidate/client"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-3-candidate/prepare.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ProcessHost.dll",
"server",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-3-candidate/server"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-3-candidate/server.log"
},
{
"argv": [
"dotnet",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/ProcessHost.dll",
"performance",
"/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-3-candidate/client",
"opc.tcp://127.0.0.1:55174/OtOpcUa"
],
"log": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/performance-01/pair-3-candidate/client.log"
}
]
@@ -0,0 +1,683 @@
[
{
"sdk": "1.5.378.0",
"process": 28670,
"cases": [
{
"name": "warmed-production-client-read-write-measurement",
"status": "passed"
}
],
"observations": [
{
"SecurityMode": "SignAndEncrypt",
"SecurityPolicyUri": "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256",
"tieredCompilation": "0",
"scope": "Two scalar/Int32[3] variables; zero data monitors during measurement; five fixture variables and one native condition"
},
{
"benchmarkRound": 1,
"warmupOperationsPerKind": 10,
"operationsPerKind": 50,
"latencyMilliseconds": {
"scalar-read": {
"p50": 0.1084,
"p95": 0.1271,
"p99": 0.1421,
"throughputOperationsPerSecond": 9285.499392728334
},
"scalar-write": {
"p50": 0.1098,
"p95": 0.131,
"p99": 0.1444,
"throughputOperationsPerSecond": 9130.46892262293
},
"array-read": {
"p50": 0.1261,
"p95": 0.1622,
"p99": 0.1971,
"throughputOperationsPerSecond": 7760.146294277952
},
"array-write": {
"p50": 0.129,
"p95": 0.1618,
"p99": 0.214,
"throughputOperationsPerSecond": 7611.187074986556
}
},
"clientCpuMilliseconds": 1561.548,
"clientAllocatedBytes": 52843304,
"clientAllocatedBytesPerOperation": 6605.413,
"clientWorkingSetBytes": 137527296,
"clientPrivateBytes": 0,
"clientThreads": 17,
"clientHandles": 0,
"collections": [
6,
0,
0
]
}
],
"failures": 0,
"loadedSdkAssemblies": [
{
"name": "Opc.Ua.Core",
"version": "1.5.378.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Core.dll"
},
{
"name": "Opc.Ua.Types",
"version": "1.5.378.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Types.dll"
},
{
"name": "Opc.Ua.Configuration",
"version": "1.5.378.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Configuration.dll"
},
{
"name": "Opc.Ua.Security.Certificates",
"version": "1.5.378.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Security.Certificates.dll"
},
{
"name": "Opc.Ua.Client",
"version": "1.5.378.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Client.dll"
}
],
"pair": 1,
"version": "baseline",
"server": {
"endpoint": "opc.tcp://127.0.0.1:54954/OtOpcUa",
"process": 28663,
"sdk": "1.5.378.0",
"rawUri": "https://zb.com/otopcua/raw",
"unsUri": "https://zb.com/otopcua/uns"
},
"serverResources": {
"WorkingSet64": 223887360,
"PrivateMemorySize64": 0,
"HandleCount": 0,
"threads": 30,
"cpuMilliseconds": 3865.762,
"allocatedBytes": 282715048,
"collections": [
36,
10,
3
]
}
},
{
"sdk": "2.0.1.0",
"process": 29028,
"cases": [
{
"name": "warmed-production-client-read-write-measurement",
"status": "passed"
}
],
"observations": [
{
"SecurityMode": "SignAndEncrypt",
"SecurityPolicyUri": "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256",
"tieredCompilation": "0",
"scope": "Two scalar/Int32[3] variables; zero data monitors during measurement; five fixture variables and one native condition"
},
{
"benchmarkRound": 1,
"warmupOperationsPerKind": 10,
"operationsPerKind": 50,
"latencyMilliseconds": {
"scalar-read": {
"p50": 0.1066,
"p95": 0.123,
"p99": 0.1348,
"throughputOperationsPerSecond": 9448.442447883575
},
"scalar-write": {
"p50": 0.1102,
"p95": 0.1273,
"p99": 0.1392,
"throughputOperationsPerSecond": 9103.911131261095
},
"array-read": {
"p50": 0.1061,
"p95": 0.1224,
"p99": 0.138,
"throughputOperationsPerSecond": 9477.231188999194
},
"array-write": {
"p50": 0.1087,
"p95": 0.1253,
"p99": 0.1361,
"throughputOperationsPerSecond": 9297.171056790836
}
},
"clientCpuMilliseconds": 1592.923,
"clientAllocatedBytes": 26878216,
"clientAllocatedBytesPerOperation": 3359.777,
"clientWorkingSetBytes": 151945216,
"clientPrivateBytes": 0,
"clientThreads": 18,
"clientHandles": 0,
"collections": [
3,
0,
0
]
}
],
"failures": 0,
"loadedSdkAssemblies": [
{
"name": "Opc.Ua.Core",
"version": "2.0.1.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Core.dll"
},
{
"name": "Opc.Ua.Types",
"version": "2.0.1.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Types.dll"
},
{
"name": "Opc.Ua.Core.Types",
"version": "2.0.1.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Core.Types.dll"
},
{
"name": "Opc.Ua.Configuration",
"version": "2.0.1.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Configuration.dll"
},
{
"name": "Opc.Ua.Security.Certificates",
"version": "2.0.1.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Security.Certificates.dll"
},
{
"name": "Opc.Ua.Client",
"version": "2.0.1.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Client.dll"
}
],
"pair": 1,
"version": "candidate",
"server": {
"endpoint": "opc.tcp://127.0.0.1:54976/OtOpcUa",
"process": 29017,
"sdk": "2.0.1.0",
"rawUri": "https://zb.com/otopcua/raw",
"unsUri": "https://zb.com/otopcua/uns"
},
"serverResources": {
"WorkingSet64": 225460224,
"PrivateMemorySize64": 0,
"HandleCount": 0,
"threads": 28,
"cpuMilliseconds": 14631.949,
"allocatedBytes": 152391328,
"collections": [
20,
5,
2
]
}
},
{
"sdk": "1.5.378.0",
"process": 29047,
"cases": [
{
"name": "warmed-production-client-read-write-measurement",
"status": "passed"
}
],
"observations": [
{
"SecurityMode": "SignAndEncrypt",
"SecurityPolicyUri": "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256",
"tieredCompilation": "0",
"scope": "Two scalar/Int32[3] variables; zero data monitors during measurement; five fixture variables and one native condition"
},
{
"benchmarkRound": 1,
"warmupOperationsPerKind": 10,
"operationsPerKind": 50,
"latencyMilliseconds": {
"scalar-read": {
"p50": 0.1066,
"p95": 0.1238,
"p99": 0.139,
"throughputOperationsPerSecond": 9484.700940265833
},
"scalar-write": {
"p50": 0.1124,
"p95": 0.1331,
"p99": 0.1461,
"throughputOperationsPerSecond": 8957.752105855532
},
"array-read": {
"p50": 0.115,
"p95": 0.1462,
"p99": 0.166,
"throughputOperationsPerSecond": 8586.679741317685
},
"array-write": {
"p50": 0.1177,
"p95": 0.152,
"p99": 0.1822,
"throughputOperationsPerSecond": 8333.253472987553
}
},
"clientCpuMilliseconds": 1437.526,
"clientAllocatedBytes": 52855576,
"clientAllocatedBytesPerOperation": 6606.947,
"clientWorkingSetBytes": 137445376,
"clientPrivateBytes": 0,
"clientThreads": 17,
"clientHandles": 0,
"collections": [
6,
0,
0
]
}
],
"failures": 0,
"loadedSdkAssemblies": [
{
"name": "Opc.Ua.Core",
"version": "1.5.378.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Core.dll"
},
{
"name": "Opc.Ua.Types",
"version": "1.5.378.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Types.dll"
},
{
"name": "Opc.Ua.Configuration",
"version": "1.5.378.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Configuration.dll"
},
{
"name": "Opc.Ua.Security.Certificates",
"version": "1.5.378.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Security.Certificates.dll"
},
{
"name": "Opc.Ua.Client",
"version": "1.5.378.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Client.dll"
}
],
"pair": 2,
"version": "baseline",
"server": {
"endpoint": "opc.tcp://127.0.0.1:55036/OtOpcUa",
"process": 29040,
"sdk": "1.5.378.0",
"rawUri": "https://zb.com/otopcua/raw",
"unsUri": "https://zb.com/otopcua/uns"
},
"serverResources": {
"WorkingSet64": 226934784,
"PrivateMemorySize64": 0,
"HandleCount": 0,
"threads": 29,
"cpuMilliseconds": 3721.212,
"allocatedBytes": 283566920,
"collections": [
36,
10,
3
]
}
},
{
"sdk": "2.0.1.0",
"process": 29091,
"cases": [
{
"name": "warmed-production-client-read-write-measurement",
"status": "passed"
}
],
"observations": [
{
"SecurityMode": "SignAndEncrypt",
"SecurityPolicyUri": "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256",
"tieredCompilation": "0",
"scope": "Two scalar/Int32[3] variables; zero data monitors during measurement; five fixture variables and one native condition"
},
{
"benchmarkRound": 1,
"warmupOperationsPerKind": 10,
"operationsPerKind": 50,
"latencyMilliseconds": {
"scalar-read": {
"p50": 0.1148,
"p95": 0.1461,
"p99": 0.1916,
"throughputOperationsPerSecond": 8453.853583482856
},
"scalar-write": {
"p50": 0.1106,
"p95": 0.1352,
"p99": 0.1535,
"throughputOperationsPerSecond": 9031.92242662466
},
"array-read": {
"p50": 0.1037,
"p95": 0.1201,
"p99": 0.1278,
"throughputOperationsPerSecond": 9771.59387887815
},
"array-write": {
"p50": 0.1098,
"p95": 0.1258,
"p99": 0.1357,
"throughputOperationsPerSecond": 9218.53127594152
}
},
"clientCpuMilliseconds": 1599.821,
"clientAllocatedBytes": 26876112,
"clientAllocatedBytesPerOperation": 3359.514,
"clientWorkingSetBytes": 151846912,
"clientPrivateBytes": 0,
"clientThreads": 18,
"clientHandles": 0,
"collections": [
3,
0,
0
]
}
],
"failures": 0,
"loadedSdkAssemblies": [
{
"name": "Opc.Ua.Core",
"version": "2.0.1.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Core.dll"
},
{
"name": "Opc.Ua.Types",
"version": "2.0.1.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Types.dll"
},
{
"name": "Opc.Ua.Core.Types",
"version": "2.0.1.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Core.Types.dll"
},
{
"name": "Opc.Ua.Configuration",
"version": "2.0.1.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Configuration.dll"
},
{
"name": "Opc.Ua.Security.Certificates",
"version": "2.0.1.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Security.Certificates.dll"
},
{
"name": "Opc.Ua.Client",
"version": "2.0.1.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Client.dll"
}
],
"pair": 2,
"version": "candidate",
"server": {
"endpoint": "opc.tcp://127.0.0.1:55057/OtOpcUa",
"process": 29069,
"sdk": "2.0.1.0",
"rawUri": "https://zb.com/otopcua/raw",
"unsUri": "https://zb.com/otopcua/uns"
},
"serverResources": {
"WorkingSet64": 221364224,
"PrivateMemorySize64": 0,
"HandleCount": 0,
"threads": 28,
"cpuMilliseconds": 14663.082,
"allocatedBytes": 152371904,
"collections": [
20,
5,
2
]
}
},
{
"sdk": "1.5.378.0",
"process": 29442,
"cases": [
{
"name": "warmed-production-client-read-write-measurement",
"status": "passed"
}
],
"observations": [
{
"SecurityMode": "SignAndEncrypt",
"SecurityPolicyUri": "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256",
"tieredCompilation": "0",
"scope": "Two scalar/Int32[3] variables; zero data monitors during measurement; five fixture variables and one native condition"
},
{
"benchmarkRound": 1,
"warmupOperationsPerKind": 10,
"operationsPerKind": 50,
"latencyMilliseconds": {
"scalar-read": {
"p50": 0.1113,
"p95": 0.1326,
"p99": 0.1514,
"throughputOperationsPerSecond": 8990.861239093505
},
"scalar-write": {
"p50": 0.1122,
"p95": 0.135,
"p99": 0.1487,
"throughputOperationsPerSecond": 8981.86516513833
},
"array-read": {
"p50": 0.1088,
"p95": 0.1297,
"p99": 0.148,
"throughputOperationsPerSecond": 9229.57080649836
},
"array-write": {
"p50": 0.1013,
"p95": 0.1363,
"p99": 0.1511,
"throughputOperationsPerSecond": 9424.462228404675
}
},
"clientCpuMilliseconds": 1460.265,
"clientAllocatedBytes": 52847520,
"clientAllocatedBytesPerOperation": 6605.94,
"clientWorkingSetBytes": 137510912,
"clientPrivateBytes": 0,
"clientThreads": 17,
"clientHandles": 0,
"collections": [
6,
0,
0
]
}
],
"failures": 0,
"loadedSdkAssemblies": [
{
"name": "Opc.Ua.Core",
"version": "1.5.378.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Core.dll"
},
{
"name": "Opc.Ua.Types",
"version": "1.5.378.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Types.dll"
},
{
"name": "Opc.Ua.Configuration",
"version": "1.5.378.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Configuration.dll"
},
{
"name": "Opc.Ua.Security.Certificates",
"version": "1.5.378.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Security.Certificates.dll"
},
{
"name": "Opc.Ua.Client",
"version": "1.5.378.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-baseline/bin/Release/net10.0/Opc.Ua.Client.dll"
}
],
"pair": 3,
"version": "baseline",
"server": {
"endpoint": "opc.tcp://127.0.0.1:55141/OtOpcUa",
"process": 29435,
"sdk": "1.5.378.0",
"rawUri": "https://zb.com/otopcua/raw",
"unsUri": "https://zb.com/otopcua/uns"
},
"serverResources": {
"WorkingSet64": 218546176,
"PrivateMemorySize64": 0,
"HandleCount": 0,
"threads": 30,
"cpuMilliseconds": 3652.527,
"allocatedBytes": 284326360,
"collections": [
35,
9,
2
]
}
},
{
"sdk": "2.0.1.0",
"process": 29814,
"cases": [
{
"name": "warmed-production-client-read-write-measurement",
"status": "passed"
}
],
"observations": [
{
"SecurityMode": "SignAndEncrypt",
"SecurityPolicyUri": "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256",
"tieredCompilation": "0",
"scope": "Two scalar/Int32[3] variables; zero data monitors during measurement; five fixture variables and one native condition"
},
{
"benchmarkRound": 1,
"warmupOperationsPerKind": 10,
"operationsPerKind": 50,
"latencyMilliseconds": {
"scalar-read": {
"p50": 0.1134,
"p95": 0.2219,
"p99": 0.2964,
"throughputOperationsPerSecond": 7963.32094373317
},
"scalar-write": {
"p50": 0.1177,
"p95": 0.1569,
"p99": 0.2249,
"throughputOperationsPerSecond": 8232.878494239445
},
"array-read": {
"p50": 0.1161,
"p95": 0.1455,
"p99": 0.171,
"throughputOperationsPerSecond": 8493.990077320781
},
"array-write": {
"p50": 0.1183,
"p95": 0.1433,
"p99": 0.1651,
"throughputOperationsPerSecond": 8450.517361799171
}
},
"clientCpuMilliseconds": 1754.458,
"clientAllocatedBytes": 26950792,
"clientAllocatedBytesPerOperation": 3368.849,
"clientWorkingSetBytes": 151781376,
"clientPrivateBytes": 0,
"clientThreads": 18,
"clientHandles": 0,
"collections": [
3,
0,
0
]
}
],
"failures": 0,
"loadedSdkAssemblies": [
{
"name": "Opc.Ua.Core",
"version": "2.0.1.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Core.dll"
},
{
"name": "Opc.Ua.Types",
"version": "2.0.1.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Types.dll"
},
{
"name": "Opc.Ua.Core.Types",
"version": "2.0.1.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Core.Types.dll"
},
{
"name": "Opc.Ua.Configuration",
"version": "2.0.1.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Configuration.dll"
},
{
"name": "Opc.Ua.Security.Certificates",
"version": "2.0.1.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Security.Certificates.dll"
},
{
"name": "Opc.Ua.Client",
"version": "2.0.1.0",
"path": "/Users/dohertj2/.codex/worktrees/opcua2-cross-version/OtOpcUa/artifacts/cross-version/run-09/build-candidate/bin/Release/net10.0/Opc.Ua.Client.dll"
}
],
"pair": 3,
"version": "candidate",
"server": {
"endpoint": "opc.tcp://127.0.0.1:55174/OtOpcUa",
"process": 29458,
"sdk": "2.0.1.0",
"rawUri": "https://zb.com/otopcua/raw",
"unsUri": "https://zb.com/otopcua/uns"
},
"serverResources": {
"WorkingSet64": 226361344,
"PrivateMemorySize64": 0,
"HandleCount": 0,
"threads": 28,
"cpuMilliseconds": 15066.213,
"allocatedBytes": 152491688,
"collections": [
20,
5,
2
]
}
}
]
@@ -0,0 +1,220 @@
{
"scope": "Supplementary warmed, matched native-process experiment; not full Gate G4.",
"environment": {
"DOTNET_TieredCompilation": "0",
"OPCUA_MATRIX_PERF_ROUNDS": "1",
"OPCUA_MATRIX_PERF_WARMUPS": "1000",
"OPCUA_MATRIX_PERF_MEASURED": "2000"
},
"dotnetSdk": "10.0.403",
"p95BudgetPercent": 15,
"dateUtc": "2026-11-10T17:16:20Z",
"logicalCpuCount": 14,
"summary": [
{
"operation": "scalar-read",
"samples": {
"baseline": {
"p95Milliseconds": [
0.1271,
0.1238,
0.1326
],
"allocatedBytesPerOperation": [
6605.413,
6606.947,
6605.94
],
"clientWorkingSetBytes": [
137527296,
137445376,
137510912
],
"serverWorkingSetBytes": [
223887360,
226934784,
218546176
]
},
"candidate": {
"p95Milliseconds": [
0.123,
0.1461,
0.2219
],
"allocatedBytesPerOperation": [
3359.777,
3359.514,
3368.849
],
"clientWorkingSetBytes": [
151945216,
151846912,
151781376
],
"serverWorkingSetBytes": [
225460224,
221364224,
226361344
]
}
},
"medianP95ChangePercent": 14.94885916601103,
"status": "diagnostic-within-budget"
},
{
"operation": "scalar-write",
"samples": {
"baseline": {
"p95Milliseconds": [
0.131,
0.1331,
0.135
],
"allocatedBytesPerOperation": [
6605.413,
6606.947,
6605.94
],
"clientWorkingSetBytes": [
137527296,
137445376,
137510912
],
"serverWorkingSetBytes": [
223887360,
226934784,
218546176
]
},
"candidate": {
"p95Milliseconds": [
0.1273,
0.1352,
0.1569
],
"allocatedBytesPerOperation": [
3359.777,
3359.514,
3368.849
],
"clientWorkingSetBytes": [
151945216,
151846912,
151781376
],
"serverWorkingSetBytes": [
225460224,
221364224,
226361344
]
}
},
"medianP95ChangePercent": 1.577761081893314,
"status": "diagnostic-within-budget"
},
{
"operation": "array-read",
"samples": {
"baseline": {
"p95Milliseconds": [
0.1622,
0.1462,
0.1297
],
"allocatedBytesPerOperation": [
6605.413,
6606.947,
6605.94
],
"clientWorkingSetBytes": [
137527296,
137445376,
137510912
],
"serverWorkingSetBytes": [
223887360,
226934784,
218546176
]
},
"candidate": {
"p95Milliseconds": [
0.1224,
0.1201,
0.1455
],
"allocatedBytesPerOperation": [
3359.777,
3359.514,
3368.849
],
"clientWorkingSetBytes": [
151945216,
151846912,
151781376
],
"serverWorkingSetBytes": [
225460224,
221364224,
226361344
]
}
},
"medianP95ChangePercent": -16.279069767441868,
"status": "diagnostic-within-budget"
},
{
"operation": "array-write",
"samples": {
"baseline": {
"p95Milliseconds": [
0.1618,
0.152,
0.1363
],
"allocatedBytesPerOperation": [
6605.413,
6606.947,
6605.94
],
"clientWorkingSetBytes": [
137527296,
137445376,
137510912
],
"serverWorkingSetBytes": [
223887360,
226934784,
218546176
]
},
"candidate": {
"p95Milliseconds": [
0.1253,
0.1258,
0.1433
],
"allocatedBytesPerOperation": [
3359.777,
3359.514,
3368.849
],
"clientWorkingSetBytes": [
151945216,
151846912,
151781376
],
"serverWorkingSetBytes": [
225460224,
221364224,
226361344
]
}
},
"medianP95ChangePercent": -17.23684210526316,
"status": "diagnostic-within-budget"
}
],
"status": "diagnostic-within-budget"
}
@@ -0,0 +1,115 @@
#!/usr/bin/env python3
"""Supplementary warmed, matched native-process experiment; not full Gate G4."""
import argparse
import hashlib
import json
import os
from pathlib import Path
import secrets
import shutil
import statistics
import subprocess
import time
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--baseline-harness', type=Path, required=True)
parser.add_argument('--candidate-harness', type=Path, required=True)
parser.add_argument('--output', type=Path, required=True)
parser.add_argument('--tiered-compilation', choices=['0', '1'], default='0')
args = parser.parse_args()
output = args.output.resolve()
output.mkdir(parents=True, exist_ok=False)
env = dict(os.environ, OPCUA_MATRIX_PASSWORD='otua_matrix_' + secrets.token_urlsafe(32),
DOTNET_TieredCompilation=args.tiered_compilation, OPCUA_MATRIX_PERF_ROUNDS='1',
OPCUA_MATRIX_PERF_WARMUPS='1000', OPCUA_MATRIX_PERF_MEASURED='2000')
for key in ('OPCUA_MATRIX_SERVER_SECURITY', 'OPCUA_MATRIX_CLIENT_PKI'):
env.pop(key, None)
metadata = {'scope': __doc__, 'environment': {k: env[k] for k in ('DOTNET_TieredCompilation', 'OPCUA_MATRIX_PERF_ROUNDS', 'OPCUA_MATRIX_PERF_WARMUPS', 'OPCUA_MATRIX_PERF_MEASURED')},
'dotnetSdk': subprocess.check_output(['dotnet', '--version'], text=True).strip(), 'p95BudgetPercent': 15,
'dateUtc': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()), 'logicalCpuCount': os.cpu_count()}
commands, results, hashes = [], [], []
def write(path, data):
path.write_text(json.dumps(data, indent=2) + '\n')
def run(command, log):
commands.append({'argv': list(map(str, command)), 'log': str(log)})
with log.open('w') as stream:
result = subprocess.run(list(map(str, command)), stdout=stream, stderr=subprocess.STDOUT, env=env, timeout=240)
if result.returncode:
raise RuntimeError(f'Process exited {result.returncode}; {log}')
def copy_certs(source, destination):
destination.mkdir(parents=True, exist_ok=True)
files = list(source.glob('*'))
if not files:
raise RuntimeError('Missing public certificates: ' + str(source))
for path in files:
shutil.copy2(path, destination / path.name)
try:
for pair in range(1, 4):
for version, harness in [('baseline', args.baseline_harness.resolve()), ('candidate', args.candidate_harness.resolve())]:
leg = output / f'pair-{pair}-{version}'
leg.mkdir()
client, server = leg / 'client', leg / 'server'
client.mkdir()
server.mkdir()
run(['dotnet', harness, 'prepare', client], leg / 'prepare.log')
copy_certs(client / 'client-pki/own/certs', server / 'server-pki/trusted/certs')
server_log = (leg / 'server.log').open('w')
command = ['dotnet', str(harness), 'server', str(server)]
commands.append({'argv': command, 'log': str(leg / 'server.log')})
process = subprocess.Popen(command, env=env, stdout=server_log, stderr=subprocess.STDOUT)
try:
deadline = time.monotonic() + 60
while not (server / 'ready.json').exists():
if process.poll() is not None or time.monotonic() > deadline:
raise RuntimeError('Server failed to start: ' + str(leg / 'server.log'))
time.sleep(.1)
ready = json.loads((server / 'ready.json').read_text())
copy_certs(server / 'server-pki/own/certs', client / 'client-pki/trusted/certs')
run(['dotnet', harness, 'performance', client, ready['endpoint']], leg / 'client.log')
result = json.loads((client / 'result.json').read_text())
result.update({'pair': pair, 'version': version, 'server': ready})
finally:
(server / 'stop').touch()
try:
process.wait(timeout=15)
except subprocess.TimeoutExpired:
process.terminate()
process.wait(timeout=10)
server_log.close()
result['serverResources'] = json.loads((server / 'server-resources.json').read_text())
results.append(result)
write(leg / 'measurement.json', result)
summary = []
for operation in ('scalar-read', 'scalar-write', 'array-read', 'array-write'):
samples = {}
for version in ('baseline', 'candidate'):
rounds = [o for r in results if r['version'] == version for o in r['observations'] if 'benchmarkRound' in o]
samples[version] = {'p95Milliseconds': [r['latencyMilliseconds'][operation]['p95'] for r in rounds],
'allocatedBytesPerOperation': [r['clientAllocatedBytesPerOperation'] for r in rounds],
'clientWorkingSetBytes': [r['clientWorkingSetBytes'] for r in rounds],
'serverWorkingSetBytes': [r['serverResources']['WorkingSet64'] for r in results if r['version'] == version]}
ratio = statistics.median(samples['candidate']['p95Milliseconds']) / statistics.median(samples['baseline']['p95Milliseconds'])
summary.append({'operation': operation, 'samples': samples, 'medianP95ChangePercent': 100 * (ratio - 1), 'status': 'diagnostic-regression' if ratio > 1.15 else 'diagnostic-within-budget'})
metadata['summary'] = summary
metadata['status'] = 'diagnostic-regression' if any(r['status'] == 'diagnostic-regression' for r in summary) else 'diagnostic-within-budget'
for row in summary:
print(f"{row['operation']}: median p95 {row['medianP95ChangePercent']:+.1f}% ({row['status']})")
finally:
for harness in (args.baseline_harness.resolve(), args.candidate_harness.resolve()):
for path in [harness, *harness.parent.glob('Opc.Ua*.dll'), *harness.parent.glob('ZB.MOM.WW.OtOpcUa*.dll'), *harness.parent.glob('OtOpcUa.Host.dll')]:
hashes.append({'path': str(path), 'sha256': hashlib.sha256(path.read_bytes()).hexdigest(), 'bytes': path.stat().st_size})
write(output / 'artifact-hashes.json', hashes)
write(output / 'commands.json', commands)
write(output / 'measurements.json', results)
write(output / 'summary.json', metadata)
return 2 if metadata.get('status') == 'diagnostic-regression' else 0
if __name__ == '__main__':
raise SystemExit(main())
@@ -0,0 +1,194 @@
#!/usr/bin/env python3
"""Separate-process production application compatibility gate (no mixed SDK process)."""
import argparse
import hashlib
import json
import os
from pathlib import Path
import secrets
import shutil
import subprocess
import time
import uuid
import xml.etree.ElementTree as ET
HERE = Path(__file__).resolve().parent
HOST = 'src/Server/ZB.MOM.WW.OtOpcUa.Host/ZB.MOM.WW.OtOpcUa.Host.csproj'
CLIENT = 'src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/ZB.MOM.WW.OtOpcUa.Client.Shared.csproj'
DRIVER = 'src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.csproj'
CLI = 'src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI/ZB.MOM.WW.OtOpcUa.Client.CLI.csproj'
def write_json(path, value):
path.write_text(json.dumps(value, indent=2) + '\n')
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--baseline-root', required=True, type=Path)
parser.add_argument('--baseline-artifacts', required=True, type=Path, help='Existing immutable host/ and client/ publishes')
parser.add_argument('--candidate-root', required=True, type=Path, help='Dedicated candidate checkout; this script builds it')
parser.add_argument('--container-image', default='mcr.microsoft.com/dotnet/sdk:10.0.103-noble', help='Disposable local driver/CLI isolation; must supply .NET 10 and bash')
parser.add_argument('--output', required=True, type=Path, help='Fresh nonexistent evidence directory')
args = parser.parse_args()
args.baseline_root = args.baseline_root.resolve()
args.candidate_root = args.candidate_root.resolve()
args.baseline_artifacts = args.baseline_artifacts.resolve()
args.output = args.output.resolve()
args.output.mkdir(parents=True, exist_ok=False)
source_info = {}
for name, root in [('baseline', args.baseline_root), ('candidate', args.candidate_root)]:
source_info[name] = {'root': str(root), 'commit': subprocess.check_output(['git', '-C', str(root), 'rev-parse', 'HEAD'], text=True).strip(),
'sdkPackages': str(root / 'Directory.Packages.props')}
source_info['scope'] = 'Loopback application contract and 3 warmed 50-operation rounds per scalar/Int32[3] read/write kind; shared development host, no full load/reconnect/failover/backend/soak performance claim.'
source_info['dateUtc'] = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
write_json(args.output / 'source-info.json', source_info)
commands = []
matrix = []
env = dict(os.environ, OPCUA_MATRIX_PASSWORD='otua_matrix_' + secrets.token_urlsafe(32))
env.pop('OPCUA_MATRIX_CLIENT_PKI', None)
env.pop('OPCUA_MATRIX_SERVER_SECURITY', None)
def run(command, log, process_env=env, timeout=300):
commands.append({'argv': ['<ephemeral-key>' if x == env['OPCUA_MATRIX_PASSWORD'] else str(x) for x in command], 'log': str(log)})
with log.open('w') as stream:
result = subprocess.run([str(x) for x in command], stdout=stream, stderr=subprocess.STDOUT, env=process_env, timeout=timeout)
return result.returncode
def build(version):
folder = args.output / ('build-' + version)
folder.mkdir()
project = ET.Element('Project', Sdk='Microsoft.NET.Sdk')
props = ET.SubElement(project, 'PropertyGroup')
for k, v in {'OutputType': 'Exe', 'TargetFramework': 'net10.0', 'Nullable': 'enable', 'ImplicitUsings': 'enable', 'LangVersion': 'latest', 'DefineConstants': 'SDK2' if version == 'candidate' else ''}.items():
ET.SubElement(props, k).text = v
items = ET.SubElement(project, 'ItemGroup')
ET.SubElement(items, 'FrameworkReference', Include='Microsoft.AspNetCore.App')
if version == 'candidate':
for path in (HOST, CLIENT, DRIVER):
ET.SubElement(items, 'ProjectReference', Include=str(args.candidate_root / path))
else:
dlls = {}
for path in ('host', 'client'):
for dll in (args.baseline_artifacts / path).glob('*.dll'):
dlls[dll.name] = dll
for name, dll in sorted(dlls.items()):
ref = ET.SubElement(items, 'Reference', Include=name[:-4])
ET.SubElement(ref, 'HintPath').text = str(dll)
ET.SubElement(ref, 'Private').text = 'true'
ET.SubElement(items, 'Compile', Include=str(HERE / 'ProcessHost.cs'))
csproj = folder / 'ProcessHost.csproj'
ET.ElementTree(project).write(csproj, encoding='unicode')
status = run(['dotnet', 'build', csproj, '-c', 'Release', '-m:1', '--nologo'], folder / 'build.log')
if status:
raise RuntimeError(f'{version} harness build failed ({status}); see {folder / "build.log"}')
return folder / 'bin/Release/net10.0/ProcessHost.dll'
def certificates(source, target):
target.mkdir(parents=True, exist_ok=True)
files = list(source.glob('*'))
if not files:
raise RuntimeError('No public certificates at ' + str(source))
for file in files:
shutil.copy2(file, target / file.name)
def receipt(paths):
return [{'path': str(p), 'sha256': hashlib.sha256(p.read_bytes()).hexdigest(), 'bytes': p.stat().st_size} for p in sorted(set(paths)) if p.is_file()]
baseline = candidate = candidate_cli = None
try:
# Baseline builds only the portable harness against the exact published production DLLs.
baseline = build('baseline')
candidate = build('candidate')
candidate_cli = args.output / 'candidate-cli'
status = run(['dotnet', 'publish', args.candidate_root / CLI, '-c', 'Release', '-o', candidate_cli, '-m:1', '--nologo'], args.output / 'candidate-cli-build.log')
if status:
raise RuntimeError('Candidate CLI publish failed')
for name, client, server in [('baseline-client-to-baseline-server', baseline, baseline), ('baseline-client-to-candidate-server', baseline, candidate), ('candidate-client-to-baseline-server', candidate, baseline), ('candidate-client-to-candidate-server', candidate, candidate)]:
leg = args.output / name
leg.mkdir()
server_root = leg / 'server'
client_root = leg / 'shared'
server_root.mkdir()
client_root.mkdir()
status = run(['dotnet', client, 'prepare', client_root], leg / 'prepare.log')
if status:
matrix.append({'name': name, 'status': 'failed', 'stage': 'client-certificate-preparation'})
continue
certificates(client_root / 'client-pki/own/certs', server_root / 'server-pki/trusted/certs')
server_log = (leg / 'server.log').open('w')
command = ['dotnet', str(server), 'server', str(server_root)]
commands.append({'argv': command, 'log': str(leg / 'server.log')})
proc = subprocess.Popen(command, stdout=server_log, stderr=subprocess.STDOUT, env=env)
try:
deadline = time.monotonic() + 60
while not (server_root / 'ready.json').exists():
if proc.poll() is not None or time.monotonic() > deadline:
raise RuntimeError(f'{name}: server did not become ready; see server.log')
time.sleep(.1)
ready = json.loads((server_root / 'ready.json').read_text())
certificates(server_root / 'server-pki/own/certs', client_root / 'client-pki/trusted/certs')
status = run(['dotnet', client, 'client', client_root, ready['endpoint']], leg / 'shared.log')
result = json.loads((client_root / 'result.json').read_text())
history = [json.loads(x) for x in (server_root / 'history-requests.jsonl').read_text().splitlines()] if (server_root / 'history-requests.jsonl').exists() else []
result.update({'name': name, 'server': ready, 'status': 'passed' if status == 0 else 'failed', 'historyBackendRequests': history, 'certificateValidation': 'explicit trusted peers; server and Shared client autoaccept disabled', 'authentication': 'ephemeral API-key-shaped credential, exact fixture authenticator; real authority/ACL/method gates'})
write_json(leg / 'leg.json', result)
matrix.append(result)
except Exception as exc:
matrix.append({'name': name, 'status': 'failed', 'error': str(exc)})
finally:
(server_root / 'stop').touch()
try:
proc.wait(timeout=15)
except subprocess.TimeoutExpired:
proc.terminate()
proc.wait(timeout=10)
server_log.close()
image_info = subprocess.check_output(['docker', 'image', 'inspect', args.container_image, '--format', '{{json .}}'], text=True)
image_data = json.loads(image_info)
write_json(args.output / 'container-image.json', {k: image_data.get(k) for k in ('Id', 'RepoDigests', 'Architecture', 'Os')})
for server_name, server in [('baseline', baseline), ('candidate', candidate)]:
name = 'candidate-driver-cli-to-' + server_name + '-server'
leg = args.output / name
leg.mkdir()
shutil.copy2(HERE / 'container-leg.sh', leg / 'run.sh')
container_name = 'otopcua-sdk2-matrix-' + uuid.uuid4().hex[:12]
command = ['docker', 'run', '--rm', '--name', container_name, '--network', 'none', '-e', 'OPCUA_MATRIX_PASSWORD',
'-v', str(candidate.parent) + ':/candidate:ro', '-v', str(server.parent) + ':/server:ro',
'-v', str(candidate_cli) + ':/cli:ro', '-v', str(leg) + ':/task', args.container_image, 'bash', '/task/run.sh']
try:
status = run(command, leg / 'container.log')
finally:
subprocess.run(['docker', 'rm', '-f', container_name], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
if (leg / 'driver/result.json').exists():
result = json.loads((leg / 'driver/result.json').read_text())
else:
result = {'cases': [], 'error': 'Driver result missing; inspect preparation/container/server logs'}
for operation, expected in [('read', 'Value: 92'), ('raw-history', '25 values returned.'), ('event-history', '25 events returned.')]:
log = leg / ('cli-' + operation + '.log')
passed = log.exists() and expected in log.read_text()
result['cases'].append({'name': 'production-CLI-' + operation, 'status': 'passed' if passed else 'failed', 'log': str(log), 'expectedOutput': expected})
result.update({'name': name, 'status': 'passed' if status == 0 and all(x['status'] == 'passed' for x in result['cases']) else 'failed',
'container': container_name, 'network': 'none; all peers in task container, separate SDK processes',
'driverCertificateValidation': 'explicit trusted peers; autoaccept disabled', 'cliCertificateValidation': 'explicit exchanged peers; CLI always configures AutoAcceptCertificates=true (CommandBase.CreateConnectionSettings)',
'server': json.loads((leg / 'server/ready.json').read_text()) if (leg / 'server/ready.json').exists() else None})
write_json(leg / 'leg.json', result)
matrix.append(result)
finally:
paths = [p for p in (baseline, candidate, args.baseline_root / 'Directory.Packages.props', args.candidate_root / 'Directory.Packages.props', HERE / 'ProcessHost.cs', HERE / 'run.py', HERE / 'container-leg.sh') if p is not None]
for folder in (args.baseline_artifacts / 'host', args.baseline_artifacts / 'client', candidate.parent if candidate else None, candidate_cli):
if folder is not None:
paths.extend(folder.glob('Opc.Ua*.dll'))
paths.extend(folder.glob('*.deps.json'))
paths.extend(folder.glob('ZB.MOM.WW.OtOpcUa*.dll'))
paths.extend(folder.glob('OtOpcUa.Host.dll'))
paths.extend(folder.glob('*.runtimeconfig.json'))
write_json(args.output / 'artifact-hashes.json', receipt(paths))
write_json(args.output / 'commands.json', commands)
write_json(args.output / 'matrix.json', matrix)
return 1 if not matrix or any(x['status'] != 'passed' for x in matrix) else 0
if __name__ == '__main__':
raise SystemExit(main())
@@ -1533,9 +1533,6 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
alarm.ShelvingState = new ShelvedStateMachineState(alarm);
alarm.MaxTimeShelved = new PropertyState<double>.Implementation<VariantBuilder>(alarm);
alarm.SymbolicName = displayName;
// HasComponent so the parent folder "owns" the condition (matches the T13 notes' pattern).
alarm.ReferenceTypeId = ReferenceTypeIds.HasComponent;
// Create builds the full mandatory Part 9 child set (EnabledState, AckedState,
// ActiveState, the Acknowledge/Confirm/AddComment/Enable/Disable methods, ...) from the
// type's embedded definition; we do not hand-build them.
@@ -1545,6 +1542,10 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
new QualifiedName(displayName, ns),
new LocalizedText(displayName),
assignNodeIds: true);
// Create initializes the embedded definition and resets ReferenceTypeId in SDK 2.0.1.
// Restore the application's parent ownership before AddChild: hierarchical folder browses
// must expose the alarm as a HasComponent object, as they did before the SDK upgrade.
alarm.ReferenceTypeId = ReferenceTypeIds.HasComponent;
// Main-branch id MUST be a concrete (null) NodeId before any Set* call: SetEnableState ->
// UpdateRetainState -> GetRetainState -> IsBranch() dereferences BranchId.Value, which
@@ -47,9 +47,9 @@ public sealed class StatusCodeParityTests
/// <summary>Every <c>Opc.Ua.StatusCodes</c> member, by name — the oracle this test checks against.</summary>
private static readonly IReadOnlyDictionary<string, uint> SdkStatusCodes =
typeof(Opc.Ua.StatusCodes)
.GetFields(BindingFlags.Public | BindingFlags.Static)
.Where(f => f.IsLiteral && f.FieldType == typeof(uint))
.ToDictionary(f => f.Name, f => (uint)f.GetRawConstantValue()!, StringComparer.Ordinal);
.GetProperties(BindingFlags.Public | BindingFlags.Static)
.Where(p => p.PropertyType == typeof(Opc.Ua.StatusCode))
.ToDictionary(p => p.Name, p => ((Opc.Ua.StatusCode)p.GetValue(null)!).Code, StringComparer.Ordinal);
/// <summary>
/// Every status-shaped <c>const uint</c> declared anywhere in the deployed driver assemblies, as
@@ -24,7 +24,7 @@ public abstract class AtTimeConformance : ConformanceBase
var code = new Opc.Ua.StatusCode(GoodInterpolated);
code.AggregateBits.ShouldBe(Opc.Ua.AggregateBits.Interpolated);
Opc.Ua.StatusCode.IsGood(code).ShouldBeTrue();
new Opc.Ua.StatusCode(UncertainInterpolated).CodeBits.ShouldBe(Opc.Ua.StatusCodes.UncertainDataSubNormal);
new Opc.Ua.StatusCode(UncertainInterpolated).CodeBits.ShouldBe(Opc.Ua.StatusCodes.UncertainDataSubNormal.Code);
await Task.CompletedTask;
}
@@ -31,10 +31,10 @@ public abstract class ProcessedConformance : ConformanceBase
{
new Opc.Ua.StatusCode(GoodCalc).AggregateBits.ShouldBe(Opc.Ua.AggregateBits.Calculated);
new Opc.Ua.StatusCode(UncertainCalc).AggregateBits.ShouldBe(Opc.Ua.AggregateBits.Calculated);
new Opc.Ua.StatusCode(UncertainCalc).CodeBits.ShouldBe(Opc.Ua.StatusCodes.UncertainDataSubNormal);
new Opc.Ua.StatusCode(NoDataCalc).CodeBits.ShouldBe(Opc.Ua.StatusCodes.BadNoData);
new Opc.Ua.StatusCode(UncertainCalc).CodeBits.ShouldBe(Opc.Ua.StatusCodes.UncertainDataSubNormal.Code);
new Opc.Ua.StatusCode(NoDataCalc).CodeBits.ShouldBe(Opc.Ua.StatusCodes.BadNoData.Code);
new Opc.Ua.StatusCode(NoDataCalc).AggregateBits.ShouldBe(Opc.Ua.AggregateBits.Calculated);
new Opc.Ua.StatusCode(InvalidInputsCalc).CodeBits.ShouldBe(Opc.Ua.StatusCodes.BadAggregateInvalidInputs);
new Opc.Ua.StatusCode(InvalidInputsCalc).CodeBits.ShouldBe(Opc.Ua.StatusCodes.BadAggregateInvalidInputs.Code);
}
[Theory]
@@ -1,13 +1,14 @@
using Opc.Ua;
using Opc.Ua.Server;
using ZB.MOM.WW.OtOpcUa.OpcUaServer.Compatibility.Aggregates;
using Shouldly;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Conformance;
/// <summary>
/// Drives the pinned OPC UA SDK's own Part 13 aggregate calculators (<c>Opc.Ua.Server</c> — the classes the SDK's
/// <c>AggregateManager</c> registers) in-process over a sample set, so our processed reads can be compared with the
/// Drives the application's versioned 1.5.378.182 Part 13 compatibility calculators — the same factories
/// the upgraded server registers for live aggregates in-process over a sample set, so our processed reads can be compared with the
/// reference implementation bucket by bucket (#677). The calculators run under the server's default
/// <see cref="AggregateConfiguration"/> (TreatUncertainAsBad = false, PercentDataBad = PercentDataGood = 100,
/// UseSlopedExtrapolation = false), sloped interpolation, source timestamps.
@@ -17,6 +18,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Conformance;
/// </remarks>
public static class SdkAggregateOracle
{
private static readonly ITelemetryContext Telemetry = DefaultTelemetry.Create(_ => { });
/// <summary>One processed value: value (double / int / null), full status code, timestamp.</summary>
public readonly record struct Result(object? Value, uint StatusCode, DateTime Timestamp);
@@ -43,22 +45,7 @@ public static class SdkAggregateOracle
var id = AggregateId(aggregate);
var ms = interval.TotalMilliseconds;
var cfg = DefaultConfiguration;
IAggregateCalculator calc = aggregate switch
{
HistoryAggregateType.Average or HistoryAggregateType.TimeAverage or HistoryAggregateType.TimeAverage2
or HistoryAggregateType.Total or HistoryAggregateType.Total2 =>
new AverageAggregateCalculator(id, start, end, ms, false, cfg, null),
HistoryAggregateType.Minimum or HistoryAggregateType.Maximum or HistoryAggregateType.Range
or HistoryAggregateType.MinimumActualTime or HistoryAggregateType.MaximumActualTime =>
new MinMaxAggregateCalculator(id, start, end, ms, false, cfg, null),
HistoryAggregateType.Count => new CountAggregateCalculator(id, start, end, ms, false, cfg, null),
HistoryAggregateType.PercentGood or HistoryAggregateType.PercentBad =>
new StatusAggregateCalculator(id, start, end, ms, false, cfg, null),
HistoryAggregateType.Start or HistoryAggregateType.End or HistoryAggregateType.Delta =>
new StartEndAggregateCalculator(id, start, end, ms, false, cfg, null),
HistoryAggregateType.Interpolative => new AggregateCalculator(id, start, end, ms, false, cfg, null),
_ => throw new ArgumentOutOfRangeException(nameof(aggregate), aggregate, "no SDK calculator mapped"),
};
IAggregateCalculator calc = CompatibilityAggregateFactory.Create(id, start, end, ms, false, cfg, Telemetry);
// The SDK expects values in the direction of the read: ascending forward, descending reverse.
var ordered = samples.OrderBy(s => s.Time).ToList();
@@ -68,9 +55,8 @@ public static class SdkAggregateOracle
var results = new List<Result>();
for (var guard = 0; guard < 100_000; guard++)
{
var dv = calc.GetProcessedValue(true);
if (dv is null) break;
results.Add(new Result(dv.Value, dv.StatusCode.Code, DateTime.SpecifyKind(dv.SourceTimestamp, DateTimeKind.Utc)));
if (!calc.TryGetProcessedValue(true, out var dv)) break;
results.Add(new Result(dv.WrappedValue.AsBoxedObject(Variant.BoxingBehavior.Legacy), dv.StatusCode.Code, (DateTime)dv.SourceTimestamp));
}
return results;
}
@@ -123,20 +109,16 @@ public static class SdkAggregateOracle
problems.ShouldBeEmpty($"{label} differs from the SDK:\n" + string.Join("\n", problems));
}
private static DataValue ToDataValue(DateTime t, HistorianValue v, uint status) => new()
{
WrappedValue = v.Kind switch
private static DataValue ToDataValue(DateTime t, HistorianValue v, uint status) => new(
v.Kind switch
{
HistorianValueKind.Empty => Variant.Null,
HistorianValueKind.Int64 => new Variant(v.Integer),
HistorianValueKind.Boolean => new Variant(v.Number != 0),
HistorianValueKind.String => new Variant(v.Text),
_ => new Variant(v.Number),
HistorianValueKind.Int64 => Variant.From(v.Integer),
HistorianValueKind.Boolean => Variant.From(v.Number != 0),
HistorianValueKind.String => v.Text is { } text ? Variant.From(text) : Variant.Null,
_ => Variant.From(v.Number),
},
StatusCode = status,
SourceTimestamp = t,
ServerTimestamp = t,
};
status, t, t);
private static NodeId AggregateId(HistoryAggregateType a) => a switch
{
@@ -114,7 +114,7 @@ public sealed class GatewayBoundingValueTests
{
var bound = await HistoryBounds.StartBoundAsync(Source(Aveva()), "T", At(-30), reverse: false, 65536, Now, Ct);
bound!.StatusCode.ShouldBe(Opc.Ua.StatusCodes.BadBoundNotFound);
bound!.StatusCode.ShouldBe(Opc.Ua.StatusCodes.BadBoundNotFound.Code);
bound.SourceTimestampUtc.ShouldBe(At(-30));
bound.Value.ShouldBeNull();
}
@@ -134,7 +134,7 @@ public sealed class GatewayBoundingValueTests
after.Value.ShouldBe(5d);
var missing = await HistoryBounds.EndBoundAsync(source, "T", HistoryBounds.For(At(0), At(101)), At(100), 65536, Now, Ct);
missing.StatusCode.ShouldBe(Opc.Ua.StatusCodes.BadBoundNotFound);
missing.StatusCode.ShouldBe(Opc.Ua.StatusCodes.BadBoundNotFound.Code);
missing.SourceTimestampUtc.ShouldBe(At(101));
}
@@ -156,7 +156,7 @@ public sealed class GatewayBoundingValueTests
var calls = fake.ReadRawCallCount;
var beyond = Now.AddDays(3);
var missing = await HistoryBounds.EndBoundAsync(source, "T", HistoryBounds.For(At(0), beyond), At(100), 65536, Now, Ct);
missing.StatusCode.ShouldBe(Opc.Ua.StatusCodes.BadBoundNotFound);
missing.StatusCode.ShouldBe(Opc.Ua.StatusCodes.BadBoundNotFound.Code);
missing.SourceTimestampUtc.ShouldBe(beyond);
fake.ReadRawCallCount.ShouldBe(calls, "a bound beyond the horizon is not found without a read");
source.GetHealthSnapshot().TotalFailures.ShouldBe(0);
@@ -106,7 +106,7 @@ public sealed class GatewayCeilingPagingTests : IDisposable
try
{
var first = ReadRaw(nm, nodeId, At(0), At(3600), 5000, null);
first.Error.StatusCode.Code.ShouldBe(StatusCodes.Good);
first.Error.StatusCode.ShouldBe(StatusCodes.Good);
first.Values.Count.ShouldBe(FirstPage);
first.Cp.ShouldNotBeNull();
// The budget the client asked for (+ lookahead + allowance, refused naming 1000), the ceiling re-read that cannot
@@ -114,7 +114,7 @@ public sealed class GatewayCeilingPagingTests : IDisposable
fake.RawCalls.Select(c => c.MaxValues).ShouldBe([5000 + 1 + RawAggregateReader.PreWindowAllowance, Ceiling, Ceiling]);
var (rest, _, status) = DrainFrom(nm, nodeId, At(0), At(3600), 5000, first.Cp);
status.Code.ShouldBe(StatusCodes.Good);
status.ShouldBe(StatusCodes.Good);
first.Values.Concat(rest).ShouldBe(PerSecond(3600).Select(s => s.Item2));
Refusals(fake).ShouldBe(1);
}
@@ -140,7 +140,7 @@ public sealed class GatewayCeilingPagingTests : IDisposable
? DrainFrom(nm, nodeId, At(3600), At(-1), n, null)
: DrainFrom(nm, nodeId, At(0), At(3600), n, null);
status.Code.ShouldBe(StatusCodes.Good);
status.ShouldBe(StatusCodes.Good);
var expected = stored.Select(s => s.Item2);
values.ShouldBe(reverse ? expected.Reverse() : expected);
pages[0].ShouldBe(FirstPage);
@@ -217,10 +217,10 @@ public sealed class GatewayCeilingPagingTests : IDisposable
var (values, pages, status) = DrainFrom(nm, nodeId, At(0), At(5000), n, null, maxPages: 60);
var expected = stored.Select(s => s.Item2).ToList();
if (status.Code == StatusCodes.Good) values.ShouldBe(expected);
if (status == StatusCodes.Good) values.ShouldBe(expected);
else
{
status.Code.ShouldBe(StatusCodes.BadTooManyOperations);
status.ShouldBe(StatusCodes.BadTooManyOperations);
values.ShouldBe(expected.Take(values.Count)); // an exact prefix
size.ShouldBeGreaterThan(L - 2, "a cluster that fits the over-fetch is always served");
}
@@ -254,7 +254,7 @@ public sealed class GatewayCeilingPagingTests : IDisposable
ds.MaxRawValuesPerRead.ShouldBe(0);
var (rest, pages, status) = DrainFrom(nm, nodeId, At(0), At(5000), 0, first.Cp, maxPages: 20);
status.Code.ShouldBe(StatusCodes.Good);
status.ShouldBe(StatusCodes.Good);
first.Values.Concat(rest).ShouldBe(stored.Select(s => s.Item2));
pages.Count.ShouldBeLessThan(20);
}
@@ -278,7 +278,7 @@ public sealed class GatewayCeilingPagingTests : IDisposable
fake.MaxValuesCeiling = 500; // the operator lowered it
fake.RawCalls.Clear();
var (rest, pages, status) = DrainFrom(nm, nodeId, At(0), At(3600), 5000, p2.Cp, maxPages: 20);
status.Code.ShouldBe(StatusCodes.Good);
status.ShouldBe(StatusCodes.Good);
got.AddRange(rest);
got.ShouldBe(stored.Select(s => s.Item2));
pages.ShouldAllBe(p => p <= 500 - RawAggregateReader.PreWindowAllowance);
@@ -302,7 +302,7 @@ public sealed class GatewayCeilingPagingTests : IDisposable
var start = At(10).AddMilliseconds(500);
var end = At(3000).AddMilliseconds(500);
var (values, pages, status) = DrainFrom(nm, nodeId, start, end, 5000, null, returnBounds: true);
status.Code.ShouldBe(StatusCodes.Good);
status.ShouldBe(StatusCodes.Good);
values.ShouldBe(Enumerable.Range(10, 2992).Select(i => (double)i));
pages.ShouldAllBe(p => p <= L);
Refusals(fake).ShouldBe(0);
@@ -325,7 +325,7 @@ public sealed class GatewayCeilingPagingTests : IDisposable
try
{
var r = ReadRaw(nm, nodeId, DateTime.MinValue, T0.AddMilliseconds(200_000), n, null);
r.Error.StatusCode.Code.ShouldBe(StatusCodes.Good);
r.Error.StatusCode.ShouldBe(StatusCodes.Good);
r.Cp.ShouldNotBeNull();
var page = ceiling - RawAggregateReader.PreWindowAllowance - 1;
r.Values.ShouldBe(Enumerable.Range(200_000 - r.Values.Count, r.Values.Count).Reverse().Select(i => (double)i));
@@ -360,7 +360,7 @@ public sealed class GatewayCeilingPagingTests : IDisposable
try
{
var (values, pages, status) = DrainFrom(nm, nodeId, DateTime.UnixEpoch, DateTime.UnixEpoch.AddDays(1), 0, null);
status.Code.ShouldBe(StatusCodes.Good);
status.ShouldBe(StatusCodes.Good);
values.ShouldBe(stored.Select(s => s.Item2));
pages.Count.ShouldBeGreaterThan(1);
}
@@ -377,7 +377,7 @@ public sealed class GatewayCeilingPagingTests : IDisposable
{
ReadRaw(nm, nodeId, DateTime.UnixEpoch, DateTime.UnixEpoch.AddSeconds(5), 5000, null); // learns the ceiling
var r = ReadRaw(nm, nodeId, DateTime.MinValue, DateTime.UnixEpoch.AddDays(30), 10, null);
r.Error.StatusCode.Code.ShouldBe(StatusCodes.Good);
r.Error.StatusCode.ShouldBe(StatusCodes.Good);
r.Values.ShouldBe(Enumerable.Range(2991, 10).Reverse().Select(i => (double)i)); // the LATEST ten, newest first
}
finally { await host.DisposeAsync(); }
@@ -425,7 +425,7 @@ public sealed class GatewayCeilingPagingTests : IDisposable
fake.RawCalls.Clear();
var (values, _, status) = DrainFrom(nm, nodeId, At(0), x.AddDays(1), n, null);
status.Code.ShouldBe(StatusCodes.Good);
status.ShouldBe(StatusCodes.Good);
values.ShouldBe(stored.Select(s => s.Item2));
Refusals(fake).ShouldBe(0);
EveryRequestFits(fake);
@@ -476,7 +476,7 @@ public sealed class GatewayCeilingPagingTests : IDisposable
try
{
var r = ReadRaw(nm, nodeId, At(0), At(10), 10, null);
r.Error.StatusCode.Code.ShouldBe(StatusCodes.BadNoCommunication);
r.Error.StatusCode.ShouldBe(StatusCodes.BadNoCommunication);
}
finally { await host.DisposeAsync(); }
}
@@ -528,7 +528,7 @@ public sealed class GatewayCeilingPagingTests : IDisposable
private static (IReadOnlyList<double> Values, ServiceResult Error, byte[]? Cp) ReadRaw(
OtOpcUaNodeManager nm, NodeId nodeId, DateTime start, DateTime end, uint max, byte[]? inboundCp, bool returnBounds = false)
{
var context = new OperationContext(new RequestHeader(), secureChannelContext: null, RequestType.HistoryRead, identity: null);
var context = new OperationContext(new RequestHeader(), secureChannelContext: null, RequestType.HistoryRead, requestLifetime: RequestLifetime.None, identity: null);
var details = new ReadRawModifiedDetails
{
ReturnBounds = returnBounds,
@@ -537,16 +537,19 @@ public sealed class GatewayCeilingPagingTests : IDisposable
EndTime = end,
NumValuesPerNode = max,
};
var nodesToRead = new List<HistoryReadValueId> { new() { NodeId = nodeId, ContinuationPoint = inboundCp } };
var nodesToRead = new List<HistoryReadValueId> { new() { NodeId = nodeId, ContinuationPoint = inboundCp is null ? default : ByteString.From(inboundCp) } };
var results = new List<SdkHistoryReadResult> { null! };
var errors = new List<ServiceResult> { null! };
nm.HistoryRead(context, details, TimestampsToReturn.Both, releaseContinuationPoints: false, nodesToRead, results, errors);
var values = new List<double>();
if (results[0]?.HistoryData is { } ho && !ExtensionObject.IsNull(ho))
foreach (var dv in ((HistoryData)ExtensionObject.ToEncodeable(ho)).DataValues)
values.Add(Convert.ToDouble(dv.Value));
return (values, errors[0], results[0]?.ContinuationPoint);
if (results[0]?.HistoryData is { IsNull: false } historyData)
{
var data = (HistoryData)ExtensionObject.ToEncodeable(historyData)!;
foreach (var dv in data.DataValues)
values.Add(Convert.ToDouble(SdkValueBoundary.ToClr(dv.WrappedValue)));
}
return (values, errors[0], results[0]?.ContinuationPoint is { IsNull: false } cp ? cp.ToArray() : null);
}
public void Dispose()
@@ -205,7 +205,7 @@ public sealed class GatewayRawCapTests : IDisposable
? DrainFrom(nm, nodeId, At(3600), At(-1), n, null)
: DrainFrom(nm, nodeId, At(0), At(3600), n, null);
status.Code.ShouldBe(StatusCodes.Good);
status.ShouldBe(StatusCodes.Good);
var expected = Enumerable.Range(0, 3600).Select(i => (double)i);
values.ShouldBe(reverse ? expected.Reverse() : expected);
pages.Count.ShouldBeGreaterThan(1, "a year-sized window at N = 0 is paged, not one shot");
@@ -224,7 +224,7 @@ public sealed class GatewayRawCapTests : IDisposable
try
{
var (values, pages, status) = DrainFrom(nm, nodeId, At(10).AddMilliseconds(500), At(3000).AddMilliseconds(500), 0, null, returnBounds: true);
status.Code.ShouldBe(StatusCodes.Good);
status.ShouldBe(StatusCodes.Good);
values.ShouldBe(Enumerable.Range(10, 2992).Select(i => (double)i)); // start bound +10, data +11..+3000, end bound +3001
pages.ShouldAllBe(p => p <= Cap);
fake.RawCalls.ShouldAllBe(c => c.MaxValues > 0 && c.MaxValues <= Cap + RawAggregateReader.PreWindowAllowance);
@@ -277,21 +277,24 @@ public sealed class GatewayRawCapTests : IDisposable
private static (IReadOnlyList<double> Values, ServiceResult Error, byte[]? Cp) ReadRaw(
OtOpcUaNodeManager nm, NodeId nodeId, DateTime start, DateTime end, uint max, byte[]? inboundCp, bool returnBounds = false)
{
var context = new OperationContext(new RequestHeader(), secureChannelContext: null, RequestType.HistoryRead, identity: null);
var context = new OperationContext(new RequestHeader(), secureChannelContext: null, RequestType.HistoryRead, requestLifetime: RequestLifetime.None, identity: null);
var details = new ReadRawModifiedDetails
{
ReturnBounds = returnBounds, IsReadModified = false, StartTime = start, EndTime = end, NumValuesPerNode = max,
};
var nodesToRead = new List<HistoryReadValueId> { new() { NodeId = nodeId, ContinuationPoint = inboundCp } };
var nodesToRead = new List<HistoryReadValueId> { new() { NodeId = nodeId, ContinuationPoint = inboundCp is null ? default : ByteString.From(inboundCp) } };
var results = new List<SdkHistoryReadResult> { null! };
var errors = new List<ServiceResult> { null! };
nm.HistoryRead(context, details, TimestampsToReturn.Both, releaseContinuationPoints: false, nodesToRead, results, errors);
var values = new List<double>();
if (results[0]?.HistoryData is { } ho && !ExtensionObject.IsNull(ho))
foreach (var dv in ((HistoryData)ExtensionObject.ToEncodeable(ho)).DataValues)
values.Add(Convert.ToDouble(dv.Value));
return (values, errors[0], results[0]?.ContinuationPoint);
if (results[0]?.HistoryData is { IsNull: false } historyData)
{
var data = (HistoryData)ExtensionObject.ToEncodeable(historyData)!;
foreach (var dv in data.DataValues)
values.Add(Convert.ToDouble(SdkValueBoundary.ToClr(dv.WrappedValue)));
}
return (values, errors[0], results[0]?.ContinuationPoint is { IsNull: false } cp ? cp.ToArray() : null);
}
public void Dispose()
@@ -23,6 +23,8 @@
</ItemGroup>
<ItemGroup>
<!-- Test the same preserved aggregate algorithms registered by the application server. -->
<ProjectReference Include="..\..\..\src\Server\ZB.MOM.WW.OtOpcUa.OpcUaServer\ZB.MOM.WW.OtOpcUa.OpcUaServer.csproj"/>
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb\ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb.csproj"/>
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.Historian.Sql.Common\ZB.MOM.WW.OtOpcUa.Driver.Historian.Sql.Common.csproj"/>
</ItemGroup>
@@ -9,7 +9,7 @@ public sealed class StatusCodeOracleTests
[Fact]
public void Calculated_bits_match_the_SDK()
{
var sdk = new Opc.Ua.StatusCode(Opc.Ua.StatusCodes.Good).SetAggregateBits(Opc.Ua.AggregateBits.Calculated);
var sdk = Opc.Ua.StatusCodes.Good.WithAggregateBits(Opc.Ua.AggregateBits.Calculated);
sdk.Code.ShouldBe(HistorianInfoBits.WithCalculated(HistorianStatusCodes.Good));
((uint)Opc.Ua.AggregateBits.Calculated).ShouldBe(HistorianInfoBits.HistorianCalculated);
}
@@ -17,7 +17,7 @@ public sealed class StatusCodeOracleTests
[Fact]
public void Interpolated_bits_match_the_SDK()
{
var sdk = new Opc.Ua.StatusCode(Opc.Ua.StatusCodes.Good).SetAggregateBits(Opc.Ua.AggregateBits.Interpolated);
var sdk = Opc.Ua.StatusCodes.Good.WithAggregateBits(Opc.Ua.AggregateBits.Interpolated);
sdk.Code.ShouldBe(HistorianInfoBits.WithInterpolated(HistorianStatusCodes.Good));
((uint)Opc.Ua.AggregateBits.Interpolated).ShouldBe(HistorianInfoBits.HistorianInterpolated);
}
@@ -25,7 +25,7 @@ public sealed class StatusCodeOracleTests
/// <summary>
/// A stored status that already carries a historian bit (a sample written via HistoryUpdate with the Calculated
/// bit, say) must come back with ONE historian bit, never both: the existing pair is masked off first. The SDK
/// agrees — <c>SetAggregateBits</c> replaces, it does not OR.
/// agrees — <c>WithAggregateBits</c> replaces, it does not OR.
/// </summary>
[Fact]
public void Setting_one_historian_bit_clears_the_other()
@@ -45,7 +45,7 @@ public sealed class StatusCodeOracleTests
[Fact]
public void Generic_bad_matches_the_SDK() =>
HistorianStatusCodes.Bad.ShouldBe(Opc.Ua.StatusCodes.Bad);
HistorianStatusCodes.Bad.ShouldBe(Opc.Ua.StatusCodes.Bad.Code);
[Fact]
public void Info_type_is_data_value() =>
@@ -28,6 +28,8 @@
</ItemGroup>
<ItemGroup>
<!-- Test the same preserved aggregate algorithms registered by the application server. -->
<ProjectReference Include="..\..\..\src\Server\ZB.MOM.WW.OtOpcUa.OpcUaServer\ZB.MOM.WW.OtOpcUa.OpcUaServer.csproj"/>
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.Historian.Sql.Common\ZB.MOM.WW.OtOpcUa.Driver.Historian.Sql.Common.csproj"/>
</ItemGroup>
@@ -22,6 +22,8 @@
</ItemGroup>
<ItemGroup>
<!-- Test the same preserved aggregate algorithms registered by the application server. -->
<ProjectReference Include="..\..\..\src\Server\ZB.MOM.WW.OtOpcUa.OpcUaServer\ZB.MOM.WW.OtOpcUa.OpcUaServer.csproj"/>
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.Historian.SqlServer\ZB.MOM.WW.OtOpcUa.Driver.Historian.SqlServer.csproj"/>
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.Historian.Sql.Common\ZB.MOM.WW.OtOpcUa.Driver.Historian.Sql.Common.csproj"/>
</ItemGroup>
@@ -137,6 +137,7 @@ public sealed class AclServerObjectModelChangeTests
rig.EnsureEquipment();
rig.Sink.EnsureFolder(RawDeviceFolder, parentNodeId: null, displayName: "dev1", realm: AddressSpaceRealm.Raw);
rig.Sink.EnsureVariable(RawVariable, RawDeviceFolder, "Speed", "Int32", writable: true, AddressSpaceRealm.Raw);
rig.Sink.WriteValue(RawVariable, 7, OpcUaStatus.Good, DateTime.UtcNow, AddressSpaceRealm.Raw);
rig.Nm.NodeWriteGateway = new RejectingGateway();
var (granted, grantedEvents, grantedItem, grantedSubscription) =
@@ -145,6 +146,15 @@ public sealed class AclServerObjectModelChangeTests
await ServerObjectAsync(rig, anonymous: true, ct, encrypted: true);
var variable = new NodeId(RawVariable, (ushort)granted.NamespaceUris.GetIndex(V3NodeIds.RawNamespaceUri));
var serverSession = rig.Server.CurrentInstance.SessionManager.GetSessions().Single(s =>
s.Identity is ZB.MOM.WW.OtOpcUa.OpcUaServer.Security.RoleCarryingUserIdentity { ApiKeyId: KeyId });
using var permissionContext = new Opc.Ua.Server.OperationContext(new RequestHeader(), null!,
Opc.Ua.Server.RequestType.Read, RequestLifetime.None, serverSession);
var typePermission = rig.Nm.ValidateRolePermissions(permissionContext, ObjectTypeIds.AuditWriteUpdateEventType, PermissionType.ReceiveEvents);
var sourcePermission = rig.Nm.ValidateRolePermissions(permissionContext, variable, PermissionType.ReceiveEvents);
typePermission.StatusCode.ShouldBe(StatusCodes.BadUserAccessDenied,
"ordinary SDK audit emission must stay denied; only the scoped application path may grant receive permission");
ServiceResult.IsGood(sourcePermission).ShouldBeTrue($"Audit source permission: {sourcePermission}");
var write = await granted.WriteAsync(
null,
[new WriteValue { NodeId = variable, AttributeId = Attributes.Value, Value = new DataValue(new Variant(42)) }],
@@ -156,8 +166,11 @@ public sealed class AclServerObjectModelChangeTests
// Reported after the audit event through the same delivery, so it queues behind it on the item.
rig.Sink.RaiseNodesAddedModelChange(EqA, AddressSpaceRealm.Uns);
await RecordedEvents.WaitUntilAsync(() => narrowEvents.Of(narrowItem).Any(NamesEqA));
await RecordedEvents.WaitUntilAsync(() => narrowEvents.Of(narrowItem).Any(NamesEqA)
&& grantedEvents.Of(grantedItem).Any(NamesEqA));
grantedEvents.Of(grantedItem).Count(f => f[RecordedEvents.EventTypeField].GetNodeId() == ObjectTypeIds.AuditWriteUpdateEventType)
.ShouldBe(1, "one application audit is delivered without an automatic SDK duplicate");
narrowEvents.HasType(narrowItem, ObjectTypeIds.AuditWriteUpdateEventType).ShouldBeFalse(
"a session that may not read the node must not receive its written values through an audit event");
@@ -144,7 +144,7 @@ public sealed class AdvertisedAggregateFunctionsWireTests
rig.Enforce(false, [Key()]);
var id = rig.EnsureRawVariable("agg", "Double", writable: false);
using var session = await rig.ConnectAsync(anonymous: true, ct);
var aggregateId = (NodeId)typeof(ObjectIds).GetField("AggregateFunction_" + aggregateName)!.GetValue(null)!;
var aggregateId = (NodeId)typeof(ObjectIds).GetProperty("AggregateFunction_" + aggregateName)!.GetValue(null)!;
(await AdvertisedAsync(session, ObjectIds.HistoryServerCapabilities_AggregateFunctions, ct))
.ShouldNotContain(aggregateName);
@@ -164,6 +164,7 @@ public sealed class AdvertisedAggregateFunctionsWireTests
{
AggregateType = aggregateId,
ProcessingInterval = 1000,
AggregateConfiguration = new AggregateConfiguration { UseServerCapabilitiesDefaults = true },
StartTime = DateTime.UtcNow,
},
};
File diff suppressed because one or more lines are too long
@@ -11,9 +11,14 @@ namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests;
/// <summary>Compares all preserved aggregate factories with independently generated SDK 1.5.378.182 vectors.</summary>
public sealed class Sdk2AggregateCompatibilityTests
{
[Fact]
public void All_37_factories_preserve_the_4144_baseline_vectors()
[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task All_37_factories_preserve_the_6144_baseline_vectors(bool throughRuntimeManager)
{
await using var rig = throughRuntimeManager
? await ServerEventRig.StartAsync("AggregateBaseline", encrypted: false, TestContext.Current.CancellationToken)
: null;
var baseline = JsonSerializer.Deserialize<Baseline>(File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "Fixtures", "opcua-1.5.378.182-aggregates.json")), new JsonSerializerOptions { PropertyNameCaseInsensitive = true })!;
var telemetry = DefaultTelemetry.Create(_ => { });
var epoch = new DateTime(baseline.EpochTicks, DateTimeKind.Utc);
@@ -22,22 +27,32 @@ public sealed class Sdk2AggregateCompatibilityTests
{
var id = (NodeId)typeof(ObjectIds).GetProperty(vector.Aggregate)!.GetValue(null)!;
var start = epoch.AddSeconds(vector.Reverse ? 70 : 0); var end = epoch.AddSeconds(vector.Reverse ? 0 : 70);
var config = new AggregateConfiguration { TreatUncertainAsBad = vector.UncertainAsBad, PercentDataBad = 100, PercentDataGood = 100, UseSlopedExtrapolation = false, UseServerCapabilitiesDefaults = false };
var calculator = CompatibilityAggregateFactory.Create(id, start, end, baseline.IntervalMilliseconds, vector.Stepped, config, telemetry);
var output = new List<Value>(); string? error = null;
var profile = vector.Configuration;
var config = new AggregateConfiguration
{
TreatUncertainAsBad = profile?.TreatUncertainAsBad ?? vector.UncertainAsBad,
PercentDataBad = profile?.PercentDataBad ?? 100, PercentDataGood = profile?.PercentDataGood ?? 100,
UseSlopedExtrapolation = profile?.UseSlopedExtrapolation ?? false,
UseServerCapabilitiesDefaults = profile?.UseServerCapabilitiesDefaults ?? false,
};
var calculator = throughRuntimeManager
? rig!.Server.CurrentInstance.AggregateManager.CreateCalculator(id, start, end, baseline.IntervalMilliseconds, vector.Stepped, config)
: CompatibilityAggregateFactory.Create(id, start, end, baseline.IntervalMilliseconds, vector.Stepped, config, telemetry);
calculator.ShouldNotBeNull($"Runtime registration must serve {vector.Aggregate}.");
var output = new List<Value>(); var accepted = new List<bool>(); string? error = null;
try
{
var samples = baseline.Data[vector.DataSet].OrderBy(x => x.Seconds);
foreach (var sample in vector.Reverse ? samples.Reverse() : samples)
{
var time = epoch.AddSeconds(sample.Seconds);
calculator.QueueRawValue(new DataValue(SdkValueBoundary.FromClr(Payload(sample)), sample.Status, time, time));
accepted.Add(calculator.QueueRawValue(new DataValue(SdkValueBoundary.FromClr(Payload(sample)), sample.Status, time, time.AddTicks(sample.ServerOffsetTicks), sample.SourcePicoseconds, sample.ServerPicoseconds)));
if (vector.Incremental) Drain(false);
}
Drain(true);
}
catch (Exception ex) { error = ex.GetType().FullName; }
if (error != vector.Exception || !output.SequenceEqual(vector.Output))
if (error != vector.Exception || !output.SequenceEqual(vector.Output) || (vector.QueueAccepted is { } expectedAccepted && !accepted.SequenceEqual(expectedAccepted)))
{
failures.Add($"{vector.Aggregate}/{vector.DataSet}/rev={vector.Reverse}/step={vector.Stepped}/uncertainBad={vector.UncertainAsBad}/incremental={vector.Incremental}: error {vector.Exception} vs {error}; expected {JsonSerializer.Serialize(vector.Output)} actual {JsonSerializer.Serialize(output)}");
}
@@ -47,20 +62,31 @@ public sealed class Sdk2AggregateCompatibilityTests
{
if (!calculator.TryGetProcessedValue(partial, out var value)) return;
var payload = SdkValueBoundary.ToClr(value.WrappedValue);
output.Add(new(partial, payload?.GetType().Name, Format(payload), value.StatusCode.Code, ((DateTime)value.SourceTimestamp).Ticks, ((DateTime)value.ServerTimestamp).Ticks, value.SourcePicoseconds, value.ServerPicoseconds));
output.Add(new(partial, payload?.GetType().Name, Format(payload), value.StatusCode.Code, LegacyTimestampTicks(value.SourceTimestamp), LegacyTimestampTicks(value.ServerTimestamp), value.SourcePicoseconds, value.ServerPicoseconds));
}
throw new InvalidOperationException("Baseline calculator did not terminate.");
}
}
baseline.Vectors.Length.ShouldBe(4144);
baseline.Vectors.Length.ShouldBe(6144);
baseline.Vectors.Select(x => x.Aggregate).Distinct().Count().ShouldBe(37);
failures.ShouldBeEmpty("The application must retain baseline values, CLR types, complete status bits, timestamps and incremental processing.");
}
static object? Payload(Sample sample) => sample.Type switch { "Double" => double.Parse(sample.Payload!, CultureInfo.InvariantCulture), "Int64" => long.Parse(sample.Payload!, CultureInfo.InvariantCulture), "Boolean" => bool.Parse(sample.Payload!), _ => null };
// UA FILETIME zero is the old CLR unset sentinel, not a different historical instant.
private static long LegacyTimestampTicks(DateTimeUtc time) => time.IsNull ? DateTime.MinValue.Ticks : ((DateTime)time).Ticks;
static object? Payload(Sample sample) => sample.Type switch { "SByte" => sbyte.Parse(sample.Payload!, CultureInfo.InvariantCulture),
"Byte" => byte.Parse(sample.Payload!, CultureInfo.InvariantCulture),
"Int16" => short.Parse(sample.Payload!, CultureInfo.InvariantCulture),
"UInt16" => ushort.Parse(sample.Payload!, CultureInfo.InvariantCulture),
"Int32" => int.Parse(sample.Payload!, CultureInfo.InvariantCulture),
"UInt32" => uint.Parse(sample.Payload!, CultureInfo.InvariantCulture),
"UInt64" => ulong.Parse(sample.Payload!, CultureInfo.InvariantCulture),
"Single" => float.Parse(sample.Payload!, CultureInfo.InvariantCulture),
"Double" => double.Parse(sample.Payload!, CultureInfo.InvariantCulture), "Int64" => long.Parse(sample.Payload!, CultureInfo.InvariantCulture), "Boolean" => bool.Parse(sample.Payload!), _ => null };
static string? Format(object? value) => value switch { null => null, double number => number.ToString("R", CultureInfo.InvariantCulture), float number => number.ToString("R", CultureInfo.InvariantCulture), IFormattable f => f.ToString(null, CultureInfo.InvariantCulture), _ => value.ToString() };
record Baseline(long EpochTicks, int IntervalMilliseconds, Dictionary<string,Sample[]> Data, Vector[] Vectors);
record Sample(double Seconds,string Type,string? Payload,uint Status);
record Sample(double Seconds,string Type,string? Payload,uint Status,ushort SourcePicoseconds = 0,ushort ServerPicoseconds = 0,long ServerOffsetTicks = 0);
record Value(bool PartialRequested,string? Type,string? Payload,uint Status,long SourceTicks,long ServerTicks,ushort SourcePicoseconds,ushort ServerPicoseconds);
record Vector(string Aggregate,string DataSet,bool Reverse,bool Stepped,bool UncertainAsBad,bool Incremental,Value[] Output,string? Exception);
record Vector(string Aggregate,string DataSet,bool Reverse,bool Stepped,bool UncertainAsBad,bool Incremental,Value[] Output,string? Exception, Configuration? Configuration = null,bool[]? QueueAccepted = null);
record Configuration(bool TreatUncertainAsBad,byte PercentDataBad,byte PercentDataGood,bool UseSlopedExtrapolation,bool UseServerCapabilitiesDefaults);
}
@@ -0,0 +1,71 @@
using Opc.Ua;
using Opc.Ua.Client;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests;
/// <summary>Preserves baseline alarm discovery through hierarchical folder browses in both application realms.</summary>
public sealed class Sdk2AlarmBrowseCompatibilityTests
{
[Theory]
[InlineData("OffNormalAlarm", AddressSpaceRealm.Raw)]
[InlineData("DiscreteAlarm", AddressSpaceRealm.Raw)]
[InlineData("AlarmCondition", AddressSpaceRealm.Raw)]
[InlineData("OffNormalAlarm", AddressSpaceRealm.Uns)]
[InlineData("DiscreteAlarm", AddressSpaceRealm.Uns)]
[InlineData("AlarmCondition", AddressSpaceRealm.Uns)]
public async Task Alarm_is_a_hierarchical_component_with_the_same_inverse_parent_and_object_type(
string type, AddressSpaceRealm realm)
{
var ct = TestContext.Current.CancellationToken;
await using var rig = await ServerEventRig.StartAsync("AlarmBrowseCompatibility", false, ct);
rig.Enforce(false, [ServerEventRig.Key()]);
rig.Sink.EnsureFolder("browse-parent", null, "Parent", realm);
rig.Sink.MaterialiseAlarmCondition("browse-alarm", "browse-parent", "Alarm", type, 500, realm,
isNative: realm == AddressSpaceRealm.Raw);
using var session = await rig.ConnectAsync(true, ct);
var uri = realm == AddressSpaceRealm.Raw ? V3NodeIds.RawNamespaceUri : V3NodeIds.UnsNamespaceUri;
var ns = session.NamespaceUris.GetIndex(uri);
ns.ShouldBeGreaterThan(0);
var parent = new NodeId("browse-parent", (ushort)ns);
var alarm = new NodeId("browse-alarm", (ushort)ns);
var expectedType = type switch
{
"OffNormalAlarm" => ObjectTypeIds.OffNormalAlarmType,
"DiscreteAlarm" => ObjectTypeIds.DiscreteAlarmType,
_ => ObjectTypeIds.AlarmConditionType,
};
var children = await BrowseAsync(session, parent, ReferenceTypeIds.HierarchicalReferences, BrowseDirection.Forward, true, ct);
var discovered = children.SingleOrDefault(reference => ExpandedNodeId.ToNodeId(reference.NodeId, session.NamespaceUris) == alarm);
discovered.ShouldNotBeNull("the baseline folder browse exposes every materialised alarm object");
discovered.ReferenceTypeId.ShouldBe(ReferenceTypeIds.HasComponent);
discovered.IsForward.ShouldBeTrue();
discovered.NodeClass.ShouldBe(NodeClass.Object);
ExpandedNodeId.ToNodeId(discovered.TypeDefinition, session.NamespaceUris).ShouldBe(expectedType);
var components = await BrowseAsync(session, parent, ReferenceTypeIds.HasComponent, BrowseDirection.Forward, false, ct);
components.ShouldContain(reference => ExpandedNodeId.ToNodeId(reference.NodeId, session.NamespaceUris) == alarm);
var inverse = await BrowseAsync(session, alarm, ReferenceTypeIds.HasComponent, BrowseDirection.Inverse, false, ct);
inverse.ShouldContain(reference => !reference.IsForward
&& ExpandedNodeId.ToNodeId(reference.NodeId, session.NamespaceUris) == parent);
var types = await BrowseAsync(session, alarm, ReferenceTypeIds.HasTypeDefinition, BrowseDirection.Forward, false, ct);
types.ShouldContain(reference => ExpandedNodeId.ToNodeId(reference.NodeId, session.NamespaceUris) == expectedType);
await session.CloseAsync(ct);
}
private static async Task<IReadOnlyList<ReferenceDescription>> BrowseAsync(ISession session, NodeId node,
NodeId reference, BrowseDirection direction, bool subtypes, CancellationToken ct)
{
var result = (await session.BrowseAsync(null, null, 0, [new BrowseDescription
{
NodeId = node, ReferenceTypeId = reference, BrowseDirection = direction, IncludeSubtypes = subtypes,
ResultMask = (uint)BrowseResultMask.All,
}], ct)).Results[0];
result.StatusCode.ShouldBe(StatusCodes.Good);
result.ContinuationPoint.IsEmpty.ShouldBeTrue();
return result.References.ToList();
}
}
@@ -0,0 +1,119 @@
using System.Security.Cryptography.X509Certificates;
using Opc.Ua;
using Opc.Ua.Configuration;
using Opc.Ua.Security.Certificates;
using Shouldly;
using Xunit;
namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests;
/// <summary>Exercises the target certificate manager with real issuer stores and signed certificates.</summary>
public sealed class Sdk2CertificateValidationTests
{
[Theory]
[InlineData("valid")]
[InlineData("expired")]
[InlineData("revoked")]
[InlineData("missing-issuer")]
[InlineData("bad-issuer-usage")]
public async Task Trust_and_certificate_failures_keep_the_configured_rejection_policy(string scenario)
{
var ct = TestContext.Current.CancellationToken;
await using var pki = await ValidationPki.CreateAsync(scenario, ct);
var result = await pki.Configuration.CertificateManager.ValidateAsync(pki.Leaf, ct: ct);
if (scenario == "valid") result.IsValid.ShouldBeTrue(result.ToString());
else
{
result.IsValid.ShouldBeFalse();
var expected = scenario switch
{
"expired" => StatusCodes.BadCertificateTimeInvalid,
"revoked" => StatusCodes.BadCertificateRevoked,
"missing-issuer" => StatusCodes.BadCertificateChainIncomplete,
_ => StatusCodes.BadCertificateIssuerUseNotAllowed,
};
(result.StatusCode == expected || result.Errors.Any(e => e.StatusCode == expected)).ShouldBeTrue(
$"Expected {expected}; actual {result.StatusCode}: {string.Join(", ", result.Errors)}");
}
pki.Configuration.SecurityConfiguration.AutoAcceptUntrustedCertificates.ShouldBeFalse();
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task A_trusted_chain_does_not_bypass_endpoint_identity_checks(bool wrongUri)
{
await using var pki = await ValidationPki.CreateAsync("valid", TestContext.Current.CancellationToken);
(await pki.Configuration.CertificateManager.ValidateAsync(pki.Leaf, ct: TestContext.Current.CancellationToken))
.IsValid.ShouldBeTrue();
var endpoint = new ConfiguredEndpoint(null, new EndpointDescription
{
EndpointUrl = wrongUri ? "opc.tcp://localhost:4840" : "opc.tcp://other.invalid:4840",
Server = new ApplicationDescription { ApplicationUri = wrongUri ? "urn:wrong-application" : ValidationPki.PeerUri },
}, EndpointConfiguration.Create(pki.Configuration));
var error = Should.Throw<ServiceResultException>(() =>
{
if (wrongUri) pki.Configuration.CertificateManager.ValidateApplicationUri(pki.Leaf, endpoint);
else pki.Configuration.CertificateManager.ValidateDomains(pki.Leaf, endpoint);
});
error.StatusCode.ShouldBe(wrongUri ? StatusCodes.BadCertificateUriInvalid : StatusCodes.BadCertificateHostNameInvalid);
}
private sealed class ValidationPki : IAsyncDisposable
{
internal const string PeerUri = "urn:Sdk2ValidationPeer";
private readonly string _root = Path.Combine(Path.GetTempPath(), $"otua-validation-{Guid.NewGuid():N}");
private ApplicationInstance? _application;
private Certificate? _issuer;
internal Certificate Leaf { get; private set; } = null!;
internal ApplicationConfiguration Configuration { get; private set; } = null!;
internal static async Task<ValidationPki> CreateAsync(string scenario, CancellationToken ct)
{
var pki = new ValidationPki();
try
{
var now = DateTime.UtcNow;
var issuerBuilder = CertificateBuilder.Create("CN=Sdk2ValidationIssuer")
.SetNotBefore(now.AddDays(-10)).SetNotAfter(now.AddDays(365)).SetCAConstraint(-1);
if (scenario == "bad-issuer-usage")
issuerBuilder.AddExtension(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, true));
pki._issuer = issuerBuilder.SetRSAKeySize(2048).CreateForRSA();
var names = new SubjectAlternativeNameBuilder();
names.AddUri(new Uri(PeerUri)); names.AddDnsName("localhost");
pki.Leaf = CertificateBuilder.Create("CN=Sdk2ValidationPeer")
.SetNotBefore(now.AddDays(-2)).SetNotAfter(scenario == "expired" ? now.AddDays(-1) : now.AddDays(30))
.AddExtension(names.Build()).SetIssuer(pki._issuer).SetRSAKeySize(2048).CreateForRSA();
if (scenario != "missing-issuer")
{
var certs = Path.Combine(pki._root, "trusted", "certs"); Directory.CreateDirectory(certs);
await File.WriteAllBytesAsync(Path.Combine(certs, "issuer.der"), pki._issuer.RawData, ct);
if (scenario == "revoked")
{
var crls = Path.Combine(pki._root, "trusted", "crl"); Directory.CreateDirectory(crls);
var crl = new X509CRL(CrlBuilder.Create(pki._issuer.SubjectName).AddRevokedCertificate(pki.Leaf).CreateForRSA(pki._issuer));
await File.WriteAllBytesAsync(Path.Combine(crls, "issuer.crl"), crl.RawData, ct);
}
}
var telemetry = DefaultTelemetry.Create(_ => { });
var security = TestClientSecurity.Build(pki._root); security.AutoAcceptUntrustedCertificates = false;
pki.Configuration = new ApplicationConfiguration(telemetry)
{
ApplicationName = "Sdk2Validation", ApplicationUri = "urn:Sdk2Validation", ApplicationType = ApplicationType.Client,
SecurityConfiguration = security, ClientConfiguration = new ClientConfiguration(),
};
pki._application = new ApplicationInstance(telemetry) { ApplicationConfiguration = pki.Configuration };
await pki.Configuration.ValidateAsync(ApplicationType.Client, ct);
return pki;
}
catch { await pki.DisposeAsync(); throw; }
}
public async ValueTask DisposeAsync()
{
if (_application is not null) await _application.DisposeAsync();
Leaf?.Dispose(); _issuer?.Dispose();
if (Directory.Exists(_root)) Directory.Delete(_root, true);
}
}
}
@@ -0,0 +1,156 @@
using Opc.Ua;
using Opc.Ua.Client;
using Microsoft.Extensions.Logging.Abstractions;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Core.Authorization;
using ZB.MOM.WW.OtOpcUa.OpcUaServer.Security;
using static ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests.ServerEventRig;
using CoreReadResult = ZB.MOM.WW.OtOpcUa.Core.Abstractions.HistoryReadResult;
namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests;
/// <summary>Uses activated sessions and the production continuation store; denied resumes never reach a backend.</summary>
public sealed class Sdk2HistoryAuthorityWireTests
{
private const string ValuePath = RawDeviceFolder + "/History";
private static readonly DateTime Epoch = new(2026, 10, 1, 0, 0, 0, DateTimeKind.Utc);
public static TheoryData<string, string> Cases => new(
from kind in new[] { "raw", "modified", "events" }
from change in new[] { "alias", "replace", "rebuild", "grant", "key", "expiry" }
select (kind, change));
[Theory]
[MemberData(nameof(Cases))]
public async Task A_saved_cursor_requires_its_live_node_and_current_authority(string kind, string change)
{
var ct = TestContext.Current.CancellationToken;
await using var rig = await StartAsync("HistoryAuthority", encrypted: true, ct);
var source = new CountingSource();
var clock = new Clock();
var key = Key();
key.ExpiresUtc = clock.GetUtcNow().UtcDateTime.AddMinutes(10);
var grant = ClusterGrant(KeyPrincipal, "history-allow", NodePermissions.ReadOnly);
var state = rig.Enforce(true, [key], grant);
rig.Nm.Authorizer = new DataPlaneAuthorizer(state, new TriePermissionEvaluator(state.Tries), true,
NullLogger.Instance, clock);
rig.Nm.HistorianDataSource = source;
Materialise(rig);
using var session = await rig.ConnectAsync(anonymous: false, ct, encrypted: true);
var first = await PageAsync(session, kind, alias: false, default, ct);
first.StatusCode.ShouldBe(StatusCodes.Good);
first.ContinuationPoint.IsEmpty.ShouldBeFalse("the production session store must own a real next page");
source.Calls.ShouldBeGreaterThan(0);
switch (change)
{
case "replace":
if (kind == "events") rig.Nm.RemoveEquipmentSubtree(RawDeviceFolder, AddressSpaceRealm.Raw).ShouldBeTrue();
else rig.Nm.RemoveVariableNode(ValuePath, AddressSpaceRealm.Raw).ShouldBeTrue();
Materialise(rig);
break;
case "rebuild":
rig.Nm.RebuildAddressSpace(); Materialise(rig); rig.Nm.CompleteAddressSpaceApply();
break;
case "grant": Apply(state, key, []); break;
case "key":
var disabled = Key(); disabled.Enabled = false; Apply(state, disabled, [grant]); break;
case "expiry": clock.Advance(TimeSpan.FromMinutes(20)); break;
}
var before = source.Calls;
var denied = await PageAsync(session, kind, change == "alias", first.ContinuationPoint, ct);
denied.StatusCode.ShouldBe(change is "grant" or "key" or "expiry"
? StatusCodes.BadUserAccessDenied : StatusCodes.BadContinuationPointInvalid);
denied.HistoryData.IsNull.ShouldBeTrue("a denied resume must not disclose saved data");
denied.ContinuationPoint.IsEmpty.ShouldBeTrue();
source.Calls.ShouldBe(before, "permission and exact-node binding must be checked before backend dispatch");
// A fresh authorized request remains usable: this is not an always-deny fixture.
clock.Reset(); Apply(state, key, [grant]);
(await PageAsync(session, kind, alias: false, default, ct)).StatusCode.ShouldBe(StatusCodes.Good);
source.Calls.ShouldBeGreaterThan(before);
await session.CloseAsync(ct);
}
private static void Apply(DataPlaneAuthorityState state, ApiKey key, NodeAcl[] grants) =>
state.Apply(ServerEventRig.Cluster, Guid.NewGuid().ToString("N"), [key], grants,
new Dictionary<AclScopeRef, IReadOnlyList<NodeAclPath>>());
private static void Materialise(ServerEventRig rig)
{
rig.MaterialiseNative();
rig.Sink.EnsureVariable(ValuePath, RawDeviceFolder, "History", "Double", false, AddressSpaceRealm.Raw, "same-backend-tag");
rig.Sink.EnsureVariable(ValuePath, EqA, "HistoryAlias", "Double", false, AddressSpaceRealm.Uns, "same-backend-tag");
}
private static async Task<Opc.Ua.HistoryReadResult> PageAsync(ISession session, string kind, bool alias,
ByteString point, CancellationToken ct)
{
var realm = alias ? V3NodeIds.UnsNamespaceUri : V3NodeIds.RawNamespaceUri;
var path = kind == "events" ? alias ? EqA : RawDeviceFolder : ValuePath;
var node = new NodeId(path, (ushort)session.NamespaceUris.GetIndex(realm));
HistoryReadDetails details = kind == "events" ? new ReadEventDetails
{
StartTime = Epoch, EndTime = Epoch.AddMinutes(1), NumValuesPerNode = 2,
Filter = new EventFilter { SelectClauses =
[new SimpleAttributeOperand(ObjectTypeIds.BaseEventType, QualifiedName.From(BrowseNames.EventId))] },
} : new ReadRawModifiedDetails
{ StartTime = Epoch, EndTime = Epoch.AddMinutes(1), NumValuesPerNode = 2, IsReadModified = kind == "modified" };
var response = await session.HistoryReadAsync(null, new ExtensionObject(details), TimestampsToReturn.Both, false,
[new HistoryReadValueId { NodeId = node, ContinuationPoint = point }], ct);
return response.Results.ToList().ShouldHaveSingleItem();
}
private sealed class Clock : TimeProvider
{
private readonly DateTimeOffset _initial = DateTimeOffset.UtcNow;
private TimeSpan _offset;
public override DateTimeOffset GetUtcNow() => _initial + _offset;
public void Advance(TimeSpan amount) => _offset += amount;
public void Reset() => _offset = TimeSpan.Zero;
}
private sealed class CountingSource : IHistorianDataSource
{
private int _calls;
public int Calls => Volatile.Read(ref _calls);
private static DataValueSnapshot Value(int i) => new((double)i, OpcUaStatus.Good, Epoch.AddSeconds(i), Epoch.AddSeconds(i));
public Task<CoreReadResult> ReadRawAsync(string reference, DateTime start, DateTime end, uint max, CancellationToken ct)
{
Interlocked.Increment(ref _calls);
var rows = Enumerable.Range(0, 25).Select(Value).Where(x => x.SourceTimestampUtc >= start && x.SourceTimestampUtc < end).ToList();
var count = max == 0 ? rows.Count : checked((int)max);
return Task.FromResult(new CoreReadResult(rows.Take(count).ToList(), rows.Count > count ? [1] : null));
}
public Task<HistoryModifiedReadResult> ReadModifiedAsync(string reference, DateTime start, DateTime end,
uint max, HistoryModifiedCursor? resume, CancellationToken ct)
{
Interlocked.Increment(ref _calls);
var rows = Enumerable.Range(0, 25).Where(i => Epoch.AddSeconds(i) >= start && Epoch.AddSeconds(i) < end
&& (resume is null || i > resume.ModifiedId))
.Select(i => new HistoryModifiedValue(Value(i), Epoch.AddMinutes(2), HistoryModificationType.Replace,
"fixture", i, Epoch.AddSeconds(i))).ToList();
var count = max == 0 ? rows.Count : checked((int)max);
return Task.FromResult(new HistoryModifiedReadResult(rows.Take(count).ToList(), rows.Count > count));
}
public Task<HistoricalEventsResult> ReadEventsAsync(string? source, DateTime start, DateTime end, int max, CancellationToken ct)
{
Interlocked.Increment(ref _calls);
var rows = Enumerable.Range(0, 25).Select(i => new HistoricalEvent($"e{i:00}", "src", Epoch.AddSeconds(i), Epoch, "m", 500))
.Where(x => x.EventTimeUtc >= start && x.EventTimeUtc < end).ToList();
var count = max <= 0 ? rows.Count : max;
return Task.FromResult(new HistoricalEventsResult(rows.Take(count).ToList(), rows.Count > count ? [1] : null));
}
public Task<CoreReadResult> ReadProcessedAsync(string r, DateTime s, DateTime e, TimeSpan interval, HistoryAggregateType a, CancellationToken ct) =>
NullHistorianDataSource.Instance.ReadProcessedAsync(r, s, e, interval, a, ct);
public Task<CoreReadResult> ReadAtTimeAsync(string r, IReadOnlyList<DateTime> times, CancellationToken ct) =>
NullHistorianDataSource.Instance.ReadAtTimeAsync(r, times, ct);
public HistorianHealthSnapshot GetHealthSnapshot() => NullHistorianDataSource.Instance.GetHealthSnapshot();
public void Dispose() { }
}
}
@@ -0,0 +1,670 @@
using System.Collections;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Globalization;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Text.Json;
using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Configuration;
using Opc.Ua.Server;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Core.Authorization;
using ClientSession = Opc.Ua.Client.ISession;
using Subscription = Opc.Ua.Client.Subscription;
using MonitoredItem = Opc.Ua.Client.MonitoredItem;
using ServerSession = Opc.Ua.Server.ISession;
namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests;
/// <summary>
/// Opt-in Task 12 lifecycle evidence. Diagnostic mode is deliberately separate from the two-hour gate.
/// Run only this test in an otherwise idle test process; RSS/FD counts describe the whole process.
/// No key, certificate, store path, token, or OPC UA payload is written to the JSONL evidence.
/// </summary>
[Trait("Category", "E2E")]
public sealed class Sdk2LifecycleSoakTests
{
private const string Scalar = ServerEventRig.RawDeviceFolder + "/SoakScalar";
private const string ArrayTag = ServerEventRig.RawDeviceFolder + "/SoakArray";
private const string UnsScalar = "SoakEquipment/Scalar";
private static readonly DateTime Epoch = new(2026, 10, 1, 0, 0, 0, DateTimeKind.Utc);
public static bool Enabled => Environment.GetEnvironmentVariable("OTOPCUA_SDK2_SOAK") == "1";
[Fact(SkipUnless = nameof(Enabled), Skip = "Opt in with OTOPCUA_SDK2_SOAK=1 and an explicit diagnostic/full mode.")]
public async Task Repeated_lifecycles_return_resources_to_a_steady_baseline()
{
var mode = Environment.GetEnvironmentVariable("OTOPCUA_SDK2_SOAK_MODE");
(mode is "diagnostic" or "full").ShouldBeTrue("explicit diagnostic or full mode is required");
var seconds = int.Parse(Environment.GetEnvironmentVariable("OTOPCUA_SDK2_SOAK_SECONDS")
?? (mode == "full" ? "7200" : "90"), CultureInfo.InvariantCulture);
if (mode == "full") seconds.ShouldBeGreaterThanOrEqualTo(7200);
else seconds.ShouldBeInRange(60, 120);
var output = Environment.GetEnvironmentVariable("OTOPCUA_SDK2_SOAK_OUTPUT")
?? throw new InvalidOperationException("An absolute, fresh OTOPCUA_SDK2_SOAK_OUTPUT JSONL path is required.");
Path.IsPathFullyQualified(output).ShouldBeTrue();
Directory.CreateDirectory(Path.GetDirectoryName(output)!);
await using var evidence = new StreamWriter(new FileStream(output, FileMode.CreateNew, FileAccess.Write, FileShare.Read));
using var run = CancellationTokenSource.CreateLinkedTokenSource(TestContext.Current.CancellationToken);
run.CancelAfter(TimeSpan.FromSeconds(seconds + 120));
var ct = run.Token;
var watch = Stopwatch.StartNew();
var clientRoot = Path.Combine(Path.GetTempPath(), $"otua-soak-client-{Guid.NewGuid():N}");
ApplicationInstance? application = null;
ServerEventRig? rig = null;
string? serverRoot = null;
var cycles = 0;
var restarts = 0;
Sample? baseline = null;
var retained = new Queue<Sample>();
try
{
var telemetry = DefaultTelemetry.Create(_ => { });
var security = TestClientSecurity.Build(clientRoot);
security.ApplicationCertificate!.SubjectName = "CN=SDK2LifecycleSoak";
var config = new ApplicationConfiguration(telemetry)
{
ApplicationName = "SDK2LifecycleSoak", ApplicationUri = $"urn:SDK2LifecycleSoak:{Guid.NewGuid():N}",
ApplicationType = ApplicationType.Client, SecurityConfiguration = security,
ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 15_000 },
TransportQuotas = new TransportQuotas { OperationTimeout = 5000 },
};
application = new ApplicationInstance(telemetry) { ApplicationConfiguration = config };
await config.ValidateAsync(ApplicationType.Client, ct);
await application.CheckApplicationInstanceCertificatesAsync(true, null, ct);
rig = await ServerEventRig.StartAsync("SDK2LifecycleSoak", encrypted: true, ct);
serverRoot = RequiredField<string>(rig, "_pkiRoot");
Configure(rig);
// The required observation period excludes initial certificate creation and server startup.
watch.Restart();
await LogAsync(evidence, new
{
kind = "start", utc = DateTimeOffset.UtcNow, processId = Environment.ProcessId,
mode, requestedSeconds = seconds, minimumFullSeconds = 7200,
build = Environment.GetEnvironmentVariable("OTOPCUA_SOAK_BUILD_SHA"),
sdk = typeof(OperationContext).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion,
diagnosticInstrumentation = "SDK2.0.1 CP lists and certificate-validation cached stores; read-only",
limits = new { retainedManagedGrowthBytes = 64L << 20, rssGrowthBytes = 128L << 20, fdGrowth = 32 },
coverage = "encrypted sessions; scalar/Int16[] Raw+UNS; subscription removal; browse abandonment; history release/cross-session/close; surgical changes/rebuild; revocation; paused TCP read/recovery; same-PKI restart",
});
while (watch.Elapsed < TimeSpan.FromSeconds(seconds))
{
var cycle = Stopwatch.StartNew();
await ExerciseAsync(rig, config, ++cycles, evidence, ct);
await EventuallyAsync(() => PublicCounts(rig) == (0, 0, 0), ct);
var sample = await CaptureAsync(rig, config.CertificateManager, watch.Elapsed, cycles, clientRoot, serverRoot, ct);
sample.Sessions.ShouldBe(0); sample.Subscriptions.ShouldBe(0); sample.Items.ShouldBe(0);
sample.BrowsePoints.ShouldBe(0); sample.HistoryPoints.ShouldBe(0);
sample.LiveDataItems.ShouldBe(0); sample.LiveEventItems.ShouldBe(0); sample.MonitoredNodes.ShouldBe(0);
sample.PkiDescriptors.ShouldBe(0, "quiescent certificate file descriptors must be closed");
if (cycles >= 3)
{
baseline ??= sample;
sample.CachedCertificateStores.ShouldBe(baseline.CachedCertificateStores);
sample.Variables.ShouldBe(baseline.Variables); sample.Folders.ShouldBe(baseline.Folders);
sample.Conditions.ShouldBe(baseline.Conditions);
sample.Descriptors.ShouldBeLessThanOrEqualTo(baseline.Descriptors + 32);
sample.ManagedBytes.ShouldBeLessThanOrEqualTo(baseline.ManagedBytes + (64L << 20));
sample.RssBytes.ShouldBeLessThanOrEqualTo(baseline.RssBytes + (128L << 20));
retained.Enqueue(sample);
if (retained.Count > 24) retained.Dequeue();
if (retained.Count == 24)
{
var window = retained.ToArray();
var strictlyIncreasing = window.Zip(window.Skip(1)).All(pair => pair.Second.ManagedBytes > pair.First.ManagedBytes);
(strictlyIncreasing && window[^1].ManagedBytes - window[0].ManagedBytes > (16L << 20)).ShouldBeFalse(
"retained managed memory must not grow monotonically across 24 quiescent cycles");
}
}
await LogAsync(evidence, new { kind = "quiescent", cycleSeconds = cycle.Elapsed.TotalSeconds, resources = sample });
if (cycles % 4 == 0)
{
var restart = Stopwatch.StartNew();
await RestartWithActiveResourcesAsync(rig, config, ct).WaitAsync(TimeSpan.FromSeconds(25), ct);
Configure(rig);
restarts++;
await LogAsync(evidence, new { kind = "restart", cycles, restarts, seconds = restart.Elapsed.TotalSeconds });
}
}
cycles.ShouldBeGreaterThanOrEqualTo(4, "diagnostic mode must exercise at least one actual restart");
restarts.ShouldBeGreaterThan(0);
watch.Elapsed.TotalSeconds.ShouldBeGreaterThanOrEqualTo(seconds);
var finalClientCores = CertificateCores(config.CertificateManager);
var finalServerCores = CertificateCores(rig.Server.CertificateManager!);
await rig.DisposeAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(20), ct);
rig = null;
await application.DisposeAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(20), ct);
application = null;
finalClientCores.Sum(CachedStores).ShouldBe(0);
finalServerCores.Sum(CachedStores).ShouldBe(0);
(await DescriptorCountsAsync(ct, clientRoot, serverRoot)).Pki.ShouldBe(0);
await LogAsync(evidence, new { kind = "complete", utc = DateTimeOffset.UtcNow, mode, cycles, restarts, seconds = watch.Elapsed.TotalSeconds,
satisfiesTwoHourDuration = mode == "full" && watch.Elapsed.TotalSeconds >= 7200 });
}
catch (Exception error)
{
await LogAsync(evidence, new { kind = "failed", cycles, restarts, seconds = watch.Elapsed.TotalSeconds,
exceptionType = error.GetType().FullName });
throw;
}
finally
{
if (rig is not null) await rig.DisposeAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(20), TestContext.Current.CancellationToken);
if (application is not null) await application.DisposeAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(20), TestContext.Current.CancellationToken);
if (Directory.Exists(clientRoot)) Directory.Delete(clientRoot, true);
if (serverRoot is not null) Directory.Exists(serverRoot).ShouldBeFalse("the task-owned server PKI must be released");
}
}
private static void Configure(ServerEventRig rig)
{
rig.MaterialiseNative();
rig.Nm.HistorianDataSource = new EventHistoryPagingOverTheWireTests.SqlLikeEventSource(Enumerable.Range(0, 40)
.Select(i => new HistoricalEvent($"soak-{i}", "source", Epoch.AddSeconds(i), Epoch, "soak", 500)).ToList());
rig.Nm.NodeWriteGateway = new AcceptWrites();
MaterialiseValues(rig);
rig.Enforce(true, [ServerEventRig.Key()], Grant());
}
private static ZB.MOM.WW.OtOpcUa.Configuration.Entities.NodeAcl Grant() => ServerEventRig.ClusterGrant(
ServerEventRig.KeyPrincipal, "soak-grant", NodePermissions.Browse | NodePermissions.Read | NodePermissions.Subscribe
| NodePermissions.WriteOperate | NodePermissions.HistoryRead);
private static void MaterialiseValues(ServerEventRig rig)
{
rig.Sink.EnsureFolder(ServerEventRig.RawDeviceFolder, null, "Device", AddressSpaceRealm.Raw);
rig.Sink.EnsureVariable(Scalar, ServerEventRig.RawDeviceFolder, "Scalar", "Int32", true, AddressSpaceRealm.Raw);
rig.Sink.EnsureVariable(ArrayTag, ServerEventRig.RawDeviceFolder, "Array", "Int16", true, AddressSpaceRealm.Raw,
isArray: true, arrayLength: 4);
rig.Sink.EnsureFolder("SoakEquipment", null, "Equipment", AddressSpaceRealm.Uns);
rig.Sink.EnsureVariable(UnsScalar, "SoakEquipment", "Scalar", "Int32", true, AddressSpaceRealm.Uns);
for (var i = 0; i < 16; i++) rig.Sink.EnsureVariable($"{ServerEventRig.RawDeviceFolder}/Browse{i}",
ServerEventRig.RawDeviceFolder, $"Browse{i}", "Int32", false, AddressSpaceRealm.Raw);
rig.Sink.WriteValue(Scalar, 0, OpcUaStatus.Good, DateTime.UtcNow, AddressSpaceRealm.Raw);
rig.Sink.WriteValue(ArrayTag, new short[] { 0, 1, 2, 3 }, OpcUaStatus.Good, DateTime.UtcNow, AddressSpaceRealm.Raw);
rig.Sink.WriteValue(UnsScalar, 0, OpcUaStatus.Good, DateTime.UtcNow, AddressSpaceRealm.Uns);
}
private static async Task ExerciseAsync(ServerEventRig rig, ApplicationConfiguration config, int cycle,
StreamWriter evidence, CancellationToken ct)
{
await using var proxy = new PauseProxy(new Uri(rig.EndpointUrl).Port);
using var owner = await ConnectAsync(rig, config, proxy.Port, ct);
using var other = await ConnectAsync(rig, config, null, ct);
var ownedSessions = rig.Server.CurrentInstance.SessionManager.GetSessions().ToArray();
ownedSessions.Length.ShouldBe(2);
var scalar = Raw(owner, Scalar);
var array = Raw(owner, ArrayTag);
var unsNs = owner.NamespaceUris.GetIndex(V3NodeIds.UnsNamespaceUri);
unsNs.ShouldBeGreaterThan(0);
var uns = new NodeId(UnsScalar, (ushort)unsNs);
owner.NamespaceUris.GetString(scalar.NamespaceIndex).ShouldBe(V3NodeIds.RawNamespaceUri);
owner.NamespaceUris.GetString(uns.NamespaceIndex).ShouldBe(V3NodeIds.UnsNamespaceUri);
using var subscription = new Subscription(owner.DefaultSubscription) { PublishingInterval = 100, KeepAliveCount = 10 };
owner.AddSubscription(subscription);
try
{
await subscription.CreateAsync(ct);
var seen = new ConcurrentDictionary<NodeId, Variant>();
foreach (var id in new[] { scalar, array, uns })
{
var item = new MonitoredItem(subscription.DefaultItem) { StartNodeId = id, AttributeId = Attributes.Value,
SamplingInterval = 50, QueueSize = 10 };
item.Notification += (sender, _) =>
{
foreach (var value in sender.DequeueValues()) if (StatusCode.IsGood(value.StatusCode)) seen[id] = value.WrappedValue;
};
subscription.AddItem(item);
}
await subscription.ApplyChangesAsync(ct);
subscription.MonitoredItems.ShouldAllBe(item => ServiceResult.IsGood(item.Status.Error));
PublicCounts(rig).ShouldBe((2, 1, 3));
var values = new short[] { (short)(cycle % 30000), 2, 3, 4 };
var writes = await owner.WriteAsync(null, new[]
{
new WriteValue { NodeId = scalar, AttributeId = Attributes.Value, Value = new DataValue(Variant.From(cycle)) },
new WriteValue { NodeId = array, AttributeId = Attributes.Value, Value = new DataValue(SdkTestValues.FromClr(values)) },
new WriteValue { NodeId = uns, AttributeId = Attributes.Value, Value = new DataValue(Variant.From(cycle)) },
}, ct);
writes.Results.ToList().ShouldAllBe(status => status == StatusCodes.Good);
(await owner.ReadValueAsync(scalar, ct)).WrappedValue.GetInt32().ShouldBe(cycle);
(await owner.ReadValueAsync(array, ct)).ClrValue().ShouldBeOfType<short[]>().ShouldBe(values);
(await owner.ReadValueAsync(uns, ct)).WrappedValue.GetInt32().ShouldBe(cycle);
await EventuallyAsync(() => seen.TryGetValue(scalar, out var value) && value.GetInt32() == cycle
&& seen.ContainsKey(array) && seen.TryGetValue(uns, out var mirror) && mirror.GetInt32() == cycle, ct);
// One explicitly released and one deliberately abandoned browse point.
var browse = await BrowseAsync(owner, ct);
browse.IsEmpty.ShouldBeFalse();
(await owner.BrowseNextAsync(null, true, [browse], ct)).Results[0].StatusCode.ShouldBe(StatusCodes.Good);
(await BrowseAsync(owner, ct)).IsEmpty.ShouldBeFalse();
// Production history points: cross-session denial, single-use resume, explicit release and close cleanup.
var first = (await PageAsync(owner, default, false, ct)).ContinuationPoint;
first.IsEmpty.ShouldBeFalse();
(await PageAsync(other, first, false, ct)).StatusCode.ShouldBe(StatusCodes.BadContinuationPointInvalid);
var next = (await PageAsync(owner, first, false, ct)).ContinuationPoint;
next.IsEmpty.ShouldBeFalse();
(await PageAsync(owner, next, true, ct)).StatusCode.ShouldBe(StatusCodes.Good);
for (var i = 0; i < 6; i++) (await PageAsync(owner, default, false, ct)).ContinuationPoint.IsEmpty.ShouldBeFalse();
var held = ownedSessions.Select(ContinuationCounts).ToArray();
held.Sum(count => count.Browse).ShouldBeGreaterThan(0);
held.Sum(count => count.History).ShouldBeGreaterThan(0);
var active = PublicCounts(rig);
await LogAsync(evidence, new { kind = "active", cycle, sessions = active.Sessions,
subscriptions = active.Subscriptions, items = active.Items, browsePoints = held.Sum(x => x.Browse), historyPoints = held.Sum(x => x.History) });
rig.Nm.UpdateTagAttributes(Scalar, false, null, "Int32", false, null, AddressSpaceRealm.Raw).ShouldBeTrue();
rig.Nm.UpdateTagAttributes(Scalar, true, null, "Int32", false, null, AddressSpaceRealm.Raw).ShouldBeTrue();
rig.Nm.RemoveVariableNode(ArrayTag, AddressSpaceRealm.Raw).ShouldBeTrue();
rig.Sink.EnsureVariable(ArrayTag, ServerEventRig.RawDeviceFolder, "Array", "Int16", true, AddressSpaceRealm.Raw,
isArray: true, arrayLength: 4);
rig.Sink.WriteValue(ArrayTag, values, OpcUaStatus.Good, DateTime.UtcNow, AddressSpaceRealm.Raw);
var authority = rig.Nm.DataPlaneAuthority.ShouldBeOfType<DataPlaneAuthorityState>();
authority.Apply(ServerEventRig.Cluster, $"revoked-{cycle}", [], [Grant()],
new Dictionary<AclScopeRef, IReadOnlyList<NodeAclPath>>());
var denied = await owner.ReadAsync(null, 0, TimestampsToReturn.Neither,
[new ReadValueId { NodeId = scalar, AttributeId = Attributes.Value }], ct);
denied.Results[0].StatusCode.ShouldBe(StatusCodes.BadUserAccessDenied);
rig.Enforce(true, [ServerEventRig.Key()], Grant());
proxy.Pause();
var stalled = Stopwatch.StartNew();
string stalledStatus;
try
{
using var timeout = CancellationTokenSource.CreateLinkedTokenSource(ct);
timeout.CancelAfter(TimeSpan.FromSeconds(1));
await owner.ReadValueAsync(scalar, timeout.Token).WaitAsync(TimeSpan.FromSeconds(5), ct);
throw new InvalidOperationException("The deliberately paused network unexpectedly completed the read.");
}
catch (OperationCanceledException) when (!ct.IsCancellationRequested) { stalledStatus = "OperationCanceled"; }
// Exact-tag ChannelAsyncOperation maps a canceled completion waiter to BadRequestInterrupted.
catch (ServiceResultException error) when (error.StatusCode == StatusCodes.BadTimeout
|| error.StatusCode == StatusCodes.BadRequestCancelledByClient || error.StatusCode == StatusCodes.BadRequestTimeout
|| error.StatusCode == StatusCodes.BadRequestInterrupted) { stalledStatus = error.StatusCode.ToString(); }
finally { proxy.Resume(); }
(await owner.ReadValueAsync(scalar, ct)).StatusCode.ShouldBe(StatusCodes.Good);
await LogAsync(evidence, new { kind = "network-stall-recovered", cycle, status = stalledStatus, seconds = stalled.Elapsed.TotalSeconds });
await subscription.DeleteAsync(true, ct);
await owner.RemoveSubscriptionAsync(subscription, ct);
rig.Sink.RebuildAddressSpace();
Configure(rig);
}
finally
{
proxy.Resume();
await owner.CloseAsync(ct).WaitAsync(TimeSpan.FromSeconds(10), ct);
await other.CloseAsync(ct).WaitAsync(TimeSpan.FromSeconds(10), ct);
foreach (var closed in ownedSessions)
{
ContinuationCounts(closed).ShouldBe((0, 0));
}
}
}
private static async Task RestartWithActiveResourcesAsync(ServerEventRig rig, ApplicationConfiguration config,
CancellationToken ct)
{
using var client = await ConnectAsync(rig, config, null, ct);
using var subscription = new Subscription(client.DefaultSubscription) { PublishingInterval = 100 };
client.AddSubscription(subscription);
await subscription.CreateAsync(ct);
subscription.AddItem(new MonitoredItem(subscription.DefaultItem)
{
StartNodeId = Raw(client, Scalar), AttributeId = Attributes.Value, SamplingInterval = 50,
});
await subscription.ApplyChangesAsync(ct);
PublicCounts(rig).ShouldBe((1, 1, 1));
(await BrowseAsync(client, ct)).IsEmpty.ShouldBeFalse();
(await PageAsync(client, default, false, ct)).ContinuationPoint.IsEmpty.ShouldBeFalse();
var oldInternal = rig.Server.CurrentInstance;
var oldSessionManager = oldInternal.SessionManager;
var oldSubscriptionManager = oldInternal.SubscriptionManager;
var oldSessions = oldSessionManager.GetSessions().ToArray();
var oldSubscriptions = oldSubscriptionManager.GetSubscriptions().ToArray();
var oldManager = rig.Server.CertificateManager!;
var oldCores = CertificateCores(oldManager);
await rig.RestartAsync(ct).WaitAsync(TimeSpan.FromSeconds(20), ct);
oldSessionManager.GetSessions().ShouldBeEmpty();
oldSubscriptionManager.GetSubscriptions().ShouldBeEmpty();
oldSubscriptions.ShouldAllBe(item => item.MonitoredItemCount == 0);
foreach (var session in oldSessions) ContinuationCounts(session).ShouldBe((0, 0));
RequiredField<bool>(oldManager, "m_disposed").ShouldBeTrue();
oldCores.Sum(CachedStores).ShouldBe(0, "stopped server validators must release every cached store");
PublicCounts(rig).ShouldBe((0, 0, 0));
}
private static async Task<ClientSession> ConnectAsync(ServerEventRig rig, ApplicationConfiguration config,
int? proxyPort, CancellationToken ct)
{
var telemetry = DefaultTelemetry.Create(_ => { });
var endpoint = await CoreClientUtils.SelectEndpointAsync(config, rig.EndpointUrl, true, telemetry, ct)
?? throw new InvalidOperationException("No encrypted endpoint.");
endpoint.SecurityMode.ShouldBe(MessageSecurityMode.SignAndEncrypt);
if (proxyPort is { } port) endpoint.EndpointUrl = $"opc.tcp://127.0.0.1:{port}/OtOpcUa";
return await new DefaultSessionFactory(telemetry).CreateAsync(config,
new ConfiguredEndpoint(null, endpoint, EndpointConfiguration.Create(config)), false, false,
"SDK2LifecycleSoak", 15_000, new UserIdentity("apikey", Encoding.UTF8.GetBytes(ServerEventRig.KeyPassword)), default, ct);
}
private static NodeId Raw(ClientSession session, string id)
{
var ns = session.NamespaceUris.GetIndex(V3NodeIds.RawNamespaceUri);
ns.ShouldBeGreaterThan(0);
return new NodeId(id, (ushort)ns);
}
private static async Task<ByteString> BrowseAsync(ClientSession session, CancellationToken ct) =>
(await session.BrowseAsync(null, null, 1, [new BrowseDescription
{
NodeId = Raw(session, ServerEventRig.RawDeviceFolder), BrowseDirection = BrowseDirection.Forward,
ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences, IncludeSubtypes = true,
ResultMask = (uint)BrowseResultMask.All,
}], ct)).Results[0].ContinuationPoint;
private static async Task<Opc.Ua.HistoryReadResult> PageAsync(ClientSession session, ByteString point,
bool release, CancellationToken ct) => (await session.HistoryReadAsync(null, new ExtensionObject(new ReadEventDetails
{
StartTime = Epoch.AddDays(-1), EndTime = Epoch.AddDays(1), NumValuesPerNode = 4,
Filter = new EventFilter { SelectClauses = [new SimpleAttributeOperand(ObjectTypeIds.BaseEventType,
QualifiedName.From(BrowseNames.EventId))] },
}), TimestampsToReturn.Both, release,
[new HistoryReadValueId { NodeId = Raw(session, ServerEventRig.RawDeviceFolder), ContinuationPoint = point }], ct)).Results[0];
private static (int Sessions, int Subscriptions, int Items) PublicCounts(ServerEventRig rig)
{
var subscriptions = rig.Server.CurrentInstance.SubscriptionManager.GetSubscriptions();
return (rig.Server.CurrentInstance.SessionManager.GetSessions().Count, subscriptions.Count,
subscriptions.Sum(subscription => subscription.MonitoredItemCount));
}
// Exact-tag diagnostic instrumentation only. Fail if fields/types change; never replace or mutate a store.
private static (int Browse, int History) ContinuationCounts(ServerSession session)
{
var points = session.ContinuationPoints;
using (RequiredField<System.Threading.Lock>(points, "m_lock").EnterScope())
return (NullableCollectionCount(points, "m_browse"), NullableCollectionCount(points, "m_history"));
}
private static object[] CertificateCores(object manager)
{
using (RequiredField<System.Threading.Lock>(manager, "m_certificatesLock").EnterScope())
{
var cores = new List<object>();
foreach (var field in new[] { "m_peerCore", "m_userCore", "m_httpsCore" })
if (Field(manager, field).GetValue(manager) is { } core) cores.Add(core);
cores.AddRange(RequiredField<IDictionary>(manager, "m_customCores").Values.Cast<object>());
return cores.Distinct(System.Collections.Generic.ReferenceEqualityComparer.Instance).ToArray();
}
}
private static int CachedStores(object core) => RequiredField<IDictionary>(core, "m_stores").Count;
private static int NullableCollectionCount(object owner, string name) => Field(owner, name).GetValue(owner) switch
{
null => 0,
ICollection items => items.Count,
_ => throw new InvalidOperationException($"SDK diagnostic field {name} no longer has the expected collection type."),
};
private static FieldInfo Field(object owner, string name) => owner.GetType().GetField(name,
BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new InvalidOperationException($"Missing SDK diagnostic field {name}.");
private static T RequiredField<T>(object owner, string name) => Field(owner, name).GetValue(owner) is T value ? value
: throw new InvalidOperationException($"SDK diagnostic field {name} no longer has the expected type {typeof(T).Name}.");
private sealed record Sample(int Cycle, double Seconds, int Sessions, int Subscriptions, int Items,
int BrowsePoints, int HistoryPoints, int LiveDataItems, int LiveEventItems, int MonitoredNodes,
int Variables, int Folders, int Conditions, int CachedCertificateStores,
int Descriptors, int PkiDescriptors, long ManagedBytes, long RssBytes, long AllocatedBytes,
int Gen0, int Gen1, int Gen2);
private static async Task<Sample> CaptureAsync(ServerEventRig rig, object clientManager, TimeSpan elapsed, int cycle,
string clientRoot, string serverRoot, CancellationToken ct)
{
var counts = PublicCounts(rig);
var points = rig.Server.CurrentInstance.SessionManager.GetSessions().Select(ContinuationCounts).ToArray();
var descriptors = await DescriptorCountsAsync(ct, clientRoot, serverRoot);
var retained = GC.GetTotalMemory(forceFullCollection: true);
using var process = Process.GetCurrentProcess();
process.Refresh();
return new Sample(cycle, elapsed.TotalSeconds, counts.Sessions, counts.Subscriptions, counts.Items,
points.Sum(x => x.Browse), points.Sum(x => x.History), rig.Nm.LiveItemCount, rig.Nm.LiveEventItemCount,
rig.Nm.MonitoredNodeCount, rig.Nm.VariableCount, rig.Nm.FolderCount, rig.Nm.AlarmConditionCount,
CertificateCores(rig.Server.CertificateManager!).Sum(CachedStores) + CertificateCores(clientManager).Sum(CachedStores),
descriptors.Total, descriptors.Pki, retained, process.WorkingSet64, GC.GetTotalAllocatedBytes(),
GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2));
}
public static bool DescriptorProbeSupported => OperatingSystem.IsLinux() || OperatingSystem.IsMacOS();
[Fact(SkipUnless = nameof(DescriptorProbeSupported), Skip = "Descriptor probes require Linux procfs or macOS lsof.")]
public async Task Descriptor_probe_observes_an_open_PKI_file_through_a_symlink_alias_and_its_close()
{
var ct = TestContext.Current.CancellationToken;
var temporary = Path.Combine(OperatingSystem.IsMacOS() ? "/tmp" : Path.GetTempPath(), $"otua-pki-probe-{Guid.NewGuid():N}");
var pki = Path.Combine(temporary, "pki");
var sibling = Path.Combine(temporary, "pki-other");
var alias = Path.Combine(temporary, "pki-alias");
Directory.CreateDirectory(pki);
Directory.CreateDirectory(sibling);
try
{
// On macOS the temporary root itself can use /var -> /private/var. The additional
// task-owned alias also checks links whose target contains a linked parent component.
Directory.CreateSymbolicLink(alias, pki);
await using var outside = new FileStream(Path.Combine(sibling, "control"), FileMode.Create,
FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete);
CanonicalPath(alias).ShouldBe(CanonicalPath(pki));
var before = (await DescriptorCountsAsync(ct, pki, alias)).Pki;
before.ShouldBe(0, "a sibling sharing the PKI path prefix is outside the root");
int opened;
await using (var held = new FileStream(Path.Combine(alias, "control"), FileMode.Create,
FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete))
{
opened = (await DescriptorCountsAsync(ct, pki, alias)).Pki;
opened.ShouldBeGreaterThan(0, "the positive control must observe the task-owned file opened through the alias");
}
var closed = (await DescriptorCountsAsync(ct, pki, alias)).Pki;
closed.ShouldBe(0, "closing the positive control must release its descriptor");
TestContext.Current.TestOutputHelper!.WriteLine($"PKI descriptor control: before={before}, opened={opened}, closed={closed}; siblingExcluded=true; aliasResolved=true");
}
finally
{
if (Directory.Exists(alias)) Directory.Delete(alias);
if (Directory.Exists(temporary)) Directory.Delete(temporary, recursive: true);
}
}
[Fact(SkipUnless = nameof(DescriptorProbeSupported), Skip = "The bounded probe control uses a task-owned Unix process.")]
public async Task Descriptor_probe_times_out_and_reaps_a_process_that_keeps_both_streams_open()
{
var ct = TestContext.Current.CancellationToken;
using var process = new Process
{
StartInfo = new ProcessStartInfo("/bin/sh")
{
RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false,
ArgumentList = { "-c", "printf control; printf control >&2; exec /bin/sleep 30" },
},
};
var elapsed = Stopwatch.StartNew();
await Should.ThrowAsync<TimeoutException>(() => ReadDescriptorProbeAsync(process, TimeSpan.FromMilliseconds(250), ct));
elapsed.Elapsed.ShouldBeLessThan(TimeSpan.FromSeconds(4), "timeout includes stream drains and bounded process cleanup");
process.HasExited.ShouldBeTrue("the task-owned timed-out process must be killed and reaped before returning");
TestContext.Current.TestOutputHelper!.WriteLine($"Descriptor timeout control: deadlineMs=250, elapsedMs={elapsed.Elapsed.TotalMilliseconds:F0}, childExited={process.HasExited}");
}
private static async Task<(int Total, int Pki)> DescriptorCountsAsync(CancellationToken ct, params string[] roots)
{
ct.ThrowIfCancellationRequested();
var canonicalRoots = roots.Select(root => CanonicalPath(root)).Distinct(StringComparer.Ordinal).ToArray();
if (OperatingSystem.IsLinux())
{
var targets = Directory.EnumerateFileSystemEntries("/proc/self/fd")
.Select(path => new FileInfo(path).LinkTarget).ToArray();
return (targets.Length, targets.Count(path => IsWithinRoots(path, canonicalRoots)));
}
if (OperatingSystem.IsMacOS())
{
using var process = new Process { StartInfo = new ProcessStartInfo("/usr/sbin/lsof")
{ RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false } };
foreach (var arg in new[] { "-a", "-p", Environment.ProcessId.ToString(CultureInfo.InvariantCulture), "-Fnf" })
process.StartInfo.ArgumentList.Add(arg);
var text = await ReadDescriptorProbeAsync(process, TimeSpan.FromSeconds(5), ct);
process.ExitCode.ShouldBe(0);
var total = 0;
var pki = 0;
var descriptor = false;
foreach (var line in text.Split('\n'))
{
if (line.StartsWith('f'))
{
descriptor = line.Length > 1 && char.IsDigit(line[1]);
if (descriptor) total++;
}
else if (descriptor && line.StartsWith('n') && IsWithinRoots(line[1..], canonicalRoots)) pki++;
}
return (total, pki);
}
throw new PlatformNotSupportedException("This soak requires Linux procfs or macOS lsof for actual descriptor evidence.");
}
private static async Task<string> ReadDescriptorProbeAsync(Process process, TimeSpan timeout, CancellationToken ct)
{
using var deadline = CancellationTokenSource.CreateLinkedTokenSource(ct);
deadline.CancelAfter(timeout);
process.Start();
var stdout = process.StandardOutput.ReadToEndAsync(deadline.Token);
var stderr = process.StandardError.ReadToEndAsync(deadline.Token);
var exited = process.WaitForExitAsync(deadline.Token);
try
{
// Waiting for exit alone does not bound reads: both redirected pipes must also finish.
await Task.WhenAll(stdout, stderr, exited).WaitAsync(deadline.Token).ConfigureAwait(false);
return await stdout.ConfigureAwait(false);
}
catch (OperationCanceledException)
{
try { if (!process.HasExited) process.Kill(entireProcessTree: true); }
catch (InvalidOperationException) when (process.HasExited) { /* It exited between the check and kill. */ }
await process.WaitForExitAsync(CancellationToken.None).WaitAsync(TimeSpan.FromSeconds(2)).ConfigureAwait(false);
// Observe both cancelled drains; never leave a pipe read running after returning.
try { await Task.WhenAll(stdout, stderr).WaitAsync(TimeSpan.FromSeconds(2)).ConfigureAwait(false); }
catch (OperationCanceledException) { }
if (ct.IsCancellationRequested) throw;
throw new TimeoutException("Descriptor probe exceeded its process and redirected-stream deadline.");
}
}
private static bool IsWithinRoots(string? path, IReadOnlyList<string> roots)
{
if (path is null || !Path.IsPathFullyQualified(path)) return false;
// procfs marks an unlinked but still open file; its containing physical directory still counts.
const string deleted = " (deleted)";
if (path.EndsWith(deleted, StringComparison.Ordinal)) path = path[..^deleted.Length];
var canonical = CanonicalPath(path);
return roots.Any(root => canonical.Equals(root, StringComparison.Ordinal)
|| canonical.StartsWith(Path.EndsInDirectorySeparator(root) ? root : root + Path.DirectorySeparatorChar,
StringComparison.Ordinal));
}
private static string CanonicalPath(string path, int depth = 0)
{
if (depth > 32) throw new IOException("Descriptor path link resolution exceeded its bound.");
var full = Path.GetFullPath(path);
var current = Path.GetPathRoot(full)!;
foreach (var component in full[current.Length..].Split(Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries))
{
current = Path.Combine(current, component);
FileSystemInfo entry = Directory.Exists(current) ? new DirectoryInfo(current) : new FileInfo(current);
if (entry.LinkTarget is not null)
{
var target = entry.ResolveLinkTarget(returnFinalTarget: true)
?? throw new IOException("Descriptor path link resolution returned no target.");
// Resolve parent links too: an alias target can itself begin with /var or /tmp.
current = CanonicalPath(target.FullName, depth + 1);
}
}
return Path.TrimEndingDirectorySeparator(current);
}
private static async Task EventuallyAsync(Func<bool> condition, CancellationToken ct)
{
var deadline = Stopwatch.StartNew();
while (!condition() && deadline.Elapsed < TimeSpan.FromSeconds(10)) await Task.Delay(25, ct);
condition().ShouldBeTrue("resource or notification condition did not settle within ten seconds");
}
private static async Task LogAsync(StreamWriter output, object value)
{
await output.WriteLineAsync(JsonSerializer.Serialize(value));
await output.FlushAsync();
}
private sealed class AcceptWrites : IOpcUaNodeWriteGateway
{
public Task<NodeWriteOutcome> WriteAsync(string id, object? value, AddressSpaceRealm realm, CancellationToken ct) =>
Task.FromResult(new NodeWriteOutcome(true, null));
}
/// <summary>Task-owned TCP relay. Pause blocks forwarding in both directions without closing either socket.</summary>
private sealed class PauseProxy : IAsyncDisposable
{
private readonly TcpListener _listener = new(IPAddress.Loopback, 0);
private readonly CancellationTokenSource _stop = new();
private readonly ConcurrentBag<TcpClient> _sockets = [];
private readonly ConcurrentBag<Task> _pumps = [];
private readonly Task _accept;
private TaskCompletionSource? _pause;
internal int Port => ((IPEndPoint)_listener.LocalEndpoint).Port;
internal PauseProxy(int targetPort)
{
_listener.Start();
_accept = AcceptAsync(targetPort);
}
internal void Pause() => Volatile.Write(ref _pause, new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously));
internal void Resume() => Interlocked.Exchange(ref _pause, null)?.TrySetResult();
private async Task AcceptAsync(int port)
{
try
{
while (!_stop.IsCancellationRequested)
{
var incoming = await _listener.AcceptTcpClientAsync(_stop.Token);
_sockets.Add(incoming);
var outgoing = new TcpClient(); _sockets.Add(outgoing);
await outgoing.ConnectAsync(IPAddress.Loopback, port, _stop.Token);
_pumps.Add(PumpAsync(incoming.GetStream(), outgoing.GetStream()));
_pumps.Add(PumpAsync(outgoing.GetStream(), incoming.GetStream()));
}
}
catch (OperationCanceledException) when (_stop.IsCancellationRequested) { }
}
private async Task PumpAsync(NetworkStream source, NetworkStream destination)
{
var buffer = new byte[16 * 1024];
try
{
while (true)
{
var length = await source.ReadAsync(buffer, _stop.Token);
if (length == 0) return;
if (Volatile.Read(ref _pause) is { } paused) await paused.Task.WaitAsync(_stop.Token);
await destination.WriteAsync(buffer.AsMemory(0, length), _stop.Token);
}
}
catch (OperationCanceledException) when (_stop.IsCancellationRequested) { }
catch (IOException) when (_stop.IsCancellationRequested) { }
}
public async ValueTask DisposeAsync()
{
Resume(); _stop.Cancel(); _listener.Stop();
foreach (var socket in _sockets) socket.Dispose();
try { await _accept; await Task.WhenAll(_pumps); }
finally { _stop.Dispose(); }
}
}
}
@@ -0,0 +1,242 @@
using System.Text;
using System.Net;
using System.Collections.Concurrent;
using Microsoft.Extensions.Logging.Abstractions;
using Opc.Ua;
using Opc.Ua.Client;
using Shouldly;
using Xunit;
using Opc.Ua.Configuration;
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
using ZB.MOM.WW.OtOpcUa.Core.Authorization;
using ZB.MOM.WW.OtOpcUa.OpcUaServer.Security;
namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests;
/// <summary>Real, mutually trusted channels: no auto-accept callbacks or insecure fallback.</summary>
public sealed class Sdk2TrustedChannelTests
{
[Theory]
[InlineData(OpcUaSecurityProfile.Basic256Sha256Sign, MessageSecurityMode.Sign)]
[InlineData(OpcUaSecurityProfile.Basic256Sha256SignAndEncrypt, MessageSecurityMode.SignAndEncrypt)]
public async Task Trusted_key_retains_narrow_authority_and_revocation_reaches_its_open_session(
OpcUaSecurityProfile profile, MessageSecurityMode mode)
{
var ct = TestContext.Current.CancellationToken;
await using var rig = await TrustedRig.StartAsync(profile, ct);
using var session = await rig.ConnectAsync("contract-key", ct);
session.Endpoint.SecurityMode.ShouldBe(mode);
(await session.ReadValueAsync(rig.Node(session), ct)).WrappedValue.GetInt32().ShouldBe(7);
(await rig.WriteAsync(session, 42, ct)).ShouldBe(StatusCodes.Good);
rig.Writes.ShouldBe(1, "a narrow grant with no application-wide roles must permit the write");
var observedPeer = rig.Peers.ShouldHaveSingleItem();
observedPeer.ShouldNotBeNull();
IPAddress.IsLoopback(observedPeer.Address).ShouldBeTrue();
observedPeer.Port.ShouldBe(0, "the public SDK evidence supplies an IP address, not an invented client port");
var originalSessionId = session.SessionId;
await session.ReconnectAsync(null, null, ct);
session.SessionId.ShouldBe(originalSessionId, "the test must reactivate the existing session on a new channel");
rig.Peers.Count.ShouldBe(2);
rig.Peers.Last().ShouldNotBeNull().Address.ShouldBe(observedPeer.Address);
(await session.ReadValueAsync(rig.Node(session), ct)).StatusCode.ShouldBe(StatusCodes.Good);
var identity = rig.Server.CurrentInstance.SessionManager.GetSessions().ShouldHaveSingleItem().EffectiveIdentity
.ShouldBeOfType<RoleCarryingUserIdentity>();
identity.Principal.ShouldBe(TrustedRig.Principal);
identity.ApiKeyId.ShouldBe(TrustedRig.KeyId);
identity.Roles.ShouldBeEmpty();
rig.Revoke();
(await rig.WriteAsync(session, 99, ct)).ShouldBe(StatusCodes.BadUserAccessDenied);
rig.Writes.ShouldBe(1, "revocation must be checked before a cached narrow grant or dispatch");
}
[Theory]
[InlineData(OpcUaSecurityProfile.Basic256Sha256Sign)]
[InlineData(OpcUaSecurityProfile.Basic256Sha256SignAndEncrypt)]
public async Task Trusted_channel_does_not_turn_invalid_credentials_into_another_identity(OpcUaSecurityProfile profile)
{
var ct = TestContext.Current.CancellationToken;
await using var rig = await TrustedRig.StartAsync(profile, ct);
var error = await Should.ThrowAsync<ServiceResultException>(() => rig.ConnectAsync("invalid", ct));
error.StatusCode.ShouldBe(StatusCodes.BadIdentityTokenRejected);
rig.Writes.ShouldBe(0);
rig.Server.CurrentInstance.SessionManager.GetSessions().ShouldAllBe(s => !s.Activated);
}
[Fact]
public async Task An_untrusted_client_is_rejected_before_application_authentication()
{
var ct = TestContext.Current.CancellationToken;
await using var rig = await TrustedRig.StartAsync(OpcUaSecurityProfile.Basic256Sha256SignAndEncrypt, ct);
var before = rig.AuthCalls;
var error = await Should.ThrowAsync<ServiceResultException>(() => rig.ConnectAsync("contract-key", ct, untrustedClient: true));
new[] { StatusCodes.BadCertificateUntrusted, StatusCodes.BadSecurityChecksFailed, StatusCodes.BadSecureChannelClosed }
.ShouldContain(error.StatusCode);
rig.AuthCalls.ShouldBe(before);
rig.Writes.ShouldBe(0);
}
private sealed class TrustedRig : IAsyncDisposable, IOpcUaUserAuthenticator, IOpcUaNodeWriteGateway
{
internal const string Principal = "trusted-contract";
internal const string KeyId = "k-trusted-contract";
private const string Cluster = "SDK2";
private const string PathId = "Plant/Device/Value";
private readonly string _root = Path.Combine(Path.GetTempPath(), $"otua-trusted-{Guid.NewGuid():N}");
private readonly Dictionary<ApplicationConfiguration, ApplicationInstance> _applications = [];
private readonly DataPlaneAuthorityState _authority = new();
private readonly OpcUaApplicationHostOptions _options;
private OpcUaApplicationHost _host = null!;
private int _port;
private int _writes;
private int _authCalls;
internal ConcurrentQueue<IPEndPoint?> Peers { get; } = new();
internal OtOpcUaSdkServer Server { get; private set; } = null!;
internal int Writes => Volatile.Read(ref _writes);
internal int AuthCalls => Volatile.Read(ref _authCalls);
private string ServerPki => System.IO.Path.Combine(_root, "server");
private string ClientPki => System.IO.Path.Combine(_root, "client");
private TrustedRig(OpcUaSecurityProfile profile)
{
_options = new OpcUaApplicationHostOptions
{
ApplicationName = "Sdk2TrustedContract", ApplicationUri = $"urn:Sdk2TrustedContract:{Guid.NewGuid():N}",
PublicHostname = "localhost", PkiStoreRoot = ServerPki, AllowAnonymous = false,
AutoAcceptUntrustedClientCertificates = false, EnabledSecurityProfiles = [profile],
};
}
internal static async Task<TrustedRig> StartAsync(OpcUaSecurityProfile profile, CancellationToken ct)
{
var rig = new TrustedRig(profile);
try
{
// Provision both certificates, then reload the managers with explicit trusted-peer stores.
await rig.BootAsync(ct);
await rig._host.DisposeAsync();
var client = await rig.ConfigurationAsync(rig.ClientPki, ct);
CopyCertificates(rig.ServerPki, rig.ClientPki);
CopyCertificates(rig.ClientPki, rig.ServerPki);
await rig.ReleaseConfigurationAsync(client);
await rig.BootAsync(ct);
rig.InstallAuthority(enabled: true);
var nm = rig.Server.NodeManager!;
nm.DataPlaneAuthority = rig._authority;
nm.Authorizer = new DataPlaneAuthorizer(rig._authority, new TriePermissionEvaluator(rig._authority.Tries),
true, NullLogger.Instance, TimeProvider.System);
nm.NodeWriteGateway = rig;
var sink = new SdkAddressSpaceSink(nm);
sink.EnsureFolder("Plant", null, "Plant", AddressSpaceRealm.Raw);
sink.EnsureFolder("Plant/Device", "Plant", "Device", AddressSpaceRealm.Raw);
sink.EnsureVariable(PathId, "Plant/Device", "Value", "Int32", true, AddressSpaceRealm.Raw);
sink.WriteValue(PathId, 7, OpcUaStatus.Good, DateTime.UtcNow, AddressSpaceRealm.Raw);
return rig;
}
catch { await rig.DisposeAsync(); throw; }
}
private async Task BootAsync(CancellationToken ct)
{
(_host, Server) = await OpcUaHostStart.StartAsync(port =>
{
_port = port; _options.OpcUaPort = port;
return new OpcUaApplicationHost(_options, NullLogger<OpcUaApplicationHost>.Instance, this);
}, () => new OtOpcUaSdkServer(), ct);
}
private static void CopyCertificates(string from, string to)
{
var destination = System.IO.Path.Combine(to, "trusted", "certs");
Directory.CreateDirectory(destination);
foreach (var file in Directory.GetFiles(System.IO.Path.Combine(from, "own", "certs"), "*.der"))
File.Copy(file, System.IO.Path.Combine(destination, System.IO.Path.GetFileName(file)), overwrite: true);
}
private async Task<ApplicationConfiguration> ConfigurationAsync(string pki, CancellationToken ct)
{
var telemetry = DefaultTelemetry.Create(_ => { });
var security = TestClientSecurity.Build(pki);
security.AutoAcceptUntrustedCertificates = false;
security.ApplicationCertificate!.SubjectName = "CN=Sdk2TrustedClient";
var config = new ApplicationConfiguration(telemetry)
{
ApplicationName = "Sdk2TrustedClient", ApplicationUri = "urn:localhost:Sdk2TrustedClient",
ApplicationType = ApplicationType.Client, SecurityConfiguration = security,
ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60_000 },
};
var app = new ApplicationInstance(telemetry)
{ ApplicationName = config.ApplicationName, ApplicationType = ApplicationType.Client, ApplicationConfiguration = config };
try
{
await config.ValidateAsync(ApplicationType.Client, ct);
await app.CheckApplicationInstanceCertificatesAsync(true, lifeTimeInMonths: 12, ct);
_applications.Add(config, app);
return config;
}
catch { await app.DisposeAsync(); throw; }
}
private async Task ReleaseConfigurationAsync(ApplicationConfiguration config)
{
if (_applications.Remove(config, out var app)) await app.DisposeAsync();
}
internal async Task<ISession> ConnectAsync(string credential, CancellationToken ct, bool untrustedClient = false)
{
var pki = untrustedClient ? System.IO.Path.Combine(_root, $"untrusted-{Guid.NewGuid():N}") : ClientPki;
var config = await ConfigurationAsync(pki, ct);
if (untrustedClient)
{
await ReleaseConfigurationAsync(config);
CopyCertificates(ServerPki, pki);
config = await ConfigurationAsync(pki, ct);
}
config.SecurityConfiguration.AutoAcceptUntrustedCertificates.ShouldBeFalse();
var telemetry = DefaultTelemetry.Create(_ => { });
var endpoint = await CoreClientUtils.SelectEndpointAsync(config, $"opc.tcp://localhost:{_port}/OtOpcUa", true, telemetry, ct)
?? throw new InvalidOperationException("No secure endpoint.");
return await new DefaultSessionFactory(telemetry).CreateAsync(config,
new ConfiguredEndpoint(null, endpoint, EndpointConfiguration.Create(config)), false, true,
"Sdk2TrustedContract", 60_000, new UserIdentity("apikey", Encoding.UTF8.GetBytes(credential)), default, ct);
}
internal NodeId Node(ISession session) => new(PathId, (ushort)session.NamespaceUris.GetIndex(V3NodeIds.RawNamespaceUri));
internal async Task<StatusCode> WriteAsync(ISession session, int value, CancellationToken ct) =>
(await session.WriteAsync(null, [new WriteValue { NodeId = Node(session), AttributeId = Attributes.Value,
Value = new DataValue(Variant.From(value)) }], ct)).Results[0];
internal void Revoke() => InstallAuthority(enabled: false);
private void InstallAuthority(bool enabled) => _authority.Apply(Cluster, enabled ? "allowed" : "revoked",
[new ApiKey { ApiKeyId = KeyId, Name = KeyId, Principal = Principal, Enabled = enabled,
SecretHash = "hash", CreatedUtc = DateTime.UtcNow.AddDays(-1) }],
[new NodeAcl { NodeAclId = "narrow", ClusterId = Cluster, Principal = Principal,
ScopeKind = NodeAclScopeKind.RawDevice, ScopeId = "device", PermissionFlags =
NodePermissions.Read | NodePermissions.Browse | NodePermissions.Subscribe | NodePermissions.WriteOperate }],
new Dictionary<AclScopeRef, IReadOnlyList<NodeAclPath>>
{ [new(NodeAclScopeKind.RawDevice, "device")] = [new([NodeAclRealms.Raw, "Plant", "Device"])] });
public Task<OpcUaUserAuthResult> AuthenticateUserNameAsync(string username, string password, IPEndPoint? peer, CancellationToken ct)
{
Peers.Enqueue(peer is null ? null : new IPEndPoint(peer.Address, peer.Port));
return AuthenticateUserNameAsync(username, password, ct);
}
public Task<OpcUaUserAuthResult> AuthenticateUserNameAsync(string username, string password, CancellationToken ct)
{
Interlocked.Increment(ref _authCalls);
return Task.FromResult(username == "apikey" && password == "contract-key"
? OpcUaUserAuthResult.AllowKey(Principal, [], KeyId) : OpcUaUserAuthResult.Deny("Invalid credential."));
}
public Task<NodeWriteOutcome> WriteAsync(string nodeId, object? value, AddressSpaceRealm realm, CancellationToken ct)
{ Interlocked.Increment(ref _writes); return Task.FromResult(new NodeWriteOutcome(true, null)); }
public async ValueTask DisposeAsync()
{
if (_host is not null) await _host.DisposeAsync();
foreach (var app in _applications.Values) await app.DisposeAsync();
_applications.Clear();
if (Directory.Exists(_root)) Directory.Delete(_root, true);
}
}
}
@@ -46,6 +46,8 @@ internal sealed class ServerEventRig : IAsyncDisposable
public OtOpcUaNodeManager Nm => _server.NodeManager!;
internal OtOpcUaSdkServer Server => _server;
/// <summary>
/// The roles the <c>apikey</c> session activates with (read at each activation). Defaults to the two
/// server-wide roles; a flag-off test sets it from <c>ApiKeyInterimRoleMapper.Map</c> over its own grants, as
@@ -41,6 +41,31 @@ public sealed class NodeManagerAlarmSourceFieldsTests : IDisposable
Path.GetTempPath(),
$"otopcua-alarm-src-fields-{Guid.NewGuid():N}");
[Theory]
[InlineData("OffNormalAlarm", AddressSpaceRealm.Raw)]
[InlineData("DiscreteAlarm", AddressSpaceRealm.Raw)]
[InlineData("AlarmCondition", AddressSpaceRealm.Raw)]
[InlineData("OffNormalAlarm", AddressSpaceRealm.Uns)]
[InlineData("DiscreteAlarm", AddressSpaceRealm.Uns)]
[InlineData("AlarmCondition", AddressSpaceRealm.Uns)]
public async Task Created_alarm_retains_the_parent_component_reference(string type, AddressSpaceRealm realm)
{
await using var host = await BootAsync();
var nm = host.Nm;
nm.EnsureFolder("browse-parent", null, "Parent", realm);
nm.MaterialiseAlarmCondition("browse-alarm", "browse-parent", "Alarm", type, 500, realm, isNative: realm == AddressSpaceRealm.Raw);
var condition = nm.TryGetAlarmCondition("browse-alarm", realm)!;
condition.ReferenceTypeId.ShouldBe(ReferenceTypeIds.HasComponent);
condition.Parent!.NodeId.ShouldBe(new NodeId("browse-parent", nm.NamespaceIndexForRealm(realm)));
condition.TypeDefinitionId.ShouldBe(type switch
{
"OffNormalAlarm" => ObjectTypeIds.OffNormalAlarmType,
"DiscreteAlarm" => ObjectTypeIds.DiscreteAlarmType,
_ => ObjectTypeIds.AlarmConditionType,
});
condition.NodeClass.ShouldBe(NodeClass.Object);
}
/// <summary>A NATIVE condition (Raw realm, ConditionId = RawPath) carries all three identity fields:
/// EventType = its type definition, SourceNode = its own NodeId, SourceName = the RawPath. The leaf name
/// remains on ConditionName so the short name is still available to clients.</summary>
@@ -28,7 +28,6 @@ using LocalizedText = Opc.Ua.LocalizedText;
using Proto = ZB.MOM.WW.MxGateway.Contracts.Proto;
using UaNodeId = Opc.Ua.NodeId;
using Variant = Opc.Ua.Variant;
using VariantCollection = Opc.Ua.VariantCollection;
namespace ZB.MOM.WW.OtOpcUa.Runtime.Tests.Drivers;
@@ -505,9 +504,9 @@ public sealed class NativeAlarmRestatementEndToEndTests : RuntimeActorTestBase
}
/// <summary>An upstream A&amp;C condition event, in the driver's select-clause order.</summary>
private static VariantCollection ConditionFields(bool active, bool acked) =>
private static Variant[] ConditionFields(bool active, bool acked) =>
[
new Variant(new byte[] { 1 }), // EventId
new Variant(Opc.Ua.ByteString.From(new byte[] { 1 })), // EventId
new Variant(new UaNodeId("TripAlarmType", 3)), // EventType
new Variant(UaNodeId.Parse(UpstreamSource)), // SourceNode
new Variant(new LocalizedText("tripped")), // Message
@@ -1,6 +1,5 @@
using Akka.Actor;
using Microsoft.Extensions.Logging.Abstractions;
using Opc.Ua.Server;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Redundancy;
@@ -14,7 +13,7 @@ namespace ZB.MOM.WW.OtOpcUa.Runtime.Tests.OpcUa;
/// <summary>
/// Task 60 / #81 — verifies the full path from cluster redundancy state to OPC UA
/// <c>Server.ServiceLevel</c> visible on the wire. Boots a real <see cref="StandardServer"/>,
/// <c>Server.ServiceLevel</c> visible on the wire. Boots a real <see cref="OtOpcUaSdkServer"/>,
/// wires <see cref="SdkServiceLevelPublisher"/> into a <see cref="DeferredServiceLevelPublisher"/>
/// (the production binding pattern), spawns <see cref="OpcUaPublishActor"/> against the
/// deferred publisher, and sends a <see cref="RedundancyStateChanged"/> snapshot. Asserts
@@ -35,7 +34,7 @@ public sealed class ServiceLevelEndToEndTests : RuntimeActorTestBase
port => new OpcUaApplicationHost(
BuildOptions("PrimaryLeader", pkiRoot, port),
NullLogger<OpcUaApplicationHost>.Instance),
() => new StandardServer(), Ct);
() => new OtOpcUaSdkServer(), Ct);
await using var host = started.Host;
var server = started.Server;
@@ -58,7 +57,7 @@ public sealed class ServiceLevelEndToEndTests : RuntimeActorTestBase
CorrelationId: CorrelationId.NewId()));
AwaitAssertion(() =>
server.CurrentInstance.ServerObject.ServiceLevel.Value.ShouldBe((byte)240));
server.CurrentInstance.ServerObject.ServiceLevel.ShouldNotBeNull().Value.ShouldBe((byte)240));
}
finally
{
@@ -77,7 +76,7 @@ public sealed class ServiceLevelEndToEndTests : RuntimeActorTestBase
port => new OpcUaApplicationHost(
BuildOptions("Secondary", pkiRoot, port),
NullLogger<OpcUaApplicationHost>.Instance),
() => new StandardServer(), Ct);
() => new OtOpcUaSdkServer(), Ct);
await using var host = started.Host;
var server = started.Server;
@@ -100,7 +99,7 @@ public sealed class ServiceLevelEndToEndTests : RuntimeActorTestBase
CorrelationId: CorrelationId.NewId()));
AwaitAssertion(() =>
server.CurrentInstance.ServerObject.ServiceLevel.Value.ShouldBe((byte)100));
server.CurrentInstance.ServerObject.ServiceLevel.ShouldNotBeNull().Value.ShouldBe((byte)100));
}
finally
{