/// <summary> /// Converts the value to a human readable string. /// </summary> /// <returns>A string.</returns> public override string ToString() { StringBuilder buffer = new StringBuilder(); buffer.Append(StatusCodes.GetDefaultMessage(StatusCode)); if (!string.IsNullOrEmpty(SymbolicId)) { if (!string.IsNullOrEmpty(NamespaceUri)) { buffer.AppendFormat(" ({0}:{1})", NamespaceUri, SymbolicId); } else if (SymbolicId != buffer.ToString()) { buffer.AppendFormat(" ({0})", SymbolicId); } } if (!string.IsNullOrEmpty(LocalizedText)) { buffer.AppendFormat(" '{0}'", LocalizedText); } if ((0x0000FFFF & StatusCode) != 0) { buffer.AppendFormat(" [{0:X4}]", 0x0000FFFF & StatusCode); } return(buffer.ToString()); }
public virtual void OnWriteResult(StatusCode result) { this.StatusCode = result; if (StatusCode.IsBad(result)) { Trace.TraceError($"Error writing value for {this.NodeId}. {StatusCodes.GetDefaultMessage(result)}"); } }
public virtual void OnCreateResult(MonitoredItemCreateResult result) { this.ServerId = result.MonitoredItemId; this.StatusCode = result.StatusCode; if (StatusCode.IsBad(result.StatusCode)) { Trace.TraceError($"Error creating MonitoredItem for {this.NodeId}. {StatusCodes.GetDefaultMessage(result.StatusCode)}"); } }
private async void OpcTag_ValueChanged(object sender, PropertyChangedEventArgs e) { if (isPublishing || string.IsNullOrEmpty(e.PropertyName)) { return; } if (monitoredItems.TryGetValueByName(e.PropertyName, out MonitoredItemBase item)) { if (item.TryGetValue(out DataValue value)) { StatusCode statusCode; var writeRequest = new WriteRequest { NodesToWrite = new[] { new WriteValue { NodeId = item.NodeId, AttributeId = item.AttributeId, IndexRange = item.IndexRange, Value = value } } }; try { var writeResponse = await opcConnection.Channel.WriteAsync(writeRequest).ConfigureAwait(false); statusCode = writeResponse.Results[0]; } catch (ServiceResultException ex) { statusCode = ex.StatusCode; } catch (Exception) { statusCode = StatusCodes.BadServerNotConnected; } item.OnWriteResult(statusCode); if (StatusCode.IsBad(statusCode)) { logger?.LogError($"Error writing value for {item.NodeId}. {StatusCodes.GetDefaultMessage(statusCode)}"); } } } }
private void SetDataErrorInfo(StatusCode statusCode) { if (this.statusCode == statusCode) { return; } this.statusCode = statusCode; var targetAsDataErrorInfo = Target as ISetDataErrorInfo; if (targetAsDataErrorInfo != null) { if (!StatusCode.IsGood(statusCode)) { targetAsDataErrorInfo.SetErrors(Property.Name, new string[] { StatusCodes.GetDefaultMessage(statusCode) }); } else { targetAsDataErrorInfo.SetErrors(Property.Name, null); } } }
/// <summary> /// The state machine manages the state of the subscription. /// </summary> /// <param name="token">A cancellation token.</param> /// <returns>A task.</returns> private async Task StateMachineAsync(CancellationToken token = default(CancellationToken)) { while (!token.IsCancellationRequested) { await this.whenSubscribed.Task; this.progress.Report(CommunicationState.Opening); try { // get a channel. this.innerChannel = await this.application.GetChannelAsync(this.endpointUrl, token); try { // create the subscription. var subscriptionRequest = new CreateSubscriptionRequest { RequestedPublishingInterval = this.publishingInterval, RequestedMaxKeepAliveCount = this.keepAliveCount, RequestedLifetimeCount = Math.Max(this.lifetimeCount, 3 * this.keepAliveCount), PublishingEnabled = true }; var subscriptionResponse = await this.innerChannel.CreateSubscriptionAsync(subscriptionRequest).ConfigureAwait(true); // link up the dataflow blocks var id = this.subscriptionId = subscriptionResponse.SubscriptionId; var linkToken = this.innerChannel.LinkTo(this.actionBlock, pr => pr.SubscriptionId == id); try { // create the monitored items. var items = this.monitoredItems.ToList(); if (items.Count > 0) { var requests = items.Select(m => new MonitoredItemCreateRequest { ItemToMonitor = new ReadValueId { NodeId = m.NodeId, AttributeId = m.AttributeId, IndexRange = m.IndexRange }, MonitoringMode = m.MonitoringMode, RequestedParameters = new MonitoringParameters { ClientHandle = m.ClientId, DiscardOldest = m.DiscardOldest, QueueSize = m.QueueSize, SamplingInterval = m.SamplingInterval, Filter = m.Filter } }).ToArray(); var itemsRequest = new CreateMonitoredItemsRequest { SubscriptionId = id, ItemsToCreate = requests, }; var itemsResponse = await this.innerChannel.CreateMonitoredItemsAsync(itemsRequest); for (int i = 0; i < itemsResponse.Results.Length; i++) { var item = items[i]; var result = itemsResponse.Results[i]; item.OnCreateResult(result); if (StatusCode.IsBad(result.StatusCode)) { this.logger?.LogError($"Error creating MonitoredItem for {item.NodeId}. {StatusCodes.GetDefaultMessage(result.StatusCode)}"); } } } this.progress.Report(CommunicationState.Opened); // wait here until channel is closing, unsubscribed or token cancelled. try { await Task.WhenAny( this.WhenChannelClosingAsync(this.innerChannel, token), this.whenUnsubscribed.Task); } catch { } finally { this.progress.Report(CommunicationState.Closing); } } catch (Exception ex) { this.logger?.LogError($"Error creating MonitoredItems. {ex.Message}"); this.progress.Report(CommunicationState.Faulted); } finally { linkToken.Dispose(); } if (this.innerChannel.State == CommunicationState.Opened) { try { // delete the subscription. var deleteRequest = new DeleteSubscriptionsRequest { SubscriptionIds = new uint[] { id } }; await this.innerChannel.DeleteSubscriptionsAsync(deleteRequest); } catch (Exception ex) { this.logger?.LogError($"Error deleting subscription. {ex.Message}"); await Task.Delay(2000); } } this.progress.Report(CommunicationState.Closed); } catch (Exception ex) { this.logger?.LogError($"Error creating subscription. {ex.Message}"); this.progress.Report(CommunicationState.Faulted); await Task.Delay(2000); } } catch (Exception ex) { this.logger?.LogTrace($"Error getting channel. {ex.Message}"); this.progress.Report(CommunicationState.Faulted); await Task.Delay(2000); } } }
public ServiceResultException(StatusCode statusCode) : base(StatusCodes.GetDefaultMessage(statusCode)) { this.HResult = unchecked ((int)(uint)statusCode); }
/// <summary> /// Handles PropertyChanged event. If the property is associated with a MonitoredItem, writes the property value to the node of the server. /// </summary> /// <param name="sender">the sender.</param> /// <param name="e">the event.</param> private async void OnPropertyChanged(object sender, PropertyChangedEventArgs e) { if (this.isPublishing || string.IsNullOrEmpty(e.PropertyName)) { return; } if (this.monitoredItems.TryGetValueByName(e.PropertyName, out var item)) { if (item.TryGetValue(out var value)) { StatusCode statusCode; try { var writeRequest = new WriteRequest { NodesToWrite = new[] { new WriteValue { NodeId = ExpandedNodeId.ToNodeId(item.NodeId, this.InnerChannel.NamespaceUris), AttributeId = item.AttributeId, IndexRange = item.IndexRange, Value = value } } }; var writeResponse = await this.InnerChannel.WriteAsync(writeRequest).ConfigureAwait(false); statusCode = writeResponse?.Results?[0] ?? StatusCodes.BadDataEncodingInvalid; } catch (ServiceResultException ex) { statusCode = ex.StatusCode; } catch (Exception) { statusCode = StatusCodes.BadServerNotConnected; } item.OnWriteResult(statusCode); if (StatusCode.IsBad(statusCode)) { this.logger?.LogError($"Error writing value for {item.NodeId}. {StatusCodes.GetDefaultMessage(statusCode)}"); } } } }
/// <summary> /// Creates the subscriptions on the server. /// </summary> /// <param name="token">A cancellation token. </param> /// <returns>A task.</returns> private async Task AutoCreateSubscriptionsAsync(CancellationToken token = default(CancellationToken)) { var tcs = new TaskCompletionSource <bool>(); NotifyCollectionChangedEventHandler handler = async(o, e) => { if (e.Action == NotifyCollectionChangedAction.Add) { foreach (var subscription in e.NewItems.OfType <Subscription>()) { var target = subscription.Target; if (target == null) { continue; } try { // create the subscription. var subscriptionRequest = new CreateSubscriptionRequest { RequestedPublishingInterval = subscription.PublishingInterval, RequestedMaxKeepAliveCount = subscription.KeepAliveCount, RequestedLifetimeCount = Math.Max(subscription.LifetimeCount, 3 * subscription.KeepAliveCount), PublishingEnabled = subscription.PublishingEnabled }; var subscriptionResponse = await this.CreateSubscriptionAsync(subscriptionRequest).ConfigureAwait(false); var id = subscription.SubscriptionId = subscriptionResponse.SubscriptionId; // add the items. if (subscription.MonitoredItems.Count > 0) { var items = subscription.MonitoredItems.ToList(); var requests = items.Select(m => new MonitoredItemCreateRequest { ItemToMonitor = new ReadValueId { NodeId = m.NodeId, AttributeId = m.AttributeId, IndexRange = m.IndexRange }, MonitoringMode = m.MonitoringMode, RequestedParameters = new MonitoringParameters { ClientHandle = m.ClientId, DiscardOldest = m.DiscardOldest, QueueSize = m.QueueSize, SamplingInterval = m.SamplingInterval, Filter = m.Filter } }).ToArray(); var itemsRequest = new CreateMonitoredItemsRequest { SubscriptionId = id, ItemsToCreate = requests, }; var itemsResponse = await this.CreateMonitoredItemsAsync(itemsRequest).ConfigureAwait(false); for (int i = 0; i < itemsResponse.Results.Length; i++) { var item = items[i]; var result = itemsResponse.Results[i]; item.OnCreateResult(target, result); if (StatusCode.IsBad(result.StatusCode)) { this.Logger?.LogError($"Error creating MonitoredItem for {item.NodeId}. {StatusCodes.GetDefaultMessage(result.StatusCode)}"); } } } } catch (ServiceResultException ex) { this.Logger?.LogError($"Error creating subscription. {ex.Message}"); this.innerChannel.Fault(ex); } } } else if (e.Action == NotifyCollectionChangedAction.Remove) { try { // delete the subscriptions. var request = new DeleteSubscriptionsRequest { SubscriptionIds = e.OldItems.OfType <Subscription>().Select(s => s.SubscriptionId).ToArray() }; await this.DeleteSubscriptionsAsync(request).ConfigureAwait(false); } catch (ServiceResultException ex) { this.Logger?.LogError($"Error deleting subscriptions. {ex.Message}"); } } }; using (token.Register(state => ((TaskCompletionSource <bool>)state).TrySetResult(true), tcs, false)) { try { handler.Invoke(this.subscriptions, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, this.subscriptions.ToList())); this.subscriptions.CollectionChanged += handler; await tcs.Task; } finally { this.subscriptions.CollectionChanged -= handler; foreach (var subscription in this.subscriptions) { subscription.SubscriptionId = 0; } } } }
private async Task <bool> TryWriteAsync(WriteRequest request) { try { var response = await this.Session.WriteAsync(request); for (int i = 0; i < response.Results.Length; i++) { if (StatusCode.IsBad(response.Results[i])) { Debug.WriteLine($"Error writing output '{request.NodesToWrite[i].NodeId}' {request.NodesToWrite[i].Value.GetValue()}: {StatusCodes.GetDefaultMessage(response.Results[i])}"); return(false); } } return(true); } catch (Exception ex) { Debug.WriteLine($"Error writing outputs : {ex.Message}"); return(false); } }
/// <summary> /// The state machine manages the state of the subscription. /// </summary> /// <param name="token">A cancellation token.</param> /// <returns>A task.</returns> private async Task StateMachineAsync(CancellationToken token = default) { while (!token.IsCancellationRequested) { await this.whenSubscribed.Task; this.progress.Report(CommunicationState.Opening); try { if (this.endpointUrl is null) { throw new InvalidOperationException("The endpointUrl field must not be null. Please, use the Subscription attribute properly."); } // get a channel. this.innerChannel = await this.application.GetChannelAsync(this.endpointUrl, token); try { // create the subscription. var subscriptionRequest = new CreateSubscriptionRequest { RequestedPublishingInterval = this.publishingInterval, RequestedMaxKeepAliveCount = this.keepAliveCount, RequestedLifetimeCount = Math.Max(this.lifetimeCount, 3 * this.keepAliveCount), PublishingEnabled = true }; var subscriptionResponse = await this.innerChannel.CreateSubscriptionAsync(subscriptionRequest).ConfigureAwait(false); // link up the dataflow blocks var id = this.subscriptionId = subscriptionResponse.SubscriptionId; var linkToken = this.innerChannel.LinkTo(this.actionBlock, pr => pr.SubscriptionId == id); try { // create the monitored items. var items = this.monitoredItems.ToList(); if (items.Count > 0) { var requests = items.Select(m => new MonitoredItemCreateRequest { ItemToMonitor = new ReadValueId { NodeId = ExpandedNodeId.ToNodeId(m.NodeId, this.InnerChannel.NamespaceUris), AttributeId = m.AttributeId, IndexRange = m.IndexRange }, MonitoringMode = m.MonitoringMode, RequestedParameters = new MonitoringParameters { ClientHandle = m.ClientId, DiscardOldest = m.DiscardOldest, QueueSize = m.QueueSize, SamplingInterval = m.SamplingInterval, Filter = m.Filter } }).ToArray(); //split requests array to MaxMonitoredItemsPerCall chunks int maxmonitoreditemspercall = 100; MonitoredItemCreateRequest[] requests_chunk; int chunk_size; for (int i_chunk = 0; i_chunk < requests.Length; i_chunk += maxmonitoreditemspercall) { chunk_size = Math.Min(maxmonitoreditemspercall, requests.Length - i_chunk); requests_chunk = new MonitoredItemCreateRequest[chunk_size]; Array.Copy(requests, i_chunk, requests_chunk, 0, chunk_size); var itemsRequest = new CreateMonitoredItemsRequest { SubscriptionId = id, ItemsToCreate = requests_chunk, }; var itemsResponse = await this.innerChannel.CreateMonitoredItemsAsync(itemsRequest); if (itemsResponse.Results is { } results) { for (int i = 0; i < results.Length; i++) { var item = items[i]; var result = results[i]; if (result is null) { this.logger?.LogError($"Error creating MonitoredItem for {item.NodeId}. The result is null."); continue; } item.OnCreateResult(result); if (StatusCode.IsBad(result.StatusCode)) { this.logger?.LogError($"Error creating MonitoredItem for {item.NodeId}. {StatusCodes.GetDefaultMessage(result.StatusCode)}"); } } } } } this.progress.Report(CommunicationState.Opened); // wait here until channel is closing, unsubscribed or token cancelled. try { await Task.WhenAny( this.WhenChannelClosingAsync(this.innerChannel, token), this.whenUnsubscribed.Task); } catch { } finally { this.progress.Report(CommunicationState.Closing); } } catch (Exception ex) { this.logger?.LogError($"Error creating MonitoredItems. {ex.Message}"); this.progress.Report(CommunicationState.Faulted); } finally { linkToken.Dispose(); } if (this.innerChannel.State == CommunicationState.Opened) { try { // delete the subscription. var deleteRequest = new DeleteSubscriptionsRequest { SubscriptionIds = new uint[] { id } }; await this.innerChannel.DeleteSubscriptionsAsync(deleteRequest); } catch (Exception ex) { this.logger?.LogError($"Error deleting subscription. {ex.Message}"); await Task.Delay(2000); } } this.progress.Report(CommunicationState.Closed); } catch (Exception ex) { this.logger?.LogError($"Error creating subscription. {ex.Message}"); this.progress.Report(CommunicationState.Faulted); await Task.Delay(2000); } } catch (Exception ex) { this.logger?.LogTrace($"Error getting channel. {ex.Message}"); this.progress.Report(CommunicationState.Faulted); await Task.Delay(2000); } } }
private void SetDataErrorInfo(object target, StatusCode statusCode) { var targetAsDataErrorInfo = target as ISetDataErrorInfo; if (targetAsDataErrorInfo != null) { if (!StatusCode.IsGood(statusCode)) { targetAsDataErrorInfo.SetErrors(this.Property.Name, new string[] { StatusCodes.GetDefaultMessage(statusCode) }); } else { targetAsDataErrorInfo.SetErrors(this.Property.Name, null); } } }