From efd1cf4a02db14bcf24b2f2d5650eefd804a8d7a Mon Sep 17 00:00:00 2001 From: Bhupendra Naphade Date: Fri, 19 Jul 2024 14:57:28 +0200 Subject: [PATCH 01/10] Removed reduntand dimensions and renamed file for consistency with classname --- src/Helpers/{Metrics.cs => MetricsHelper.cs} | 19 ++-- src/Helpers/OtelHelper.cs | 4 +- src/PlcServer.cs | 16 +-- tests/MetricsTests.cs | 108 +++++++++++++++++++ 4 files changed, 124 insertions(+), 23 deletions(-) rename src/Helpers/{Metrics.cs => MetricsHelper.cs} (89%) create mode 100644 tests/MetricsTests.cs diff --git a/src/Helpers/Metrics.cs b/src/Helpers/MetricsHelper.cs similarity index 89% rename from src/Helpers/Metrics.cs rename to src/Helpers/MetricsHelper.cs index ec72e120..ad6d9edf 100644 --- a/src/Helpers/Metrics.cs +++ b/src/Helpers/MetricsHelper.cs @@ -5,7 +5,7 @@ namespace OpcPlc; using System.Diagnostics.Metrics; using System.Linq; -public static class MetricsConfig +public static class MetricsHelper { /// /// The name of the service. @@ -21,7 +21,6 @@ public static class MetricsConfig private const string OPC_PLC_SESSION_COUNT_METRIC = "opc_plc_session_count"; private const string OPC_PLC_SUBSCRIPTION_COUNT_METRIC = "opc_plc_subscription_count"; private const string OPC_PLC_MONITORED_ITEM_COUNT_METRIC = "opc_plc_monitored_item_count"; - private const string OPC_PLC_PUBLISHED_COUNT_METRIC = "opc_plc_published_count"; private const string OPC_PLC_PUBLISHED_COUNT_WITH_TYPE_METRIC = "opc_plc_published_count_with_type"; private const string OPC_PLC_TOTAL_ERRORS_METRIC = "opc_plc_total_errors"; @@ -103,7 +102,6 @@ private static string CLUSTER_NAME private static readonly UpDownCounter SessionCount = Meter.CreateUpDownCounter(OPC_PLC_SESSION_COUNT_METRIC); private static readonly UpDownCounter SubscriptionCount = Meter.CreateUpDownCounter(OPC_PLC_SUBSCRIPTION_COUNT_METRIC); private static readonly UpDownCounter MonitoredItemCount = Meter.CreateUpDownCounter(OPC_PLC_MONITORED_ITEM_COUNT_METRIC); - private static readonly Counter PublishedCount = Meter.CreateCounter(OPC_PLC_PUBLISHED_COUNT_METRIC); private static readonly Counter PublishedCountWithType = Meter.CreateCounter(OPC_PLC_PUBLISHED_COUNT_WITH_TYPE_METRIC); private static readonly Counter TotalErrors = Meter.CreateCounter(OPC_PLC_TOTAL_ERRORS_METRIC); @@ -135,11 +133,10 @@ public static void AddSubscriptionCount(string sessionId, string subscriptionId, /// /// Add a monitored item count. /// - public static void AddMonitoredItemCount(string sessionId, string subscriptionId, int delta = 1) + public static void AddMonitoredItemCount(string sessionId, int delta = 1) { var dimensions = MergeWithBaseDimensions( - new KeyValuePair("session", sessionId), - new KeyValuePair("subscription", subscriptionId)); + new KeyValuePair("session", sessionId)); MonitoredItemCount.Add(delta, dimensions); } @@ -148,14 +145,11 @@ public static void AddMonitoredItemCount(string sessionId, string subscriptionId /// public static void AddPublishedCount(string sessionId, string subscriptionId, int dataPoints, int events) { - var dimensions = ConvertDictionaryToKeyVaultPairArray(BaseDimensions); - PublishedCount.Add(1, dimensions); - if (dataPoints > 0) { var dataPointsDimensions = MergeWithBaseDimensions( new KeyValuePair("type", "data_point")); - PublishedCountWithType.Add(dataPoints, dataPointsDimensions);; + PublishedCountWithType.Add(dataPoints, dataPointsDimensions); } if (events > 0) @@ -169,11 +163,10 @@ public static void AddPublishedCount(string sessionId, string subscriptionId, in /// /// Record total errors. /// - public static void RecordTotalErrors(string operation, string errorType, int delta = 1) + public static void RecordTotalErrors(string operation, int delta = 1) { var dimensions = MergeWithBaseDimensions( - new KeyValuePair("operation", operation), - new KeyValuePair("error_type", errorType)); + new KeyValuePair("operation", operation)); TotalErrors.Add(delta, dimensions); } diff --git a/src/Helpers/OtelHelper.cs b/src/Helpers/OtelHelper.cs index e455844d..7356287a 100644 --- a/src/Helpers/OtelHelper.cs +++ b/src/Helpers/OtelHelper.cs @@ -30,8 +30,8 @@ public static void ConfigureOpenTelemetry(string serviceName, string exportEndpo _ = Sdk.CreateMeterProviderBuilder() .SetResourceBuilder(ResourceBuilder.CreateDefault() - .AddService(MetricsConfig.ServiceName).AddTelemetrySdk()) - .AddMeter(MetricsConfig.Meter.Name) + .AddService(MetricsHelper.ServiceName).AddTelemetrySdk()) + .AddMeter(MetricsHelper.Meter.Name) .AddRuntimeInstrumentation() .AddOtlpExporter((exporterOptions, metricsReaderOptions) => { exporterOptions.Endpoint = new Uri(exportEndpointUri); diff --git a/src/PlcServer.cs b/src/PlcServer.cs index 3f4abecb..b6f8acdf 100644 --- a/src/PlcServer.cs +++ b/src/PlcServer.cs @@ -18,7 +18,7 @@ namespace OpcPlc; using System.IO; using System.Reflection; using System.Threading; -using Meters = OpcPlc.MetricsConfig; +using Meters = OpcPlc.MetricsHelper; public partial class PlcServer : StandardServer { @@ -79,7 +79,7 @@ public override ResponseHeader CreateSession( } catch (Exception ex) { - Meters.RecordTotalErrors(nameof(CreateSession), ex.GetType().ToString()); + Meters.RecordTotalErrors(nameof(CreateSession)); _logger.LogError(ex, "Error creating session"); throw; } @@ -116,7 +116,7 @@ public override ResponseHeader CreateSubscription( } catch (Exception ex) { - Meters.RecordTotalErrors(nameof(CreateSubscription), ex.GetType().ToString()); + Meters.RecordTotalErrors(nameof(CreateSubscription)); _logger.LogError(ex, "Error creating subscription"); throw; } @@ -136,7 +136,7 @@ public override ResponseHeader CreateMonitoredItems( var responseHeader = base.CreateMonitoredItems(requestHeader, subscriptionId, timestampsToReturn, itemsToCreate, out results, out diagnosticInfos); - Meters.AddMonitoredItemCount(context.SessionId.ToString(), subscriptionId.ToString(), itemsToCreate.Count); + Meters.AddMonitoredItemCount(context.SessionId.ToString(), itemsToCreate.Count); _logger.LogDebug("{function} completed successfully with sessionId: {sessionId}, subscriptionId: {subscriptionId} and count: {count}", nameof(CreateMonitoredItems), @@ -148,7 +148,7 @@ public override ResponseHeader CreateMonitoredItems( } catch (Exception ex) { - Meters.RecordTotalErrors(nameof(CreateMonitoredItems), ex.GetType().ToString()); + Meters.RecordTotalErrors(nameof(CreateMonitoredItems)); _logger.LogError(ex, "Error creating monitored items"); throw; } @@ -201,7 +201,7 @@ public override ResponseHeader Publish( } catch (Exception ex) { - Meters.RecordTotalErrors(nameof(Publish), ex.GetType().ToString()); + Meters.RecordTotalErrors(nameof(Publish)); _logger.LogError(ex, "Error publishing"); throw; } @@ -225,7 +225,7 @@ public override ResponseHeader Read( } catch (Exception ex) { - Meters.RecordTotalErrors(nameof(Read), ex.GetType().ToString()); + Meters.RecordTotalErrors(nameof(Read)); _logger.LogError(ex, "Error reading"); throw; } @@ -243,7 +243,7 @@ public override ResponseHeader Write(RequestHeader requestHeader, WriteValueColl } catch (Exception ex) { - Meters.RecordTotalErrors(nameof(Write), ex.GetType().ToString()); + Meters.RecordTotalErrors(nameof(Write)); _logger.LogError(ex, "Error writing"); throw; } diff --git a/tests/MetricsTests.cs b/tests/MetricsTests.cs new file mode 100644 index 00000000..63cb313c --- /dev/null +++ b/tests/MetricsTests.cs @@ -0,0 +1,108 @@ +namespace OpcPlc.Tests; + +using FluentAssertions; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Diagnostics.Metrics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Meters = OpcPlc.MetricsHelper; + +/// +/// Tests for Metrics. +/// +internal class MetricsTests +{ + private readonly MeterListener _meterListener; + private readonly Dictionary _metrics; + + public MetricsTests() + { + _metrics = new Dictionary(); + _meterListener = new MeterListener(); + _meterListener.InstrumentPublished = (instrument, listener) => { + if (instrument.Meter.Name == MetricsHelper.Meter.Name) + { + listener.EnableMeasurementEvents(instrument); + } + }; + + _meterListener.SetMeasurementEventCallback( + (Instrument instrument, long measurement, ReadOnlySpan> tags, object? state) => { + _metrics.Add(instrument.Name, measurement); + }); + + _meterListener.SetMeasurementEventCallback( + (Instrument instrument, double measurement, ReadOnlySpan> tags, object? state) => { + _metrics.Add(instrument.Name, measurement); + }); + + _meterListener.SetMeasurementEventCallback( + (Instrument instrument, int measurement, ReadOnlySpan> tags, object? state) => { + _metrics.Add(instrument.Name, measurement); + }); + + _meterListener.Start(); + } + + [SetUp] + public void SetUp() + { + _metrics.Clear(); + } + + [TearDown] + public void TearDown() + { + _meterListener.Dispose(); + } + + [Test] + public void TestAddSessionCount() + { + var sessionId = Guid.NewGuid().ToString(); + Meters.AddSessionCount(sessionId.ToString()); + _metrics.TryGetValue("opc_plc_session_count", out var counter).Should().BeTrue(); + counter.Should().Be(1); + } + + [Test] + public void TestAddSubscriptionCount() + { + var sessionId = Guid.NewGuid().ToString(); + var subscriptionId = Guid.NewGuid().ToString(); + Meters.AddSubscriptionCount(sessionId, subscriptionId); + _metrics.TryGetValue("opc_plc_subscription_count", out var counter).Should().BeTrue(); + counter.Should().Be(1); + } + + [Test] + public void TestAddMonitoredItemCount() + { + var sessionId = Guid.NewGuid().ToString(); + Meters.AddMonitoredItemCount(sessionId); + _metrics.TryGetValue("opc_plc_monitored_item_count", out var counter).Should().BeTrue(); + counter.Should().Be(1); + } + + [Test] + public void TestAddPublishedCount() + { + var sessionId = Guid.NewGuid().ToString(); + var subscriptionId = Guid.NewGuid().ToString(); + Meters.AddPublishedCount(sessionId, subscriptionId, 1, 0); + _metrics.TryGetValue("opc_plc_published_count_with_type", out var counter).Should().BeTrue(); + counter.Should().Be(1); + } + + [Test] + public void TestRecordTotalErrors() + { + Meters.RecordTotalErrors("operation"); + _metrics.TryGetValue("opc_plc_total_errors", out var counter).Should().BeTrue(); ; + counter.Should().Be(1); + } +} + From 406667593f66ad4b2d00e203d4bb7da63961ef9f Mon Sep 17 00:00:00 2001 From: Bhupendra Naphade Date: Fri, 19 Jul 2024 15:46:41 +0200 Subject: [PATCH 02/10] Removed sessionId from MoniterItemCount metric --- src/Helpers/MetricsHelper.cs | 6 ++---- src/PlcServer.cs | 2 +- tests/MetricsTests.cs | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Helpers/MetricsHelper.cs b/src/Helpers/MetricsHelper.cs index ad6d9edf..f3f46396 100644 --- a/src/Helpers/MetricsHelper.cs +++ b/src/Helpers/MetricsHelper.cs @@ -133,11 +133,9 @@ public static void AddSubscriptionCount(string sessionId, string subscriptionId, /// /// Add a monitored item count. /// - public static void AddMonitoredItemCount(string sessionId, int delta = 1) + public static void AddMonitoredItemCount(int delta = 1) { - var dimensions = MergeWithBaseDimensions( - new KeyValuePair("session", sessionId)); - MonitoredItemCount.Add(delta, dimensions); + MonitoredItemCount.Add(delta, ConvertDictionaryToKeyVaultPairArray(BaseDimensions)); } /// diff --git a/src/PlcServer.cs b/src/PlcServer.cs index b6f8acdf..c32ac7ad 100644 --- a/src/PlcServer.cs +++ b/src/PlcServer.cs @@ -136,7 +136,7 @@ public override ResponseHeader CreateMonitoredItems( var responseHeader = base.CreateMonitoredItems(requestHeader, subscriptionId, timestampsToReturn, itemsToCreate, out results, out diagnosticInfos); - Meters.AddMonitoredItemCount(context.SessionId.ToString(), itemsToCreate.Count); + Meters.AddMonitoredItemCount(itemsToCreate.Count); _logger.LogDebug("{function} completed successfully with sessionId: {sessionId}, subscriptionId: {subscriptionId} and count: {count}", nameof(CreateMonitoredItems), diff --git a/tests/MetricsTests.cs b/tests/MetricsTests.cs index 63cb313c..2d00c869 100644 --- a/tests/MetricsTests.cs +++ b/tests/MetricsTests.cs @@ -81,8 +81,7 @@ public void TestAddSubscriptionCount() [Test] public void TestAddMonitoredItemCount() { - var sessionId = Guid.NewGuid().ToString(); - Meters.AddMonitoredItemCount(sessionId); + Meters.AddMonitoredItemCount(1); _metrics.TryGetValue("opc_plc_monitored_item_count", out var counter).Should().BeTrue(); counter.Should().Be(1); } From 372c2066b3bb161b150c74679f805c7cf5159418 Mon Sep 17 00:00:00 2001 From: Bhupendra Naphade Date: Mon, 22 Jul 2024 11:23:07 +0200 Subject: [PATCH 03/10] updated version --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index af81c6a3..53dc46b7 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "2.12.16", + "version": "2.12.17", "versionHeightOffset": -1, "publicReleaseRefSpec": [ "^refs/heads/main$", From f3438ab0678d89d9082991d711f18adf6a52342f Mon Sep 17 00:00:00 2001 From: Bhupendra Naphade Date: Mon, 22 Jul 2024 12:00:53 +0200 Subject: [PATCH 04/10] remove unused using --- tests/MetricsTests.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/MetricsTests.cs b/tests/MetricsTests.cs index 2d00c869..b934adce 100644 --- a/tests/MetricsTests.cs +++ b/tests/MetricsTests.cs @@ -5,9 +5,6 @@ namespace OpcPlc.Tests; using System; using System.Collections.Generic; using System.Diagnostics.Metrics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Meters = OpcPlc.MetricsHelper; /// From c4ccb255fc61eac215f00c719be731e6515b38dc Mon Sep 17 00:00:00 2001 From: Luis Cantero Date: Mon, 22 Jul 2024 13:50:46 +0200 Subject: [PATCH 05/10] Cosmetic --- src/Helpers/OtelHelper.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Helpers/OtelHelper.cs b/src/Helpers/OtelHelper.cs index 7356287a..58710774 100644 --- a/src/Helpers/OtelHelper.cs +++ b/src/Helpers/OtelHelper.cs @@ -6,7 +6,6 @@ namespace OpcPlc.Helpers; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; -using OpenTelemetry.Extensions.Hosting; using System; From e9938ff2ea90e205d21774a02ec487331872ded1 Mon Sep 17 00:00:00 2001 From: Luis Cantero Date: Mon, 22 Jul 2024 14:06:11 +0200 Subject: [PATCH 06/10] Remove unused and refactor --- src/Helpers/MetricsHelper.cs | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/src/Helpers/MetricsHelper.cs b/src/Helpers/MetricsHelper.cs index f3f46396..ecca5b71 100644 --- a/src/Helpers/MetricsHelper.cs +++ b/src/Helpers/MetricsHelper.cs @@ -17,7 +17,6 @@ public static class MetricsHelper /// public static readonly Meter Meter = new(ServiceName); - private const string OPC_PLC_POD_COUNT_METRIC = "opc_plc_pod_count"; private const string OPC_PLC_SESSION_COUNT_METRIC = "opc_plc_session_count"; private const string OPC_PLC_SUBSCRIPTION_COUNT_METRIC = "opc_plc_subscription_count"; private const string OPC_PLC_MONITORED_ITEM_COUNT_METRIC = "opc_plc_monitored_item_count"; @@ -53,13 +52,11 @@ private static string SIMULATION_ID { try { - var simulationId = Environment.GetEnvironmentVariable("SIMULATION_ID"); - if (string.IsNullOrEmpty(simulationId)) - { - return null; - } + string simulationId = Environment.GetEnvironmentVariable("SIMULATION_ID"); - return simulationId; + return string.IsNullOrEmpty(simulationId) + ? null + : simulationId; } catch (Exception) { @@ -74,14 +71,11 @@ private static string CLUSTER_NAME { try { - var clusterName = Environment.GetEnvironmentVariable("DEPLOYMENT_NAME"); + string clusterName = Environment.GetEnvironmentVariable("DEPLOYMENT_NAME"); - if (string.IsNullOrEmpty(clusterName)) - { - return null; - } - - return clusterName; + return string.IsNullOrEmpty(clusterName) + ? null + : clusterName; } catch (Exception) { @@ -90,7 +84,7 @@ private static string CLUSTER_NAME } } - private static readonly IDictionary BaseDimensions = new Dictionary + private static readonly IDictionary _baseDimensions = new Dictionary { { "kubernetes_node", KUBERNETES_NODE ?? "node" }, { "role_instance", ROLE_INSTANCE ?? "host" }, @@ -105,10 +99,6 @@ private static string CLUSTER_NAME private static readonly Counter PublishedCountWithType = Meter.CreateCounter(OPC_PLC_PUBLISHED_COUNT_WITH_TYPE_METRIC); private static readonly Counter TotalErrors = Meter.CreateCounter(OPC_PLC_TOTAL_ERRORS_METRIC); - private static readonly ObservableGauge PodCountGauge = Meter.CreateObservableGauge(OPC_PLC_POD_COUNT_METRIC, () => { - return new Measurement(1, ConvertDictionaryToKeyVaultPairArray(BaseDimensions)); - }); - /// /// Add a session count. /// @@ -135,7 +125,7 @@ public static void AddSubscriptionCount(string sessionId, string subscriptionId, /// public static void AddMonitoredItemCount(int delta = 1) { - MonitoredItemCount.Add(delta, ConvertDictionaryToKeyVaultPairArray(BaseDimensions)); + MonitoredItemCount.Add(delta, ConvertDictionaryToKeyVaultPairArray(_baseDimensions)); } /// @@ -181,7 +171,7 @@ private static KeyValuePair[] ConvertDictionaryToKeyVaultPairArr /// private static KeyValuePair[] MergeWithBaseDimensions(params KeyValuePair[] items) { - var newDimensions = new Dictionary(BaseDimensions); + var newDimensions = new Dictionary(_baseDimensions); foreach (var item in items) { newDimensions[item.Key] = item.Value; From 2750ad1717af26c250b41076741d3ca798bbedec Mon Sep 17 00:00:00 2001 From: Luis Cantero Date: Mon, 22 Jul 2024 14:10:17 +0200 Subject: [PATCH 07/10] Cosmetic --- src/PlcServer.cs | 643 +++++++++++++++++++++++------------------------ 1 file changed, 317 insertions(+), 326 deletions(-) diff --git a/src/PlcServer.cs b/src/PlcServer.cs index c32ac7ad..1e2eb849 100644 --- a/src/PlcServer.cs +++ b/src/PlcServer.cs @@ -3,22 +3,14 @@ namespace OpcPlc; using AlarmCondition; using Microsoft.Extensions.Logging; using Opc.Ua; -using Opc.Ua.Bindings; using Opc.Ua.Server; -using OpcPlc.CompanionSpecs.DI; using OpcPlc.Configuration; using OpcPlc.DeterministicAlarms; using OpcPlc.PluginNodes.Models; using OpcPlc.Reference; using SimpleEvents; -using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; -using System.IO; -using System.Reflection; -using System.Threading; -using Meters = OpcPlc.MetricsHelper; +using Meters = MetricsHelper; public partial class PlcServer : StandardServer { @@ -69,13 +61,13 @@ public override ResponseHeader CreateSession( { try { - var responseHeader = base.CreateSession(requestHeader, clientDescription, serverUri, endpointUrl, sessionName, clientNonce, clientCertificate, requestedSessionTimeout, maxResponseMessageSize, out sessionId, out authenticationToken, out revisedSessionTimeout, out serverNonce, out serverCertificate, out serverEndpoints, out serverSoftwareCertificates, out serverSignature, out maxRequestMessageSize); - - Meters.AddSessionCount(sessionId.ToString()); - - _logger.LogDebug("{function} completed successfully with sessionId: {sessionId}", nameof(CreateSession), sessionId); + var responseHeader = base.CreateSession(requestHeader, clientDescription, serverUri, endpointUrl, sessionName, clientNonce, clientCertificate, requestedSessionTimeout, maxResponseMessageSize, out sessionId, out authenticationToken, out revisedSessionTimeout, out serverNonce, out serverCertificate, out serverEndpoints, out serverSoftwareCertificates, out serverSignature, out maxRequestMessageSize); + + Meters.AddSessionCount(sessionId.ToString()); - return responseHeader; + _logger.LogDebug("{function} completed successfully with sessionId: {sessionId}", nameof(CreateSession), sessionId); + + return responseHeader; } catch (Exception ex) { @@ -96,159 +88,158 @@ public override ResponseHeader CreateSubscription( out uint subscriptionId, out double revisedPublishingInterval, out uint revisedLifetimeCount, - out uint revisedMaxKeepAliveCount) - { - try - { - OperationContext context = ValidateRequest(requestHeader, RequestType.CreateSubscription); - - var responseHeader = base.CreateSubscription(requestHeader, requestedPublishingInterval, requestedLifetimeCount, requestedMaxKeepAliveCount, maxNotificationsPerPublish, publishingEnabled, priority, out subscriptionId, out revisedPublishingInterval, out revisedLifetimeCount, out revisedMaxKeepAliveCount); - - Meters.AddSubscriptionCount(context.SessionId.ToString(), subscriptionId.ToString()); - - _logger.LogDebug( - "{function} completed successfully with sessionId: {sessionId} and subscriptionId: {subscriptionId}", - nameof(CreateSubscription), - context.SessionId, - subscriptionId); - - return responseHeader; - } - catch (Exception ex) - { - Meters.RecordTotalErrors(nameof(CreateSubscription)); - _logger.LogError(ex, "Error creating subscription"); - throw; - } - } - - public override ResponseHeader CreateMonitoredItems( - RequestHeader requestHeader, - uint subscriptionId, - TimestampsToReturn timestampsToReturn, - MonitoredItemCreateRequestCollection itemsToCreate, - out MonitoredItemCreateResultCollection results, - out DiagnosticInfoCollection diagnosticInfos) - { - try - { - OperationContext context = ValidateRequest(requestHeader, RequestType.CreateSubscription); - - var responseHeader = base.CreateMonitoredItems(requestHeader, subscriptionId, timestampsToReturn, itemsToCreate, out results, out diagnosticInfos); - - Meters.AddMonitoredItemCount(itemsToCreate.Count); - - _logger.LogDebug("{function} completed successfully with sessionId: {sessionId}, subscriptionId: {subscriptionId} and count: {count}", - nameof(CreateMonitoredItems), - context.SessionId, - subscriptionId, - itemsToCreate.Count); - - return responseHeader; - } - catch (Exception ex) - { - Meters.RecordTotalErrors(nameof(CreateMonitoredItems)); - _logger.LogError(ex, "Error creating monitored items"); - throw; - } - } - - public override ResponseHeader Publish( - RequestHeader requestHeader, - SubscriptionAcknowledgementCollection subscriptionAcknowledgements, - out uint subscriptionId, - out UInt32Collection availableSequenceNumbers, - out bool moreNotifications, - out NotificationMessage notificationMessage, - out StatusCodeCollection results, - out DiagnosticInfoCollection diagnosticInfos) - { - try - { - OperationContext context = ValidateRequest(requestHeader, RequestType.CreateSubscription); - - var responseHeader = base.Publish(requestHeader, subscriptionAcknowledgements, out subscriptionId, out availableSequenceNumbers, out moreNotifications, out notificationMessage, out results, out diagnosticInfos); - - int events = 0; - int dataChanges = 0; - int diagnostics = 0; - notificationMessage.NotificationData.ForEach(x => - { - if (x.Body is DataChangeNotification changeNotification) - { - dataChanges += changeNotification.MonitoredItems.Count; - diagnostics += changeNotification.DiagnosticInfos.Count; - } - else if (x.Body is EventNotificationList eventNotification) - { - events += eventNotification.Events.Count; - } - else - { - _logger.LogDebug("Unknown notification type: {notificationType}", x.Body.GetType().Name); - } - }); - - Meters.AddPublishedCount(context.SessionId.ToString(), subscriptionId.ToString(), dataChanges, events); - - _logger.LogDebug("{function} successfully with session: {sessionId} and subscriptionId: {subscriptionId}", - nameof(Publish), - context.SessionId, - subscriptionId); - - return responseHeader; - } - catch (Exception ex) - { - Meters.RecordTotalErrors(nameof(Publish)); - _logger.LogError(ex, "Error publishing"); - throw; - } - } - - public override ResponseHeader Read( - RequestHeader requestHeader, - double maxAge, - TimestampsToReturn timestampsToReturn, - ReadValueIdCollection nodesToRead, - out DataValueCollection results, - out DiagnosticInfoCollection diagnosticInfos) - { - try - { - var responseHeader = base.Read(requestHeader, maxAge, timestampsToReturn, nodesToRead, out results, out diagnosticInfos); - - _logger.LogDebug("{function} completed successfully", nameof(Read)); - - return responseHeader; - } - catch (Exception ex) - { - Meters.RecordTotalErrors(nameof(Read)); - _logger.LogError(ex, "Error reading"); - throw; - } - } - - public override ResponseHeader Write(RequestHeader requestHeader, WriteValueCollection nodesToWrite, out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos) - { - try - { - var responseHeader = base.Write(requestHeader, nodesToWrite, out results, out diagnosticInfos); - - _logger.LogDebug("{function} completed successfully", nameof(Write)); - - return responseHeader; - } - catch (Exception ex) - { - Meters.RecordTotalErrors(nameof(Write)); - _logger.LogError(ex, "Error writing"); - throw; - } - } - + out uint revisedMaxKeepAliveCount) + { + try + { + OperationContext context = ValidateRequest(requestHeader, RequestType.CreateSubscription); + + var responseHeader = base.CreateSubscription(requestHeader, requestedPublishingInterval, requestedLifetimeCount, requestedMaxKeepAliveCount, maxNotificationsPerPublish, publishingEnabled, priority, out subscriptionId, out revisedPublishingInterval, out revisedLifetimeCount, out revisedMaxKeepAliveCount); + + Meters.AddSubscriptionCount(context.SessionId.ToString(), subscriptionId.ToString()); + + _logger.LogDebug( + "{function} completed successfully with sessionId: {sessionId} and subscriptionId: {subscriptionId}", + nameof(CreateSubscription), + context.SessionId, + subscriptionId); + + return responseHeader; + } + catch (Exception ex) + { + Meters.RecordTotalErrors(nameof(CreateSubscription)); + _logger.LogError(ex, "Error creating subscription"); + throw; + } + } + + public override ResponseHeader CreateMonitoredItems( + RequestHeader requestHeader, + uint subscriptionId, + TimestampsToReturn timestampsToReturn, + MonitoredItemCreateRequestCollection itemsToCreate, + out MonitoredItemCreateResultCollection results, + out DiagnosticInfoCollection diagnosticInfos) + { + try + { + OperationContext context = ValidateRequest(requestHeader, RequestType.CreateSubscription); + + var responseHeader = base.CreateMonitoredItems(requestHeader, subscriptionId, timestampsToReturn, itemsToCreate, out results, out diagnosticInfos); + + Meters.AddMonitoredItemCount(itemsToCreate.Count); + + _logger.LogDebug("{function} completed successfully with sessionId: {sessionId}, subscriptionId: {subscriptionId} and count: {count}", + nameof(CreateMonitoredItems), + context.SessionId, + subscriptionId, + itemsToCreate.Count); + + return responseHeader; + } + catch (Exception ex) + { + Meters.RecordTotalErrors(nameof(CreateMonitoredItems)); + _logger.LogError(ex, "Error creating monitored items"); + throw; + } + } + + public override ResponseHeader Publish( + RequestHeader requestHeader, + SubscriptionAcknowledgementCollection subscriptionAcknowledgements, + out uint subscriptionId, + out UInt32Collection availableSequenceNumbers, + out bool moreNotifications, + out NotificationMessage notificationMessage, + out StatusCodeCollection results, + out DiagnosticInfoCollection diagnosticInfos) + { + try + { + OperationContext context = ValidateRequest(requestHeader, RequestType.CreateSubscription); + + var responseHeader = base.Publish(requestHeader, subscriptionAcknowledgements, out subscriptionId, out availableSequenceNumbers, out moreNotifications, out notificationMessage, out results, out diagnosticInfos); + + int events = 0; + int dataChanges = 0; + int diagnostics = 0; + notificationMessage.NotificationData.ForEach(x => { + if (x.Body is DataChangeNotification changeNotification) + { + dataChanges += changeNotification.MonitoredItems.Count; + diagnostics += changeNotification.DiagnosticInfos.Count; + } + else if (x.Body is EventNotificationList eventNotification) + { + events += eventNotification.Events.Count; + } + else + { + _logger.LogDebug("Unknown notification type: {notificationType}", x.Body.GetType().Name); + } + }); + + Meters.AddPublishedCount(context.SessionId.ToString(), subscriptionId.ToString(), dataChanges, events); + + _logger.LogDebug("{function} successfully with session: {sessionId} and subscriptionId: {subscriptionId}", + nameof(Publish), + context.SessionId, + subscriptionId); + + return responseHeader; + } + catch (Exception ex) + { + Meters.RecordTotalErrors(nameof(Publish)); + _logger.LogError(ex, "Error publishing"); + throw; + } + } + + public override ResponseHeader Read( + RequestHeader requestHeader, + double maxAge, + TimestampsToReturn timestampsToReturn, + ReadValueIdCollection nodesToRead, + out DataValueCollection results, + out DiagnosticInfoCollection diagnosticInfos) + { + try + { + var responseHeader = base.Read(requestHeader, maxAge, timestampsToReturn, nodesToRead, out results, out diagnosticInfos); + + _logger.LogDebug("{function} completed successfully", nameof(Read)); + + return responseHeader; + } + catch (Exception ex) + { + Meters.RecordTotalErrors(nameof(Read)); + _logger.LogError(ex, "Error reading"); + throw; + } + } + + public override ResponseHeader Write(RequestHeader requestHeader, WriteValueCollection nodesToWrite, out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos) + { + try + { + var responseHeader = base.Write(requestHeader, nodesToWrite, out results, out diagnosticInfos); + + _logger.LogDebug("{function} completed successfully", nameof(Write)); + + return responseHeader; + } + catch (Exception ex) + { + Meters.RecordTotalErrors(nameof(Write)); + _logger.LogError(ex, "Error writing"); + throw; + } + } + /// /// Creates the node managers for the server. /// @@ -257,8 +248,8 @@ public override ResponseHeader Write(RequestHeader requestHeader, WriteValueColl /// always creates a CoreNodesManager which handles the built-in nodes defined by the specification. /// Any additional NodeManagers are expected to handle application specific nodes. /// - protected override MasterNodeManager CreateMasterNodeManager(IServerInternal server, ApplicationConfiguration configuration) - { + protected override MasterNodeManager CreateMasterNodeManager(IServerInternal server, ApplicationConfiguration configuration) + { var nodeManagers = new List(); // When used via NuGet package in-memory, the server needs to use its own encodable factory. @@ -267,193 +258,193 @@ protected override MasterNodeManager CreateMasterNodeManager(IServerInternal ser // require the StandardServer or ServerInternalData as objects, so we need to use reflection to set it. var serverInternalDataField = typeof(StandardServer).GetField("m_serverInternal", BindingFlags.Instance | BindingFlags.NonPublic); var encodableFactoryField = serverInternalDataField.FieldType.GetField("m_factory", BindingFlags.Instance | BindingFlags.NonPublic); - encodableFactoryField.SetValue(server, new EncodeableFactory(false)); - - // Add encodable complex types. - server.Factory.AddEncodeableTypes(Assembly.GetExecutingAssembly()); - - // Add DI node manager first so that it gets the namespace index 2. - var diNodeManager = new DiNodeManager(server, configuration); - nodeManagers.Add(diNodeManager); - - PlcNodeManager = new PlcNodeManager( + encodableFactoryField.SetValue(server, new EncodeableFactory(false)); + + // Add encodable complex types. + server.Factory.AddEncodeableTypes(Assembly.GetExecutingAssembly()); + + // Add DI node manager first so that it gets the namespace index 2. + var diNodeManager = new DiNodeManager(server, configuration); + nodeManagers.Add(diNodeManager); + + PlcNodeManager = new PlcNodeManager( server, - Config, + Config, appConfig: configuration, TimeService, PlcSimulation, - _pluginNodes, - _logger); - - nodeManagers.Add(PlcNodeManager); - - if (PlcSimulation.AddSimpleEventsSimulation) - { - SimpleEventsNodeManager = new SimpleEventsNodeManager(server, configuration); - nodeManagers.Add(SimpleEventsNodeManager); - } - - if (PlcSimulation.AddAlarmSimulation) - { - AlarmNodeManager = new AlarmConditionServerNodeManager(server, configuration); - nodeManagers.Add(AlarmNodeManager); - } - - if (PlcSimulation.AddReferenceTestSimulation) - { - SimulationNodeManager = new ReferenceNodeManager(server, configuration); - nodeManagers.Add(SimulationNodeManager); - } - - if (PlcSimulation.DeterministicAlarmSimulationFile != null) - { - var scriptFileName = PlcSimulation.DeterministicAlarmSimulationFile; - if (string.IsNullOrWhiteSpace(scriptFileName)) - { - string errorMessage = "The script file for deterministic testing is not set (deterministicalarms)"; - _logger.LogError(errorMessage); - throw new Exception(errorMessage); - } - if (!File.Exists(scriptFileName)) - { - string errorMessage = $"The script file ({scriptFileName}) for deterministic testing does not exist"; - _logger.LogError(errorMessage); - throw new Exception(errorMessage); - } - - DeterministicAlarmsNodeManager = new DeterministicAlarmsNodeManager(server, configuration, TimeService, scriptFileName, _logger); - nodeManagers.Add(DeterministicAlarmsNodeManager); - } - - var masterNodeManager = new MasterNodeManager(server, configuration, dynamicNamespaceUri: null, nodeManagers.ToArray()); - + _pluginNodes, + _logger); + + nodeManagers.Add(PlcNodeManager); + + if (PlcSimulation.AddSimpleEventsSimulation) + { + SimpleEventsNodeManager = new SimpleEventsNodeManager(server, configuration); + nodeManagers.Add(SimpleEventsNodeManager); + } + + if (PlcSimulation.AddAlarmSimulation) + { + AlarmNodeManager = new AlarmConditionServerNodeManager(server, configuration); + nodeManagers.Add(AlarmNodeManager); + } + + if (PlcSimulation.AddReferenceTestSimulation) + { + SimulationNodeManager = new ReferenceNodeManager(server, configuration); + nodeManagers.Add(SimulationNodeManager); + } + + if (PlcSimulation.DeterministicAlarmSimulationFile != null) + { + var scriptFileName = PlcSimulation.DeterministicAlarmSimulationFile; + if (string.IsNullOrWhiteSpace(scriptFileName)) + { + string errorMessage = "The script file for deterministic testing is not set (deterministicalarms)"; + _logger.LogError(errorMessage); + throw new Exception(errorMessage); + } + if (!File.Exists(scriptFileName)) + { + string errorMessage = $"The script file ({scriptFileName}) for deterministic testing does not exist"; + _logger.LogError(errorMessage); + throw new Exception(errorMessage); + } + + DeterministicAlarmsNodeManager = new DeterministicAlarmsNodeManager(server, configuration, TimeService, scriptFileName, _logger); + nodeManagers.Add(DeterministicAlarmsNodeManager); + } + + var masterNodeManager = new MasterNodeManager(server, configuration, dynamicNamespaceUri: null, nodeManagers.ToArray()); + return masterNodeManager; } - + /// /// Loads the non-configurable properties for the application. /// /// /// These properties are exposed by the server but cannot be changed by administrators. /// - protected override ServerProperties LoadServerProperties() - { - var fileVersion = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location); - - string opcPlcBuildNumber = fileVersion.ProductVersion[(fileVersion.ProductVersion.IndexOf('+') + 1)..]; - string opcUaSdkVersion = Utils.GetAssemblySoftwareVersion(); - string opcUaSdkBuildNumber = opcUaSdkVersion[(opcUaSdkVersion.IndexOf('+') + 1)..]; - - var properties = new ServerProperties { - ManufacturerName = "Microsoft", - ProductName = "IoT Edge OPC UA PLC", - ProductUri = "https://github.com/Azure-Samples/iot-edge-opc-plc", - SoftwareVersion = $"{fileVersion.ProductMajorPart}.{fileVersion.ProductMinorPart}.{fileVersion.ProductBuildPart} (OPC UA SDK {Utils.GetAssemblyBuildNumber()})", - BuildNumber = $"{opcPlcBuildNumber} (OPC UA SDK {opcUaSdkBuildNumber} from {Utils.GetAssemblyTimestamp():yyyy-MM-ddTHH:mm:ssZ})", - BuildDate = File.GetLastWriteTimeUtc(Assembly.GetExecutingAssembly().Location), - }; - - return properties; - } - + protected override ServerProperties LoadServerProperties() + { + var fileVersion = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location); + + string opcPlcBuildNumber = fileVersion.ProductVersion[(fileVersion.ProductVersion.IndexOf('+') + 1)..]; + string opcUaSdkVersion = Utils.GetAssemblySoftwareVersion(); + string opcUaSdkBuildNumber = opcUaSdkVersion[(opcUaSdkVersion.IndexOf('+') + 1)..]; + + var properties = new ServerProperties { + ManufacturerName = "Microsoft", + ProductName = "IoT Edge OPC UA PLC", + ProductUri = "https://github.com/Azure-Samples/iot-edge-opc-plc", + SoftwareVersion = $"{fileVersion.ProductMajorPart}.{fileVersion.ProductMinorPart}.{fileVersion.ProductBuildPart} (OPC UA SDK {Utils.GetAssemblyBuildNumber()})", + BuildNumber = $"{opcPlcBuildNumber} (OPC UA SDK {opcUaSdkBuildNumber} from {Utils.GetAssemblyTimestamp():yyyy-MM-ddTHH:mm:ssZ})", + BuildDate = File.GetLastWriteTimeUtc(Assembly.GetExecutingAssembly().Location), + }; + + return properties; + } + /// /// Creates the resource manager for the server. /// - protected override ResourceManager CreateResourceManager(IServerInternal server, ApplicationConfiguration configuration) - { - var resourceManager = new ResourceManager(server, configuration); - - FieldInfo[] fields = typeof(StatusCodes).GetFields(BindingFlags.Public | BindingFlags.Static); - - foreach (FieldInfo field in fields) - { - uint? id = field.GetValue(typeof(StatusCodes)) as uint?; - - if (id != null) + protected override ResourceManager CreateResourceManager(IServerInternal server, ApplicationConfiguration configuration) + { + var resourceManager = new ResourceManager(server, configuration); + + FieldInfo[] fields = typeof(StatusCodes).GetFields(BindingFlags.Public | BindingFlags.Static); + + foreach (FieldInfo field in fields) + { + uint? id = field.GetValue(typeof(StatusCodes)) as uint?; + + if (id != null) { - resourceManager.Add(id.Value, locale: string.Empty, field.Name); // Empty locale name: Invariant. - } - } - - return resourceManager; - } - + resourceManager.Add(id.Value, locale: string.Empty, field.Name); // Empty locale name: Invariant. + } + } + + return resourceManager; + } + /// /// Initializes the server before it starts up. /// - protected override void OnServerStarting(ApplicationConfiguration configuration) - { - base.OnServerStarting(configuration); - - // it is up to the application to decide how to validate user identity tokens. - // this function creates validator for X509 identity tokens. - CreateUserIdentityValidators(configuration); - } - - protected override void OnServerStarted(IServerInternal server) - { - // start the simulation - base.OnServerStarted(server); - - // request notifications when the user identity is changed, all valid users are accepted by default. - server.SessionManager.ImpersonateUser += new ImpersonateEventHandler(SessionManager_ImpersonateUser); + protected override void OnServerStarting(ApplicationConfiguration configuration) + { + base.OnServerStarting(configuration); + + // it is up to the application to decide how to validate user identity tokens. + // this function creates validator for X509 identity tokens. + CreateUserIdentityValidators(configuration); + } + + protected override void OnServerStarted(IServerInternal server) + { + // start the simulation + base.OnServerStarted(server); + + // request notifications when the user identity is changed, all valid users are accepted by default. + server.SessionManager.ImpersonateUser += new ImpersonateEventHandler(SessionManager_ImpersonateUser); } /// - protected override void ProcessRequest(IEndpointIncomingRequest request, object calldata) - { - if (request is IAsyncResult asyncResult && - asyncResult.AsyncState is object[] asyncStateArray && - asyncStateArray[0] is TcpServerChannel channel) - { + protected override void ProcessRequest(IEndpointIncomingRequest request, object calldata) + { + if (request is IAsyncResult asyncResult && + asyncResult.AsyncState is object[] asyncStateArray && + asyncStateArray[0] is TcpServerChannel channel) + { using var scope = _logger.BeginScope("ChannelId:\"{ChannelId}\"", channel.Id); - base.ProcessRequest(request, calldata); - } - else - { - base.ProcessRequest(request, calldata); - } - } - + base.ProcessRequest(request, calldata); + } + else + { + base.ProcessRequest(request, calldata); + } + } + /// /// Cleans up before the server shuts down. /// /// /// This method is called before any shutdown processing occurs. /// - protected override void OnServerStopping() - { - try - { - // check for connected clients - IList currentSessions = ServerInternal.SessionManager.GetSessions(); - - if (currentSessions.Count > 0) + protected override void OnServerStopping() + { + try + { + // check for connected clients + IList currentSessions = ServerInternal.SessionManager.GetSessions(); + + if (currentSessions.Count > 0) { // provide some time for the connected clients to detect the shutdown state. ServerInternal.Status.Value.ShutdownReason = new LocalizedText(string.Empty, "Application closed."); // Invariant. - ServerInternal.Status.Variable.ShutdownReason.Value = new LocalizedText(string.Empty, "Application closed."); // Invariant. - ServerInternal.Status.Value.State = ServerState.Shutdown; - ServerInternal.Status.Variable.State.Value = ServerState.Shutdown; - ServerInternal.Status.Variable.ClearChangeMasks(ServerInternal.DefaultSystemContext, true); - - for (uint secondsUntilShutdown = _plcShutdownWaitSeconds; secondsUntilShutdown > 0; secondsUntilShutdown--) - { - ServerInternal.Status.Value.SecondsTillShutdown = secondsUntilShutdown; - ServerInternal.Status.Variable.SecondsTillShutdown.Value = secondsUntilShutdown; - ServerInternal.Status.Variable.ClearChangeMasks(ServerInternal.DefaultSystemContext, includeChildren: true); - - Thread.Sleep(TimeSpan.FromSeconds(1)); - } - } - } - catch - { - // ignore error during shutdown procedure - } - - base.OnServerStopping(); - } - + ServerInternal.Status.Variable.ShutdownReason.Value = new LocalizedText(string.Empty, "Application closed."); // Invariant. + ServerInternal.Status.Value.State = ServerState.Shutdown; + ServerInternal.Status.Variable.State.Value = ServerState.Shutdown; + ServerInternal.Status.Variable.ClearChangeMasks(ServerInternal.DefaultSystemContext, true); + + for (uint secondsUntilShutdown = _plcShutdownWaitSeconds; secondsUntilShutdown > 0; secondsUntilShutdown--) + { + ServerInternal.Status.Value.SecondsTillShutdown = secondsUntilShutdown; + ServerInternal.Status.Variable.SecondsTillShutdown.Value = secondsUntilShutdown; + ServerInternal.Status.Variable.ClearChangeMasks(ServerInternal.DefaultSystemContext, includeChildren: true); + + Thread.Sleep(TimeSpan.FromSeconds(1)); + } + } + } + catch + { + // ignore error during shutdown procedure + } + + base.OnServerStopping(); + } + private const uint _plcShutdownWaitSeconds = 10; } From a97585c48ffc1fca984134b87ea5fa80f58750a9 Mon Sep 17 00:00:00 2001 From: Luis Cantero Date: Mon, 22 Jul 2024 14:21:38 +0200 Subject: [PATCH 08/10] Improve namespaces --- src/PlcServer.cs | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/PlcServer.cs b/src/PlcServer.cs index 1e2eb849..ee25dd15 100644 --- a/src/PlcServer.cs +++ b/src/PlcServer.cs @@ -3,15 +3,22 @@ namespace OpcPlc; using AlarmCondition; using Microsoft.Extensions.Logging; using Opc.Ua; +using Opc.Ua.Bindings; using Opc.Ua.Server; +using OpcPlc.CompanionSpecs.DI; using OpcPlc.Configuration; using OpcPlc.DeterministicAlarms; using OpcPlc.PluginNodes.Models; using OpcPlc.Reference; using SimpleEvents; +using System; +using System.Collections.Generic; using System.Collections.Immutable; -using Meters = MetricsHelper; - +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Threading; + public partial class PlcServer : StandardServer { public PlcNodeManager PlcNodeManager { get; set; } @@ -37,6 +44,8 @@ public PlcServer(OpcPlcConfiguration config, PlcSimulation plcSimulation, TimeSe TimeService = timeService; _pluginNodes = pluginNodes; _logger = logger; + + MetricsHelper.IsEnabled = Config.OtlpEndpointUri is not null; } public override ResponseHeader CreateSession( @@ -63,7 +72,7 @@ public override ResponseHeader CreateSession( { var responseHeader = base.CreateSession(requestHeader, clientDescription, serverUri, endpointUrl, sessionName, clientNonce, clientCertificate, requestedSessionTimeout, maxResponseMessageSize, out sessionId, out authenticationToken, out revisedSessionTimeout, out serverNonce, out serverCertificate, out serverEndpoints, out serverSoftwareCertificates, out serverSignature, out maxRequestMessageSize); - Meters.AddSessionCount(sessionId.ToString()); + MetricsHelper.AddSessionCount(sessionId.ToString()); _logger.LogDebug("{function} completed successfully with sessionId: {sessionId}", nameof(CreateSession), sessionId); @@ -71,7 +80,7 @@ public override ResponseHeader CreateSession( } catch (Exception ex) { - Meters.RecordTotalErrors(nameof(CreateSession)); + MetricsHelper.RecordTotalErrors(nameof(CreateSession)); _logger.LogError(ex, "Error creating session"); throw; } @@ -96,7 +105,7 @@ public override ResponseHeader CreateSubscription( var responseHeader = base.CreateSubscription(requestHeader, requestedPublishingInterval, requestedLifetimeCount, requestedMaxKeepAliveCount, maxNotificationsPerPublish, publishingEnabled, priority, out subscriptionId, out revisedPublishingInterval, out revisedLifetimeCount, out revisedMaxKeepAliveCount); - Meters.AddSubscriptionCount(context.SessionId.ToString(), subscriptionId.ToString()); + MetricsHelper.AddSubscriptionCount(context.SessionId.ToString(), subscriptionId.ToString()); _logger.LogDebug( "{function} completed successfully with sessionId: {sessionId} and subscriptionId: {subscriptionId}", @@ -108,7 +117,7 @@ public override ResponseHeader CreateSubscription( } catch (Exception ex) { - Meters.RecordTotalErrors(nameof(CreateSubscription)); + MetricsHelper.RecordTotalErrors(nameof(CreateSubscription)); _logger.LogError(ex, "Error creating subscription"); throw; } @@ -128,7 +137,7 @@ public override ResponseHeader CreateMonitoredItems( var responseHeader = base.CreateMonitoredItems(requestHeader, subscriptionId, timestampsToReturn, itemsToCreate, out results, out diagnosticInfos); - Meters.AddMonitoredItemCount(itemsToCreate.Count); + MetricsHelper.AddMonitoredItemCount(itemsToCreate.Count); _logger.LogDebug("{function} completed successfully with sessionId: {sessionId}, subscriptionId: {subscriptionId} and count: {count}", nameof(CreateMonitoredItems), @@ -140,7 +149,7 @@ public override ResponseHeader CreateMonitoredItems( } catch (Exception ex) { - Meters.RecordTotalErrors(nameof(CreateMonitoredItems)); + MetricsHelper.RecordTotalErrors(nameof(CreateMonitoredItems)); _logger.LogError(ex, "Error creating monitored items"); throw; } @@ -181,7 +190,7 @@ public override ResponseHeader Publish( } }); - Meters.AddPublishedCount(context.SessionId.ToString(), subscriptionId.ToString(), dataChanges, events); + MetricsHelper.AddPublishedCount(context.SessionId.ToString(), subscriptionId.ToString(), dataChanges, events); _logger.LogDebug("{function} successfully with session: {sessionId} and subscriptionId: {subscriptionId}", nameof(Publish), @@ -192,7 +201,7 @@ public override ResponseHeader Publish( } catch (Exception ex) { - Meters.RecordTotalErrors(nameof(Publish)); + MetricsHelper.RecordTotalErrors(nameof(Publish)); _logger.LogError(ex, "Error publishing"); throw; } @@ -216,7 +225,7 @@ public override ResponseHeader Read( } catch (Exception ex) { - Meters.RecordTotalErrors(nameof(Read)); + MetricsHelper.RecordTotalErrors(nameof(Read)); _logger.LogError(ex, "Error reading"); throw; } @@ -234,7 +243,7 @@ public override ResponseHeader Write(RequestHeader requestHeader, WriteValueColl } catch (Exception ex) { - Meters.RecordTotalErrors(nameof(Write)); + MetricsHelper.RecordTotalErrors(nameof(Write)); _logger.LogError(ex, "Error writing"); throw; } @@ -428,7 +437,7 @@ protected override void OnServerStopping() ServerInternal.Status.Variable.State.Value = ServerState.Shutdown; ServerInternal.Status.Variable.ClearChangeMasks(ServerInternal.DefaultSystemContext, true); - for (uint secondsUntilShutdown = _plcShutdownWaitSeconds; secondsUntilShutdown > 0; secondsUntilShutdown--) + for (uint secondsUntilShutdown = PlcShutdownWaitSeconds; secondsUntilShutdown > 0; secondsUntilShutdown--) { ServerInternal.Status.Value.SecondsTillShutdown = secondsUntilShutdown; ServerInternal.Status.Variable.SecondsTillShutdown.Value = secondsUntilShutdown; @@ -446,5 +455,5 @@ protected override void OnServerStopping() base.OnServerStopping(); } - private const uint _plcShutdownWaitSeconds = 10; + private const uint PlcShutdownWaitSeconds = 10; } From fa6fbd0d3024e336491366453a4060c2b938980d Mon Sep 17 00:00:00 2001 From: Luis Cantero Date: Mon, 22 Jul 2024 14:21:55 +0200 Subject: [PATCH 09/10] Add isEnabled for metrics --- src/Helpers/MetricsHelper.cs | 52 ++++++++++++++++++++++++++++-------- tests/MetricsTests.cs | 2 ++ 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/Helpers/MetricsHelper.cs b/src/Helpers/MetricsHelper.cs index ecca5b71..e4fc6297 100644 --- a/src/Helpers/MetricsHelper.cs +++ b/src/Helpers/MetricsHelper.cs @@ -17,6 +17,11 @@ public static class MetricsHelper /// public static readonly Meter Meter = new(ServiceName); + /// + /// Gets or sets whether the meter is enabled. + /// + public static bool IsEnabled { get; set; } + private const string OPC_PLC_SESSION_COUNT_METRIC = "opc_plc_session_count"; private const string OPC_PLC_SUBSCRIPTION_COUNT_METRIC = "opc_plc_subscription_count"; private const string OPC_PLC_MONITORED_ITEM_COUNT_METRIC = "opc_plc_monitored_item_count"; @@ -93,19 +98,24 @@ private static string CLUSTER_NAME { "cluster", CLUSTER_NAME ?? "cluster" }, }; - private static readonly UpDownCounter SessionCount = Meter.CreateUpDownCounter(OPC_PLC_SESSION_COUNT_METRIC); - private static readonly UpDownCounter SubscriptionCount = Meter.CreateUpDownCounter(OPC_PLC_SUBSCRIPTION_COUNT_METRIC); - private static readonly UpDownCounter MonitoredItemCount = Meter.CreateUpDownCounter(OPC_PLC_MONITORED_ITEM_COUNT_METRIC); - private static readonly Counter PublishedCountWithType = Meter.CreateCounter(OPC_PLC_PUBLISHED_COUNT_WITH_TYPE_METRIC); - private static readonly Counter TotalErrors = Meter.CreateCounter(OPC_PLC_TOTAL_ERRORS_METRIC); + private static readonly UpDownCounter _sessionCount = Meter.CreateUpDownCounter(OPC_PLC_SESSION_COUNT_METRIC); + private static readonly UpDownCounter _subscriptionCount = Meter.CreateUpDownCounter(OPC_PLC_SUBSCRIPTION_COUNT_METRIC); + private static readonly UpDownCounter _monitoredItemCount = Meter.CreateUpDownCounter(OPC_PLC_MONITORED_ITEM_COUNT_METRIC); + private static readonly Counter _publishedCountWithType = Meter.CreateCounter(OPC_PLC_PUBLISHED_COUNT_WITH_TYPE_METRIC); + private static readonly Counter _totalErrors = Meter.CreateCounter(OPC_PLC_TOTAL_ERRORS_METRIC); /// /// Add a session count. /// public static void AddSessionCount(string sessionId, int delta = 1) { + if (!IsEnabled) + { + return; + } + var dimensions = MergeWithBaseDimensions(new KeyValuePair("session", sessionId)); - SessionCount.Add(delta, dimensions); + _sessionCount.Add(delta, dimensions); } /// @@ -113,11 +123,16 @@ public static void AddSessionCount(string sessionId, int delta = 1) /// public static void AddSubscriptionCount(string sessionId, string subscriptionId, int delta = 1) { + if (!IsEnabled) + { + return; + } + var dimensions = MergeWithBaseDimensions( new KeyValuePair("session", sessionId), new KeyValuePair("subscription", subscriptionId)); - SubscriptionCount.Add(delta, dimensions); + _subscriptionCount.Add(delta, dimensions); } /// @@ -125,7 +140,12 @@ public static void AddSubscriptionCount(string sessionId, string subscriptionId, /// public static void AddMonitoredItemCount(int delta = 1) { - MonitoredItemCount.Add(delta, ConvertDictionaryToKeyVaultPairArray(_baseDimensions)); + if (!IsEnabled) + { + return; + } + + _monitoredItemCount.Add(delta, ConvertDictionaryToKeyVaultPairArray(_baseDimensions)); } /// @@ -133,18 +153,23 @@ public static void AddMonitoredItemCount(int delta = 1) /// public static void AddPublishedCount(string sessionId, string subscriptionId, int dataPoints, int events) { + if (!IsEnabled) + { + return; + } + if (dataPoints > 0) { var dataPointsDimensions = MergeWithBaseDimensions( new KeyValuePair("type", "data_point")); - PublishedCountWithType.Add(dataPoints, dataPointsDimensions); + _publishedCountWithType.Add(dataPoints, dataPointsDimensions); } if (events > 0) { var eventsDimensions = MergeWithBaseDimensions( new KeyValuePair("type", "event")); - PublishedCountWithType.Add(events, eventsDimensions); + _publishedCountWithType.Add(events, eventsDimensions); } } @@ -153,9 +178,14 @@ public static void AddPublishedCount(string sessionId, string subscriptionId, in /// public static void RecordTotalErrors(string operation, int delta = 1) { + if (!IsEnabled) + { + return; + } + var dimensions = MergeWithBaseDimensions( new KeyValuePair("operation", operation)); - TotalErrors.Add(delta, dimensions); + _totalErrors.Add(delta, dimensions); } /// diff --git a/tests/MetricsTests.cs b/tests/MetricsTests.cs index b934adce..ee57bbdf 100644 --- a/tests/MetricsTests.cs +++ b/tests/MetricsTests.cs @@ -48,6 +48,8 @@ public MetricsTests() public void SetUp() { _metrics.Clear(); + + MetricsHelper.IsEnabled = true; } [TearDown] From 1d8bd9062c3f47d190e6f3fe9a8cf419cdba514b Mon Sep 17 00:00:00 2001 From: Luis Cantero Date: Mon, 22 Jul 2024 14:23:18 +0200 Subject: [PATCH 10/10] Rename, add NonParallelizable --- tests/MetricsTests.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/MetricsTests.cs b/tests/MetricsTests.cs index ee57bbdf..ca6c05e4 100644 --- a/tests/MetricsTests.cs +++ b/tests/MetricsTests.cs @@ -5,11 +5,11 @@ namespace OpcPlc.Tests; using System; using System.Collections.Generic; using System.Diagnostics.Metrics; -using Meters = OpcPlc.MetricsHelper; /// /// Tests for Metrics. /// +[NonParallelizable] internal class MetricsTests { private readonly MeterListener _meterListener; @@ -62,7 +62,7 @@ public void TearDown() public void TestAddSessionCount() { var sessionId = Guid.NewGuid().ToString(); - Meters.AddSessionCount(sessionId.ToString()); + MetricsHelper.AddSessionCount(sessionId.ToString()); _metrics.TryGetValue("opc_plc_session_count", out var counter).Should().BeTrue(); counter.Should().Be(1); } @@ -72,7 +72,7 @@ public void TestAddSubscriptionCount() { var sessionId = Guid.NewGuid().ToString(); var subscriptionId = Guid.NewGuid().ToString(); - Meters.AddSubscriptionCount(sessionId, subscriptionId); + MetricsHelper.AddSubscriptionCount(sessionId, subscriptionId); _metrics.TryGetValue("opc_plc_subscription_count", out var counter).Should().BeTrue(); counter.Should().Be(1); } @@ -80,7 +80,7 @@ public void TestAddSubscriptionCount() [Test] public void TestAddMonitoredItemCount() { - Meters.AddMonitoredItemCount(1); + MetricsHelper.AddMonitoredItemCount(1); _metrics.TryGetValue("opc_plc_monitored_item_count", out var counter).Should().BeTrue(); counter.Should().Be(1); } @@ -90,7 +90,7 @@ public void TestAddPublishedCount() { var sessionId = Guid.NewGuid().ToString(); var subscriptionId = Guid.NewGuid().ToString(); - Meters.AddPublishedCount(sessionId, subscriptionId, 1, 0); + MetricsHelper.AddPublishedCount(sessionId, subscriptionId, 1, 0); _metrics.TryGetValue("opc_plc_published_count_with_type", out var counter).Should().BeTrue(); counter.Should().Be(1); } @@ -98,9 +98,8 @@ public void TestAddPublishedCount() [Test] public void TestRecordTotalErrors() { - Meters.RecordTotalErrors("operation"); + MetricsHelper.RecordTotalErrors("operation"); _metrics.TryGetValue("opc_plc_total_errors", out var counter).Should().BeTrue(); ; counter.Should().Be(1); } } -