Example #1
0
 /// <summary>
 /// Ctor of the object.
 /// </summary>
 public EventConfigurationModel(
     string endpointId,
     string endpointName,
     string endpointUrl,
     bool?useSecurity,
     OpcAuthenticationMode opcAuthenticationMode,
     EncryptedNetworkCredential encryptedAuthCredential,
     Guid id, string eventNotifierId, string key,
     List <SelectClause> selectClauses,
     List <WhereClauseElement> whereClause,
     IotCentralEventPublishMode?iotCentralEventPublishMode = null)
 {
     EndpointId              = endpointId;
     EndpointName            = endpointName;
     EndpointUrl             = endpointUrl;
     UseSecurity             = useSecurity ?? true;
     OpcAuthenticationMode   = opcAuthenticationMode;
     EncryptedAuthCredential = encryptedAuthCredential;
     Id = id;
     EventNotifierId            = eventNotifierId;
     Key                        = key;
     IotCentralEventPublishMode = iotCentralEventPublishMode;
     SelectClauses              = selectClauses;
     WhereClause                = whereClause;
 }
 /// <summary>
 /// Ctor of the object.
 /// </summary>
 public NodePublishingConfigurationModel(NodeId nodeId, string originalId, string endpointUrl, bool?useSecurity,
                                         int?opcPublishingInterval, int?opcSamplingInterval, string displayName, int?heartbeatInterval, bool?skipFirst, OpcAuthenticationMode opcAuthenticationMode, EncryptedNetworkCredential encryptedAuthCredential)
 {
     NodeId                  = nodeId;
     ExpandedNodeId          = null;
     OriginalId              = originalId;
     EndpointUrl             = endpointUrl;
     UseSecurity             = useSecurity ?? true;
     DisplayName             = displayName;
     OpcSamplingInterval     = opcSamplingInterval;
     OpcPublishingInterval   = opcPublishingInterval;
     HeartbeatInterval       = heartbeatInterval;
     SkipFirst               = skipFirst;
     OpcAuthenticationMode   = opcAuthenticationMode;
     EncryptedAuthCredential = encryptedAuthCredential;
 }
        public async static Task <EncryptedNetworkCredential> Encrypt(NetworkCredential networkCredential)
        {
            EncryptedNetworkCredential result = new EncryptedNetworkCredential();

            X509Certificate2 cert = await OpcApplicationConfiguration.ApplicationConfiguration.SecurityConfiguration.ApplicationCertificate.LoadPrivateKey(null).ConfigureAwait(false);

            if (networkCredential.UserName != null)
            {
                result.UserName = Convert.ToBase64String(cert.GetRSAPublicKey().EncryptValue(Convert.FromBase64String(networkCredential.UserName)));
            }

            if (networkCredential.Password != null)
            {
                result.Password = Convert.ToBase64String(cert.GetRSAPublicKey().EncryptValue(Convert.FromBase64String(networkCredential.Password)));
            }

            return(result);
        }
 /// <summary>
 /// Ctor of the object.
 /// </summary>
 public NodePublishingConfigurationModel(string id, Guid endpointId, string endpointName, string endpointUrl, bool?useSecurity,
                                         int?opcPublishingInterval, int?opcSamplingInterval, string key, string displayName, int?heartbeatInterval, bool?skipFirst,
                                         OpcAuthenticationMode opcAuthenticationMode, EncryptedNetworkCredential encryptedAuthCredential, IotCentralItemPublishMode?iotCentralItemPublishMode)
 {
     NodeId                    = null;
     ExpandedNodeId            = null;
     Id                        = id;
     EndpointId                = endpointId;
     EndpointName              = endpointName;
     EndpointUrl               = endpointUrl;
     UseSecurity               = useSecurity ?? true;
     Key                       = key;
     DisplayName               = displayName;
     OpcSamplingInterval       = opcSamplingInterval;
     OpcPublishingInterval     = opcPublishingInterval;
     HeartbeatInterval         = heartbeatInterval;
     SkipFirst                 = skipFirst;
     IotCentralItemPublishMode = iotCentralItemPublishMode;
     OpcAuthenticationMode     = opcAuthenticationMode;
     EncryptedAuthCredential   = encryptedAuthCredential;
 }
        /// <summary>
        /// Create the publisher data structures to manage OPC sessions, subscriptions and monitored items.
        /// </summary>
        /// <returns></returns>
        public async Task <bool> CreateOpcPublishingDataAsync()
        {
            // create a list to manage sessions, subscriptions and monitored items.
            try
            {
                await PublisherNodeConfigurationSemaphore.WaitAsync().ConfigureAwait(false);

                await OpcSessionsListSemaphore.WaitAsync().ConfigureAwait(false);

                var uniqueEndpointUrls = _nodePublishingConfiguration.Select(n => n.EndpointUrl).Distinct();
                foreach (var endpointUrl in uniqueEndpointUrls)
                {
                    var currentNodePublishingConfiguration = _nodePublishingConfiguration.Where(n => n.EndpointUrl == endpointUrl).First();

                    EncryptedNetworkCredential encryptedAuthCredential = null;

                    if (currentNodePublishingConfiguration.OpcAuthenticationMode == OpcAuthenticationMode.UsernamePassword)
                    {
                        if (currentNodePublishingConfiguration.EncryptedAuthCredential == null)
                        {
                            throw new NullReferenceException($"Could not retrieve credentials to authenticate to the server. Please check if 'OpcAuthenticationUsername' and 'OpcAuthenticationPassword' are set in configuration.");
                        }

                        encryptedAuthCredential = currentNodePublishingConfiguration.EncryptedAuthCredential;
                    }

                    // create new session info.
                    IOpcSession opcSession = new OpcSession(endpointUrl, currentNodePublishingConfiguration.UseSecurity, OpcSessionCreationTimeout, currentNodePublishingConfiguration.OpcAuthenticationMode, encryptedAuthCredential);

                    // create a subscription for each distinct publishing inverval
                    var nodesDistinctPublishingInterval = _nodePublishingConfiguration.Where(n => n.EndpointUrl.Equals(endpointUrl, StringComparison.OrdinalIgnoreCase)).Select(c => c.OpcPublishingInterval).Distinct();
                    foreach (var nodeDistinctPublishingInterval in nodesDistinctPublishingInterval)
                    {
                        // create a subscription for the publishing interval and add it to the session.
                        IOpcSubscription opcSubscription = new OpcSubscription(nodeDistinctPublishingInterval);

                        // add all nodes with this OPC publishing interval to this subscription.
                        var nodesWithSamePublishingInterval = _nodePublishingConfiguration.Where(n => n.EndpointUrl.Equals(endpointUrl, StringComparison.OrdinalIgnoreCase)).Where(n => n.OpcPublishingInterval == nodeDistinctPublishingInterval);
                        foreach (var nodeInfo in nodesWithSamePublishingInterval)
                        {
                            // differentiate if NodeId or ExpandedNodeId format is used
                            if (nodeInfo.ExpandedNodeId != null)
                            {
                                // create a monitored item for the node, we do not have the namespace index without a connected session.
                                // so request a namespace update.
                                OpcMonitoredItem opcMonitoredItem = new OpcMonitoredItem(nodeInfo.ExpandedNodeId, opcSession.EndpointUrl,
                                                                                         nodeInfo.OpcSamplingInterval, nodeInfo.DisplayName, nodeInfo.HeartbeatInterval, nodeInfo.SkipFirst);
                                opcSubscription.OpcMonitoredItems.Add(opcMonitoredItem);
                                Interlocked.Increment(ref NodeConfigVersion);
                            }
                            else if (nodeInfo.NodeId != null)
                            {
                                // create a monitored item for the node with the configured or default sampling interval
                                OpcMonitoredItem opcMonitoredItem = new OpcMonitoredItem(nodeInfo.NodeId, opcSession.EndpointUrl,
                                                                                         nodeInfo.OpcSamplingInterval, nodeInfo.DisplayName, nodeInfo.HeartbeatInterval, nodeInfo.SkipFirst);
                                opcSubscription.OpcMonitoredItems.Add(opcMonitoredItem);
                                Interlocked.Increment(ref NodeConfigVersion);
                            }
                            else
                            {
                                Logger.Error($"Node {nodeInfo.OriginalId} has an invalid format. Skipping...");
                            }
                        }

                        // add subscription to session.
                        opcSession.OpcSubscriptions.Add(opcSubscription);
                    }

                    // add session.
                    OpcSessions.Add(opcSession);
                }
            }
            catch (Exception e)
            {
                Logger.Fatal(e, "Creation of the internal OPC data managment structures failed. Exiting...");
                return(false);
            }
            finally
            {
                OpcSessionsListSemaphore.Release();
                PublisherNodeConfigurationSemaphore.Release();
            }
            return(true);
        }
 public virtual IOpcSession CreateOpcSession(string endpointUrl, bool useSecurity, uint sessionTimeout, OpcAuthenticationMode opcAuthenticationMode, EncryptedNetworkCredential encryptedAuthCredential)
 {
     return(new OpcSession(endpointUrl, _nodePublishingConfiguration.Where(n => n.EndpointUrl == endpointUrl).First().UseSecurity, OpcSessionCreationTimeout, opcAuthenticationMode, encryptedAuthCredential));
 }
Example #7
0
        /// <summary>
        /// Adds a event node to be monitored. If there is no subscription with the requested publishing interval,
        /// one is created.
        /// </summary>
        public async Task <HttpStatusCode> AddEventNodeForMonitoringAsync(NodeId nodeId, ExpandedNodeId expandedNodeId,
                                                                          int?opcPublishingInterval, int?opcSamplingInterval, string displayName,
                                                                          int?heartbeatInterval, bool?skipFirst, CancellationToken ct, IotCentralItemPublishMode?iotCentralItemPublishMode,
                                                                          PublishNodesMethodRequestModel publishEventsMethodData)
        {
            string logPrefix     = "AddEventNodeForMonitoringAsync:";
            bool   sessionLocked = false;

            try
            {
                sessionLocked = await LockSessionAsync().ConfigureAwait(false);

                if (!sessionLocked || ct.IsCancellationRequested)
                {
                    return(HttpStatusCode.Gone);
                }

                // check if there is already a subscription with the same publishing interval, which can be used to monitor the node
                int opcPublishingIntervalForNode      = opcPublishingInterval ?? OpcPublishingIntervalDefault;
                IOpcSubscription opcEventSubscription = OpcEventSubscriptions.FirstOrDefault(s => s.RequestedPublishingInterval == opcPublishingIntervalForNode);

                // if there was none found, create one
                if (opcEventSubscription == null)
                {
                    if (opcPublishingInterval == null)
                    {
                        Logger.Information($"{logPrefix} No matching subscription with default publishing interval found.");
                        Logger.Information($"Create a new subscription with a default publishing interval.");
                    }
                    else
                    {
                        Logger.Information($"{logPrefix} No matching subscription with publishing interval of {opcPublishingInterval} found.");
                        Logger.Information($"Create a new subscription with a publishing interval of {opcPublishingInterval}.");
                    }
                    opcEventSubscription = new OpcSubscription(opcPublishingInterval);
                    OpcEventSubscriptions.Add(opcEventSubscription);
                }

                // create objects for publish check
                ExpandedNodeId expandedNodeIdCheck = expandedNodeId;
                NodeId         nodeIdCheck         = nodeId;
                if (State == SessionState.Connected)
                {
                    if (expandedNodeId == null)
                    {
                        string namespaceUri = _namespaceTable.ToArray().ElementAtOrDefault(nodeId.NamespaceIndex);
                        expandedNodeIdCheck = new ExpandedNodeId(nodeId.Identifier, nodeId.NamespaceIndex, namespaceUri, 0);
                    }
                    if (nodeId == null)
                    {
                        nodeIdCheck = new NodeId(expandedNodeId.Identifier, (ushort)(_namespaceTable.GetIndex(expandedNodeId.NamespaceUri)));
                    }
                }

                // if it is already published, we do nothing, else we create a new monitored event item
                if (!IsEventNodePublishedInSessionInternal(nodeIdCheck, expandedNodeIdCheck))
                {
                    OpcMonitoredItem           opcMonitoredItem     = null;
                    EncryptedNetworkCredential encryptedCredentials = null;

                    if (publishEventsMethodData.OpcAuthenticationMode == OpcAuthenticationMode.UsernamePassword)
                    {
                        if (string.IsNullOrWhiteSpace(publishEventsMethodData.UserName) && string.IsNullOrWhiteSpace(publishEventsMethodData.Password))
                        {
                            throw new ArgumentException($"If {nameof(publishEventsMethodData.OpcAuthenticationMode)} is set to '{OpcAuthenticationMode.UsernamePassword}', you have to specify '{nameof(publishEventsMethodData.UserName)}' and/or '{nameof(publishEventsMethodData.Password)}'.");
                        }

                        encryptedCredentials = await EncryptedNetworkCredential.FromPlainCredential(publishEventsMethodData.UserName, publishEventsMethodData.Password);
                    }

                    // add a new item to monitor
                    if (expandedNodeId == null)
                    {
                        opcMonitoredItem = new OpcMonitoredItem(new EventConfigurationModel(publishEventsMethodData.EndpointId, publishEventsMethodData.EndpointName, publishEventsMethodData.EndpointUrl, publishEventsMethodData.UseSecurity, publishEventsMethodData.OpcAuthenticationMode ?? OpcAuthenticationMode.Anonymous, encryptedCredentials,
                                                                                            publishEventsMethodData.OpcEvents[0].Id, publishEventsMethodData.OpcEvents[0].DisplayName, publishEventsMethodData.OpcEvents[0].SelectClauses,
                                                                                            publishEventsMethodData.OpcEvents[0].WhereClause, publishEventsMethodData.OpcEvents[0].IotCentralEventPublishMode), EndpointId, EndpointUrl);;
                    }
                    else
                    {
                        opcMonitoredItem = new OpcMonitoredItem(new EventConfigurationModel(publishEventsMethodData.EndpointId, publishEventsMethodData.EndpointName, publishEventsMethodData.EndpointUrl, publishEventsMethodData.UseSecurity, publishEventsMethodData.OpcAuthenticationMode ?? OpcAuthenticationMode.Anonymous, encryptedCredentials,
                                                                                            expandedNodeId.ToString(), publishEventsMethodData.OpcEvents[0].DisplayName, publishEventsMethodData.OpcEvents[0].SelectClauses,
                                                                                            publishEventsMethodData.OpcEvents[0].WhereClause, publishEventsMethodData.OpcEvents[0].IotCentralEventPublishMode), EndpointId, EndpointUrl);
                    }
                    opcEventSubscription.OpcMonitoredItems.Add(opcMonitoredItem);
                    Interlocked.Increment(ref NodeConfigVersion);
                    Logger.Debug($"{logPrefix} Added event item with nodeId '{(expandedNodeId == null ? nodeId.ToString() : expandedNodeId.ToString())}' for monitoring.");

                    // trigger the actual OPC communication with the server to be done
                    ConnectAndMonitorSession.Set();
                    return(HttpStatusCode.Accepted);
                }
                else
                {
                    Logger.Debug($"{logPrefix} Node with Id '{(expandedNodeId == null ? nodeId.ToString() : expandedNodeId.ToString())}' is already monitored.");
                }
            }
            catch (Exception e)
            {
                Logger.Error(e, $"{logPrefix} Exception while trying to add node '{(expandedNodeId == null ? nodeId.ToString() : expandedNodeId.ToString())}' for monitoring.");
                return(HttpStatusCode.InternalServerError);
            }
            finally
            {
                if (sessionLocked)
                {
                    ReleaseSession();
                }
            }
            return(HttpStatusCode.OK);
        }
 /// <summary>
 /// Ctor of the object.
 /// </summary>
 public NodePublishingConfigurationModel(NodeId nodeId, string originalId, Guid endpointId, string endpointName, string endpointUrl, bool?useSecurity,
                                         int?opcPublishingInterval, int?opcSamplingInterval, string key, string displayName, int?heartbeatInterval, bool?skipFirst,
                                         OpcAuthenticationMode opcAuthenticationMode, EncryptedNetworkCredential encryptedAuthCredential, IotCentralItemPublishMode?iotCentralItemPublishMode)
     : this(originalId, endpointId, endpointName, endpointUrl, useSecurity, opcPublishingInterval, opcSamplingInterval, key, displayName, heartbeatInterval, skipFirst, opcAuthenticationMode, encryptedAuthCredential, iotCentralItemPublishMode)
 {
     NodeId = nodeId;
 }
Example #9
0
        /// <summary>
        /// Handle publish event node method call.
        /// </summary>
        public virtual async Task <MethodResponse> HandlePublishEventsMethodAsync(MethodRequest methodRequest,
                                                                                  object userContext)
        {
            var    logPrefix    = "HandlePublishEventsMethodAsync:";
            var    useSecurity  = true;
            Guid   endpointId   = Guid.Empty;
            string endpointName = null;
            Uri    endpointUri  = null;

            OpcAuthenticationMode?     desiredAuthenticationMode  = null;
            EncryptedNetworkCredential desiredEncryptedCredential = null;

            PublishNodesMethodRequestModel  publishEventsMethodData      = null;
            PublishNodesMethodResponseModel publishedEventMethodResponse = null;
            var    statusCode     = HttpStatusCode.OK;
            var    statusResponse = new List <string>();
            string statusMessage;

            try
            {
                _logger.Debug($"{logPrefix} called");
                publishEventsMethodData = JsonConvert.DeserializeObject <PublishNodesMethodRequestModel>(methodRequest.DataAsJson);
                endpointId   = publishEventsMethodData.EndpointId == null ? Guid.Empty : new Guid(publishEventsMethodData.EndpointId);
                endpointName = publishEventsMethodData.EndpointName;
                endpointUri  = new Uri(publishEventsMethodData.EndpointUrl);
                useSecurity  = publishEventsMethodData.UseSecurity;

                if (publishEventsMethodData.OpcAuthenticationMode == OpcAuthenticationMode.UsernamePassword)
                {
                    if (string.IsNullOrWhiteSpace(publishEventsMethodData.UserName) && string.IsNullOrWhiteSpace(publishEventsMethodData.Password))
                    {
                        throw new ArgumentException($"If {nameof(publishEventsMethodData.OpcAuthenticationMode)} is set to '{OpcAuthenticationMode.UsernamePassword}', you have to specify '{nameof(publishEventsMethodData.UserName)}' and/or '{nameof(publishEventsMethodData.Password)}'.");
                    }

                    desiredAuthenticationMode  = OpcAuthenticationMode.UsernamePassword;
                    desiredEncryptedCredential = await EncryptedNetworkCredential.FromPlainCredential(publishEventsMethodData.UserName, publishEventsMethodData.Password);
                }
                if (publishEventsMethodData.OpcEvents.Count != 1)
                {
                    statusMessage =
                        $"You can only configure one Event simultaneously, but you trying to configure {publishEventsMethodData.OpcEvents.Count + 1} events";
                    _logger.Error($"{logPrefix} {statusMessage}");
                    statusResponse.Add(statusMessage);
                    statusCode = HttpStatusCode.NotAcceptable;
                }
            }
            catch (UriFormatException e)
            {
                statusMessage = $"Exception ({e.Message}) while parsing EndpointUrl '{publishEventsMethodData?.EndpointUrl}'";
                _logger.Error(e, $"{logPrefix} {statusMessage}");
                statusResponse.Add(statusMessage);
                statusCode = HttpStatusCode.NotAcceptable;
            }
            catch (FormatException e)
            {
                statusMessage = $"Exception ({e.Message}) while parsing EndpointId '{publishEventsMethodData?.EndpointId}'";
                _logger.Error(e, $"{logPrefix} {statusMessage}");
                statusResponse.Add(statusMessage);
                statusCode = HttpStatusCode.NotAcceptable;
            }
            catch (Exception e)
            {
                statusMessage = $"Exception ({e.Message}) while deserializing message payload";
                _logger.Error(e, $"{logPrefix} {statusMessage}");
                statusResponse.Add(statusMessage);
                statusCode = HttpStatusCode.InternalServerError;
            }

            if (statusCode == HttpStatusCode.OK)
            {
                // find/create a session to the endpoint URL and start monitoring the node.
                try
                {
                    // lock the publishing configuration till we are done
                    await NodeConfiguration.OpcSessionsListSemaphore.WaitAsync(_shutdownToken).ConfigureAwait(false);

                    if (ShutdownTokenSource.IsCancellationRequested)
                    {
                        statusMessage = $"Publisher is in shutdown";
                        _logger.Warning($"{logPrefix} {statusMessage}");
                        statusResponse.Add(statusMessage);
                        statusCode = HttpStatusCode.Gone;
                    }
                    else
                    {
                        IOpcSession opcSession = null;

                        /* we create new sessions in two cases
                         * 1. For new endpoints
                         * 2. For existing endpoints which do not have a OpcSession configured:
                         *    this happens if for an existing endpoint all monitored items, commands and events are removed (unused sessions are removed).
                         */
                        var isNewEndpoint = endpointId == Guid.Empty;
                        var isExistingEndpointWithoutSession = !isNewEndpoint && NodeConfiguration.OpcSessions.FirstOrDefault(s => s.EndpointId.Equals(endpointId)) == null;
                        if (isNewEndpoint || isExistingEndpointWithoutSession)
                        {
                            // if the no OpcAuthenticationMode is specified, we create the new session with "Anonymous" auth
                            if (!desiredAuthenticationMode.HasValue)
                            {
                                desiredAuthenticationMode = OpcAuthenticationMode.Anonymous;
                            }

                            if (isNewEndpoint)
                            {
                                endpointId = Guid.NewGuid();
                            }
                            // create new session info.
                            opcSession = new OpcSession(endpointId, endpointName, endpointUri.OriginalString, useSecurity, OpcSessionCreationTimeout, desiredAuthenticationMode.Value, desiredEncryptedCredential);
                            NodeConfiguration.OpcSessions.Add(opcSession);
                            Logger.Information($"{logPrefix} No matching session found for endpoint '{endpointUri.OriginalString}'. Requested to create a new one.");
                        }
                        else
                        {
                            // find the session we need to monitor the node
                            opcSession = NodeConfiguration.OpcSessions.FirstOrDefault(s => s.EndpointUrl.Equals(endpointUri?.OriginalString, StringComparison.OrdinalIgnoreCase));

                            // a session already exists, so we check, if we need to change authentication settings. This is only true, if the payload contains an OpcAuthenticationMode-Property
                            if (desiredAuthenticationMode.HasValue)
                            {
                                bool reconnectRequired = false;

                                if (opcSession.OpcAuthenticationMode != desiredAuthenticationMode.Value)
                                {
                                    opcSession.OpcAuthenticationMode = desiredAuthenticationMode.Value;
                                    reconnectRequired = true;
                                }

                                if (opcSession.EncryptedAuthCredential != desiredEncryptedCredential)
                                {
                                    opcSession.EncryptedAuthCredential = desiredEncryptedCredential;
                                    reconnectRequired = true;
                                }

                                if (reconnectRequired)
                                {
                                    await opcSession.Reconnect();
                                }
                            }
                        }

                        // process all nodes
                        if (publishEventsMethodData?.OpcEvents != null)
                        {
                            foreach (var eventNode in publishEventsMethodData?.OpcEvents)
                            {
                                NodeId         nodeId         = null;
                                ExpandedNodeId expandedNodeId = null;
                                bool           isNodeIdFormat;
                                try
                                {
                                    if (eventNode.Id.Contains("nsu=", StringComparison.InvariantCulture))
                                    {
                                        expandedNodeId = ExpandedNodeId.Parse(eventNode.Id);
                                        isNodeIdFormat = false;
                                    }
                                    else
                                    {
                                        nodeId         = NodeId.Parse(eventNode.Id);
                                        isNodeIdFormat = true;
                                    }
                                }
                                catch (Exception e)
                                {
                                    statusMessage = $"Exception in ({e.Message}) while formatting node '{eventNode.Id}'!";
                                    Logger.Error(e, $"{logPrefix} {statusMessage}");
                                    statusResponse.Add(statusMessage);
                                    statusCode = HttpStatusCode.NotAcceptable;
                                    continue;
                                }

                                try
                                {
                                    HttpStatusCode nodeStatusCode;
                                    if (isNodeIdFormat)
                                    {
                                        // add the event node info to the subscription with the default publishing interval, execute synchronously
                                        Logger.Debug(
                                            $"{logPrefix} Request to monitor eventNode with NodeId '{eventNode.Id}'");
                                        nodeStatusCode = await opcSession.AddEventNodeForMonitoringAsync(nodeId, null, 5000,
                                                                                                         2000, eventNode.DisplayName, null, null, ShutdownTokenSource.Token,
                                                                                                         null, publishEventsMethodData)
                                                         .ConfigureAwait(false);
                                    }
                                    else
                                    {
                                        // add the event node info to the subscription with the default publishing interval, execute synchronously
                                        Logger.Debug(
                                            $"{logPrefix} Request to monitor eventNode with ExpandedNodeId '{eventNode.Id}'");
                                        nodeStatusCode = await opcSession.AddEventNodeForMonitoringAsync(null,
                                                                                                         expandedNodeId, 5000, 2000, eventNode.DisplayName,
                                                                                                         null, null, ShutdownTokenSource.Token, null,
                                                                                                         publishEventsMethodData)
                                                         .ConfigureAwait(false);
                                    }

                                    // check and store a result message in case of an error
                                    switch (nodeStatusCode)
                                    {
                                    case HttpStatusCode.OK:
                                        statusMessage = $"'{eventNode.Id}': already monitored";
                                        Logger.Debug($"{logPrefix} {statusMessage}");
                                        statusResponse.Add(statusMessage);
                                        break;

                                    case HttpStatusCode.Accepted:
                                        statusMessage = $"'{eventNode.Id}': added";
                                        Logger.Debug($"{logPrefix} {statusMessage}");
                                        statusResponse.Add(statusMessage);
                                        break;

                                    case HttpStatusCode.Gone:
                                        statusMessage =
                                            $"'{eventNode.Id}': session to endpoint does not exist anymore";
                                        Logger.Debug($"{logPrefix} {statusMessage}");
                                        statusResponse.Add(statusMessage);
                                        statusCode = HttpStatusCode.Gone;
                                        break;

                                    case HttpStatusCode.InternalServerError:
                                        statusMessage = $"'{eventNode.Id}': error while trying to configure";
                                        Logger.Debug($"{logPrefix} {statusMessage}");
                                        statusResponse.Add(statusMessage);
                                        statusCode = HttpStatusCode.InternalServerError;
                                        break;
                                    }
                                }
                                catch (Exception e)
                                {
                                    statusMessage =
                                        $"Exception ({e.Message}) while trying to configure publishing node '{eventNode.Id}'";
                                    Logger.Error(e, $"{logPrefix} {statusMessage}");
                                    statusResponse.Add(statusMessage);
                                    statusCode = HttpStatusCode.InternalServerError;
                                }
                            }
                        }
                        else
                        {
                            statusMessage =
                                $"There are no EventConfigurations provided with the current call, provided JSON Data was: {methodRequest.DataAsJson}";
                            Logger.Error($"{logPrefix} {statusMessage}");
                            statusResponse.Add(statusMessage);
                            statusCode = HttpStatusCode.BadRequest;
                        }
                    }
                }
                catch (AggregateException e)
                {
                    foreach (var ex in e.InnerExceptions)
                    {
                        Logger.Error(ex, $"{logPrefix} Exception");
                    }
                    statusMessage = $"EndpointUrl: '{publishEventsMethodData.EndpointUrl}': exception ({e.Message}) while trying to publish";
                    Logger.Error(e, $"{logPrefix} {statusMessage}");
                    statusResponse.Add(statusMessage);
                    statusCode = HttpStatusCode.InternalServerError;
                }
                catch (Exception e)
                {
                    statusMessage = $"EndpointUrl: '{publishEventsMethodData.EndpointUrl}': exception ({e.Message}) while trying to publish";
                    Logger.Error(e, $"{logPrefix} {statusMessage}");
                    statusResponse.Add(statusMessage);
                    statusCode = HttpStatusCode.InternalServerError;
                }
                finally
                {
                    NodeConfiguration.OpcSessionsListSemaphore.Release();
                }
            }

            // build response
            publishedEventMethodResponse = new PublishNodesMethodResponseModel(endpointId.ToString());
            string resultString = statusCode == HttpStatusCode.OK || statusCode == HttpStatusCode.Accepted ?
                                  JsonConvert.SerializeObject(publishedEventMethodResponse):
                                  JsonConvert.SerializeObject(statusResponse);

            byte[] result = Encoding.UTF8.GetBytes(resultString);

            if (result.Length > MaxResponsePayloadLength)
            {
                Logger.Error($"{logPrefix} Response size is too long");
                Array.Resize(ref result, result.Length > MaxResponsePayloadLength ? MaxResponsePayloadLength : result.Length);
            }
            MethodResponse methodResponse = new MethodResponse(result, (int)statusCode);

            Logger.Information($"{logPrefix} completed with result {statusCode.ToString()}");
            return(methodResponse);
        }