Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c794ae33a5 |
@@ -126,6 +126,7 @@
|
||||
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
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
#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
|
||||
@@ -1,7 +0,0 @@
|
||||
__pycache__/
|
||||
bin/
|
||||
obj/
|
||||
private-fixture/
|
||||
*.pfx
|
||||
*.der
|
||||
*.nettrace
|
||||
@@ -1,39 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text.Json;
|
||||
using Opc.Ua.Security.Certificates;
|
||||
var mode = args[0];
|
||||
var directory = args[1];
|
||||
Directory.CreateDirectory(directory);
|
||||
using var key = RSA.Create(2048);
|
||||
var request = new CertificateRequest("CN=NativeMemoryControl", key, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||
var original = request.CreateSelfSigned(DateTimeOffset.UtcNow.AddDays(-1), DateTimeOffset.UtcNow.AddDays(1));
|
||||
var pfx = original.Export(X509ContentType.Pfx);
|
||||
// Certificate.From takes ownership of original; only wrapped disposes that native certificate.
|
||||
using var wrapped = Certificate.From(original);
|
||||
void Sample(int i, string kind)
|
||||
{
|
||||
var live = GC.GetTotalMemory(true);
|
||||
GC.WaitForPendingFinalizers();
|
||||
using var p = Process.GetCurrentProcess();
|
||||
p.Refresh();
|
||||
Console.WriteLine(JsonSerializer.Serialize(new { kind, i, pid = Environment.ProcessId, mode, live, rss = p.WorkingSet64, methods = JitInfo.GetCompiledMethodCount(), gc = GC.GetGCMemoryInfo().TotalCommittedBytes }));
|
||||
Console.Out.Flush();
|
||||
}
|
||||
Sample(0, "ready");
|
||||
while (!File.Exists(Path.Combine(directory, "go"))) Thread.Sleep(50);
|
||||
for (int i = 1; i <= 1000; i++)
|
||||
{
|
||||
using X509Certificate2 copy = mode switch
|
||||
{
|
||||
"pfx" => System.Security.Cryptography.X509Certificates.X509CertificateLoader.LoadPkcs12(pfx, null, X509KeyStorageFlags.Exportable),
|
||||
"sdk-copy" => wrapped.AsX509Certificate2(),
|
||||
"shared-copy" => (X509Certificate2)typeof(X509Certificate2).GetConstructor(new[] { typeof(X509Certificate) })!.Invoke(new object[] { original }),
|
||||
_ => System.Security.Cryptography.X509Certificates.X509CertificateLoader.LoadCertificate(original.RawData)
|
||||
};
|
||||
if (i % 100 == 0) Sample(i, "sample");
|
||||
}
|
||||
Sample(1000, "done");
|
||||
while (!File.Exists(Path.Combine(directory, "stop"))) Thread.Sleep(50);
|
||||
@@ -1,15 +0,0 @@
|
||||
// Read-only temporary diagnostic attachment. Requires Microsoft.Diagnostics.NETCore.Client from dotnet-trace.
|
||||
using System.Diagnostics.Tracing;
|
||||
using Microsoft.Diagnostics.NETCore.Client;
|
||||
|
||||
var seconds = int.Parse(args[1]);
|
||||
if (seconds < 1 || seconds > 300) throw new ArgumentOutOfRangeException(nameof(seconds), "Use a bounded 1..300 second diagnostic attachment");
|
||||
var client = new DiagnosticsClient(int.Parse(args[0]));
|
||||
var providers = new[] { new EventPipeProvider("Microsoft-Windows-DotNETRuntime", EventLevel.Verbose, 0x18) };
|
||||
// Match the recorded experiment's API default (256 MiB capacity, not measured resident allocation).
|
||||
using var session = client.StartEventPipeSession(providers, requestRundown: false, circularBufferMB: 256);
|
||||
await using var file = File.Create(args[2]);
|
||||
var copy = session.EventStream.CopyToAsync(file);
|
||||
await Task.Delay(TimeSpan.FromSeconds(seconds));
|
||||
session.Stop();
|
||||
await copy;
|
||||
@@ -1,208 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Client;
|
||||
using Opc.Ua.Configuration;
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
|
||||
using ZB.MOM.WW.OtOpcUa.OpcUaServer;
|
||||
using ZB.MOM.WW.OtOpcUa.OpcUaServer.Security;
|
||||
var root = Path.GetFullPath(args[0]);
|
||||
Directory.CreateDirectory(root);
|
||||
var seconds = int.Parse(args[1]);
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(seconds + 120));
|
||||
var ct = timeout.Token;
|
||||
var clientPki = Path.Combine(root, "client-pki");
|
||||
var telemetry = DefaultTelemetry.Create(_ => { });
|
||||
var config = new ApplicationConfiguration(telemetry);
|
||||
config.ApplicationName = "NativeLifecycleControl";
|
||||
config.ApplicationUri = "urn:NativeLifecycleControl:" + Guid.NewGuid();
|
||||
config.ApplicationType = ApplicationType.Client;
|
||||
config.SecurityConfiguration = new SecurityConfiguration
|
||||
{
|
||||
ApplicationCertificate = new CertificateIdentifier { StoreType = CertificateStoreType.Directory, StorePath = Path.Combine(clientPki, "own"), SubjectName = "CN=NativeLifecycleControl" },
|
||||
TrustedIssuerCertificates = new CertificateTrustList { StoreType = CertificateStoreType.Directory, StorePath = Path.Combine(clientPki, "issuer") },
|
||||
TrustedPeerCertificates = new CertificateTrustList { StoreType = CertificateStoreType.Directory, StorePath = Path.Combine(clientPki, "trusted") },
|
||||
RejectedCertificateStore = new CertificateTrustList { StoreType = CertificateStoreType.Directory, StorePath = Path.Combine(clientPki, "rejected") },
|
||||
AutoAcceptUntrustedCertificates = true
|
||||
};
|
||||
config.ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 15000 };
|
||||
var app = new ApplicationInstance(telemetry) { ApplicationConfiguration = config };
|
||||
await config.ValidateAsync(ApplicationType.Client, ct);
|
||||
await app.CheckApplicationInstanceCertificatesAsync(true, null, ct);
|
||||
int port;
|
||||
using (var listener = new TcpListener(IPAddress.Loopback, 0))
|
||||
{
|
||||
listener.Start();
|
||||
port = ((IPEndPoint)listener.LocalEndpoint).Port;
|
||||
}
|
||||
var options = new OpcUaApplicationHostOptions { ApplicationName = "NativeLifecycleServer", ApplicationUri = "urn:NativeLifecycleServer:" + Guid.NewGuid(), OpcUaPort = port, PublicHostname = "127.0.0.1", PkiStoreRoot = Path.Combine(root, "server-pki"), AllowAnonymous = false, AutoAcceptUntrustedClientCertificates = true, EnabledSecurityProfiles = [OpcUaSecurityProfile.Basic256Sha256SignAndEncrypt] };
|
||||
OpcUaApplicationHost host = null!;
|
||||
OtOpcUaSdkServer server = null!;
|
||||
async Task Boot()
|
||||
{
|
||||
server = new OtOpcUaSdkServer();
|
||||
host = new OpcUaApplicationHost(options, NullLogger<OpcUaApplicationHost>.Instance, new Auth());
|
||||
await host.StartAsync(server, ct);
|
||||
var n = server.NodeManager!;
|
||||
n.NodeWriteGateway = new Gateway();
|
||||
n.EnsureFolder("Control", null, "Control", AddressSpaceRealm.Raw);
|
||||
n.EnsureFolder("Equipment", null, "Equipment", AddressSpaceRealm.Uns);
|
||||
n.EnsureVariable("Control/Scalar", "Control", "Scalar", "Int32", true, AddressSpaceRealm.Raw);
|
||||
n.EnsureVariable("Control/Array", "Control", "Array", "Int16", true, AddressSpaceRealm.Raw, isArray: true, arrayLength: 4);
|
||||
n.EnsureVariable("Equipment/Scalar", "Equipment", "Scalar", "Int32", true, AddressSpaceRealm.Uns);
|
||||
for (var i = 0; i < 16; i++) n.EnsureVariable("Control/Browse" + i, "Control", "Browse" + i, "Int32", false, AddressSpaceRealm.Raw);
|
||||
n.MaterialiseAlarmCondition("Control/Alarm", "Control", "Alarm", "OffNormalAlarm", 500, AddressSpaceRealm.Raw, isNative: true);
|
||||
n.WriteValue("Control/Scalar", 0, 0, DateTime.UtcNow, AddressSpaceRealm.Raw);
|
||||
n.WriteValue("Control/Array", new short[] { 0, 1, 2, 3 }, 0, DateTime.UtcNow, AddressSpaceRealm.Raw);
|
||||
n.WriteValue("Equipment/Scalar", 0, 0, DateTime.UtcNow, AddressSpaceRealm.Uns);
|
||||
}
|
||||
async Task<ISession> Connect()
|
||||
{
|
||||
var t = DefaultTelemetry.Create(_ => { });
|
||||
var endpoint = await CoreClientUtils.SelectEndpointAsync(config, $"opc.tcp://127.0.0.1:{port}/OtOpcUa", true, t, ct);
|
||||
var factory = new DefaultSessionFactory(t);
|
||||
var identity = new UserIdentity("control", Encoding.UTF8.GetBytes(FixtureCredential.Value));
|
||||
return await factory.CreateAsync(config, new ConfiguredEndpoint(null, endpoint ?? throw new Exception("Missing endpoint"), EndpointConfiguration.Create(config)), false, false, "NativeLifecycleControl", 15000, identity, default, ct);
|
||||
}
|
||||
static DataValue Value(object v)
|
||||
{
|
||||
#if SDK2
|
||||
if (!VariantHelper.TryCastFrom(v, out var variant)) throw new Exception("Unsupported control value");
|
||||
return new DataValue(variant);
|
||||
#else
|
||||
return new DataValue(new Variant(v));
|
||||
#endif
|
||||
}
|
||||
static object? Clr(DataValue v)
|
||||
{
|
||||
#if SDK2
|
||||
return v.WrappedValue.AsBoxedObject(Variant.BoxingBehavior.Legacy);
|
||||
#else
|
||||
return v.Value;
|
||||
#endif
|
||||
}
|
||||
object? CertificateStats()
|
||||
{
|
||||
#if SDK2
|
||||
return new { created = Opc.Ua.Security.Certificates.Certificate.InstancesCreated, disposed = Opc.Ua.Security.Certificates.Certificate.InstancesDisposed, outstanding = Opc.Ua.Security.Certificates.Certificate.InstancesLeaked };
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
void Sample(int cycle, string kind)
|
||||
{
|
||||
var live = GC.GetTotalMemory(true);
|
||||
using var p = Process.GetCurrentProcess();
|
||||
p.Refresh();
|
||||
var m = GC.GetGCMemoryInfo();
|
||||
Console.WriteLine(JsonSerializer.Serialize(new { kind, cycle, pid = Environment.ProcessId, live, rss = p.WorkingSet64, gcCommitted = m.TotalCommittedBytes, gcHeap = m.HeapSizeBytes, fragmented = m.FragmentedBytes, jit = JitInfo.GetCompiledMethodCount(), jitIl = JitInfo.GetCompiledILBytes(), sdk = typeof(NodeId).Assembly.GetName().Version?.ToString(), scope = "matched encrypted public lifecycle subset; no history/revocation/proxy", assemblies = AppDomain.CurrentDomain.GetAssemblies().Length, dynamicAssemblies = AppDomain.CurrentDomain.GetAssemblies().Count(a => a.IsDynamic), certificate = CertificateStats(), timers = Timer.ActiveCount, threads = p.Threads.Count, sessions = server.CurrentInstance.SessionManager.GetSessions().Count, subscriptions = server.CurrentInstance.SubscriptionManager.GetSubscriptions().Count }));
|
||||
Console.Out.Flush();
|
||||
}
|
||||
int cycles = 0, restarts = 0;
|
||||
await Boot();
|
||||
var watch = Stopwatch.StartNew();
|
||||
Sample(0, "start");
|
||||
try
|
||||
{
|
||||
while (watch.Elapsed.TotalSeconds < seconds)
|
||||
{
|
||||
using (var owner = await Connect()) using (var other = await Connect())
|
||||
{
|
||||
var ns = (ushort)owner.NamespaceUris.GetIndex("https://zb.com/otopcua/raw");
|
||||
var scalar = new NodeId("Control/Scalar", ns);
|
||||
var array = new NodeId("Control/Array", ns);
|
||||
using var subscription = new Subscription(owner.DefaultSubscription) { PublishingInterval = 100 };
|
||||
owner.AddSubscription(subscription);
|
||||
await subscription.CreateAsync(ct);
|
||||
subscription.AddItem(new MonitoredItem(subscription.DefaultItem) { StartNodeId = scalar, AttributeId = Attributes.Value, SamplingInterval = 50 });
|
||||
subscription.AddItem(new MonitoredItem(subscription.DefaultItem) { StartNodeId = array, AttributeId = Attributes.Value, SamplingInterval = 50 });
|
||||
await subscription.ApplyChangesAsync(ct);
|
||||
var response = await owner.WriteAsync(null, [new WriteValue { NodeId = scalar, AttributeId = Attributes.Value, Value = Value(++cycles) }, new WriteValue { NodeId = array, AttributeId = Attributes.Value, Value = Value(new short[] { 1, 2, 3, 4 }) }], ct);
|
||||
if (response.Results.ToList().Any(s => StatusCode.IsBad(s))) throw new Exception("Control write denied");
|
||||
if (!Equals(Clr(await owner.ReadValueAsync(scalar, ct)), cycles)) throw new Exception("Scalar changed");
|
||||
if (!((short[])Clr(await owner.ReadValueAsync(array, ct))!).SequenceEqual(new short[] { 1, 2, 3, 4 })) throw new Exception("Array changed");
|
||||
var browse = await owner.BrowseAsync(null, null, 1, [new BrowseDescription { NodeId = new NodeId("Control", ns), BrowseDirection = BrowseDirection.Forward, ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences, IncludeSubtypes = true, ResultMask = (uint)BrowseResultMask.All }], ct);
|
||||
await Task.Delay(1000, ct);
|
||||
await subscription.DeleteAsync(true, ct);
|
||||
await owner.CloseAsync(ct);
|
||||
await other.CloseAsync(ct);
|
||||
}
|
||||
if (server.CurrentInstance.SessionManager.GetSessions().Count != 0 || server.CurrentInstance.SubscriptionManager.GetSubscriptions().Count != 0) throw new Exception("Control retained sessions/subscriptions");
|
||||
Sample(cycles, "sample");
|
||||
if (cycles % 4 == 0)
|
||||
{
|
||||
using var parked = await Connect();
|
||||
using var sub = new Subscription(parked.DefaultSubscription) { PublishingInterval = 100 };
|
||||
parked.AddSubscription(sub);
|
||||
await sub.CreateAsync(ct);
|
||||
sub.AddItem(new MonitoredItem(sub.DefaultItem) { StartNodeId = new NodeId("Control/Scalar", (ushort)parked.NamespaceUris.GetIndex("https://zb.com/otopcua/raw")), AttributeId = Attributes.Value, SamplingInterval = 50 });
|
||||
await sub.ApplyChangesAsync(ct);
|
||||
await host.DisposeAsync();
|
||||
await Boot();
|
||||
restarts++;
|
||||
}
|
||||
}
|
||||
Console.WriteLine(JsonSerializer.Serialize(new { kind = "complete", cycles, restarts, seconds = watch.Elapsed.TotalSeconds, fullGate = false }));
|
||||
}
|
||||
finally
|
||||
{
|
||||
await host.DisposeAsync();
|
||||
#if SDK2
|
||||
await app.DisposeAsync();
|
||||
#endif
|
||||
}
|
||||
#if SDK2
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
var certificateType = typeof(Opc.Ua.Security.Certificates.Certificate);
|
||||
var report = new Dictionary<string, object>();
|
||||
var trackingEnabled = Environment.GetEnvironmentVariable("OPCUA_CERTIFICATE_LEAK_TRACKING") == "1";
|
||||
if (trackingEnabled)
|
||||
{
|
||||
foreach (var name in new[] { "EnumerateLiveCertificates", "EnumerateUnreachableUndisposedCertificates" })
|
||||
{
|
||||
var method = certificateType.GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic)
|
||||
?? throw new InvalidOperationException("SDK2 diagnostic method missing: " + name);
|
||||
var values = method.Invoke(null, null) as IEnumerable
|
||||
?? throw new InvalidOperationException("SDK2 diagnostic method return type changed: " + name);
|
||||
var stacks = new List<string>();
|
||||
foreach (var value in values)
|
||||
{
|
||||
var field = value!.GetType().GetField(name.Contains("Unreachable") ? "Item2" : "Item4")
|
||||
?? throw new InvalidOperationException("SDK2 diagnostic tuple field missing: " + name);
|
||||
if (field.FieldType != typeof(string))
|
||||
throw new InvalidOperationException("SDK2 diagnostic stack field type changed");
|
||||
// Deliberately omit tuple Item1 (certificate identity).
|
||||
stacks.Add((string)field.GetValue(value)!);
|
||||
}
|
||||
report[name] = new
|
||||
{
|
||||
count = stacks.Count,
|
||||
allocations = stacks.GroupBy(x => x).OrderByDescending(g => g.Count()).Take(15)
|
||||
.Select(g => new { count = g.Count(), stack = g.Key }).ToArray()
|
||||
};
|
||||
}
|
||||
}
|
||||
Console.WriteLine(JsonSerializer.Serialize(new { kind = "certificate-final-diagnostics", trackingEnabled, stats = CertificateStats(), report }));
|
||||
#endif
|
||||
class Auth : IOpcUaUserAuthenticator
|
||||
{
|
||||
public Task<OpcUaUserAuthResult> AuthenticateUserNameAsync(string user, string password, CancellationToken ct) => Task.FromResult(user == "control" && password == FixtureCredential.Value ? OpcUaUserAuthResult.Allow("control", [OpcUaDataPlaneRoles.WriteOperate]) : OpcUaUserAuthResult.Deny("Control credential refused"));
|
||||
}
|
||||
class Gateway : IOpcUaNodeWriteGateway
|
||||
{
|
||||
public Task<NodeWriteOutcome> WriteAsync(string n, object? v, AddressSpaceRealm r, CancellationToken ct) => Task.FromResult(new NodeWriteOutcome(true, null));
|
||||
}
|
||||
|
||||
static class FixtureCredential
|
||||
{
|
||||
internal static string Value { get; } = Environment.GetEnvironmentVariable("OPCUA_MEMORY_PASSWORD") ?? throw new InvalidOperationException("Set OPCUA_MEMORY_PASSWORD for the task-owned fixture.");
|
||||
}
|
||||
@@ -1,286 +0,0 @@
|
||||
# SDK 2 lifecycle memory investigation
|
||||
|
||||
The required two-hour server lifecycle gate failed on macOS and Linux. The RSS
|
||||
limit remains **cycle-3 baseline + 128 MiB**; retained managed memory remains
|
||||
+64 MiB and descriptors +32. No threshold, production code, certificate provider,
|
||||
or session factory was changed by this investigation. These controls do not
|
||||
replace the full gate. Issue #799 remains open.
|
||||
|
||||
## Preserved full-workload failures
|
||||
|
||||
| Run | Cycles / restarts | Observation time | RSS / limit (bytes) | Outcome |
|
||||
|---|---:|---:|---:|---|
|
||||
| macOS `fa823378` | 250 / 62 | 1045.53 s | 365,215,744 / 363,331,584 | Failed |
|
||||
| macOS `e135b7c9` profile reproduction | 245 / 61 | 1024.14 s | 368,656,384 / 363,053,056 | Failed |
|
||||
| Linux full harness, recorded build `38098ce142ab8c382825aa754e0758071dd3a46b` | 248 / 61 | 903.508 s | 353,931,264 / 351,850,496 | Failed |
|
||||
| macOS `6b2d4b81` bounded profile | 432 / 108 | 1807.479 s | First rejection: 364,199,936 / 363,266,048 | Failed; first assertion rethrown |
|
||||
|
||||
The last macOS row requested 1,800 seconds. It first rejected RSS at cycle 253 /
|
||||
1055.482 seconds, continued recording explicitly rejected observations, then
|
||||
threw the original assertion before any completion record. Its final observation
|
||||
was RSS 380,059,648, live managed 41,364,096, GC committed 37,060,608, JIT
|
||||
compilations 81,535, descriptors 200, and zero sessions, subscriptions, monitored
|
||||
items, browse/history continuation points, and PKI descriptors. SDK cached stores
|
||||
remained two during operation. The runner and skip guard returned exit 1 with one
|
||||
executed test and zero skipped tests. There is no passing two-hour result here.
|
||||
|
||||
The first macOS run's reported PKI descriptor zero is invalid: `/var` and
|
||||
`/private/var` aliases were not canonicalized. Corrected positive-handle and timeout
|
||||
controls passed before subsequent profiles. Global descriptor counts and the RSS
|
||||
failure itself were independent of that defect. A Shouldly source-expression
|
||||
message incorrectly names `Descriptors` in the RSS failure; the actual numeric
|
||||
comparison is RSS, as retained before the assertion.
|
||||
|
||||
## What the controls demonstrate
|
||||
|
||||
`LifecycleControl.cs` uses unchanged published product DLLs and public APIs for
|
||||
both SDK generations. It opens two real encrypted **opc.tcp** sessions with
|
||||
Basic256Sha256 / SignAndEncrypt, resolves namespace URIs, reads/writes Int32 and
|
||||
Int16[] values, creates/deletes two data monitored items, abandons a browse
|
||||
continuation, closes sessions, and restarts a server with an active subscription
|
||||
every four cycles against the same task-owned PKI. Public session/subscription
|
||||
counts must return to zero. It has no historian, revocation, surgical-rebuild,
|
||||
paused-network, or private continuation-store coverage. It is a diagnostic subset,
|
||||
not TLS and not the full soak workload.
|
||||
|
||||
The original DLLs are the immutable 1.5.378.182 baseline publication associated
|
||||
with `83ed880e`; its assembly version alone is 1.5.378.0 and does not identify the
|
||||
patch. The candidate uses SDK 2.0.1 and the server implementation from `fa823378`.
|
||||
Neither baseline nor candidate product code was rebuilt for these controls.
|
||||
The controls were compiled once for each reference set, then those exact control
|
||||
artifacts were used on both operating systems. Selected DLL and input-artifact
|
||||
hashes are in `evidence.json`. Neither reference set's principal SDK/server DLLs
|
||||
has a managed native/ReadyToRun header.
|
||||
|
||||
| Ten-minute matched subset | Cycles / restarts | Final RSS | Change from cycle 3 | Final managed / change | Final JIT compilations |
|
||||
|---|---:|---:|---:|---:|---:|
|
||||
| macOS original | 212 / 53 | 330,301,440 | +121,683,968 | 271,168,424 / +169,990,456 | 28,066 |
|
||||
| macOS SDK2 | 200 / 50 | 302,170,112 | +95,862,784 | 44,569,584 / +295,120 | 62,608 |
|
||||
| Linux original | 249 / 62 | 384,950,272 | +176,836,608 | 147,139,568 / +99,356,752 | 28,321 |
|
||||
| Linux SDK2 | 259 / 64 | 390,844,416 | +194,183,168 | 19,561,120 / +736 | 71,529 |
|
||||
|
||||
Both ten-minute controls exited zero because their lifecycle checks passed;
|
||||
`fullGate=false` is explicit. They do **not** enforce the full harness memory
|
||||
assertions. In particular, both Linux subset RSS deltas exceed 128 MiB and the
|
||||
original retains managed memory/timers. These observations do not waive either
|
||||
failed full gate. They are not proof of an SDK1 defect or an SDK2 improvement.
|
||||
Both control branches explicitly delete subscriptions, close normal sessions,
|
||||
and dispose sessions/subscriptions. Parked resources intentionally encounter
|
||||
server shutdown and are then disposed. Each version calls its unchanged product
|
||||
host's DisposeAsync. The baseline host only calls ApplicationInstance.StopAsync;
|
||||
that original ApplicationInstance exposes no disposal interface. The SDK2 branch
|
||||
also uses its new ApplicationInstance.DisposeAsync ownership API. Per-connection
|
||||
DefaultTelemetry factories are left to the same ownership pattern on both
|
||||
branches, separately probed above. Thus measured original growth belongs to this
|
||||
published-product/fixture lifecycle combination; its allocation owner has not
|
||||
been isolated. These are one matched run per platform, concurrent processes,
|
||||
with different runtime versions; they are not performance distributions.
|
||||
|
||||
The thirty-minute Linux comparison uses the same immutable artifacts and image,
|
||||
fresh independent fixtures, and SDK2 certificate allocation tracking enabled
|
||||
before the first certificate (the same environment is passed to both versions).
|
||||
Both finished with exit zero and `fullGate=false`:
|
||||
|
||||
| Thirty-minute Linux subset | Cycles / restarts | Final RSS | Final live managed | GC committed | JIT compilations | Active timers |
|
||||
|---|---:|---:|---:|---:|---:|---:|
|
||||
| Original | 736 / 184 | 540,057,600 | 342,626,200 | 408,399,872 | 29,177 | 926 |
|
||||
| SDK2 | 768 / 192 | 334,180,352 | 19,248,984 | 42,983,424 | 72,087 | 19 |
|
||||
|
||||
Observed runtime was 1802.542 seconds original / 1804.523 seconds SDK2. The SDK2
|
||||
RSS fell from 390,782,976 at the 632.7-second checkpoint to 334,180,352 at the last
|
||||
observation; live managed stayed near 20 MB. After disposal and final collection,
|
||||
SDK2 wrapper counters were 19,272 created = 19,272 disposed, with zero live or
|
||||
unreachable-undisposed wrappers under enabled tracking. These finite results
|
||||
support post-transition saturation of this subset, not a two-hour full-workload
|
||||
pass or a guarantee of indefinite stability.
|
||||
|
||||
### Native and runtime observations
|
||||
|
||||
- During the macOS bounded profile, `Security::AppleDatabase` counts increased
|
||||
from 278 at cycle 52 / 211 seconds to 1,953 at cycle 370 / 1546 seconds. These
|
||||
native objects continued accumulating while managed memory stayed near 40 MiB.
|
||||
- The single-owner certificate probe makes a self-signed RSA fixture in memory,
|
||||
then disposes each of 1,000 copies/imports. On macOS, public-only and
|
||||
shared-handle copies leave AppleDatabase at 2; PFX imports and SDK
|
||||
`Certificate.AsX509Certificate2` private-key copies grow it from 2 to 1,002.
|
||||
A separate PFX process retained that count through a 180-second idle hold.
|
||||
`Certificate.From(original)` owns the original; the checked-in control has no
|
||||
second owner. All four corrected single-owner modes were rerun successfully.
|
||||
- The same isolated Linux probe ended at approximately 49 MB RSS for public or
|
||||
shared copies, 61 MB for PFX imports, and 69 MB for the SDK copy. Those are
|
||||
endpoint RSS observations including different JIT/GC costs, not evidence of a
|
||||
Linux native-object leak or a complete attribution.
|
||||
- SDK2 public certificate counters after the Linux ten-minute lifecycle run were
|
||||
6,495 created = 6,495 disposed. A separate stack-tracked macOS lifecycle run
|
||||
ended 591 = 591, zero live and zero unreachable-undisposed wrappers. Allocation
|
||||
tracking is opt-in; empty internal reports without that opt-in are not proof.
|
||||
- The Linux ten-minute native snapshots show SDK2 JIT `doublemapper` resident
|
||||
mappings increasing from about 65.4 to 106.7 MiB, anonymous writable mappings
|
||||
from 84.6 to 121.3 MiB, and `[heap]` around 19.2 MiB. Original JIT mappings were
|
||||
about 33.3 to 37.7 MiB. Mapping totals are descriptive categories, not exclusive
|
||||
object allocations: anonymous memory also contains GC regions, stacks, and
|
||||
native allocator arenas. RSS includes file/shared pages and can fall with
|
||||
reclamation. JIT counters are cumulative compilations, not unique live methods.
|
||||
- 10,000 disposed versus undisposed `DefaultTelemetry` factories ended at similar
|
||||
RSS (~63.4 MB) and live managed memory (~0.43 MB), with no timer growth. This
|
||||
does not justify changing host telemetry ownership to fix this failure.
|
||||
|
||||
Exact SDK tag source shows private-key PFX copying in nonvirtual
|
||||
`Certificate.AsX509Certificate2`. A 60-second macOS sampled-thread-time trace
|
||||
instead found the dominant sampled import path through
|
||||
`CertificateIdentifierResolver.LoadPrivateKeyCoreAsync` →
|
||||
`DirectoryCertificateStore.LoadPrivateKeyAsync` → `X509CertificateLoader.LoadPkcs12`.
|
||||
Inclusive sampled thread-time was about 4.79 seconds for store load and 2.99 seconds
|
||||
for PKCS12, versus 8.5 milliseconds for AsX509. These are neither exclusive CPU
|
||||
costs nor call counts. ApplicationInstance already normalizes its validation
|
||||
input to public DER. The validation copy delegate is internal, the manager sealed,
|
||||
and no safe narrow application copy-policy seam was identified. Changing session
|
||||
factory, certificate exportability, or key storage would alter behavior and was
|
||||
not used as a workaround.
|
||||
|
||||
A major JIT/RSS step appeared near 60–64 restarts in several workloads. This is a
|
||||
hypothesis about runtime/code-cache warmup, not a finding that all RSS growth is
|
||||
benign. A separate read-only EventPipe attachment observed the thirty-minute
|
||||
Linux candidate around that transition using `Microsoft-Windows-DotNETRuntime`,
|
||||
Verbose, keywords `0x18` (Loader + JIT), no rundown, 180 seconds. The transition was UTC 18:51:02.049–18:54:02.062. Factory JIT events
|
||||
occurred UTC 18:52:48.249–18:53:21.434: 6,690 distinct method tokens from one
|
||||
module, each once. The module is Opc.Ua.Core.Types.dll; metadata declares 8,442
|
||||
static methods on OpcUaExtensions. The observed set is a subset, so this does
|
||||
not prove every possible factory or the complete canonical workload is warmed.
|
||||
Method-load events totalled 51,327,536 native code bytes, including 51,161,724
|
||||
OptimizedTier1 bytes and 49,141,484 bytes in generated factories. Only 50
|
||||
reflection InvokeStub JIT starts occurred; the transition trace reports zero lost events. MethodSize sums count loaded code
|
||||
versions, not resident/PSS allocation bytes. An additional 180-second attachment
|
||||
after the transition checks continuing factory compilation. The second attachment (UTC 19:01:44.116–19:04:44.127) recorded 85 JIT starts,
|
||||
zero lost events, zero Opc.Ua starts, zero generated-factory starts, and zero generated-factory
|
||||
loaded bytes. Only 18 ordinary Tier1 method loads (5,720 bytes) and 20
|
||||
instrumented Tier1 loads (5,800 bytes) occurred. This supports a finite generated
|
||||
factory burst in the observed subset; it does not prove complete canonical
|
||||
workload saturation. The capture API's default circular-buffer capacity was 256 MiB; that is a
|
||||
capacity setting, not measured resident overhead. Attachments can allocate event
|
||||
buffers and perturb execution. Both prior full failures and the ten-minute
|
||||
control were uninstrumented by these JIT attachments, and the thirty-minute final
|
||||
interval is unattached. No captured native-code byte total is claimed to equal an
|
||||
RSS delta. Raw traces/heaps are not committed.
|
||||
|
||||
## Reproduce the diagnostic subset
|
||||
|
||||
Requires Python 3 and .NET 10. `run.py` references existing DLL directories,
|
||||
compiles only the control, records SHA-256 provenance, and creates a fresh local
|
||||
artifact directory. First reference directory wins duplicate filenames. Use the
|
||||
actual baseline publications, rather than restoring a different package version.
|
||||
No production project or package file is changed.
|
||||
|
||||
```bash
|
||||
python3 scripts/validation/opcua-lifecycle-memory/run.py lifecycle \
|
||||
--references "$baselineHostDlls" --references "$baselineClientDlls" \
|
||||
--output /tmp/opcua-memory-original --seconds 600
|
||||
python3 scripts/validation/opcua-lifecycle-memory/run.py lifecycle --sdk2 \
|
||||
--references "$candidateDlls" --output /tmp/opcua-memory-sdk2 --seconds 600 \
|
||||
--certificate-tracking
|
||||
```
|
||||
|
||||
Fixture credentials are generated in the runner environment and not logged.
|
||||
PKI material stays under each local `private-fixture` directory. Do not publish
|
||||
those directories or unredacted process/native logs. These controls grant a
|
||||
fixture user WriteOperate and auto-accept task-owned certificates; they are
|
||||
local test fixtures, not deployment configurations.
|
||||
|
||||
For a matched Linux pair, first build both with `--build-only` into fresh
|
||||
`/tmp/opcua-memory-original` and `/tmp/opcua-memory-sdk2` output directories, then:
|
||||
|
||||
```bash
|
||||
python3 scripts/validation/opcua-lifecycle-memory/linux_pair.py \
|
||||
--baseline /tmp/opcua-memory-original/bin/Release/net10.0 \
|
||||
--candidate /tmp/opcua-memory-sdk2/bin/Release/net10.0 \
|
||||
--output /tmp/opcua-memory-linux-pair --seconds 1800 \
|
||||
--image sha256:b04611ee9e1b7d36797012ed921962c57189cb4dbb3dff12afb8e158110f293b
|
||||
```
|
||||
|
||||
The runner uses separate task-owned `--network none` containers, fresh PKI,
|
||||
read-only executable mounts, bounded shutdown, and periodic read-only `/proc`
|
||||
snapshots. It records child and container exit statuses independently. The image
|
||||
is Ubuntu 24.04 arm64, .NET SDK 10.0.103 / runtime 10.0.3. The Mac runs used
|
||||
macOS 26.5.2 arm64, SDK 10.0.403 / runtime 10.0.14. Runtime and OS differences are
|
||||
explicit; do not attribute all cross-platform differences to SDK code.
|
||||
|
||||
On macOS, run each certificate mode with a fresh output directory:
|
||||
|
||||
```bash
|
||||
python3 scripts/validation/opcua-lifecycle-memory/run.py certificate --sdk2 \
|
||||
--references "$candidateDlls" --output /tmp/opcua-memory-pfx --mode pfx
|
||||
```
|
||||
|
||||
Other modes are `sdk-copy`, `public`, and `shared-copy`. The last intentionally
|
||||
uses the old copy constructor by reflection as a diagnostic control. It is not
|
||||
proposed production code. The control exports no fixture key/certificate data.
|
||||
`TelemetryControl.cs` is available through `run.py telemetry --sdk2` with modes
|
||||
`dispose` or `undisposed`. `summarize.py` emits Linux mapping categories without
|
||||
file paths from local `*-smaps.txt` files. All controls are opt-in and outside the
|
||||
ordinary solution/test graph.
|
||||
|
||||
Optional trace reproduction uses the installed dotnet-trace 9.0.661903 managed
|
||||
libraries, without adding a product dependency:
|
||||
|
||||
```bash
|
||||
python3 scripts/validation/opcua-lifecycle-memory/build_trace_tools.py \
|
||||
--libraries "$diagnosticToolLibraries" --output /tmp/opcua-memory-trace-tools
|
||||
# Run capture in the same process namespace / user as the task-owned target.
|
||||
dotnet /tmp/opcua-memory-trace-tools/capture/bin/Release/net10.0/capture.dll \
|
||||
"$targetPid" 180 /tmp/opcua-memory-local.nettrace
|
||||
dotnet /tmp/opcua-memory-trace-tools/summary/bin/Release/net10.0/summary.dll \
|
||||
/tmp/opcua-memory-local.nettrace > /tmp/opcua-memory-method-summary.json
|
||||
```
|
||||
|
||||
Capture is read-only and opt-in; raw traces remain local. The summary exports
|
||||
static method names and aggregate counts rather than certificate identities,
|
||||
arguments, or process mappings. The original transition parser also retained
|
||||
method tokens/modules locally to cross-check name-key uniqueness.
|
||||
|
||||
## Prospective saturation experiment (not executed)
|
||||
|
||||
The observed finite factory burst supports a separately labeled readiness
|
||||
experiment; it does not authorize moving the acceptance baseline. A reviewed
|
||||
candidate proposal is exactly 384 canonical lifecycle cycles / 96 every-four-cycle
|
||||
restarts, then one fixed settling cycle, all within 1,800 seconds. No early finish,
|
||||
extension, or RSS-selected endpoint is permitted. Both fixed readiness windows
|
||||
321–352 and 353–384 must contain zero generated-factory JIT/method-load events, zero SDK assembly
|
||||
loads, and zero lost events. Other JIT/native-code totals remain review evidence,
|
||||
not a claim that every runtime path is warmed. Trace capture is bounded and identical for both versions, then
|
||||
detached/drained before the settling cycle. Ownership, FD, PKI, store, and logical
|
||||
resource checks remain active throughout; original cold budget rejections remain
|
||||
recorded. Any later full gate requires at least 7,200 additional seconds after a
|
||||
prospectively approved baseline. No such warmed full gate has been run here.
|
||||
|
||||
The count is beyond the observed 60–64-restart transition and projects to about
|
||||
1,606 seconds from the Mac canonical run, but completion within the fixed cutoff
|
||||
is not guaranteed under load. Failure to complete or satisfy readiness is a failed
|
||||
experiment, not permission to extend. The full canonical test currently depends
|
||||
on SDK2 value APIs and read-only private SDK2 continuation/store instrumentation.
|
||||
An original-SDK adapter proving identical history, revocation, surgical-edit,
|
||||
and stalled-network operation counts is a prerequisite for a matched canonical
|
||||
experiment; the public subset does not provide that proof. Continued Mac native
|
||||
inventory growth remains an independent unresolved gate, even if JIT saturates.
|
||||
|
||||
## Evidence and limits
|
||||
|
||||
`evidence.json` contains sanitized numeric endpoints, native inventory counts,
|
||||
and hashes of retained diagnostic inputs. Full artifacts remain in the originating
|
||||
worktrees or `/tmp/opcua2-rss-investigation`; no PKI, certificate identifiers,
|
||||
credentials, raw traces, unredacted heap/process dumps, or complete test logs are
|
||||
checked in. Existing failures were preserved untouched.
|
||||
|
||||
Supported progress is a reproduced RSS failure, bounded logical/managed
|
||||
inventories in SDK2, demonstrated macOS PFX native retention, and measured
|
||||
cross-platform JIT/anonymous-memory growth. A complete allocation attribution and
|
||||
safe production correction remain unresolved. The server two-hour gate remains
|
||||
failed; a separate long-running client-only process is a different scope.
|
||||
|
||||
Package verification: baseline and candidate control builds, both optional trace
|
||||
tools, and certificate/telemetry controls built with zero warnings/errors. Local
|
||||
control smoke runs and the fresh Linux pair smoke exited zero. Python compilation
|
||||
and whitespace checks passed. One reused-output smoke failed because a manual
|
||||
incremental build used a different `/tmp` path alias and its runtimeconfig was
|
||||
missing; fresh canonical outputs passed, and the Linux runner now rejects an
|
||||
incomplete output directory before launch. These smoke checks validate tooling
|
||||
only and do not substitute for either long run.
|
||||
@@ -1,22 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Opc.Ua;
|
||||
var dispose = args[0] == "dispose";
|
||||
for (int i = 0; i <= 10000; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
var t = DefaultTelemetry.Create(_ => { });
|
||||
t.LoggerFactory.CreateLogger("TelemetryControl").LogDebug("control");
|
||||
if (dispose) ((IDisposable)t).Dispose();
|
||||
}
|
||||
if (i % 250 == 0)
|
||||
{
|
||||
var live = GC.GetTotalMemory(true);
|
||||
using var p = Process.GetCurrentProcess();
|
||||
p.Refresh();
|
||||
Console.WriteLine(JsonSerializer.Serialize(new { i, dispose, live, rss = p.WorkingSet64, methods = JitInfo.GetCompiledMethodCount(), il = JitInfo.GetCompiledILBytes(), gc = GC.GetGCMemoryInfo().TotalCommittedBytes, threads = p.Threads.Count, timers = Timer.ActiveCount }));
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
// Redacted aggregate summary. Keep raw traces local.
|
||||
using Microsoft.Diagnostics.Tracing;
|
||||
using System.Text.Json;
|
||||
|
||||
var starts = new Dictionary<string, int>();
|
||||
var tiers = new Dictionary<string, int>();
|
||||
var nativeBytes = new Dictionary<string, long>();
|
||||
var factoryNames = new Dictionary<string, int>();
|
||||
var factoryTokens = new HashSet<int>();
|
||||
var factoryModules = new HashSet<long>();
|
||||
var firstFactoryMs = double.PositiveInfinity;
|
||||
var lastFactoryMs = 0d;
|
||||
var factoryNativeBytes = 0L;
|
||||
var sdkAssemblyLoads = 0;
|
||||
using var source = new EventPipeEventSource(args[0]);
|
||||
source.Clr.MethodJittingStarted += data =>
|
||||
{
|
||||
var name = data.MethodNamespace + "." + data.MethodName;
|
||||
starts[name] = starts.GetValueOrDefault(name) + 1;
|
||||
if (!name.StartsWith("Opc.Ua.OpcUaExtensions.", StringComparison.Ordinal)) return;
|
||||
factoryNames[name] = factoryNames.GetValueOrDefault(name) + 1;
|
||||
factoryTokens.Add(data.MethodToken);
|
||||
factoryModules.Add(data.ModuleID);
|
||||
firstFactoryMs = Math.Min(firstFactoryMs, data.TimeStampRelativeMSec);
|
||||
lastFactoryMs = Math.Max(lastFactoryMs, data.TimeStampRelativeMSec);
|
||||
};
|
||||
source.Clr.MethodLoadVerbose += data =>
|
||||
{
|
||||
var tier = data.OptimizationTier.ToString();
|
||||
tiers[tier] = tiers.GetValueOrDefault(tier) + 1;
|
||||
nativeBytes[tier] = nativeBytes.GetValueOrDefault(tier) + data.MethodSize;
|
||||
if (data.MethodNamespace == "Opc.Ua.OpcUaExtensions") factoryNativeBytes += data.MethodSize;
|
||||
};
|
||||
source.Clr.LoaderAssemblyLoad += data =>
|
||||
{
|
||||
if (data.FullyQualifiedAssemblyName.StartsWith("Opc.Ua.", StringComparison.Ordinal)) sdkAssemblyLoads++;
|
||||
};
|
||||
source.Process();
|
||||
Console.WriteLine(JsonSerializer.Serialize(new
|
||||
{
|
||||
traceStartUtc = source.SessionStartTime.ToUniversalTime(),
|
||||
traceEndUtc = source.SessionEndTime.ToUniversalTime(),
|
||||
eventsLost = source.EventsLost,
|
||||
sdkAssemblyLoads,
|
||||
jittingStarts = starts.Values.Sum(),
|
||||
// Name keys omit generic signatures; token/module counts independently identify the factory set.
|
||||
distinctNameKeys = starts.Count,
|
||||
opcUaJittingStarts = starts.Where(x => x.Key.StartsWith("Opc.Ua.", StringComparison.Ordinal)).Sum(x => x.Value),
|
||||
invokeStubStarts = starts.Where(x => x.Key.StartsWith("dynamicClass.InvokeStub", StringComparison.Ordinal)).Sum(x => x.Value),
|
||||
tiers,
|
||||
summedLoadedNativeMethodBytes = nativeBytes,
|
||||
factory = new
|
||||
{
|
||||
starts = factoryNames.Values.Sum(), distinctNames = factoryNames.Count,
|
||||
distinctTokens = factoryTokens.Count, moduleCount = factoryModules.Count,
|
||||
everyNameOnce = factoryNames.Values.All(x => x == 1),
|
||||
firstMs = double.IsFinite(firstFactoryMs) ? (double?)firstFactoryMs : null,
|
||||
lastMs = factoryNames.Count == 0 ? (double?)null : lastFactoryMs,
|
||||
summedLoadedNativeMethodBytes = factoryNativeBytes,
|
||||
examples = factoryNames.Keys.Take(10).ToArray()
|
||||
}
|
||||
}, new JsonSerializerOptions { WriteIndented = true }));
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build optional local capture/summary tools using the user's installed dotnet-trace libraries."""
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--libraries", required=True, type=Path, help="dotnet-trace tools/net8.0/any directory")
|
||||
parser.add_argument("--output", required=True, type=Path, help="Fresh local output directory")
|
||||
args = parser.parse_args()
|
||||
args.output = args.output.resolve()
|
||||
args.output.mkdir(parents=True, exist_ok=False)
|
||||
for name, source, references in [
|
||||
("capture", "EventPipeCapture.cs", ["Microsoft.Diagnostics.NETCore.Client"]),
|
||||
("summary", "TraceSummary.cs", ["Microsoft.Diagnostics.Tracing.TraceEvent", "Microsoft.Diagnostics.FastSerialization", "Microsoft.Diagnostics.NETCore.Client"])
|
||||
]:
|
||||
output = args.output / name
|
||||
output.mkdir()
|
||||
project = ET.Element("Project", Sdk="Microsoft.NET.Sdk")
|
||||
properties = ET.SubElement(project, "PropertyGroup")
|
||||
for key, value in {"OutputType": "Exe", "TargetFramework": "net10.0", "ImplicitUsings": "enable",
|
||||
"EnableDefaultCompileItems": "false"}.items():
|
||||
ET.SubElement(properties, key).text = value
|
||||
items = ET.SubElement(project, "ItemGroup")
|
||||
ET.SubElement(items, "FrameworkReference", Include="Microsoft.AspNetCore.App")
|
||||
ET.SubElement(items, "Compile", Include=str(Path(__file__).resolve().parent / source))
|
||||
for reference in references:
|
||||
dll = (args.libraries / (reference + ".dll")).resolve(strict=True)
|
||||
item = ET.SubElement(items, "Reference", Include=reference)
|
||||
ET.SubElement(item, "HintPath").text = str(dll)
|
||||
path = output / (name + ".csproj")
|
||||
ET.indent(project)
|
||||
ET.ElementTree(project).write(path, encoding="unicode")
|
||||
with (output / "build.log").open("w") as log:
|
||||
result = subprocess.run(["dotnet", "build", str(path), "-c", "Release", "-m:2"], stdout=log, stderr=subprocess.STDOUT)
|
||||
if result.returncode:
|
||||
raise SystemExit(result.returncode)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,113 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run two already-built public lifecycle controls in isolated, offline Linux containers."""
|
||||
import argparse
|
||||
from datetime import datetime, timezone
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import secrets
|
||||
import subprocess
|
||||
import time
|
||||
import uuid
|
||||
|
||||
|
||||
def latest_sample(path):
|
||||
# A concurrently-written final line may be incomplete. All complete rows remain in the source log.
|
||||
for row in reversed(path.read_text().splitlines()):
|
||||
try:
|
||||
value = json.loads(row)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
if value.get("kind") == "sample":
|
||||
return value
|
||||
return None
|
||||
|
||||
|
||||
def run(options):
|
||||
options.output = options.output.resolve()
|
||||
options.output.mkdir(parents=True, exist_ok=False)
|
||||
processes = []
|
||||
environment = os.environ.copy()
|
||||
environment["OPCUA_MEMORY_PASSWORD"] = secrets.token_urlsafe(32)
|
||||
# Both receive the same environment. SDK1 ignores the SDK2 diagnostic opt-in.
|
||||
environment["OPCUA_CERTIFICATE_LEAK_TRACKING"] = "1"
|
||||
started = time.monotonic()
|
||||
try:
|
||||
for label, directory in [("baseline", options.baseline), ("candidate", options.candidate)]:
|
||||
directory = directory.resolve(strict=True)
|
||||
for required in ["control.dll", "control.deps.json", "control.runtimeconfig.json"]:
|
||||
if not (directory / required).is_file():
|
||||
raise FileNotFoundError("Use a complete, canonically-built control output: missing " + required)
|
||||
output = options.output / label
|
||||
output.mkdir()
|
||||
container = "opcua-memory-" + label + "-" + uuid.uuid4().hex[:10]
|
||||
shell = ('dotnet /app/control.dll /task/private-fixture ' + str(options.seconds) +
|
||||
' > /task/samples.jsonl 2>&1 & child=$!; echo "$child" > /task/dotnet.pid; '
|
||||
'wait "$child"; status=$?; echo "$status" > /task/exit-status.txt; exit "$status"')
|
||||
command = ["docker", "run", "--rm", "--name", container, "--network", "none",
|
||||
"--env", "OPCUA_MEMORY_PASSWORD", "--env", "OPCUA_CERTIFICATE_LEAK_TRACKING",
|
||||
"-v", str(directory) + ":/app:ro", "-v", str(output) + ":/task",
|
||||
options.image, "bash", "-c", shell]
|
||||
manifest = {"argv": command, "fullGate": False, "seconds": options.seconds,
|
||||
"environmentNamesOnly": ["OPCUA_MEMORY_PASSWORD", "OPCUA_CERTIFICATE_LEAK_TRACKING"],
|
||||
"startedUtc": datetime.now(timezone.utc).isoformat(),
|
||||
"runtimeFiles": {p.name: hashlib.sha256(p.read_bytes()).hexdigest()
|
||||
for p in sorted(directory.iterdir()) if p.is_file()}}
|
||||
(output / "command.json").write_text(json.dumps(manifest, indent=2) + "\n")
|
||||
stream = (output / "container.log").open("w")
|
||||
process = subprocess.Popen(command, env=environment, stdout=stream, stderr=subprocess.STDOUT)
|
||||
processes.append((label, container, output, stream, process))
|
||||
next_sample = started + 60
|
||||
while any(process.poll() is None for _, _, _, _, process in processes):
|
||||
if time.monotonic() - started > options.seconds + 240:
|
||||
raise TimeoutError("Diagnostic controls exceeded bounded shutdown allowance")
|
||||
if time.monotonic() >= next_sample:
|
||||
for label, container, output, _, process in processes:
|
||||
pid_file = output / "dotnet.pid"
|
||||
if process.poll() is not None or not pid_file.exists():
|
||||
continue
|
||||
pid = pid_file.read_text().strip()
|
||||
if not pid.isdecimal():
|
||||
raise RuntimeError("Unexpected task-owned process id")
|
||||
stamp = str(int(time.time()))
|
||||
latest = latest_sample(output / "samples.jsonl")
|
||||
with (output / "observations.jsonl").open("a") as observed:
|
||||
observed.write(json.dumps({"utc": datetime.now(timezone.utc).isoformat(),
|
||||
"seconds": time.monotonic() - started,
|
||||
"sample": latest}) + "\n")
|
||||
for name in ["smaps", "smaps_rollup", "status"]:
|
||||
with (output / (stamp + "-" + name + ".txt")).open("w") as destination:
|
||||
result = subprocess.run(["docker", "exec", container, "cat", "/proc/" + pid + "/" + name],
|
||||
stdout=destination, stderr=subprocess.STDOUT, timeout=15)
|
||||
if result.returncode and process.poll() is None:
|
||||
raise RuntimeError(label + " live process diagnostic read failed")
|
||||
next_sample += 60
|
||||
time.sleep(.5)
|
||||
statuses = []
|
||||
for label, _, output, _, process in processes:
|
||||
status = process.wait()
|
||||
(output / "docker-exit-status.txt").write_text(str(status) + "\n")
|
||||
print(label + " exit " + str(status), flush=True)
|
||||
statuses.append(status)
|
||||
return 0 if all(status == 0 for status in statuses) else 1
|
||||
finally:
|
||||
for _, container, _, stream, process in processes:
|
||||
if process.poll() is None:
|
||||
subprocess.run(["docker", "stop", "--time", "10", container], stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL, timeout=20)
|
||||
process.wait(timeout=20)
|
||||
stream.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--baseline", type=Path, required=True, help="Already-built control output directory")
|
||||
parser.add_argument("--candidate", type=Path, required=True, help="Already-built control output directory")
|
||||
parser.add_argument("--output", type=Path, required=True, help="New local diagnostic directory")
|
||||
parser.add_argument("--image", required=True, help="Immutable Linux .NET10 image digest")
|
||||
parser.add_argument("--seconds", type=int, default=1800)
|
||||
options = parser.parse_args()
|
||||
if not 1 <= options.seconds <= 1800 or not options.image.startswith("sha256:"):
|
||||
parser.error("Use an immutable image digest and duration 1..1800; never a full-soak gate")
|
||||
raise SystemExit(run(options))
|
||||
@@ -1,134 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build opt-in controls against existing published DLLs; never rebuild product code."""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import secrets
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
|
||||
def digest(path):
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("kind", choices=["lifecycle", "certificate", "telemetry"])
|
||||
parser.add_argument("--references", type=Path, action="append", required=True,
|
||||
help="Existing published DLL directory; first directory wins duplicate names")
|
||||
parser.add_argument("--output", type=Path, required=True, help="New local artifact directory")
|
||||
parser.add_argument("--sdk2", action="store_true")
|
||||
parser.add_argument("--seconds", type=int, default=600)
|
||||
parser.add_argument("--mode", help="Certificate: public/pfx/shared-copy/sdk-copy; telemetry: dispose/undisposed")
|
||||
parser.add_argument("--build-only", action="store_true")
|
||||
parser.add_argument("--certificate-tracking", action="store_true", help="SDK2 opt-in allocation-stack diagnostics")
|
||||
options = parser.parse_args()
|
||||
if options.kind != "lifecycle" and not options.sdk2:
|
||||
parser.error("certificate and telemetry controls require --sdk2")
|
||||
if not 1 <= options.seconds <= 1800:
|
||||
parser.error("Diagnostic duration must be 1..1800 seconds; this is never a full soak")
|
||||
options.output = options.output.resolve()
|
||||
options.output.mkdir(parents=True, exist_ok=False)
|
||||
names = {"lifecycle": "LifecycleControl.cs", "certificate": "CertificateControl.cs", "telemetry": "TelemetryControl.cs"}
|
||||
source = Path(__file__).resolve().parent / names[options.kind]
|
||||
references = {}
|
||||
for directory in options.references:
|
||||
directory = directory.resolve(strict=True)
|
||||
for dll in sorted(directory.glob("*.dll")):
|
||||
references.setdefault(dll.name, dll)
|
||||
if "Opc.Ua.Core.dll" not in references:
|
||||
parser.error("References must contain actual OPC UA managed assemblies")
|
||||
project = ET.Element("Project", Sdk="Microsoft.NET.Sdk")
|
||||
properties = ET.SubElement(project, "PropertyGroup")
|
||||
for key, value in {"OutputType": "Exe", "TargetFramework": "net10.0", "ImplicitUsings": "enable",
|
||||
"Nullable": "enable", "EnableDefaultCompileItems": "false",
|
||||
"DefineConstants": "SDK2" if options.sdk2 else ""}.items():
|
||||
ET.SubElement(properties, key).text = value
|
||||
items = ET.SubElement(project, "ItemGroup")
|
||||
ET.SubElement(items, "FrameworkReference", Include="Microsoft.AspNetCore.App")
|
||||
ET.SubElement(items, "Compile", Include=str(source))
|
||||
for name, path in references.items():
|
||||
reference = ET.SubElement(items, "Reference", Include=path.stem)
|
||||
ET.SubElement(reference, "HintPath").text = str(path)
|
||||
project_path = options.output / "control.csproj"
|
||||
ET.indent(project)
|
||||
ET.ElementTree(project).write(project_path, encoding="unicode")
|
||||
manifest = {"kind": options.kind, "sdk2": options.sdk2, "seconds": options.seconds,
|
||||
"fullSoakGate": False, "source": {source.name: digest(source)},
|
||||
"referencePrecedence": "first directory wins duplicate names",
|
||||
"references": {name: digest(path) for name, path in references.items()}}
|
||||
(options.output / "provenance.json").write_text(json.dumps(manifest, indent=2) + "\n")
|
||||
with (options.output / "build.log").open("w") as log:
|
||||
result = subprocess.run(["dotnet", "build", str(project_path), "-c", "Release", "-m:2"],
|
||||
stdout=log, stderr=subprocess.STDOUT)
|
||||
if result.returncode:
|
||||
return result.returncode
|
||||
if options.build_only:
|
||||
return 0
|
||||
executable = options.output / "bin/Release/net10.0/control.dll"
|
||||
environment = os.environ.copy()
|
||||
environment["OPCUA_MEMORY_PASSWORD"] = secrets.token_urlsafe(32)
|
||||
if options.certificate_tracking:
|
||||
environment["OPCUA_CERTIFICATE_LEAK_TRACKING"] = "1"
|
||||
if options.kind == "lifecycle":
|
||||
command = ["dotnet", str(executable), str(options.output / "private-fixture"), str(options.seconds)]
|
||||
with (options.output / "samples.jsonl").open("w") as log:
|
||||
result = subprocess.run(command, env=environment, stdout=log, stderr=subprocess.STDOUT,
|
||||
timeout=options.seconds + 180)
|
||||
status = result.returncode
|
||||
elif options.kind == "telemetry":
|
||||
if options.mode not in ["dispose", "undisposed"]:
|
||||
parser.error("Telemetry mode must be dispose or undisposed")
|
||||
with (options.output / "samples.jsonl").open("w") as log:
|
||||
result = subprocess.run(["dotnet", str(executable), options.mode], env=environment,
|
||||
stdout=log, stderr=subprocess.STDOUT, timeout=180)
|
||||
status = result.returncode
|
||||
else:
|
||||
if sys.platform != "darwin":
|
||||
parser.error("Native certificate control currently requires macOS heap/vmmap")
|
||||
if options.mode not in ["public", "pfx", "shared-copy", "sdk-copy"]:
|
||||
parser.error("Certificate mode must be public, pfx, shared-copy or sdk-copy")
|
||||
status = certificate_control(executable, options.output, options.mode, environment)
|
||||
(options.output / "exit-status.txt").write_text(str(status) + "\n")
|
||||
return status
|
||||
|
||||
|
||||
def certificate_control(executable, output, mode, environment):
|
||||
# Native snapshots contain process metadata; retain locally, do not publish them unredacted.
|
||||
with (output / "samples.jsonl").open("w") as log:
|
||||
process = subprocess.Popen(["dotnet", str(executable), mode, str(output)], env=environment,
|
||||
stdout=log, stderr=subprocess.STDOUT)
|
||||
try:
|
||||
deadline = time.monotonic() + 180
|
||||
while not (output / "samples.jsonl").stat().st_size:
|
||||
if process.poll() is not None or time.monotonic() > deadline:
|
||||
raise RuntimeError("Certificate control failed before ready")
|
||||
time.sleep(.05)
|
||||
for label in ["before", "after"]:
|
||||
if label == "after":
|
||||
(output / "go").touch()
|
||||
while True:
|
||||
rows = (output / "samples.jsonl").read_text().splitlines()
|
||||
if rows and json.loads(rows[-1]).get("kind") == "done":
|
||||
break
|
||||
if process.poll() is not None or time.monotonic() > deadline:
|
||||
raise RuntimeError("Certificate control did not finish")
|
||||
time.sleep(.1)
|
||||
with (output / ("heap-" + label + ".txt")).open("w") as log_heap:
|
||||
subprocess.run(["/usr/bin/heap", "--noContent", "-s", str(process.pid)],
|
||||
stdout=log_heap, stderr=subprocess.STDOUT, timeout=20, check=True)
|
||||
(output / "stop").touch()
|
||||
return process.wait(timeout=10)
|
||||
finally:
|
||||
if process.poll() is None:
|
||||
process.terminate()
|
||||
process.wait(timeout=10)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Emit mapping-category totals without path names from local Linux /proc/PID/smaps."""
|
||||
import argparse
|
||||
from collections import defaultdict
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
HEADER = re.compile(r"^[0-9a-f]+-[0-9a-f]+\s+(\S+)\s+\S+\s+\S+\s+\S+\s*(.*)$")
|
||||
METRIC = re.compile(r"^(Size|Rss|Pss|Private_Dirty|Private_Clean|Anonymous):\s+(\d+) kB$")
|
||||
|
||||
|
||||
def summarize(path):
|
||||
groups = defaultdict(lambda: defaultdict(int))
|
||||
group = None
|
||||
for line in path.read_text().splitlines():
|
||||
header = HEADER.match(line)
|
||||
if header:
|
||||
permissions, name = header.groups()
|
||||
if "doublemapper" in name:
|
||||
group = "jit-doublemapper"
|
||||
elif name == "[heap]":
|
||||
group = "native-heap"
|
||||
elif name.startswith("[stack"):
|
||||
group = "stack"
|
||||
elif not name:
|
||||
group = "anonymous-exec" if "x" in permissions else "anonymous-write" if "w" in permissions else "anonymous-read"
|
||||
elif name.endswith(".dll"):
|
||||
group = "managed-assembly-files"
|
||||
elif ".so" in name:
|
||||
group = "native-library-files"
|
||||
elif name.startswith("["):
|
||||
group = "other-kernel-mappings"
|
||||
else:
|
||||
group = "other-files"
|
||||
elif group and (metric := METRIC.match(line)):
|
||||
groups[group][metric[1]] += int(metric[2]) * 1024
|
||||
return dict(groups)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("files", nargs="+", type=Path)
|
||||
args = parser.parse_args()
|
||||
print(json.dumps([{"sample": index, "groups": summarize(path)} for index, path in enumerate(args.files)], indent=2))
|
||||
@@ -1,374 +0,0 @@
|
||||
#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() { }
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,34 +0,0 @@
|
||||
#!/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"
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"Id": "sha256:b04611ee9e1b7d36797012ed921962c57189cb4dbb3dff12afb8e158110f293b",
|
||||
"RepoDigests": [
|
||||
"mcr.microsoft.com/dotnet/sdk@sha256:e362a8dbcd691522456da26a5198b8f3ca1d7641c95624fadc5e3e82678bd08a"
|
||||
],
|
||||
"Architecture": "arm64",
|
||||
"Os": "linux"
|
||||
}
|
||||
-534
@@ -1,534 +0,0 @@
|
||||
{
|
||||
"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
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
-792
@@ -1,792 +0,0 @@
|
||||
[
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
-202
@@ -1,202 +0,0 @@
|
||||
[
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
-1828
File diff suppressed because it is too large
Load Diff
-17
@@ -1,17 +0,0 @@
|
||||
{"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}
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"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"}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"WorkingSet64": 231833600,
|
||||
"PrivateMemorySize64": 0,
|
||||
"HandleCount": 0,
|
||||
"threads": 31,
|
||||
"cpuMilliseconds": 1531.714,
|
||||
"allocatedBytes": 156774688,
|
||||
"collections": [
|
||||
20,
|
||||
8,
|
||||
2
|
||||
]
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
{"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}
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"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"}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"WorkingSet64": 201850880,
|
||||
"PrivateMemorySize64": 0,
|
||||
"HandleCount": 0,
|
||||
"threads": 31,
|
||||
"cpuMilliseconds": 2559.57,
|
||||
"allocatedBytes": 47810656,
|
||||
"collections": [
|
||||
5,
|
||||
1,
|
||||
0
|
||||
]
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
{"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}
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"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"}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"WorkingSet64": 229752832,
|
||||
"PrivateMemorySize64": 0,
|
||||
"HandleCount": 0,
|
||||
"threads": 30,
|
||||
"cpuMilliseconds": 1699.059,
|
||||
"allocatedBytes": 157134200,
|
||||
"collections": [
|
||||
20,
|
||||
8,
|
||||
2
|
||||
]
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
{"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}
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"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"}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"WorkingSet64": 201097216,
|
||||
"PrivateMemorySize64": 0,
|
||||
"HandleCount": 0,
|
||||
"threads": 30,
|
||||
"cpuMilliseconds": 2626.114,
|
||||
"allocatedBytes": 47846920,
|
||||
"collections": [
|
||||
5,
|
||||
1,
|
||||
0
|
||||
]
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
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.
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
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.
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
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
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
{"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}
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"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"}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"WorkingSet64": 194039808,
|
||||
"PrivateMemorySize64": 266993664,
|
||||
"HandleCount": 141,
|
||||
"threads": 19,
|
||||
"cpuMilliseconds": 1705.768,
|
||||
"allocatedBytes": 149139552,
|
||||
"collections": [
|
||||
5,
|
||||
4,
|
||||
1
|
||||
]
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
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.
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
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.
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
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
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
{"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}
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"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"}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"WorkingSet64": 195645440,
|
||||
"PrivateMemorySize64": 288792576,
|
||||
"HandleCount": 153,
|
||||
"threads": 26,
|
||||
"cpuMilliseconds": 2091.525,
|
||||
"allocatedBytes": 42602784,
|
||||
"collections": [
|
||||
1,
|
||||
0,
|
||||
0
|
||||
]
|
||||
}
|
||||
-757
@@ -1,757 +0,0 @@
|
||||
[
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
-637
@@ -1,637 +0,0 @@
|
||||
[
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
-170
@@ -1,170 +0,0 @@
|
||||
[
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
-683
@@ -1,683 +0,0 @@
|
||||
[
|
||||
{
|
||||
"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
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
-220
@@ -1,220 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
#!/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())
|
||||
@@ -1,194 +0,0 @@
|
||||
#!/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,6 +1533,9 @@ 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.
|
||||
@@ -1542,10 +1545,6 @@ 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)
|
||||
.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);
|
||||
.GetFields(BindingFlags.Public | BindingFlags.Static)
|
||||
.Where(f => f.IsLiteral && f.FieldType == typeof(uint))
|
||||
.ToDictionary(f => f.Name, f => (uint)f.GetRawConstantValue()!, 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.Code);
|
||||
new Opc.Ua.StatusCode(UncertainInterpolated).CodeBits.ShouldBe(Opc.Ua.StatusCodes.UncertainDataSubNormal);
|
||||
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.Code);
|
||||
new Opc.Ua.StatusCode(NoDataCalc).CodeBits.ShouldBe(Opc.Ua.StatusCodes.BadNoData.Code);
|
||||
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(NoDataCalc).AggregateBits.ShouldBe(Opc.Ua.AggregateBits.Calculated);
|
||||
new Opc.Ua.StatusCode(InvalidInputsCalc).CodeBits.ShouldBe(Opc.Ua.StatusCodes.BadAggregateInvalidInputs.Code);
|
||||
new Opc.Ua.StatusCode(InvalidInputsCalc).CodeBits.ShouldBe(Opc.Ua.StatusCodes.BadAggregateInvalidInputs);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
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 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
|
||||
/// 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
|
||||
/// 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.
|
||||
@@ -18,7 +17,6 @@ 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);
|
||||
|
||||
@@ -45,7 +43,22 @@ public static class SdkAggregateOracle
|
||||
var id = AggregateId(aggregate);
|
||||
var ms = interval.TotalMilliseconds;
|
||||
var cfg = DefaultConfiguration;
|
||||
IAggregateCalculator calc = CompatibilityAggregateFactory.Create(id, start, end, ms, false, cfg, Telemetry);
|
||||
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"),
|
||||
};
|
||||
|
||||
// The SDK expects values in the direction of the read: ascending forward, descending reverse.
|
||||
var ordered = samples.OrderBy(s => s.Time).ToList();
|
||||
@@ -55,8 +68,9 @@ public static class SdkAggregateOracle
|
||||
var results = new List<Result>();
|
||||
for (var guard = 0; guard < 100_000; guard++)
|
||||
{
|
||||
if (!calc.TryGetProcessedValue(true, out var dv)) break;
|
||||
results.Add(new Result(dv.WrappedValue.AsBoxedObject(Variant.BoxingBehavior.Legacy), dv.StatusCode.Code, (DateTime)dv.SourceTimestamp));
|
||||
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)));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
@@ -109,16 +123,20 @@ 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(
|
||||
v.Kind switch
|
||||
private static DataValue ToDataValue(DateTime t, HistorianValue v, uint status) => new()
|
||||
{
|
||||
WrappedValue = v.Kind switch
|
||||
{
|
||||
HistorianValueKind.Empty => Variant.Null,
|
||||
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),
|
||||
HistorianValueKind.Int64 => new Variant(v.Integer),
|
||||
HistorianValueKind.Boolean => new Variant(v.Number != 0),
|
||||
HistorianValueKind.String => new Variant(v.Text),
|
||||
_ => new Variant(v.Number),
|
||||
},
|
||||
status, t, t);
|
||||
StatusCode = status,
|
||||
SourceTimestamp = t,
|
||||
ServerTimestamp = t,
|
||||
};
|
||||
|
||||
private static NodeId AggregateId(HistoryAggregateType a) => a switch
|
||||
{
|
||||
|
||||
+3
-3
@@ -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.Code);
|
||||
bound!.StatusCode.ShouldBe(Opc.Ua.StatusCodes.BadBoundNotFound);
|
||||
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.Code);
|
||||
missing.StatusCode.ShouldBe(Opc.Ua.StatusCodes.BadBoundNotFound);
|
||||
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.Code);
|
||||
missing.StatusCode.ShouldBe(Opc.Ua.StatusCodes.BadBoundNotFound);
|
||||
missing.SourceTimestampUtc.ShouldBe(beyond);
|
||||
fake.ReadRawCallCount.ShouldBe(calls, "a bound beyond the horizon is not found without a read");
|
||||
source.GetHealthSnapshot().TotalFailures.ShouldBe(0);
|
||||
|
||||
+19
-22
@@ -106,7 +106,7 @@ public sealed class GatewayCeilingPagingTests : IDisposable
|
||||
try
|
||||
{
|
||||
var first = ReadRaw(nm, nodeId, At(0), At(3600), 5000, null);
|
||||
first.Error.StatusCode.ShouldBe(StatusCodes.Good);
|
||||
first.Error.StatusCode.Code.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.ShouldBe(StatusCodes.Good);
|
||||
status.Code.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.ShouldBe(StatusCodes.Good);
|
||||
status.Code.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 == StatusCodes.Good) values.ShouldBe(expected);
|
||||
if (status.Code == StatusCodes.Good) values.ShouldBe(expected);
|
||||
else
|
||||
{
|
||||
status.ShouldBe(StatusCodes.BadTooManyOperations);
|
||||
status.Code.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.ShouldBe(StatusCodes.Good);
|
||||
status.Code.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.ShouldBe(StatusCodes.Good);
|
||||
status.Code.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.ShouldBe(StatusCodes.Good);
|
||||
status.Code.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.ShouldBe(StatusCodes.Good);
|
||||
r.Error.StatusCode.Code.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.ShouldBe(StatusCodes.Good);
|
||||
status.Code.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.ShouldBe(StatusCodes.Good);
|
||||
r.Error.StatusCode.Code.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.ShouldBe(StatusCodes.Good);
|
||||
status.Code.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.ShouldBe(StatusCodes.BadNoCommunication);
|
||||
r.Error.StatusCode.Code.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, requestLifetime: RequestLifetime.None, identity: null);
|
||||
var context = new OperationContext(new RequestHeader(), secureChannelContext: null, RequestType.HistoryRead, identity: null);
|
||||
var details = new ReadRawModifiedDetails
|
||||
{
|
||||
ReturnBounds = returnBounds,
|
||||
@@ -537,19 +537,16 @@ public sealed class GatewayCeilingPagingTests : IDisposable
|
||||
EndTime = end,
|
||||
NumValuesPerNode = max,
|
||||
};
|
||||
var nodesToRead = new List<HistoryReadValueId> { new() { NodeId = nodeId, ContinuationPoint = inboundCp is null ? default : ByteString.From(inboundCp) } };
|
||||
var nodesToRead = new List<HistoryReadValueId> { new() { NodeId = nodeId, ContinuationPoint = 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 { 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);
|
||||
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);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
+8
-11
@@ -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.ShouldBe(StatusCodes.Good);
|
||||
status.Code.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.ShouldBe(StatusCodes.Good);
|
||||
status.Code.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,24 +277,21 @@ 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, requestLifetime: RequestLifetime.None, identity: null);
|
||||
var context = new OperationContext(new RequestHeader(), secureChannelContext: null, RequestType.HistoryRead, 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 is null ? default : ByteString.From(inboundCp) } };
|
||||
var nodesToRead = new List<HistoryReadValueId> { new() { NodeId = nodeId, ContinuationPoint = 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 { 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);
|
||||
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);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
-2
@@ -23,8 +23,6 @@
|
||||
</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>
|
||||
|
||||
+4
-4
@@ -9,7 +9,7 @@ public sealed class StatusCodeOracleTests
|
||||
[Fact]
|
||||
public void Calculated_bits_match_the_SDK()
|
||||
{
|
||||
var sdk = Opc.Ua.StatusCodes.Good.WithAggregateBits(Opc.Ua.AggregateBits.Calculated);
|
||||
var sdk = new Opc.Ua.StatusCode(Opc.Ua.StatusCodes.Good).SetAggregateBits(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 = Opc.Ua.StatusCodes.Good.WithAggregateBits(Opc.Ua.AggregateBits.Interpolated);
|
||||
var sdk = new Opc.Ua.StatusCode(Opc.Ua.StatusCodes.Good).SetAggregateBits(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>WithAggregateBits</c> replaces, it does not OR.
|
||||
/// agrees — <c>SetAggregateBits</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.Code);
|
||||
HistorianStatusCodes.Bad.ShouldBe(Opc.Ua.StatusCodes.Bad);
|
||||
|
||||
[Fact]
|
||||
public void Info_type_is_data_value() =>
|
||||
|
||||
-2
@@ -28,8 +28,6 @@
|
||||
</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>
|
||||
|
||||
|
||||
-2
@@ -22,8 +22,6 @@
|
||||
</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>
|
||||
|
||||
+1
-14
@@ -137,7 +137,6 @@ 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) =
|
||||
@@ -146,15 +145,6 @@ 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)) }],
|
||||
@@ -166,11 +156,8 @@ 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)
|
||||
&& grantedEvents.Of(grantedItem).Any(NamesEqA));
|
||||
await RecordedEvents.WaitUntilAsync(() => narrowEvents.Of(narrowItem).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");
|
||||
|
||||
|
||||
+1
-2
@@ -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).GetProperty("AggregateFunction_" + aggregateName)!.GetValue(null)!;
|
||||
var aggregateId = (NodeId)typeof(ObjectIds).GetField("AggregateFunction_" + aggregateName)!.GetValue(null)!;
|
||||
|
||||
(await AdvertisedAsync(session, ObjectIds.HistoryServerCapabilities_AggregateFunctions, ct))
|
||||
.ShouldNotContain(aggregateName);
|
||||
@@ -164,7 +164,6 @@ public sealed class AdvertisedAggregateFunctionsWireTests
|
||||
{
|
||||
AggregateType = aggregateId,
|
||||
ProcessingInterval = 1000,
|
||||
AggregateConfiguration = new AggregateConfiguration { UseServerCapabilitiesDefaults = true },
|
||||
StartTime = DateTime.UtcNow,
|
||||
},
|
||||
};
|
||||
|
||||
+4146
-6151
File diff suppressed because one or more lines are too long
+12
-38
@@ -11,14 +11,9 @@ 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
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public async Task All_37_factories_preserve_the_6144_baseline_vectors(bool throughRuntimeManager)
|
||||
[Fact]
|
||||
public void All_37_factories_preserve_the_4144_baseline_vectors()
|
||||
{
|
||||
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);
|
||||
@@ -27,32 +22,22 @@ 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 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;
|
||||
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;
|
||||
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);
|
||||
accepted.Add(calculator.QueueRawValue(new DataValue(SdkValueBoundary.FromClr(Payload(sample)), sample.Status, time, time.AddTicks(sample.ServerOffsetTicks), sample.SourcePicoseconds, sample.ServerPicoseconds)));
|
||||
calculator.QueueRawValue(new DataValue(SdkValueBoundary.FromClr(Payload(sample)), sample.Status, time, time));
|
||||
if (vector.Incremental) Drain(false);
|
||||
}
|
||||
Drain(true);
|
||||
}
|
||||
catch (Exception ex) { error = ex.GetType().FullName; }
|
||||
if (error != vector.Exception || !output.SequenceEqual(vector.Output) || (vector.QueueAccepted is { } expectedAccepted && !accepted.SequenceEqual(expectedAccepted)))
|
||||
if (error != vector.Exception || !output.SequenceEqual(vector.Output))
|
||||
{
|
||||
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)}");
|
||||
}
|
||||
@@ -62,31 +47,20 @@ 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, LegacyTimestampTicks(value.SourceTimestamp), LegacyTimestampTicks(value.ServerTimestamp), value.SourcePicoseconds, value.ServerPicoseconds));
|
||||
output.Add(new(partial, payload?.GetType().Name, Format(payload), value.StatusCode.Code, ((DateTime)value.SourceTimestamp).Ticks, ((DateTime)value.ServerTimestamp).Ticks, value.SourcePicoseconds, value.ServerPicoseconds));
|
||||
}
|
||||
throw new InvalidOperationException("Baseline calculator did not terminate.");
|
||||
}
|
||||
}
|
||||
baseline.Vectors.Length.ShouldBe(6144);
|
||||
baseline.Vectors.Length.ShouldBe(4144);
|
||||
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.");
|
||||
}
|
||||
|
||||
// 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 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 };
|
||||
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,ushort SourcePicoseconds = 0,ushort ServerPicoseconds = 0,long ServerOffsetTicks = 0);
|
||||
record Sample(double Seconds,string Type,string? Payload,uint Status);
|
||||
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, Configuration? Configuration = null,bool[]? QueueAccepted = null);
|
||||
record Configuration(bool TreatUncertainAsBad,byte PercentDataBad,byte PercentDataGood,bool UseSlopedExtrapolation,bool UseServerCapabilitiesDefaults);
|
||||
record Vector(string Aggregate,string DataSet,bool Reverse,bool Stepped,bool UncertainAsBad,bool Incremental,Value[] Output,string? Exception);
|
||||
}
|
||||
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
-119
@@ -1,119 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
-156
@@ -1,156 +0,0 @@
|
||||
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() { }
|
||||
}
|
||||
}
|
||||
-697
@@ -1,697 +0,0 @@
|
||||
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.Runtime.ExceptionServices;
|
||||
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 "profile" or "full").ShouldBeTrue("explicit diagnostic, profile 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 if (mode == "profile") seconds.ShouldBeInRange(60, 1800); // Investigative only; never satisfies the full-duration flag.
|
||||
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;
|
||||
ExceptionDispatchInfo? firstRejectedBudget = 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);
|
||||
// Retain the failing observation too; rejected diagnostic rows never count as passing quiescent samples.
|
||||
await LogAsync(evidence, new { kind = "resource-observation", resources = sample });
|
||||
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);
|
||||
if (firstRejectedBudget is null)
|
||||
{
|
||||
try
|
||||
{
|
||||
sample.Descriptors.ShouldBeLessThanOrEqualTo(baseline.Descriptors + 32);
|
||||
sample.ManagedBytes.ShouldBeLessThanOrEqualTo(baseline.ManagedBytes + (64L << 20));
|
||||
sample.RssBytes.ShouldBeLessThanOrEqualTo(baseline.RssBytes + (128L << 20));
|
||||
}
|
||||
catch (ShouldAssertException error) when (mode == "profile")
|
||||
{
|
||||
// Investigative sampling may continue after rejection, but can never turn it into a pass.
|
||||
firstRejectedBudget = ExceptionDispatchInfo.Capture(error);
|
||||
await LogAsync(evidence, new { kind = "budget-rejected", cycle = cycles,
|
||||
seconds = watch.Elapsed.TotalSeconds, resources = sample, baseline, failure = error.ToString() });
|
||||
}
|
||||
}
|
||||
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 = firstRejectedBudget is null ? "quiescent" : "quiescent-budget-rejected",
|
||||
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 });
|
||||
}
|
||||
}
|
||||
// Preserve the first assertion and its original stack even if later RSS samples decrease.
|
||||
// This happens before any completion record; finally still releases the owned rig and client.
|
||||
firstRejectedBudget?.Throw();
|
||||
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, long GcCommittedBytes, long GcHeapBytes, long GcFragmentedBytes,
|
||||
long PinnedObjects, long PendingFinalizers, long JitMethods, long JitIlBytes, long ActiveTimers, int Threads);
|
||||
|
||||
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);
|
||||
var memory = GC.GetGCMemoryInfo();
|
||||
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), memory.TotalCommittedBytes,
|
||||
memory.HeapSizeBytes, memory.FragmentedBytes, memory.PinnedObjectsCount, memory.FinalizationPendingCount,
|
||||
System.Runtime.JitInfo.GetCompiledMethodCount(), System.Runtime.JitInfo.GetCompiledILBytes(), Timer.ActiveCount,
|
||||
process.Threads.Count);
|
||||
}
|
||||
|
||||
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(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
-242
@@ -1,242 +0,0 @@
|
||||
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,8 +46,6 @@ 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,31 +41,6 @@ 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>
|
||||
|
||||
+3
-2
@@ -28,6 +28,7 @@ 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;
|
||||
|
||||
@@ -504,9 +505,9 @@ public sealed class NativeAlarmRestatementEndToEndTests : RuntimeActorTestBase
|
||||
}
|
||||
|
||||
/// <summary>An upstream A&C condition event, in the driver's select-clause order.</summary>
|
||||
private static Variant[] ConditionFields(bool active, bool acked) =>
|
||||
private static VariantCollection ConditionFields(bool active, bool acked) =>
|
||||
[
|
||||
new Variant(Opc.Ua.ByteString.From(new byte[] { 1 })), // EventId
|
||||
new Variant(new byte[] { 1 }), // EventId
|
||||
new Variant(new UaNodeId("TripAlarmType", 3)), // EventType
|
||||
new Variant(UaNodeId.Parse(UpstreamSource)), // SourceNode
|
||||
new Variant(new LocalizedText("tripped")), // Message
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Akka.Actor;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Opc.Ua.Server;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Redundancy;
|
||||
@@ -13,7 +14,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="OtOpcUaSdkServer"/>,
|
||||
/// <c>Server.ServiceLevel</c> visible on the wire. Boots a real <see cref="StandardServer"/>,
|
||||
/// 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
|
||||
@@ -34,7 +35,7 @@ public sealed class ServiceLevelEndToEndTests : RuntimeActorTestBase
|
||||
port => new OpcUaApplicationHost(
|
||||
BuildOptions("PrimaryLeader", pkiRoot, port),
|
||||
NullLogger<OpcUaApplicationHost>.Instance),
|
||||
() => new OtOpcUaSdkServer(), Ct);
|
||||
() => new StandardServer(), Ct);
|
||||
await using var host = started.Host;
|
||||
var server = started.Server;
|
||||
|
||||
@@ -57,7 +58,7 @@ public sealed class ServiceLevelEndToEndTests : RuntimeActorTestBase
|
||||
CorrelationId: CorrelationId.NewId()));
|
||||
|
||||
AwaitAssertion(() =>
|
||||
server.CurrentInstance.ServerObject.ServiceLevel.ShouldNotBeNull().Value.ShouldBe((byte)240));
|
||||
server.CurrentInstance.ServerObject.ServiceLevel.Value.ShouldBe((byte)240));
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -76,7 +77,7 @@ public sealed class ServiceLevelEndToEndTests : RuntimeActorTestBase
|
||||
port => new OpcUaApplicationHost(
|
||||
BuildOptions("Secondary", pkiRoot, port),
|
||||
NullLogger<OpcUaApplicationHost>.Instance),
|
||||
() => new OtOpcUaSdkServer(), Ct);
|
||||
() => new StandardServer(), Ct);
|
||||
await using var host = started.Host;
|
||||
var server = started.Server;
|
||||
|
||||
@@ -99,7 +100,7 @@ public sealed class ServiceLevelEndToEndTests : RuntimeActorTestBase
|
||||
CorrelationId: CorrelationId.NewId()));
|
||||
|
||||
AwaitAssertion(() =>
|
||||
server.CurrentInstance.ServerObject.ServiceLevel.ShouldNotBeNull().Value.ShouldBe((byte)100));
|
||||
server.CurrentInstance.ServerObject.ServiceLevel.Value.ShouldBe((byte)100));
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user