feat: update DebugStreamBridgeActor to use gRPC for streaming events
After receiving the initial snapshot via ClusterClient, the bridge actor now opens a gRPC server-streaming subscription via SiteStreamGrpcClient for ongoing AttributeValueChanged/AlarmStateChanged events. Adds NodeA/ NodeB failover with max 3 retries, retry count reset on successful event, and IWithTimers-based reconnect scheduling. - DebugStreamBridgeActor: gRPC stream after snapshot, reconnect state machine - DebugStreamService: inject SiteStreamGrpcClientFactory, resolve gRPC addresses - ServiceCollectionExtensions: register SiteStreamGrpcClientFactory singleton - SiteStreamGrpcClient: make SubscribeAsync/Unsubscribe virtual for testability - SiteStreamGrpcClientFactory: make GetOrCreate virtual for testability - New test suite: DebugStreamBridgeActorTests (8 tests)
This commit is contained in:
@@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging;
|
||||
using ScadaLink.Commons.Interfaces.Repositories;
|
||||
using ScadaLink.Commons.Messages.DebugView;
|
||||
using ScadaLink.Communication.Actors;
|
||||
using ScadaLink.Communication.Grpc;
|
||||
|
||||
namespace ScadaLink.Communication;
|
||||
|
||||
@@ -17,6 +18,7 @@ public class DebugStreamService
|
||||
{
|
||||
private readonly CommunicationService _communicationService;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly SiteStreamGrpcClientFactory _grpcClientFactory;
|
||||
private readonly ILogger<DebugStreamService> _logger;
|
||||
private readonly ConcurrentDictionary<string, IActorRef> _sessions = new();
|
||||
private ActorSystem? _actorSystem;
|
||||
@@ -24,10 +26,12 @@ public class DebugStreamService
|
||||
public DebugStreamService(
|
||||
CommunicationService communicationService,
|
||||
IServiceProvider serviceProvider,
|
||||
SiteStreamGrpcClientFactory grpcClientFactory,
|
||||
ILogger<DebugStreamService> logger)
|
||||
{
|
||||
_communicationService = communicationService;
|
||||
_serviceProvider = serviceProvider;
|
||||
_grpcClientFactory = grpcClientFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -56,6 +60,8 @@ public class DebugStreamService
|
||||
// Resolve instance → unique name + site
|
||||
string instanceUniqueName;
|
||||
string siteIdentifier;
|
||||
string grpcNodeAAddress;
|
||||
string grpcNodeBAddress;
|
||||
|
||||
using (var scope = _serviceProvider.CreateScope())
|
||||
{
|
||||
@@ -69,6 +75,10 @@ public class DebugStreamService
|
||||
|
||||
instanceUniqueName = instance.UniqueName;
|
||||
siteIdentifier = site.SiteIdentifier;
|
||||
grpcNodeAAddress = site.GrpcNodeAAddress
|
||||
?? throw new InvalidOperationException($"Site {siteIdentifier} has no GrpcNodeAAddress configured.");
|
||||
grpcNodeBAddress = site.GrpcNodeBAddress
|
||||
?? throw new InvalidOperationException($"Site {siteIdentifier} has no GrpcNodeBAddress configured.");
|
||||
}
|
||||
|
||||
var sessionId = Guid.NewGuid().ToString("N");
|
||||
@@ -104,7 +114,10 @@ public class DebugStreamService
|
||||
sessionId,
|
||||
commActor,
|
||||
onEventWrapper,
|
||||
onTerminatedWrapper);
|
||||
onTerminatedWrapper,
|
||||
_grpcClientFactory,
|
||||
grpcNodeAAddress,
|
||||
grpcNodeBAddress);
|
||||
|
||||
var bridgeActor = system.ActorOf(props, $"debug-stream-{sessionId}");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user