Wire DCL to Instance Actors for OPC UA tag value flow
- Add TagValueUpdate/ConnectionQualityChanged handlers to InstanceActor - InstanceActor subscribes to DCL on PreStart based on DataSourceReference - DeploymentManagerActor creates DCL connections on deploy and passes DCL ref - AkkaHostedService creates DCL Manager Actor for tag subscriptions - Move CreateConnectionCommand to Commons for cross-project access - Add ConnectionConfig to FlattenedConfiguration for deployment packaging
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using Akka.Actor;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ScadaLink.Commons.Messages.DataConnection;
|
||||
using ScadaLink.Commons.Messages.DebugView;
|
||||
using ScadaLink.Commons.Messages.Instance;
|
||||
using ScadaLink.Commons.Messages.Lifecycle;
|
||||
@@ -45,6 +46,11 @@ public class InstanceActor : ReceiveActor
|
||||
// WP-25: Debug view subscribers
|
||||
private readonly Dictionary<string, IActorRef> _debugSubscribers = new();
|
||||
|
||||
// DCL manager actor reference for subscribing to tag values
|
||||
private readonly IActorRef? _dclManager;
|
||||
// Maps tag paths back to attribute canonical names for DCL updates
|
||||
private readonly Dictionary<string, string> _tagPathToAttribute = new();
|
||||
|
||||
public InstanceActor(
|
||||
string instanceUniqueName,
|
||||
string configJson,
|
||||
@@ -53,7 +59,8 @@ public class InstanceActor : ReceiveActor
|
||||
SharedScriptLibrary sharedScriptLibrary,
|
||||
SiteStreamManager? streamManager,
|
||||
SiteRuntimeOptions options,
|
||||
ILogger logger)
|
||||
ILogger logger,
|
||||
IActorRef? dclManager = null)
|
||||
{
|
||||
_instanceUniqueName = instanceUniqueName;
|
||||
_storage = storage;
|
||||
@@ -62,6 +69,7 @@ public class InstanceActor : ReceiveActor
|
||||
_streamManager = streamManager;
|
||||
_options = options;
|
||||
_logger = logger;
|
||||
_dclManager = dclManager;
|
||||
|
||||
// Deserialize the flattened configuration
|
||||
_configuration = JsonSerializer.Deserialize<FlattenedConfiguration>(configJson);
|
||||
@@ -102,6 +110,10 @@ public class InstanceActor : ReceiveActor
|
||||
// WP-22/23: Handle attribute value changes from DCL (Tell pattern)
|
||||
Receive<AttributeValueChanged>(HandleAttributeValueChanged);
|
||||
|
||||
// Handle tag value updates from DCL — convert to AttributeValueChanged
|
||||
Receive<TagValueUpdate>(HandleTagValueUpdate);
|
||||
Receive<ConnectionQualityChanged>(HandleConnectionQualityChanged);
|
||||
|
||||
// WP-16: Handle alarm state changes from Alarm Actors (Tell pattern)
|
||||
Receive<AlarmStateChanged>(HandleAlarmStateChanged);
|
||||
|
||||
@@ -129,6 +141,9 @@ public class InstanceActor : ReceiveActor
|
||||
|
||||
// Create child Script Actors and Alarm Actors from configuration
|
||||
CreateChildActors();
|
||||
|
||||
// Subscribe to DCL for data-sourced attributes
|
||||
SubscribeToDcl();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -238,6 +253,66 @@ public class InstanceActor : ReceiveActor
|
||||
PublishAndNotifyChildren(changed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles tag value updates from DCL. Maps the tag path back to the attribute
|
||||
/// canonical name and converts to an AttributeValueChanged for unified processing.
|
||||
/// </summary>
|
||||
private void HandleTagValueUpdate(TagValueUpdate update)
|
||||
{
|
||||
if (_tagPathToAttribute.TryGetValue(update.TagPath, out var attrName))
|
||||
{
|
||||
var changed = new AttributeValueChanged(
|
||||
_instanceUniqueName, update.TagPath, attrName,
|
||||
update.Value, update.Quality.ToString(), update.Timestamp);
|
||||
HandleAttributeValueChanged(changed);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleConnectionQualityChanged(ConnectionQualityChanged qualityChanged)
|
||||
{
|
||||
_logger.LogInformation("Connection {Connection} quality changed to {Quality}",
|
||||
qualityChanged.ConnectionName, qualityChanged.Quality);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to DCL for all data-sourced attributes. Groups tag paths by connection
|
||||
/// name and sends SubscribeTagsRequest to the DCL manager.
|
||||
/// </summary>
|
||||
private void SubscribeToDcl()
|
||||
{
|
||||
if (_dclManager == null || _configuration == null) return;
|
||||
|
||||
// Group attributes by their bound connection name
|
||||
var byConnection = new Dictionary<string, List<string>>();
|
||||
foreach (var attr in _configuration.Attributes)
|
||||
{
|
||||
if (string.IsNullOrEmpty(attr.DataSourceReference) ||
|
||||
string.IsNullOrEmpty(attr.BoundDataConnectionName))
|
||||
continue;
|
||||
|
||||
_tagPathToAttribute[attr.DataSourceReference] = attr.CanonicalName;
|
||||
|
||||
if (!byConnection.ContainsKey(attr.BoundDataConnectionName))
|
||||
byConnection[attr.BoundDataConnectionName] = new List<string>();
|
||||
byConnection[attr.BoundDataConnectionName].Add(attr.DataSourceReference);
|
||||
}
|
||||
|
||||
// Send subscription requests to DCL for each connection
|
||||
foreach (var (connectionName, tagPaths) in byConnection)
|
||||
{
|
||||
var request = new SubscribeTagsRequest(
|
||||
Guid.NewGuid().ToString("N"),
|
||||
_instanceUniqueName,
|
||||
connectionName,
|
||||
tagPaths,
|
||||
DateTimeOffset.UtcNow);
|
||||
_dclManager.Tell(request, Self);
|
||||
_logger.LogInformation(
|
||||
"Instance {Instance} subscribed to {Count} tags on connection {Connection}",
|
||||
_instanceUniqueName, tagPaths.Count, connectionName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WP-16: Handles alarm state changes from Alarm Actors.
|
||||
/// Updates in-memory alarm state and publishes to stream.
|
||||
|
||||
Reference in New Issue
Block a user