1578efd4a9
Review 01 L3/H5/H4/P6/P4. - L3/H5 OnEquipmentTagWrite's dispatch is factored into DispatchDeviceWrite: a gateway that throws synchronously is refused inline with BadDeviceFailure (it used to escape the handler under the SDK Lock), and a faulted/cancelled dispatch task has its exception observed and logged and carries the real reason into the revert/audit event instead of surfacing as UnobservedTaskException at finalization. - H4 every timestamp the manager stamps (condition quality, timed-shelve expiry, method-transition events, carry-over, revert, revalidation) reads the injected Clock; RemainingShelveMs takes the clock reading. - P6 OpcUa:Quotas:* (OpcUaQuotaOptions on OpcUaApplicationHostOptions): Min/MaxRequestThreadCount, MaxQueuedRequestCount with the former hard-coded 5/100/200 as defaults, and the TransportQuotas members as nullable overrides over the SDK defaults. - P4 the SDK line exposes IHistoryReadAsyncNodeManager but CustomNodeManager2 offers no async virtual to override (adopting it means re-implementing the base dispatcher), so the lever is the admission cap: MaxConcurrentHistoryReadBatches defaults to clamp(2*ProcessorCount, 4, 16). The Host still overrides it from ServerHistorianOptions (default 16) — follow-up F3. Tests: NodeManagerWriteDispatchTests (sync throw refused; faulted task reverts; success leaves the value; clock-stamped revert), OpcUaApplicationHostQuotaTests. Refs #732 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
75 lines
2.8 KiB
C#
75 lines
2.8 KiB
C#
using Opc.Ua;
|
|
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests;
|
|
|
|
/// <summary>Review 01 P6 — the SDK request and transport quotas are configurable (<c>OpcUa:Quotas:*</c>) and
|
|
/// default to exactly what used to be hard-coded.</summary>
|
|
public sealed class OpcUaApplicationHostQuotaTests
|
|
{
|
|
[Fact]
|
|
public void Default_request_quotas_are_the_former_hard_coded_values()
|
|
{
|
|
var config = new ServerConfiguration();
|
|
OpcUaApplicationHost.ApplyRequestQuotas(config, new OpcUaQuotaOptions());
|
|
|
|
config.MinRequestThreadCount.ShouldBe(5);
|
|
config.MaxRequestThreadCount.ShouldBe(100);
|
|
config.MaxQueuedRequestCount.ShouldBe(200);
|
|
}
|
|
|
|
[Fact]
|
|
public void Configured_request_quotas_are_applied()
|
|
{
|
|
var config = new ServerConfiguration();
|
|
OpcUaApplicationHost.ApplyRequestQuotas(config, new OpcUaQuotaOptions
|
|
{
|
|
MinRequestThreadCount = 8, MaxRequestThreadCount = 250, MaxQueuedRequestCount = 1000,
|
|
});
|
|
|
|
config.MinRequestThreadCount.ShouldBe(8);
|
|
config.MaxRequestThreadCount.ShouldBe(250);
|
|
config.MaxQueuedRequestCount.ShouldBe(1000);
|
|
}
|
|
|
|
[Fact]
|
|
public void Unset_transport_quotas_keep_the_sdk_defaults()
|
|
{
|
|
var sdkDefault = new TransportQuotas();
|
|
var built = OpcUaApplicationHost.BuildTransportQuotas(new OpcUaQuotaOptions());
|
|
|
|
built.OperationTimeout.ShouldBe(sdkDefault.OperationTimeout);
|
|
built.MaxStringLength.ShouldBe(sdkDefault.MaxStringLength);
|
|
built.MaxByteStringLength.ShouldBe(sdkDefault.MaxByteStringLength);
|
|
built.MaxArrayLength.ShouldBe(sdkDefault.MaxArrayLength);
|
|
built.MaxMessageSize.ShouldBe(sdkDefault.MaxMessageSize);
|
|
built.MaxBufferSize.ShouldBe(sdkDefault.MaxBufferSize);
|
|
built.ChannelLifetime.ShouldBe(sdkDefault.ChannelLifetime);
|
|
built.SecurityTokenLifetime.ShouldBe(sdkDefault.SecurityTokenLifetime);
|
|
}
|
|
|
|
[Fact]
|
|
public void Configured_transport_quotas_replace_only_what_is_set()
|
|
{
|
|
var sdkDefault = new TransportQuotas();
|
|
var built = OpcUaApplicationHost.BuildTransportQuotas(new OpcUaQuotaOptions
|
|
{
|
|
MaxMessageSize = 8 * 1024 * 1024, OperationTimeoutMs = 45_000, MaxArrayLength = 1_000_000,
|
|
});
|
|
|
|
built.MaxMessageSize.ShouldBe(8 * 1024 * 1024);
|
|
built.OperationTimeout.ShouldBe(45_000);
|
|
built.MaxArrayLength.ShouldBe(1_000_000);
|
|
built.MaxStringLength.ShouldBe(sdkDefault.MaxStringLength);
|
|
built.ChannelLifetime.ShouldBe(sdkDefault.ChannelLifetime);
|
|
}
|
|
|
|
[Fact]
|
|
public void History_read_batch_cap_default_scales_with_the_machine()
|
|
{
|
|
OtOpcUaNodeManager.DefaultMaxConcurrentHistoryReadBatches
|
|
.ShouldBe(Math.Clamp(Environment.ProcessorCount * 2, 4, 16));
|
|
}
|
|
}
|