Apply code style formatting and restore partial modifiers on Avalonia views
Linter/formatter pass across the full codebase. Restores required partial keyword on AXAML code-behind classes that the formatter incorrectly removed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,45 +1,42 @@
|
||||
using System.Text;
|
||||
using Opc.Ua;
|
||||
using Serilog;
|
||||
using ZB.MOM.WW.LmxOpcUa.Client.Shared.Adapters;
|
||||
using ZB.MOM.WW.LmxOpcUa.Client.Shared.Helpers;
|
||||
using ZB.MOM.WW.LmxOpcUa.Client.Shared.Models;
|
||||
using BrowseResult = ZB.MOM.WW.LmxOpcUa.Client.Shared.Models.BrowseResult;
|
||||
|
||||
namespace ZB.MOM.WW.LmxOpcUa.Client.Shared;
|
||||
|
||||
/// <summary>
|
||||
/// Full implementation of <see cref="IOpcUaClientService"/> using adapter abstractions for testability.
|
||||
/// Full implementation of <see cref="IOpcUaClientService" /> using adapter abstractions for testability.
|
||||
/// </summary>
|
||||
public sealed class OpcUaClientService : IOpcUaClientService
|
||||
{
|
||||
private static readonly ILogger Logger = Log.ForContext<OpcUaClientService>();
|
||||
|
||||
private readonly IApplicationConfigurationFactory _configFactory;
|
||||
private readonly IEndpointDiscovery _endpointDiscovery;
|
||||
private readonly ISessionFactory _sessionFactory;
|
||||
|
||||
private ISessionAdapter? _session;
|
||||
private ISubscriptionAdapter? _dataSubscription;
|
||||
private ISubscriptionAdapter? _alarmSubscription;
|
||||
private ConnectionState _state = ConnectionState.Disconnected;
|
||||
private ConnectionSettings? _settings;
|
||||
private string[]? _allEndpointUrls;
|
||||
private int _currentEndpointIndex;
|
||||
private bool _disposed;
|
||||
|
||||
// Track active data subscriptions for replay after failover
|
||||
private readonly Dictionary<string, (NodeId NodeId, int IntervalMs, uint Handle)> _activeDataSubscriptions = new();
|
||||
|
||||
private readonly IApplicationConfigurationFactory _configFactory;
|
||||
private readonly IEndpointDiscovery _endpointDiscovery;
|
||||
|
||||
private readonly ISessionFactory _sessionFactory;
|
||||
|
||||
// Track alarm subscription state for replay after failover
|
||||
private (NodeId? SourceNodeId, int IntervalMs)? _activeAlarmSubscription;
|
||||
private ISubscriptionAdapter? _alarmSubscription;
|
||||
private string[]? _allEndpointUrls;
|
||||
private int _currentEndpointIndex;
|
||||
private ISubscriptionAdapter? _dataSubscription;
|
||||
private bool _disposed;
|
||||
|
||||
public event EventHandler<DataChangedEventArgs>? DataChanged;
|
||||
public event EventHandler<AlarmEventArgs>? AlarmEvent;
|
||||
public event EventHandler<ConnectionStateChangedEventArgs>? ConnectionStateChanged;
|
||||
|
||||
public bool IsConnected => _state == ConnectionState.Connected && _session?.Connected == true;
|
||||
public ConnectionInfo? CurrentConnectionInfo { get; private set; }
|
||||
private ISessionAdapter? _session;
|
||||
private ConnectionSettings? _settings;
|
||||
private ConnectionState _state = ConnectionState.Disconnected;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new OpcUaClientService with the specified adapter dependencies.
|
||||
/// Creates a new OpcUaClientService with the specified adapter dependencies.
|
||||
/// </summary>
|
||||
internal OpcUaClientService(
|
||||
IApplicationConfigurationFactory configFactory,
|
||||
@@ -52,7 +49,7 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new OpcUaClientService with default production adapters.
|
||||
/// Creates a new OpcUaClientService with default production adapters.
|
||||
/// </summary>
|
||||
public OpcUaClientService()
|
||||
: this(
|
||||
@@ -62,6 +59,13 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
{
|
||||
}
|
||||
|
||||
public event EventHandler<DataChangedEventArgs>? DataChanged;
|
||||
public event EventHandler<AlarmEventArgs>? AlarmEvent;
|
||||
public event EventHandler<ConnectionStateChangedEventArgs>? ConnectionStateChanged;
|
||||
|
||||
public bool IsConnected => _state == ConnectionState.Connected && _session?.Connected == true;
|
||||
public ConnectionInfo? CurrentConnectionInfo { get; private set; }
|
||||
|
||||
public async Task<ConnectionInfo> ConnectAsync(ConnectionSettings settings, CancellationToken ct = default)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
@@ -80,10 +84,7 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
|
||||
session.RegisterKeepAliveHandler(isGood =>
|
||||
{
|
||||
if (!isGood)
|
||||
{
|
||||
_ = HandleKeepAliveFailureAsync();
|
||||
}
|
||||
if (!isGood) _ = HandleKeepAliveFailureAsync();
|
||||
});
|
||||
|
||||
CurrentConnectionInfo = BuildConnectionInfo(session);
|
||||
@@ -112,11 +113,13 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
await _dataSubscription.DeleteAsync(ct);
|
||||
_dataSubscription = null;
|
||||
}
|
||||
|
||||
if (_alarmSubscription != null)
|
||||
{
|
||||
await _alarmSubscription.DeleteAsync(ct);
|
||||
_alarmSubscription = null;
|
||||
}
|
||||
|
||||
if (_session != null)
|
||||
{
|
||||
await _session.CloseAsync(ct);
|
||||
@@ -150,7 +153,7 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
ThrowIfNotConnected();
|
||||
|
||||
// Read current value for type coercion when value is a string
|
||||
object typedValue = value;
|
||||
var typedValue = value;
|
||||
if (value is string rawString)
|
||||
{
|
||||
var currentDataValue = await _session!.ReadValueAsync(nodeId, ct);
|
||||
@@ -161,14 +164,15 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
return await _session!.WriteValueAsync(nodeId, dataValue, ct);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<Models.BrowseResult>> BrowseAsync(NodeId? parentNodeId = null, CancellationToken ct = default)
|
||||
public async Task<IReadOnlyList<BrowseResult>> BrowseAsync(NodeId? parentNodeId = null,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotConnected();
|
||||
|
||||
var startNode = parentNodeId ?? ObjectIds.ObjectsFolder;
|
||||
var nodeClassMask = (uint)NodeClass.Object | (uint)NodeClass.Variable | (uint)NodeClass.Method;
|
||||
var results = new List<Models.BrowseResult>();
|
||||
var results = new List<BrowseResult>();
|
||||
|
||||
var (continuationPoint, references) = await _session!.BrowseAsync(startNode, nodeClassMask, ct);
|
||||
|
||||
@@ -180,7 +184,7 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
var hasChildren = reference.NodeClass == NodeClass.Object &&
|
||||
await _session.HasChildrenAsync(childNodeId, ct);
|
||||
|
||||
results.Add(new Models.BrowseResult(
|
||||
results.Add(new BrowseResult(
|
||||
reference.NodeId.ToString(),
|
||||
reference.DisplayName?.Text ?? string.Empty,
|
||||
reference.NodeClass.ToString(),
|
||||
@@ -188,13 +192,9 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
}
|
||||
|
||||
if (continuationPoint != null && continuationPoint.Length > 0)
|
||||
{
|
||||
(continuationPoint, references) = await _session.BrowseNextAsync(continuationPoint, ct);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
@@ -209,10 +209,7 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
if (_activeDataSubscriptions.ContainsKey(nodeIdStr))
|
||||
return; // Already subscribed
|
||||
|
||||
if (_dataSubscription == null)
|
||||
{
|
||||
_dataSubscription = await _session!.CreateSubscriptionAsync(intervalMs, ct);
|
||||
}
|
||||
if (_dataSubscription == null) _dataSubscription = await _session!.CreateSubscriptionAsync(intervalMs, ct);
|
||||
|
||||
var handle = await _dataSubscription.AddDataChangeMonitoredItemAsync(
|
||||
nodeId, intervalMs, OnDataChangeNotification, ct);
|
||||
@@ -229,16 +226,14 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
if (!_activeDataSubscriptions.TryGetValue(nodeIdStr, out var sub))
|
||||
return; // Not subscribed, safe to ignore
|
||||
|
||||
if (_dataSubscription != null)
|
||||
{
|
||||
await _dataSubscription.RemoveMonitoredItemAsync(sub.Handle, ct);
|
||||
}
|
||||
if (_dataSubscription != null) await _dataSubscription.RemoveMonitoredItemAsync(sub.Handle, ct);
|
||||
|
||||
_activeDataSubscriptions.Remove(nodeIdStr);
|
||||
Logger.Debug("Unsubscribed from data changes on {NodeId}", nodeId);
|
||||
}
|
||||
|
||||
public async Task SubscribeAlarmsAsync(NodeId? sourceNodeId = null, int intervalMs = 1000, CancellationToken ct = default)
|
||||
public async Task SubscribeAlarmsAsync(NodeId? sourceNodeId = null, int intervalMs = 1000,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotConnected();
|
||||
@@ -305,16 +300,18 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotConnected();
|
||||
|
||||
var redundancySupportValue = await _session!.ReadValueAsync(VariableIds.Server_ServerRedundancy_RedundancySupport, ct);
|
||||
var redundancySupportValue =
|
||||
await _session!.ReadValueAsync(VariableIds.Server_ServerRedundancy_RedundancySupport, ct);
|
||||
var redundancyMode = ((RedundancySupport)(int)redundancySupportValue.Value).ToString();
|
||||
|
||||
var serviceLevelValue = await _session.ReadValueAsync(VariableIds.Server_ServiceLevel, ct);
|
||||
var serviceLevel = (byte)serviceLevelValue.Value;
|
||||
|
||||
string[] serverUris = Array.Empty<string>();
|
||||
string[] serverUris = [];
|
||||
try
|
||||
{
|
||||
var serverUriArrayValue = await _session.ReadValueAsync(VariableIds.Server_ServerRedundancy_ServerUriArray, ct);
|
||||
var serverUriArrayValue =
|
||||
await _session.ReadValueAsync(VariableIds.Server_ServerRedundancy_ServerUriArray, ct);
|
||||
if (serverUriArrayValue.Value is string[] uris)
|
||||
serverUris = uris;
|
||||
}
|
||||
@@ -323,7 +320,7 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
// ServerUriArray may not be present when RedundancySupport is None
|
||||
}
|
||||
|
||||
string applicationUri = string.Empty;
|
||||
var applicationUri = string.Empty;
|
||||
try
|
||||
{
|
||||
var serverArrayValue = await _session.ReadValueAsync(VariableIds.Server_ServerArray, ct);
|
||||
@@ -354,7 +351,8 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
|
||||
// --- Private helpers ---
|
||||
|
||||
private async Task<ISessionAdapter> ConnectToEndpointAsync(ConnectionSettings settings, string endpointUrl, CancellationToken ct)
|
||||
private async Task<ISessionAdapter> ConnectToEndpointAsync(ConnectionSettings settings, string endpointUrl,
|
||||
CancellationToken ct)
|
||||
{
|
||||
// Create a settings copy with the current endpoint URL
|
||||
var effectiveSettings = new ConnectionSettings
|
||||
@@ -372,12 +370,13 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
var requestedMode = SecurityModeMapper.ToMessageSecurityMode(settings.SecurityMode);
|
||||
var endpoint = _endpointDiscovery.SelectEndpoint(config, endpointUrl, requestedMode);
|
||||
|
||||
UserIdentity identity = settings.Username != null
|
||||
? new UserIdentity(settings.Username, System.Text.Encoding.UTF8.GetBytes(settings.Password ?? ""))
|
||||
var identity = settings.Username != null
|
||||
? new UserIdentity(settings.Username, Encoding.UTF8.GetBytes(settings.Password ?? ""))
|
||||
: new UserIdentity();
|
||||
|
||||
var sessionTimeoutMs = (uint)(settings.SessionTimeoutSeconds * 1000);
|
||||
return await _sessionFactory.CreateSessionAsync(config, endpoint, "LmxOpcUaClient", sessionTimeoutMs, identity, ct);
|
||||
return await _sessionFactory.CreateSessionAsync(config, endpoint, "LmxOpcUaClient", sessionTimeoutMs, identity,
|
||||
ct);
|
||||
}
|
||||
|
||||
private async Task HandleKeepAliveFailureAsync()
|
||||
@@ -392,9 +391,17 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
// Close old session
|
||||
if (_session != null)
|
||||
{
|
||||
try { _session.Dispose(); } catch { }
|
||||
try
|
||||
{
|
||||
_session.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
_session = null;
|
||||
}
|
||||
|
||||
_dataSubscription = null;
|
||||
_alarmSubscription = null;
|
||||
|
||||
@@ -405,7 +412,7 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
}
|
||||
|
||||
// Try each endpoint
|
||||
for (int attempt = 0; attempt < _allEndpointUrls.Length; attempt++)
|
||||
for (var attempt = 0; attempt < _allEndpointUrls.Length; attempt++)
|
||||
{
|
||||
_currentEndpointIndex = (_currentEndpointIndex + 1) % _allEndpointUrls.Length;
|
||||
var url = _allEndpointUrls[_currentEndpointIndex];
|
||||
@@ -418,7 +425,7 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
|
||||
session.RegisterKeepAliveHandler(isGood =>
|
||||
{
|
||||
if (!isGood) { _ = HandleKeepAliveFailureAsync(); }
|
||||
if (!isGood) _ = HandleKeepAliveFailureAsync();
|
||||
});
|
||||
|
||||
CurrentConnectionInfo = BuildConnectionInfo(session);
|
||||
@@ -448,7 +455,6 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
_activeDataSubscriptions.Clear();
|
||||
|
||||
foreach (var (nodeIdStr, (nodeId, intervalMs, _)) in subscriptions)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_dataSubscription == null)
|
||||
@@ -462,7 +468,6 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
{
|
||||
Logger.Warning(ex, "Failed to replay data subscription for {NodeId}", nodeIdStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Replay alarm subscription
|
||||
@@ -569,4 +574,4 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
if (_state != ConnectionState.Connected || _session == null)
|
||||
throw new InvalidOperationException("Not connected to an OPC UA server.");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user