Resolve all 46 low-severity architecture review findings
This commit is contained in:
@@ -675,9 +675,16 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
List<DataShard> touched,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
foreach (var (index, nodeId, startNode) in resolved)
|
||||
// Plan once for this chunk while the apply gate keeps shard occupancy stable.
|
||||
var placement = MonitoredItemShardPlanner.Plan(
|
||||
_dataShards.Select(s => s.ItemCount).ToArray(), ItemsPerShard, resolved.Count);
|
||||
for (var itemIndex = 0; itemIndex < resolved.Count; itemIndex++)
|
||||
{
|
||||
var shard = await PlaceOnDataShardAsync(cancellationToken);
|
||||
var (index, nodeId, startNode) = resolved[itemIndex];
|
||||
var shardIndex = placement[itemIndex];
|
||||
var shard = shardIndex < _dataShards.Count
|
||||
? _dataShards[shardIndex]
|
||||
: await CreateDataShardAsync(cancellationToken);
|
||||
var handle = Guid.NewGuid().ToString();
|
||||
var monitoredItem = new MonitoredItem(shard.Subscription.DefaultItem)
|
||||
{
|
||||
@@ -760,20 +767,6 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
created.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the shard this item belongs on per
|
||||
/// <see cref="MonitoredItemShardPlanner"/> — first shard with free capacity, else a
|
||||
/// freshly created one. Callers must hold <see cref="_applyGate"/>.
|
||||
/// </summary>
|
||||
private async Task<DataShard> PlaceOnDataShardAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var index = MonitoredItemShardPlanner.Plan(
|
||||
_dataShards.Select(s => s.ItemCount).ToList(), ItemsPerShard, 1)[0];
|
||||
return index < _dataShards.Count
|
||||
? _dataShards[index]
|
||||
: await CreateDataShardAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drops a monitored item that failed to create from the shard it was placed on.
|
||||
/// Callers must hold <see cref="_applyGate"/>. No ApplyChanges is issued: the server
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer;
|
||||
|
||||
/// <summary>Validates the MxGateway client identity before connections start.</summary>
|
||||
public sealed class MxGatewayGlobalOptionsValidator : IValidateOptions<MxGatewayGlobalOptions>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public ValidateOptionsResult Validate(string? name, MxGatewayGlobalOptions options)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(options.ClientNamePrefix))
|
||||
return ValidateOptionsResult.Fail("ScadaBridge:MxGateway:ClientNamePrefix must not be blank.");
|
||||
return ValidateOptionsResult.Success;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer;
|
||||
|
||||
/// <summary>Validates the OPC UA application identity before connections start.</summary>
|
||||
public sealed class OpcUaGlobalOptionsValidator : IValidateOptions<OpcUaGlobalOptions>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public ValidateOptionsResult Validate(string? name, OpcUaGlobalOptions options)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(options.ApplicationName))
|
||||
return ValidateOptionsResult.Fail("ScadaBridge:OpcUa:ApplicationName must not be blank.");
|
||||
return ValidateOptionsResult.Success;
|
||||
}
|
||||
}
|
||||
@@ -24,16 +24,18 @@ public static class ServiceCollectionExtensions
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IValidateOptions<DataConnectionOptions>, DataConnectionOptionsValidator>());
|
||||
|
||||
// OpcUaGlobalOptions / MxGatewayGlobalOptions carry only string identity/path
|
||||
// fields (no numeric or duration knobs), so there is nothing to range-validate
|
||||
// — no dedicated validator is registered for them. Canonical sections are
|
||||
// ScadaBridge:OpcUa / ScadaBridge:MxGateway (NF8): the bare "OpcUa"/"MxGateway"
|
||||
// sections exist in no appsettings, so binding them read nothing.
|
||||
// Validate identities before the first connection attempts to register with a server.
|
||||
services.AddOptions<OpcUaGlobalOptions>()
|
||||
.BindConfiguration("ScadaBridge:OpcUa");
|
||||
.BindConfiguration("ScadaBridge:OpcUa")
|
||||
.ValidateOnStart();
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IValidateOptions<OpcUaGlobalOptions>, OpcUaGlobalOptionsValidator>());
|
||||
|
||||
services.AddOptions<MxGatewayGlobalOptions>()
|
||||
.BindConfiguration("ScadaBridge:MxGateway");
|
||||
.BindConfiguration("ScadaBridge:MxGateway")
|
||||
.ValidateOnStart();
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IValidateOptions<MxGatewayGlobalOptions>, MxGatewayGlobalOptionsValidator>());
|
||||
|
||||
// Register the factory for protocol extensibility
|
||||
services.AddSingleton<IDataConnectionFactory, DataConnectionFactory>();
|
||||
|
||||
Reference in New Issue
Block a user