diff --git a/src/ScadaLink.CentralUI/Components/Pages/Monitoring/Health.razor b/src/ScadaLink.CentralUI/Components/Pages/Monitoring/Health.razor index 347fd65..81a2476 100644 --- a/src/ScadaLink.CentralUI/Components/Pages/Monitoring/Health.razor +++ b/src/ScadaLink.CentralUI/Components/Pages/Monitoring/Health.razor @@ -100,6 +100,23 @@ } + @* Instances *@ +
+
Instances
+
+ Deployed + @report.DeployedInstanceCount +
+
+ Enabled + @report.EnabledInstanceCount +
+
+ Disabled + @report.DisabledInstanceCount +
+
+ @* Error Counts *@
Error Counts
diff --git a/src/ScadaLink.Commons/Messages/Health/SiteHealthReport.cs b/src/ScadaLink.Commons/Messages/Health/SiteHealthReport.cs index bdd8af1..ed9af6d 100644 --- a/src/ScadaLink.Commons/Messages/Health/SiteHealthReport.cs +++ b/src/ScadaLink.Commons/Messages/Health/SiteHealthReport.cs @@ -11,4 +11,7 @@ public record SiteHealthReport( int ScriptErrorCount, int AlarmEvaluationErrorCount, IReadOnlyDictionary StoreAndForwardBufferDepths, - int DeadLetterCount); + int DeadLetterCount, + int DeployedInstanceCount, + int EnabledInstanceCount, + int DisabledInstanceCount); diff --git a/src/ScadaLink.HealthMonitoring/HealthReportSender.cs b/src/ScadaLink.HealthMonitoring/HealthReportSender.cs index 303934e..4eda01b 100644 --- a/src/ScadaLink.HealthMonitoring/HealthReportSender.cs +++ b/src/ScadaLink.HealthMonitoring/HealthReportSender.cs @@ -49,6 +49,10 @@ public class HealthReportSender : BackgroundService { try { + // TODO: Wire S&F buffer depths when StoreAndForward service is available in DI + // e.g., var depths = await _bufferDepthProvider.GetDepthsAsync(); + // _collector.SetStoreAndForwardDepths(depths); + var seq = Interlocked.Increment(ref _sequenceNumber); var report = _collector.CollectReport(_siteId); diff --git a/src/ScadaLink.HealthMonitoring/ISiteHealthCollector.cs b/src/ScadaLink.HealthMonitoring/ISiteHealthCollector.cs index 95b9128..c64ee97 100644 --- a/src/ScadaLink.HealthMonitoring/ISiteHealthCollector.cs +++ b/src/ScadaLink.HealthMonitoring/ISiteHealthCollector.cs @@ -15,5 +15,7 @@ public interface ISiteHealthCollector void UpdateConnectionHealth(string connectionName, ConnectionHealth health); void RemoveConnection(string connectionName); void UpdateTagResolution(string connectionName, int totalSubscribed, int successfullyResolved); + void SetStoreAndForwardDepths(IReadOnlyDictionary depths); + void SetInstanceCounts(int deployed, int enabled, int disabled); SiteHealthReport CollectReport(string siteId); } diff --git a/src/ScadaLink.HealthMonitoring/SiteHealthCollector.cs b/src/ScadaLink.HealthMonitoring/SiteHealthCollector.cs index d245b08..b676d20 100644 --- a/src/ScadaLink.HealthMonitoring/SiteHealthCollector.cs +++ b/src/ScadaLink.HealthMonitoring/SiteHealthCollector.cs @@ -15,6 +15,8 @@ public class SiteHealthCollector : ISiteHealthCollector private int _deadLetterCount; private readonly ConcurrentDictionary _connectionStatuses = new(); private readonly ConcurrentDictionary _tagResolutionCounts = new(); + private IReadOnlyDictionary _sfBufferDepths = new Dictionary(); + private int _deployedInstanceCount, _enabledInstanceCount, _disabledInstanceCount; /// /// Increment the script error counter. Covers unhandled exceptions, @@ -68,6 +70,26 @@ public class SiteHealthCollector : ISiteHealthCollector _tagResolutionCounts[connectionName] = new TagResolutionStatus(totalSubscribed, successfullyResolved); } + /// + /// Set the current store-and-forward buffer depths snapshot. + /// Called before report collection with data from the S&F service. + /// + public void SetStoreAndForwardDepths(IReadOnlyDictionary depths) + { + _sfBufferDepths = depths; + } + + /// + /// Set the current instance counts. + /// Called by the Deployment Manager after instance state changes. + /// + public void SetInstanceCounts(int deployed, int enabled, int disabled) + { + Interlocked.Exchange(ref _deployedInstanceCount, deployed); + Interlocked.Exchange(ref _enabledInstanceCount, enabled); + Interlocked.Exchange(ref _disabledInstanceCount, disabled); + } + /// /// Collect the current health report for the site and reset interval counters. /// Connection statuses and tag resolution counts are NOT reset (they reflect current state). @@ -84,8 +106,8 @@ public class SiteHealthCollector : ISiteHealthCollector var connectionStatuses = new Dictionary(_connectionStatuses); var tagResolution = new Dictionary(_tagResolutionCounts); - // S&F buffer depth: placeholder (Phase 3C) - var sfBufferDepths = new Dictionary(); + // Snapshot current S&F buffer depths + var sfBufferDepths = new Dictionary(_sfBufferDepths); return new SiteHealthReport( SiteId: siteId, @@ -96,6 +118,9 @@ public class SiteHealthCollector : ISiteHealthCollector ScriptErrorCount: scriptErrors, AlarmEvaluationErrorCount: alarmErrors, StoreAndForwardBufferDepths: sfBufferDepths, - DeadLetterCount: deadLetters); + DeadLetterCount: deadLetters, + DeployedInstanceCount: _deployedInstanceCount, + EnabledInstanceCount: _enabledInstanceCount, + DisabledInstanceCount: _disabledInstanceCount); } } diff --git a/src/ScadaLink.Host/Actors/AkkaHostedService.cs b/src/ScadaLink.Host/Actors/AkkaHostedService.cs index 93fa8bb..e53aa86 100644 --- a/src/ScadaLink.Host/Actors/AkkaHostedService.cs +++ b/src/ScadaLink.Host/Actors/AkkaHostedService.cs @@ -118,8 +118,9 @@ akka {{ // Register the dead letter monitor actor var loggerFactory = _serviceProvider.GetRequiredService(); var dlmLogger = loggerFactory.CreateLogger(); + var dlmHealthCollector = _serviceProvider.GetService(); _actorSystem.ActorOf( - Props.Create(() => new DeadLetterMonitorActor(dlmLogger)), + Props.Create(() => new DeadLetterMonitorActor(dlmLogger, dlmHealthCollector)), "dead-letter-monitor"); // Register role-specific actors @@ -227,6 +228,9 @@ akka {{ _logger.LogInformation("Data Connection Layer manager actor created"); } + // Resolve the health collector for the Deployment Manager + var siteHealthCollector = _serviceProvider.GetService(); + // Create the Deployment Manager as a cluster singleton var singletonProps = ClusterSingletonManager.Props( singletonProps: Props.Create(() => new DeploymentManagerActor( @@ -236,7 +240,8 @@ akka {{ streamManager, siteRuntimeOptionsValue, dmLogger, - dclManager)), + dclManager, + siteHealthCollector)), terminationMessage: PoisonPill.Instance, settings: ClusterSingletonManagerSettings.Create(_actorSystem!) .WithRole(siteRole) diff --git a/src/ScadaLink.Host/Actors/DeadLetterMonitorActor.cs b/src/ScadaLink.Host/Actors/DeadLetterMonitorActor.cs index 1301ee5..9470bcf 100644 --- a/src/ScadaLink.Host/Actors/DeadLetterMonitorActor.cs +++ b/src/ScadaLink.Host/Actors/DeadLetterMonitorActor.cs @@ -1,6 +1,7 @@ using Akka.Actor; using Akka.Event; using Microsoft.Extensions.Logging; +using ScadaLink.HealthMonitoring; namespace ScadaLink.Host.Actors; @@ -11,12 +12,16 @@ namespace ScadaLink.Host.Actors; public class DeadLetterMonitorActor : ReceiveActor { private long _deadLetterCount; + private readonly ISiteHealthCollector? _healthCollector; - public DeadLetterMonitorActor(ILogger logger) + public DeadLetterMonitorActor(ILogger logger, ISiteHealthCollector? healthCollector = null) { + _healthCollector = healthCollector; + Receive(dl => { _deadLetterCount++; + _healthCollector?.IncrementDeadLetter(); logger.LogWarning( "Dead letter: {MessageType} from {Sender} to {Recipient}", dl.Message.GetType().Name, diff --git a/src/ScadaLink.SiteRuntime/Actors/AlarmActor.cs b/src/ScadaLink.SiteRuntime/Actors/AlarmActor.cs index 8901956..e994f4d 100644 --- a/src/ScadaLink.SiteRuntime/Actors/AlarmActor.cs +++ b/src/ScadaLink.SiteRuntime/Actors/AlarmActor.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging; using ScadaLink.Commons.Messages.Streaming; using ScadaLink.Commons.Types.Enums; using ScadaLink.Commons.Types.Flattening; +using ScadaLink.HealthMonitoring; using ScadaLink.SiteRuntime.Scripts; using System.Text.Json; @@ -34,6 +35,7 @@ public class AlarmActor : ReceiveActor private readonly SharedScriptLibrary _sharedScriptLibrary; private readonly SiteRuntimeOptions _options; private readonly ILogger _logger; + private readonly ISiteHealthCollector? _healthCollector; private AlarmState _currentState = AlarmState.Normal; private readonly AlarmTriggerType _triggerType; @@ -56,7 +58,8 @@ public class AlarmActor : ReceiveActor Script? onTriggerCompiledScript, SharedScriptLibrary sharedScriptLibrary, SiteRuntimeOptions options, - ILogger logger) + ILogger logger, + ISiteHealthCollector? healthCollector = null) { _alarmName = alarmName; _instanceName = instanceName; @@ -64,6 +67,7 @@ public class AlarmActor : ReceiveActor _sharedScriptLibrary = sharedScriptLibrary; _options = options; _logger = logger; + _healthCollector = healthCollector; _priority = alarmConfig.PriorityLevel; _onTriggerScriptName = alarmConfig.OnTriggerScriptCanonicalName; _onTriggerCompiledScript = onTriggerCompiledScript; @@ -164,6 +168,7 @@ public class AlarmActor : ReceiveActor } catch (Exception ex) { + _healthCollector?.IncrementAlarmError(); // Alarm evaluation errors logged, actor continues _logger.LogError(ex, "Alarm {Alarm} evaluation error on {Instance}", diff --git a/src/ScadaLink.SiteRuntime/Actors/DeploymentManagerActor.cs b/src/ScadaLink.SiteRuntime/Actors/DeploymentManagerActor.cs index f4e69a1..ebda674 100644 --- a/src/ScadaLink.SiteRuntime/Actors/DeploymentManagerActor.cs +++ b/src/ScadaLink.SiteRuntime/Actors/DeploymentManagerActor.cs @@ -5,6 +5,7 @@ using ScadaLink.Commons.Messages.DebugView; using ScadaLink.Commons.Messages.Deployment; using ScadaLink.Commons.Messages.Lifecycle; using ScadaLink.Commons.Types.Enums; +using ScadaLink.HealthMonitoring; using ScadaLink.SiteRuntime.Persistence; using ScadaLink.SiteRuntime.Scripts; using ScadaLink.SiteRuntime.Streaming; @@ -30,7 +31,9 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers private readonly SiteRuntimeOptions _options; private readonly ILogger _logger; private readonly IActorRef? _dclManager; + private readonly ISiteHealthCollector? _healthCollector; private readonly Dictionary _instanceActors = new(); + private int _totalDeployedCount; public ITimerScheduler Timers { get; set; } = null!; @@ -41,7 +44,8 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers SiteStreamManager? streamManager, SiteRuntimeOptions options, ILogger logger, - IActorRef? dclManager = null) + IActorRef? dclManager = null, + ISiteHealthCollector? healthCollector = null) { _storage = storage; _compilationService = compilationService; @@ -49,6 +53,7 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers _streamManager = streamManager; _options = options; _dclManager = dclManager; + _healthCollector = healthCollector; _logger = logger; // Lifecycle commands @@ -123,9 +128,11 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers } var enabledConfigs = msg.Configs.Where(c => c.IsEnabled).ToList(); + _totalDeployedCount = msg.Configs.Count; _logger.LogInformation( "Loaded {Total} deployed configs ({Enabled} enabled) from SQLite", msg.Configs.Count, enabledConfigs.Count); + UpdateInstanceCounts(); if (enabledConfigs.Count == 0) return; @@ -200,6 +207,8 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers // Create the Instance Actor immediately (no existing actor to replace) CreateInstanceActor(instanceName, command.FlattenedConfigurationJson); + _totalDeployedCount++; + UpdateInstanceCounts(); // Persist to SQLite and clear static overrides asynchronously var sender = Sender; @@ -257,6 +266,8 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers _instanceActors.Remove(instanceName); } + UpdateInstanceCounts(); + var sender = Sender; _storage.SetInstanceEnabledAsync(instanceName, false).ContinueWith(t => { @@ -313,6 +324,7 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers { CreateInstanceActor(instanceName, result.Config.ConfigJson); } + UpdateInstanceCounts(); result.OriginalSender.Tell(new InstanceLifecycleResponse( result.Command.CommandId, instanceName, true, null, DateTimeOffset.UtcNow)); @@ -333,6 +345,8 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers Context.Stop(actor); _instanceActors.Remove(instanceName); } + _totalDeployedCount = Math.Max(0, _totalDeployedCount - 1); + UpdateInstanceCounts(); var sender = Sender; _storage.RemoveDeployedConfigAsync(instanceName).ContinueWith(t => @@ -536,7 +550,8 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers _streamManager, _options, loggerFactory.CreateLogger(), - _dclManager)); + _dclManager, + _healthCollector)); var actorRef = Context.ActorOf(props, instanceName); _instanceActors[instanceName] = actorRef; @@ -549,6 +564,18 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers /// internal int InstanceActorCount => _instanceActors.Count; + /// + /// Updates the health collector with current instance counts. + /// Total deployed = _totalDeployedCount, enabled = running actors, disabled = difference. + /// + private void UpdateInstanceCounts() + { + _healthCollector?.SetInstanceCounts( + deployed: _totalDeployedCount, + enabled: _instanceActors.Count, + disabled: _totalDeployedCount - _instanceActors.Count); + } + // ── Internal messages ── internal record StartupConfigsLoaded(List Configs, string? Error); diff --git a/src/ScadaLink.SiteRuntime/Actors/InstanceActor.cs b/src/ScadaLink.SiteRuntime/Actors/InstanceActor.cs index 89be901..742b346 100644 --- a/src/ScadaLink.SiteRuntime/Actors/InstanceActor.cs +++ b/src/ScadaLink.SiteRuntime/Actors/InstanceActor.cs @@ -8,6 +8,7 @@ using ScadaLink.Commons.Messages.ScriptExecution; using ScadaLink.Commons.Messages.Streaming; using ScadaLink.Commons.Types.Enums; using ScadaLink.Commons.Types.Flattening; +using ScadaLink.HealthMonitoring; using ScadaLink.SiteRuntime.Persistence; using ScadaLink.SiteRuntime.Scripts; using ScadaLink.SiteRuntime.Streaming; @@ -37,6 +38,7 @@ public class InstanceActor : ReceiveActor private readonly SiteStreamManager? _streamManager; private readonly SiteRuntimeOptions _options; private readonly ILogger _logger; + private readonly ISiteHealthCollector? _healthCollector; private readonly Dictionary _attributes = new(); private readonly Dictionary _attributeQualities = new(); private readonly Dictionary _alarmStates = new(); @@ -61,7 +63,8 @@ public class InstanceActor : ReceiveActor SiteStreamManager? streamManager, SiteRuntimeOptions options, ILogger logger, - IActorRef? dclManager = null) + IActorRef? dclManager = null, + ISiteHealthCollector? healthCollector = null) { _instanceUniqueName = instanceUniqueName; _storage = storage; @@ -71,6 +74,7 @@ public class InstanceActor : ReceiveActor _options = options; _logger = logger; _dclManager = dclManager; + _healthCollector = healthCollector; // Deserialize the flattened configuration _configuration = JsonSerializer.Deserialize(configJson); @@ -474,7 +478,8 @@ public class InstanceActor : ReceiveActor script, _sharedScriptLibrary, _options, - _logger)); + _logger, + _healthCollector)); var actorRef = Context.ActorOf(props, $"script-{script.CanonicalName}"); _scriptActors[script.CanonicalName] = actorRef; @@ -516,7 +521,8 @@ public class InstanceActor : ReceiveActor onTriggerScript, _sharedScriptLibrary, _options, - _logger)); + _logger, + _healthCollector)); var actorRef = Context.ActorOf(props, $"alarm-{alarm.CanonicalName}"); _alarmActors[alarm.CanonicalName] = actorRef; diff --git a/src/ScadaLink.SiteRuntime/Actors/ScriptActor.cs b/src/ScadaLink.SiteRuntime/Actors/ScriptActor.cs index ecec82f..71b5e92 100644 --- a/src/ScadaLink.SiteRuntime/Actors/ScriptActor.cs +++ b/src/ScadaLink.SiteRuntime/Actors/ScriptActor.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging; using ScadaLink.Commons.Messages.ScriptExecution; using ScadaLink.Commons.Messages.Streaming; using ScadaLink.Commons.Types.Flattening; +using ScadaLink.HealthMonitoring; using ScadaLink.SiteRuntime.Scripts; using System.Text.Json; @@ -29,6 +30,7 @@ public class ScriptActor : ReceiveActor, IWithTimers private readonly SharedScriptLibrary _sharedScriptLibrary; private readonly SiteRuntimeOptions _options; private readonly ILogger _logger; + private readonly ISiteHealthCollector? _healthCollector; private Script? _compiledScript; private ScriptTriggerConfig? _triggerConfig; @@ -46,7 +48,8 @@ public class ScriptActor : ReceiveActor, IWithTimers ResolvedScript scriptConfig, SharedScriptLibrary sharedScriptLibrary, SiteRuntimeOptions options, - ILogger logger) + ILogger logger, + ISiteHealthCollector? healthCollector = null) { _scriptName = scriptName; _instanceName = instanceName; @@ -55,6 +58,7 @@ public class ScriptActor : ReceiveActor, IWithTimers _sharedScriptLibrary = sharedScriptLibrary; _options = options; _logger = logger; + _healthCollector = healthCollector; _minTimeBetweenRuns = scriptConfig.MinTimeBetweenRuns; // Parse trigger configuration @@ -207,7 +211,8 @@ public class ScriptActor : ReceiveActor, IWithTimers _options, replyTo, correlationId, - _logger)); + _logger, + _healthCollector)); Context.ActorOf(props, executionId); } diff --git a/src/ScadaLink.SiteRuntime/Actors/ScriptExecutionActor.cs b/src/ScadaLink.SiteRuntime/Actors/ScriptExecutionActor.cs index f1e845a..fe02910 100644 --- a/src/ScadaLink.SiteRuntime/Actors/ScriptExecutionActor.cs +++ b/src/ScadaLink.SiteRuntime/Actors/ScriptExecutionActor.cs @@ -2,6 +2,7 @@ using Akka.Actor; using Microsoft.CodeAnalysis.Scripting; using Microsoft.Extensions.Logging; using ScadaLink.Commons.Messages.ScriptExecution; +using ScadaLink.HealthMonitoring; using ScadaLink.SiteRuntime.Scripts; namespace ScadaLink.SiteRuntime.Actors; @@ -28,7 +29,8 @@ public class ScriptExecutionActor : ReceiveActor SiteRuntimeOptions options, IActorRef replyTo, string correlationId, - ILogger logger) + ILogger logger, + ISiteHealthCollector? healthCollector = null) { // Immediately begin execution var self = Self; @@ -37,7 +39,7 @@ public class ScriptExecutionActor : ReceiveActor ExecuteScript( scriptName, instanceName, compiledScript, parameters, callDepth, instanceActor, sharedScriptLibrary, options, replyTo, correlationId, - self, parent, logger); + self, parent, logger, healthCollector); } private static void ExecuteScript( @@ -53,7 +55,8 @@ public class ScriptExecutionActor : ReceiveActor string correlationId, IActorRef self, IActorRef parent, - ILogger logger) + ILogger logger, + ISiteHealthCollector? healthCollector) { var timeout = TimeSpan.FromSeconds(options.ScriptExecutionTimeoutSeconds); @@ -93,6 +96,7 @@ public class ScriptExecutionActor : ReceiveActor } catch (OperationCanceledException) { + healthCollector?.IncrementScriptError(); var errorMsg = $"Script '{scriptName}' on instance '{instanceName}' timed out after {timeout.TotalSeconds}s"; logger.LogWarning(errorMsg); @@ -105,6 +109,7 @@ public class ScriptExecutionActor : ReceiveActor } catch (Exception ex) { + healthCollector?.IncrementScriptError(); // WP-32: Failures logged to site event log; script NOT disabled after failure var errorMsg = $"Script '{scriptName}' on instance '{instanceName}' failed: {ex.Message}"; logger.LogError(ex, "Script execution failed: {Script} on {Instance}", scriptName, instanceName); diff --git a/src/ScadaLink.SiteRuntime/ScadaLink.SiteRuntime.csproj b/src/ScadaLink.SiteRuntime/ScadaLink.SiteRuntime.csproj index f8ce35c..a132fe8 100644 --- a/src/ScadaLink.SiteRuntime/ScadaLink.SiteRuntime.csproj +++ b/src/ScadaLink.SiteRuntime/ScadaLink.SiteRuntime.csproj @@ -22,6 +22,7 @@ + diff --git a/tests/ScadaLink.Commons.Tests/Messages/MessageConventionTests.cs b/tests/ScadaLink.Commons.Tests/Messages/MessageConventionTests.cs index 89720bb..ab108e7 100644 --- a/tests/ScadaLink.Commons.Tests/Messages/MessageConventionTests.cs +++ b/tests/ScadaLink.Commons.Tests/Messages/MessageConventionTests.cs @@ -98,7 +98,7 @@ public class MessageConventionTests }, 0, 0, new Dictionary { ["queue1"] = 5 }, - 0); + 0, 0, 0, 0); var json = JsonSerializer.Serialize(msg); var deserialized = JsonSerializer.Deserialize(json); diff --git a/tests/ScadaLink.HealthMonitoring.Tests/CentralHealthAggregatorTests.cs b/tests/ScadaLink.HealthMonitoring.Tests/CentralHealthAggregatorTests.cs index 6d37f99..d475f07 100644 --- a/tests/ScadaLink.HealthMonitoring.Tests/CentralHealthAggregatorTests.cs +++ b/tests/ScadaLink.HealthMonitoring.Tests/CentralHealthAggregatorTests.cs @@ -50,7 +50,10 @@ public class CentralHealthAggregatorTests ScriptErrorCount: 0, AlarmEvaluationErrorCount: 0, StoreAndForwardBufferDepths: new Dictionary(), - DeadLetterCount: 0); + DeadLetterCount: 0, + DeployedInstanceCount: 0, + EnabledInstanceCount: 0, + DisabledInstanceCount: 0); [Fact] public void ProcessReport_StoresState_ForNewSite() diff --git a/tests/ScadaLink.IntegrationTests/ObservabilityTests.cs b/tests/ScadaLink.IntegrationTests/ObservabilityTests.cs index e38f67e..ad54d0b 100644 --- a/tests/ScadaLink.IntegrationTests/ObservabilityTests.cs +++ b/tests/ScadaLink.IntegrationTests/ObservabilityTests.cs @@ -100,7 +100,10 @@ public class ObservabilityTests : IClassFixture ["ext-system"] = 15, ["notification"] = 2 }, - DeadLetterCount: 5); + DeadLetterCount: 5, + DeployedInstanceCount: 0, + EnabledInstanceCount: 0, + DisabledInstanceCount: 0); // Metric type 1: Data connection health Assert.Equal(2, report.DataConnectionStatuses.Count); @@ -141,7 +144,7 @@ public class ObservabilityTests : IClassFixture "site-01", 1, DateTimeOffset.UtcNow, new Dictionary(), new Dictionary(), - 0, 0, new Dictionary(), 0)); + 0, 0, new Dictionary(), 0, 0, 0, 0)); var state = aggregator.GetSiteState("site-01"); Assert.NotNull(state); @@ -152,7 +155,7 @@ public class ObservabilityTests : IClassFixture "site-01", 2, DateTimeOffset.UtcNow, new Dictionary(), new Dictionary(), - 3, 0, new Dictionary(), 0)); + 3, 0, new Dictionary(), 0, 0, 0, 0)); state = aggregator.GetSiteState("site-01"); Assert.Equal(2, state!.LastSequenceNumber); @@ -174,7 +177,7 @@ public class ObservabilityTests : IClassFixture "site-01", seq, DateTimeOffset.UtcNow, new Dictionary(), new Dictionary(), - seq, 0, new Dictionary(), 0)); + seq, 0, new Dictionary(), 0, 0, 0, 0)); } var state = aggregator.GetSiteState("site-01"); diff --git a/tests/ScadaLink.PerformanceTests/HealthAggregationTests.cs b/tests/ScadaLink.PerformanceTests/HealthAggregationTests.cs index 1211e17..b43e8ec 100644 --- a/tests/ScadaLink.PerformanceTests/HealthAggregationTests.cs +++ b/tests/ScadaLink.PerformanceTests/HealthAggregationTests.cs @@ -53,7 +53,10 @@ public class HealthAggregationTests { ["ext-system"] = i * 2 }, - DeadLetterCount: 0); + DeadLetterCount: 0, + DeployedInstanceCount: 0, + EnabledInstanceCount: 0, + DisabledInstanceCount: 0); _aggregator.ProcessReport(report); } @@ -83,7 +86,10 @@ public class HealthAggregationTests ScriptErrorCount: seq % 5 == 0 ? 1 : 0, AlarmEvaluationErrorCount: 0, StoreAndForwardBufferDepths: new Dictionary(), - DeadLetterCount: 0); + DeadLetterCount: 0, + DeployedInstanceCount: 0, + EnabledInstanceCount: 0, + DisabledInstanceCount: 0); _aggregator.ProcessReport(report); } @@ -111,14 +117,14 @@ public class HealthAggregationTests siteId, 10, DateTimeOffset.UtcNow, new Dictionary(), new Dictionary(), - 5, 0, new Dictionary(), 0)); + 5, 0, new Dictionary(), 0, 0, 0, 0)); // Send stale report with seq 5 — should be rejected _aggregator.ProcessReport(new SiteHealthReport( siteId, 5, DateTimeOffset.UtcNow, new Dictionary(), new Dictionary(), - 99, 0, new Dictionary(), 0)); + 99, 0, new Dictionary(), 0, 0, 0, 0)); var state = _aggregator.GetSiteState(siteId); Assert.NotNull(state); diff --git a/tests/ScadaLink.SiteRuntime.Tests/ScadaLink.SiteRuntime.Tests.csproj b/tests/ScadaLink.SiteRuntime.Tests/ScadaLink.SiteRuntime.Tests.csproj index 2f92078..d70ec1b 100644 --- a/tests/ScadaLink.SiteRuntime.Tests/ScadaLink.SiteRuntime.Tests.csproj +++ b/tests/ScadaLink.SiteRuntime.Tests/ScadaLink.SiteRuntime.Tests.csproj @@ -26,6 +26,7 @@ +