fix: wire DCL tag value delivery, alarm evaluation, and snapshot timestamps
Three runtime bugs fixed: - DataConnectionActor: TagValueReceived/TagResolutionSucceeded/Failed not handled in any Become state — OPC UA values went to dead letters. Added initial read after subscribe to seed current values immediately. - AlarmActor: ParseEvalConfig expected "attributeName"/"matchValue"/"min"/ "max" keys but seed data uses "attribute"/"value"/"high"/"low". Added support for both conventions and !=prefix for not-equal matching. - InstanceActor: snapshots reported all alarms (including unevaluated) with correct priorities and source timestamps instead of current UTC. Removed bogus Vibration template attribute that shadowed Speed's tag mapping.
This commit is contained in:
@@ -42,7 +42,10 @@ public class InstanceActor : ReceiveActor
|
||||
private readonly IServiceProvider? _serviceProvider;
|
||||
private readonly Dictionary<string, object?> _attributes = new();
|
||||
private readonly Dictionary<string, string> _attributeQualities = new();
|
||||
private readonly Dictionary<string, DateTimeOffset> _attributeTimestamps = new();
|
||||
private readonly Dictionary<string, AlarmState> _alarmStates = new();
|
||||
private readonly Dictionary<string, DateTimeOffset> _alarmTimestamps = new();
|
||||
private readonly Dictionary<string, int> _alarmPriorities = new();
|
||||
private readonly Dictionary<string, IActorRef> _scriptActors = new();
|
||||
private readonly Dictionary<string, IActorRef> _alarmActors = new();
|
||||
private FlattenedConfiguration? _configuration;
|
||||
@@ -124,6 +127,7 @@ public class InstanceActor : ReceiveActor
|
||||
|
||||
// Handle tag value updates from DCL — convert to AttributeValueChanged
|
||||
Receive<TagValueUpdate>(HandleTagValueUpdate);
|
||||
Receive<SubscribeTagsResponse>(_ => { }); // Ack from DCL subscribe — no action needed
|
||||
Receive<ConnectionQualityChanged>(HandleConnectionQualityChanged);
|
||||
|
||||
// WP-16: Handle alarm state changes from Alarm Actors (Tell pattern)
|
||||
@@ -267,6 +271,7 @@ public class InstanceActor : ReceiveActor
|
||||
// WP-24: State mutation serialized through this actor
|
||||
_attributes[changed.AttributeName] = changed.Value;
|
||||
_attributeQualities[changed.AttributeName] = changed.Quality;
|
||||
_attributeTimestamps[changed.AttributeName] = changed.Timestamp;
|
||||
|
||||
PublishAndNotifyChildren(changed);
|
||||
}
|
||||
@@ -338,6 +343,7 @@ public class InstanceActor : ReceiveActor
|
||||
private void HandleAlarmStateChanged(AlarmStateChanged changed)
|
||||
{
|
||||
_alarmStates[changed.AlarmName] = changed.State;
|
||||
_alarmTimestamps[changed.AlarmName] = changed.Timestamp;
|
||||
|
||||
// WP-23: Publish to site-wide stream
|
||||
_streamManager?.PublishAlarmStateChanged(changed);
|
||||
@@ -358,20 +364,21 @@ public class InstanceActor : ReceiveActor
|
||||
_debugSubscribers[subscriptionId] = Sender;
|
||||
|
||||
// Build snapshot from current state
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var attributeValues = _attributes.Select(kvp => new AttributeValueChanged(
|
||||
_instanceUniqueName,
|
||||
kvp.Key,
|
||||
kvp.Key,
|
||||
kvp.Value,
|
||||
_attributeQualities.GetValueOrDefault(kvp.Key, "Good"),
|
||||
DateTimeOffset.UtcNow)).ToList();
|
||||
_attributeTimestamps.GetValueOrDefault(kvp.Key, now))).ToList();
|
||||
|
||||
var alarmStates = _alarmStates.Select(kvp => new AlarmStateChanged(
|
||||
var alarmStates = _alarmActors.Keys.Select(name => new AlarmStateChanged(
|
||||
_instanceUniqueName,
|
||||
kvp.Key,
|
||||
kvp.Value,
|
||||
0, // Priority not tracked in _alarmStates; would need separate tracking
|
||||
DateTimeOffset.UtcNow)).ToList();
|
||||
name,
|
||||
_alarmStates.GetValueOrDefault(name, AlarmState.Normal),
|
||||
_alarmPriorities.GetValueOrDefault(name, 0),
|
||||
_alarmTimestamps[name])).ToList();
|
||||
|
||||
var snapshot = new DebugViewSnapshot(
|
||||
_instanceUniqueName,
|
||||
@@ -407,20 +414,21 @@ public class InstanceActor : ReceiveActor
|
||||
/// </summary>
|
||||
private void HandleDebugSnapshot(DebugSnapshotRequest request)
|
||||
{
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var attributeValues = _attributes.Select(kvp => new AttributeValueChanged(
|
||||
_instanceUniqueName,
|
||||
kvp.Key,
|
||||
kvp.Key,
|
||||
kvp.Value,
|
||||
_attributeQualities.GetValueOrDefault(kvp.Key, "Good"),
|
||||
DateTimeOffset.UtcNow)).ToList();
|
||||
_attributeTimestamps.GetValueOrDefault(kvp.Key, now))).ToList();
|
||||
|
||||
var alarmStates = _alarmStates.Select(kvp => new AlarmStateChanged(
|
||||
var alarmStates = _alarmActors.Keys.Select(name => new AlarmStateChanged(
|
||||
_instanceUniqueName,
|
||||
kvp.Key,
|
||||
kvp.Value,
|
||||
0,
|
||||
DateTimeOffset.UtcNow)).ToList();
|
||||
name,
|
||||
_alarmStates.GetValueOrDefault(name, AlarmState.Normal),
|
||||
_alarmPriorities.GetValueOrDefault(name, 0),
|
||||
_alarmTimestamps[name])).ToList();
|
||||
|
||||
var snapshot = new DebugViewSnapshot(
|
||||
_instanceUniqueName,
|
||||
@@ -562,6 +570,8 @@ public class InstanceActor : ReceiveActor
|
||||
|
||||
var actorRef = Context.ActorOf(props, $"alarm-{alarm.CanonicalName}");
|
||||
_alarmActors[alarm.CanonicalName] = actorRef;
|
||||
_alarmPriorities[alarm.CanonicalName] = alarm.PriorityLevel;
|
||||
_alarmTimestamps[alarm.CanonicalName] = DateTimeOffset.UtcNow;
|
||||
}
|
||||
|
||||
_logger.LogInformation(
|
||||
|
||||
Reference in New Issue
Block a user