Resolve all high-severity architecture review findings

This commit is contained in:
Joseph Doherty
2026-09-09 16:13:58 -04:00
parent 8bfe65512e
commit 146a13238f
192 changed files with 3160 additions and 753 deletions
@@ -263,7 +263,7 @@ public class MxGatewayDataConnection
public async Task UnsubscribeAsync(string subscriptionId, CancellationToken cancellationToken = default)
{
if (_subs.TryRemove(subscriptionId, out var s))
_tagToSub.TryRemove(s.TagPath, out _);
((ICollection<KeyValuePair<string, string>>)_tagToSub).Remove(new(s.TagPath, subscriptionId));
await _client!.UnsubscribeAsync(subscriptionId, cancellationToken);
}
@@ -297,7 +297,7 @@ public class MxGatewayDataConnection
foreach (var id in subscriptionIds)
{
if (_subs.TryRemove(id, out var s))
_tagToSub.TryRemove(s.TagPath, out _);
((ICollection<KeyValuePair<string, string>>)_tagToSub).Remove(new(s.TagPath, id));
}
await _client!.UnsubscribeBulkAsync(subscriptionIds, cancellationToken);
@@ -216,7 +216,7 @@ public sealed class RealMxGatewayClient : IMxGatewayClient
// UnsubscribeBulk unadvises AND removes the item, releasing every advice kind.
_supervisoryAdvised.TryRemove(handle, out _);
if (_handleToTag.TryRemove(handle, out var tag))
_tagToHandle.TryRemove(tag, out _);
((ICollection<KeyValuePair<string, int>>)_tagToHandle).Remove(new(tag, handle));
}
}
@@ -233,7 +233,7 @@ public sealed class RealMxGatewayClient : IMxGatewayClient
// so forget any supervisory-advise state for this handle.
_supervisoryAdvised.TryRemove(handle, out _);
if (_handleToTag.TryRemove(handle, out var tag))
_tagToHandle.TryRemove(tag, out _);
((ICollection<KeyValuePair<string, int>>)_tagToHandle).Remove(new(tag, handle));
}
/// <inheritdoc />
@@ -68,6 +68,7 @@ public class RealOpcUaClient : IOpcUaClient
private readonly ConcurrentDictionary<string, MonitoredItem> _alarmItems = new();
// Per-handle "currently inside a ConditionRefresh replay" flag → Snapshot kind.
private readonly ConcurrentDictionary<string, bool> _alarmInRefresh = new();
private readonly ConcurrentDictionary<string, Guid> _alarmSnapshotIds = new();
// Per-handle last (active, acked) by source reference, to derive transition kind.
private readonly ConcurrentDictionary<string, Dictionary<string, (bool Active, bool Acked)>> _alarmLastState = new();
// Int flag toggled with Interlocked.Exchange so the
@@ -543,6 +544,10 @@ public class RealOpcUaClient : IOpcUaClient
}
_monitoredItems.Clear();
_callbacks.Clear();
_alarmItems.Clear();
_alarmInRefresh.Clear();
_alarmSnapshotIds.Clear();
_alarmLastState.Clear();
}
/// <inheritdoc />
@@ -925,6 +930,7 @@ public class RealOpcUaClient : IOpcUaClient
}, cancellationToken);
}
_alarmInRefresh.TryRemove(subscriptionHandle, out _);
_alarmSnapshotIds.TryRemove(subscriptionHandle, out _);
_alarmLastState.TryRemove(subscriptionHandle, out _);
}
@@ -1160,12 +1166,16 @@ public class RealOpcUaClient : IOpcUaClient
if (eventType == ObjectTypeIds.RefreshStartEventType)
{
_alarmInRefresh[handle] = true;
var startEpoch = Guid.NewGuid();
_alarmSnapshotIds[handle] = startEpoch;
onTransition(SnapshotBoundary(AlarmTransitionKind.SnapshotStart, subscriptionSourceReference, startEpoch));
return;
}
if (eventType == ObjectTypeIds.RefreshEndEventType)
{
_alarmInRefresh[handle] = false;
onTransition(SnapshotComplete());
if (_alarmSnapshotIds.TryRemove(handle, out var endEpoch))
onTransition(SnapshotBoundary(AlarmTransitionKind.SnapshotComplete, subscriptionSourceReference, endEpoch));
return;
}
@@ -1253,13 +1263,16 @@ public class RealOpcUaClient : IOpcUaClient
LimitValue: limitValue,
// MES alarm-status API §6.4: true source ack instant (index 18) when supplied,
// else the observed event time; null unless the condition is active AND acked.
AckTime: OpcUaAlarmMapper.DeriveAckTime(active, acked, sourceAckTime, time)));
AckTime: OpcUaAlarmMapper.DeriveAckTime(active, acked, sourceAckTime, time),
SnapshotSourceReference: inRefresh ? subscriptionSourceReference ?? string.Empty : null,
SnapshotId: inRefresh && _alarmSnapshotIds.TryGetValue(handle, out var epoch) ? epoch : null));
}
private static NativeAlarmTransition SnapshotComplete() => new(
"", "", "", AlarmTransitionKind.SnapshotComplete,
private static NativeAlarmTransition SnapshotBoundary(AlarmTransitionKind kind, string? source, Guid epoch) => new(
"", source ?? string.Empty, "", kind,
new Commons.Types.Alarms.AlarmConditionState(false, true, null, AlarmShelveState.Unshelved, false, 0),
"", "", "", "", "", null, DateTimeOffset.UtcNow, "", "");
"", "", "", "", "", null, DateTimeOffset.UtcNow, "", "",
SnapshotSourceReference: source ?? string.Empty, SnapshotId: epoch);
/// <summary>
/// Nodes per OPC UA Read/Write service call. Stays under the