/// <summary> /// Initialize a new OPC UA client connection /// </summary> /// <param name="clientName"></param> /// <param name="configPath"></param> /// <param name="opcUaUrl"></param> /// <param name="sessionName"></param> /// <param name="sessionTimeout"></param> /// <param name="keepAlive"></param> /// <returns></returns> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ServiceResultException"></exception> public async Task InitializeOpcUaClientConnection(string clientName, string configPath, string opcUaUrl, string sessionName, uint sessionTimeout, KeepAliveEventHandler keepAlive = null) { this.ClientName = string.IsNullOrWhiteSpace(clientName) ? throw new ArgumentNullException(nameof(clientName)) : clientName; this.ConfigPath = string.IsNullOrWhiteSpace(configPath) ? throw new ArgumentNullException(nameof(configPath)) : configPath; this.OpcUaUrl = string.IsNullOrWhiteSpace(opcUaUrl) ? throw new ArgumentNullException(nameof(opcUaUrl)) : opcUaUrl; this.SessionName = string.IsNullOrWhiteSpace(sessionName) ? throw new ArgumentNullException(nameof(sessionName)) : sessionName; if (sessionTimeout == 0) { throw new ArgumentException(nameof(sessionTimeout)); } var clientApplicationConfig = await this.CreateOpcUaConfiguration(clientName : this.ClientName, configPath : this.ConfigPath); var selectedEndpoint = this.CreateOpcUaEndpoint(opcUrl: this.OpcUaUrl); await this.CreateOpcUaSession(applicationConfig : clientApplicationConfig, selectedEndpoint : selectedEndpoint, sessionName : this.SessionName, sessionTimeout : sessionTimeout, keepAlive : keepAlive); }
private async Task WhenKeepAliveStopped(CancellationToken token) { if (KeepAliveStopped) { return; } var tcs = new TaskCompletionSource <bool>(); KeepAliveEventHandler handler = (s, e) => { if (ServiceResult.IsBad(e.Status)) { tcs.TrySetResult(true); } }; using (token.Register(state => ((TaskCompletionSource <bool>)state).TrySetCanceled(), tcs, false)) { try { KeepAlive += handler; await tcs.Task; } finally { KeepAlive -= handler; } } }
/// <summary> /// Create OPC UA client session /// </summary> /// <param name="session"></param> /// <param name="keepAlive"></param> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ServiceResultException"></exception> public void CreateOpcUaSession(Session session, KeepAliveEventHandler keepAlive = null) { this.Session = session ?? throw new ArgumentNullException(nameof(session)); this.RegisterKeepAlive(keepAlive: keepAlive); }
/// <summary> /// Register the keep alive /// </summary> /// <param name="keepAlive"></param> private void RegisterKeepAlive(KeepAliveEventHandler keepAlive) { // register keep alive handler if (keepAlive == null) { this.Session.KeepAlive += this.SessionKeepAlive_Event; } // register what was passed in else { this.Session.KeepAlive += keepAlive; } }
/// <summary> /// Create OPC UA client session /// </summary> /// <param name="applicationConfig"></param> /// <param name="selectedEndpoint"></param> /// <param name="sessionName"></param> /// <param name="sessionTimeout"></param> /// <param name="keepAlive"></param> /// <returns></returns> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ServiceResultException"></exception> public async Task CreateOpcUaSession(ApplicationConfiguration applicationConfig, EndpointDescription selectedEndpoint, string sessionName, uint sessionTimeout, KeepAliveEventHandler keepAlive = null) { if (applicationConfig == null) { throw new ArgumentNullException(nameof(applicationConfig)); } if (selectedEndpoint == null) { throw new ArgumentNullException(nameof(selectedEndpoint)); } if (string.IsNullOrWhiteSpace(sessionName)) { throw new ArgumentNullException(nameof(sessionName)); } this.SessionTimeout = sessionTimeout == 0 ? throw new ArgumentException(nameof(sessionTimeout)) : sessionTimeout; var endpointConfiguration = EndpointConfiguration.Create(applicationConfiguration: applicationConfig); var endpoint = new ConfiguredEndpoint(collection: null, description: selectedEndpoint, configuration: endpointConfiguration); this.Session = await Session.Create(configuration : applicationConfig, endpoint : endpoint, updateBeforeConnect : false, sessionName : sessionName, sessionTimeout : this.SessionTimeout, identity : this.User, preferredLocales : null); this.RegisterKeepAlive(keepAlive: keepAlive); }
/// <summary> /// Tests the session reconnect. /// </summary> private bool DoReconnectTest() { double increment = MaxProgress/6; double position = 0; bool success = true; lock (m_messages) { m_messages.Clear(); } int currentKeepAlive = Session.KeepAliveInterval; List<Subscription> subscriptions = new List<Subscription>(); KeepAliveEventHandler keepAliveHandler = new KeepAliveEventHandler(Session_Reconnect); NotificationEventHandler notificationHandler = new NotificationEventHandler(Session_Notification); try { Session.KeepAlive += keepAliveHandler; Session.Notification += notificationHandler; for (int publishingInterval = 1000; publishingInterval <= 10000; publishingInterval += 1000) { Subscription subscription = new Subscription(); subscription.MaxMessageCount = 100; subscription.LifetimeCount = 100; subscription.KeepAliveCount = 10; subscription.PublishingEnabled = true; subscription.PublishingInterval = publishingInterval; MonitoredItem monitoredItem = new MonitoredItem(); monitoredItem.StartNodeId = VariableIds.Server_ServerStatus_CurrentTime; monitoredItem.AttributeId = Attributes.Value; monitoredItem.SamplingInterval = -1; monitoredItem.QueueSize = 0; monitoredItem.DiscardOldest = true; subscription.AddItem(monitoredItem); Session.AddSubscription(subscription); subscription.Create(); subscriptions.Add(subscription); } m_keepAliveCount = 0; Session.KeepAliveInterval = 1000; Log("Setting keep alive interval to {0}ms.", Session.KeepAliveInterval); int testDuration = 3000; for (int ii = 0; ii < 6; ii++) { Session.Reconnect(); Log("Session reconnected. KeepAlives={0}", m_keepAliveCount); if (m_errorEvent.WaitOne(testDuration, false)) { Log("Unexpected error waiting for session keep alives. {0}", m_error.ToLongString()); return false; } position += increment; ReportProgress(position); } } finally { Session.RemoveSubscriptions(subscriptions); Session.KeepAliveInterval = currentKeepAlive; Session.KeepAlive -= keepAliveHandler; Session.Notification -= notificationHandler; } ReportProgress(MaxProgress); lock (m_messages) { foreach (KeyValuePair<uint,List<uint>> entry in m_messages) { entry.Value.Sort(); for (int ii = 0; ii < entry.Value.Count-1; ii++) { if (entry.Value[ii+1] - entry.Value[ii] > 1) { Log("Missing message. Subscription={0}, SequenceNumber={1}-{2}", entry.Key, entry.Value[ii]+1, entry.Value[ii+1]-1); } if (entry.Value[ii+1] == entry.Value[ii]) { // Log("Duplicate message. Subscription={0}, SequenceNumber={1}", entry.Key, entry.Value[ii]); } } } } return success; }
/// <summary> /// Tests the session keep alive when there are no errors. /// </summary> private bool DoKeepAliveTest() { bool success = true; double increment = MaxProgress/3; double position = 0; m_keepAliveCount = 0; int currentKeepAlive = Session.KeepAliveInterval; List<Subscription> subscriptions = new List<Subscription>(); KeepAliveEventHandler handler = new KeepAliveEventHandler(Session_KeepAlive); try { Session.KeepAlive += handler; // add several subscriptions with long publish intervals. for (int publishingInterval = 10000; publishingInterval <= 20000; publishingInterval += 1000) { Subscription subscription = new Subscription(); subscription.MaxMessageCount = 100; subscription.LifetimeCount = 100; subscription.KeepAliveCount = 10; subscription.PublishingEnabled = true; subscription.PublishingInterval = publishingInterval; MonitoredItem monitoredItem = new MonitoredItem(); monitoredItem.StartNodeId = VariableIds.Server_ServerStatus_CurrentTime; monitoredItem.AttributeId = Attributes.Value; monitoredItem.SamplingInterval = -1; monitoredItem.QueueSize = 0; monitoredItem.DiscardOldest = true; subscription.AddItem(monitoredItem); Session.AddSubscription(subscription); subscription.Create(); subscriptions.Add(subscription); } // get a value to read. ReadValueId nodeToRead = new ReadValueId(); nodeToRead.NodeId = VariableIds.Server_ServerStatus; nodeToRead.AttributeId = Attributes.Value; ReadValueIdCollection nodesToRead = new ReadValueIdCollection(); nodesToRead.Add(nodeToRead); int testDuration = 5000; // make sure the keep alives come at the expected rate. for (int keepAliveInterval = 500; keepAliveInterval < 2000; keepAliveInterval += 500) { m_keepAliveCount = 0; DateTime start = DateTime.UtcNow; DataValueCollection results = null; DiagnosticInfoCollection diagnosticsInfos = null; Session.Read( null, 0, TimestampsToReturn.Neither, nodesToRead, out results, out diagnosticsInfos); ClientBase.ValidateResponse(results, nodesToRead); ClientBase.ValidateDiagnosticInfos(diagnosticsInfos, nodesToRead); ServerStatusDataType status = ExtensionObject.ToEncodeable(results[0].Value as ExtensionObject) as ServerStatusDataType; if (status == null) { Log("Server did not return a valid ServerStatusDataType structure. Value={0}, Status={1}", results[0].WrappedValue, results[0].StatusCode); return false; } if ((DateTime.UtcNow - start).TotalSeconds > 1) { Log("Unexpected delay reading the ServerStatus structure. Delay={0}s", (DateTime.UtcNow - start).TotalSeconds); return false; } Log("Setting keep alive interval to {0}ms.", keepAliveInterval); Session.KeepAliveInterval = keepAliveInterval; if (m_errorEvent.WaitOne(testDuration, false)) { Log("Unexpected error waiting for session keep alives. {0}", m_error.ToLongString()); return false; } if (m_keepAliveCount < testDuration / keepAliveInterval) { Log("Missing session keep alives. Expected={0}, Actual={1}", testDuration / keepAliveInterval, m_keepAliveCount); return false; } Log("{0} keep alives received in {1}ms.", m_keepAliveCount, testDuration); position += increment; ReportProgress(position); } ReportProgress(MaxProgress); } finally { Session.RemoveSubscriptions(subscriptions); Session.KeepAliveInterval = currentKeepAlive; Session.KeepAlive -= handler; } return success; }
/// <summary> /// Tests the session reconnect. /// </summary> private bool DoReconnectTest() { double increment = MaxProgress / 6; double position = 0; bool success = true; lock (m_messages) { m_messages.Clear(); } int currentKeepAlive = Session.KeepAliveInterval; List <Subscription> subscriptions = new List <Subscription>(); KeepAliveEventHandler keepAliveHandler = new KeepAliveEventHandler(Session_Reconnect); NotificationEventHandler notificationHandler = new NotificationEventHandler(Session_Notification); try { Session.KeepAlive += keepAliveHandler; Session.Notification += notificationHandler; for (int publishingInterval = 1000; publishingInterval <= 10000; publishingInterval += 1000) { Subscription subscription = new Subscription(); subscription.MaxMessageCount = 100; subscription.LifetimeCount = 100; subscription.KeepAliveCount = 10; subscription.PublishingEnabled = true; subscription.PublishingInterval = publishingInterval; MonitoredItem monitoredItem = new MonitoredItem(); monitoredItem.StartNodeId = VariableIds.Server_ServerStatus_CurrentTime; monitoredItem.AttributeId = Attributes.Value; monitoredItem.SamplingInterval = -1; monitoredItem.QueueSize = 0; monitoredItem.DiscardOldest = true; subscription.AddItem(monitoredItem); Session.AddSubscription(subscription); subscription.Create(); subscriptions.Add(subscription); } m_keepAliveCount = 0; Session.KeepAliveInterval = 1000; Log("Setting keep alive interval to {0}ms.", Session.KeepAliveInterval); int testDuration = 3000; for (int ii = 0; ii < 6; ii++) { Session.Reconnect(); Log("Session reconnected. KeepAlives={0}", m_keepAliveCount); if (m_errorEvent.WaitOne(testDuration, false)) { Log("Unexpected error waiting for session keep alives. {0}", m_error.ToLongString()); return(false); } position += increment; ReportProgress(position); } } finally { Session.RemoveSubscriptions(subscriptions); Session.KeepAliveInterval = currentKeepAlive; Session.KeepAlive -= keepAliveHandler; Session.Notification -= notificationHandler; } ReportProgress(MaxProgress); lock (m_messages) { foreach (KeyValuePair <uint, List <uint> > entry in m_messages) { entry.Value.Sort(); for (int ii = 0; ii < entry.Value.Count - 1; ii++) { if (entry.Value[ii + 1] - entry.Value[ii] > 1) { Log("Missing message. Subscription={0}, SequenceNumber={1}-{2}", entry.Key, entry.Value[ii] + 1, entry.Value[ii + 1] - 1); } if (entry.Value[ii + 1] == entry.Value[ii]) { // Log("Duplicate message. Subscription={0}, SequenceNumber={1}", entry.Key, entry.Value[ii]); } } } } return(success); }
/// <summary> /// Tests the session keep alive when there are no errors. /// </summary> private bool DoKeepAliveTest() { bool success = true; double increment = MaxProgress / 3; double position = 0; m_keepAliveCount = 0; int currentKeepAlive = Session.KeepAliveInterval; List <Subscription> subscriptions = new List <Subscription>(); KeepAliveEventHandler handler = new KeepAliveEventHandler(Session_KeepAlive); try { Session.KeepAlive += handler; // add several subscriptions with long publish intervals. for (int publishingInterval = 10000; publishingInterval <= 20000; publishingInterval += 1000) { Subscription subscription = new Subscription(); subscription.MaxMessageCount = 100; subscription.LifetimeCount = 100; subscription.KeepAliveCount = 10; subscription.PublishingEnabled = true; subscription.PublishingInterval = publishingInterval; MonitoredItem monitoredItem = new MonitoredItem(); monitoredItem.StartNodeId = VariableIds.Server_ServerStatus_CurrentTime; monitoredItem.AttributeId = Attributes.Value; monitoredItem.SamplingInterval = -1; monitoredItem.QueueSize = 0; monitoredItem.DiscardOldest = true; subscription.AddItem(monitoredItem); Session.AddSubscription(subscription); subscription.Create(); subscriptions.Add(subscription); } // get a value to read. ReadValueId nodeToRead = new ReadValueId(); nodeToRead.NodeId = VariableIds.Server_ServerStatus; nodeToRead.AttributeId = Attributes.Value; ReadValueIdCollection nodesToRead = new ReadValueIdCollection(); nodesToRead.Add(nodeToRead); int testDuration = 5000; // make sure the keep alives come at the expected rate. for (int keepAliveInterval = 500; keepAliveInterval < 2000; keepAliveInterval += 500) { m_keepAliveCount = 0; DateTime start = DateTime.UtcNow; DataValueCollection results = null; DiagnosticInfoCollection diagnosticsInfos = null; Session.Read( null, 0, TimestampsToReturn.Neither, nodesToRead, out results, out diagnosticsInfos); ClientBase.ValidateResponse(results, nodesToRead); ClientBase.ValidateDiagnosticInfos(diagnosticsInfos, nodesToRead); ServerStatusDataType status = ExtensionObject.ToEncodeable(results[0].Value as ExtensionObject) as ServerStatusDataType; if (status == null) { Log("Server did not return a valid ServerStatusDataType structure. Value={0}, Status={1}", results[0].WrappedValue, results[0].StatusCode); return(false); } if ((DateTime.UtcNow - start).TotalSeconds > 1) { Log("Unexpected delay reading the ServerStatus structure. Delay={0}s", (DateTime.UtcNow - start).TotalSeconds); return(false); } Log("Setting keep alive interval to {0}ms.", keepAliveInterval); Session.KeepAliveInterval = keepAliveInterval; if (m_errorEvent.WaitOne(testDuration, false)) { Log("Unexpected error waiting for session keep alives. {0}", m_error.ToLongString()); return(false); } if (m_keepAliveCount < testDuration / keepAliveInterval) { Log("Missing session keep alives. Expected={0}, Actual={1}", testDuration / keepAliveInterval, m_keepAliveCount); return(false); } Log("{0} keep alives received in {1}ms.", m_keepAliveCount, testDuration); position += increment; ReportProgress(position); } ReportProgress(MaxProgress); } finally { Session.RemoveSubscriptions(subscriptions); Session.KeepAliveInterval = currentKeepAlive; Session.KeepAlive -= handler; } return(success); }