/// <summary> /// Handler for the standard "keep alive" event sent by all OPC UA servers /// </summary> private static void StandardClient_KeepAlive(Session session, KeepAliveEventArgs e) { // Ignore if we are shutting down. if (PublisherShutdownInProgress == true) { return; } if (e != null && session != null && session.ConfiguredEndpoint != null) { OpcSession opcSession = null; try { OpcSessionsSemaphore.Wait(); var opcSessions = OpcSessions.Where(s => s.Session != null); opcSession = opcSessions.Where(s => s.Session.ConfiguredEndpoint.EndpointUrl.Equals(session.ConfiguredEndpoint.EndpointUrl)).FirstOrDefault(); if (!ServiceResult.IsGood(e.Status)) { Trace($"Session endpoint: {session.ConfiguredEndpoint.EndpointUrl} has Status: {e.Status}"); Trace($"Outstanding requests: {session.OutstandingRequestCount}, Defunct requests: {session.DefunctRequestCount}"); Trace($"Good publish requests: {session.GoodPublishRequestCount}, KeepAlive interval: {session.KeepAliveInterval}"); Trace($"SessionId: {session.SessionId}"); if (opcSession != null && opcSession.State == SessionState.Connected) { opcSession.MissedKeepAlives++; Trace($"Missed KeepAlives: {opcSession.MissedKeepAlives}"); if (opcSession.MissedKeepAlives >= OpcKeepAliveDisconnectThreshold) { Trace($"Hit configured missed keep alive threshold of {Program.OpcKeepAliveDisconnectThreshold}. Disconnecting the session to endpoint {session.ConfiguredEndpoint.EndpointUrl}."); session.KeepAlive -= StandardClient_KeepAlive; opcSession.Disconnect(); } } } else { if (opcSession != null && opcSession.MissedKeepAlives != 0) { // Reset missed keep alive count Trace($"Session endpoint: {session.ConfiguredEndpoint.EndpointUrl} got a keep alive after {opcSession.MissedKeepAlives} {(opcSession.MissedKeepAlives == 1 ? "was" : "were")} missed."); opcSession.MissedKeepAlives = 0; } } } finally { OpcSessionsSemaphore.Release(); } } else { Trace("Keep alive arguments seems to be wrong."); } }
/// <summary> /// Method to remove the node from the subscription and stop publishing telemetry to IoTHub. /// </summary> private ServiceResult OnUnpublishNodeCall(ISystemContext context, MethodState method, IList <object> inputArguments, IList <object> outputArguments) { if (inputArguments[0] == null || inputArguments[1] == null) { Trace("UnpublishNode: Invalid arguments!"); return(ServiceResult.Create(StatusCodes.BadArgumentsMissing, "Please provide all arguments!")); } NodeId nodeId = null; Uri endpointUri = null; try { if (string.IsNullOrEmpty(inputArguments[0] as string) || string.IsNullOrEmpty(inputArguments[1] as string)) { Trace($"UnpublishNode: Arguments (0 (nodeId), 1 (endpointUrl)) are not valid strings!"); return(ServiceResult.Create(StatusCodes.BadArgumentsMissing, "Please provide all arguments as strings!")); } nodeId = inputArguments[0] as string; endpointUri = new Uri(inputArguments[1] as string); } catch (UriFormatException) { Trace($"UnpublishNode: The endpointUrl is invalid '{inputArguments[1] as string}'!"); return(ServiceResult.Create(StatusCodes.BadArgumentsMissing, "Please provide a valid OPC UA endpoint URL as second argument!")); } // find the session and stop monitoring the node. try { if (PublisherShutdownInProgress) { return(ServiceResult.Create(StatusCodes.BadUnexpectedError, $"Publisher shutdown in progress.")); } // find the session we need to monitor the node OpcSession opcSession = null; try { OpcSessionsSemaphore.Wait(); opcSession = OpcSessions.FirstOrDefault(s => s.EndpointUri == endpointUri); } catch { opcSession = null; } finally { OpcSessionsSemaphore.Release(); } if (opcSession == null) { // do nothing if there is no session for this endpoint. Trace($"UnpublishNode: Session for endpoint '{endpointUri.OriginalString}' not found."); return(ServiceResult.Create(StatusCodes.BadSessionIdInvalid, "Session for endpoint of published node not found!")); } else { Trace($"UnpublishNode: Session found for endpoint '{endpointUri.OriginalString}'"); } // remove the node from the sessions monitored items list. opcSession.TagNodeForMonitoringStop(nodeId); Trace("UnpublishNode: Requested to stop monitoring of node."); // remove node from persisted config file try { PublishDataSemaphore.Wait(); var entryToRemove = PublisherConfigFileEntries.Find(l => l.NodeId == nodeId && l.EndpointUri == endpointUri); PublisherConfigFileEntries.Remove(entryToRemove); File.WriteAllText(NodesToPublishAbsFilename, JsonConvert.SerializeObject(PublisherConfigFileEntries)); } finally { PublishDataSemaphore.Release(); } } catch (Exception e) { Trace(e, $"UnpublishNode: Exception while trying to configure publishing node '{nodeId.ToString()}'"); return(ServiceResult.Create(e, StatusCodes.BadUnexpectedError, $"Unexpected error publishing node: {e.Message}")); } return(ServiceResult.Good); }
/// <summary> /// Method to start monitoring a node and publish the data to IoTHub. /// </summary> private ServiceResult OnPublishNodeCall(ISystemContext context, MethodState method, IList <object> inputArguments, IList <object> outputArguments) { if (inputArguments[0] == null || inputArguments[1] == null) { Trace("PublishNode: Invalid Arguments when trying to publish a node."); return(ServiceResult.Create(StatusCodes.BadArgumentsMissing, "Please provide all arguments!")); } NodeToPublishConfig nodeToPublish; NodeId nodeId = null; Uri endpointUri = null; try { if (string.IsNullOrEmpty(inputArguments[0] as string) || string.IsNullOrEmpty(inputArguments[1] as string)) { Trace($"PublishNode: Arguments (0 (nodeId), 1 (endpointUrl)) are not valid strings!"); return(ServiceResult.Create(StatusCodes.BadArgumentsMissing, "Please provide all arguments as strings!")); } nodeId = NodeId.Parse(inputArguments[0] as string); endpointUri = new Uri(inputArguments[1] as string); nodeToPublish = new NodeToPublishConfig(nodeId, endpointUri, OpcSamplingInterval, OpcPublishingInterval); } catch (UriFormatException) { Trace($"PublishNode: The EndpointUri has an invalid format '{inputArguments[1] as string}'!"); return(ServiceResult.Create(StatusCodes.BadArgumentsMissing, "Please provide a valid OPC UA endpoint URL as second argument!")); } catch (Exception e) { Trace(e, $"PublishNode: The NodeId has an invalid format '{inputArguments[0] as string}'!"); return(ServiceResult.Create(StatusCodes.BadArgumentsMissing, "Please provide a valid OPC UA NodeId in 'ns=' syntax as first argument!")); } // find/create a session to the endpoint URL and start monitoring the node. try { if (PublisherShutdownInProgress) { return(ServiceResult.Create(StatusCodes.BadUnexpectedError, $"Publisher shutdown in progress.")); } // find the session we need to monitor the node OpcSession opcSession = null; try { OpcSessionsSemaphore.Wait(); opcSession = OpcSessions.FirstOrDefault(s => s.EndpointUri == nodeToPublish.EndpointUri); // add a new session. if (opcSession == null) { // create new session info. opcSession = new OpcSession(nodeToPublish.EndpointUri, OpcSessionCreationTimeout); OpcSessions.Add(opcSession); Trace($"PublishNode: No matching session found for endpoint '{nodeToPublish.EndpointUri.OriginalString}'. Requested to create a new one."); } else { Trace($"PublishNode: Session found for endpoint '{nodeToPublish.EndpointUri.OriginalString}'"); } // add the node info to the subscription with the default publishing interval opcSession.AddNodeForMonitoring(OpcPublishingInterval, OpcSamplingInterval, nodeToPublish.NodeId); Trace($"PublishNode: Requested to monitor item with NodeId '{nodeToPublish.NodeId.ToString()}' (PublishingInterval: {OpcPublishingInterval}, SamplingInterval: {OpcSamplingInterval})"); } finally { OpcSessionsSemaphore.Release(); } // update our data try { PublishDataSemaphore.Wait(); PublishConfig.Add(nodeToPublish); // add it also to the publish file var publisherConfigFileEntry = new PublisherConfigFileEntry() { EndpointUri = endpointUri, NodeId = nodeId }; PublisherConfigFileEntries.Add(publisherConfigFileEntry); File.WriteAllText(NodesToPublishAbsFilename, JsonConvert.SerializeObject(PublisherConfigFileEntries)); } finally { PublishDataSemaphore.Release(); } return(ServiceResult.Good); } catch (Exception e) { Trace(e, $"PublishNode: Exception while trying to configure publishing node '{nodeToPublish.NodeId.ToString()}'"); return(ServiceResult.Create(e, StatusCodes.BadUnexpectedError, $"Unexpected error publishing node: {e.Message}")); } }