public bool WriteValue(WriteValuePayload payload) { // credits: https://github.com/OPCFoundation/UA-.NETStandard/issues/316 if (!payload.IsValid) { return(false); } try { var nodeToWrite = new WriteValue(); var expandedNodeId = ExpandedNodeId.Parse(payload.ExtendedNodeId); nodeToWrite.NodeId = GetNodeIdFromExpandedNodeId(expandedNodeId); nodeToWrite.AttributeId = Attributes.Value; nodeToWrite.Value.WrappedValue = "actual data"; var nodesToWrite = new WriteValueCollection(); nodesToWrite.Add(nodeToWrite); // read the attribute StatusCodeCollection results = null; DiagnosticInfoCollection diagInfos = null; var responseHeader = OpcUaClientSession.Write( null, nodesToWrite, out results, out diagInfos ); ClientBase.ValidateResponse(results, nodesToWrite); ClientBase.ValidateDiagnosticInfos(diagInfos, nodesToWrite); // check for error if (StatusCode.IsBad(results[0])) { throw ServiceResultException.Create(results[0], 0, diagInfos, responseHeader.StringTable); } Trace($"Value written to {payload.ExtendedNodeId}"); } catch (Exception ex) { Trace($"An error occured when writing a value: {ex.Message}"); return(false); } return(true); }
/// <summary> /// Monitoring for an event source starts if it is required. /// </summary> public async Task MonitorEventsAsync(CancellationToken ct) { bool sessionLocked = false; try { try { sessionLocked = await LockSessionAsync().ConfigureAwait(false); // if the session is not connected or shutdown in progress, return if (!sessionLocked || ct.IsCancellationRequested || State != SessionState.Connected) { return; } } catch (Exception) { throw; } // ensure all nodes in all subscriptions of this session are monitored. foreach (var opcEventSubscription in OpcEventSubscriptions) { // create the subscription, if it is not yet there. if (opcEventSubscription.OpcUaClientSubscription == null) { opcEventSubscription.OpcUaClientSubscription = CreateSubscription(opcEventSubscription.RequestedPublishingInterval, out int revisedPublishingInterval); opcEventSubscription.PublishingInterval = revisedPublishingInterval; Logger.Information($"Create Event subscription on endpoint '{EndpointUrl}' requested OPC publishing interval is {opcEventSubscription.RequestedPublishingInterval} ms. (revised: {revisedPublishingInterval} ms)"); } // process all unmonitored events. var unmonitoredEvents = opcEventSubscription.OpcMonitoredItems.Where(i => (i.State == OpcMonitoredItemState.Unmonitored || i.State == OpcMonitoredItemState.UnmonitoredNamespaceUpdateRequested)); int additionalMonitoredEventsCount = 0; int monitoredEventsCount = 0; bool haveUnmonitoredEvents = false; if (unmonitoredEvents.Count() != 0) { haveUnmonitoredEvents = true; monitoredEventsCount = opcEventSubscription.OpcMonitoredItems.Count(i => (i.State == OpcMonitoredItemState.Monitored)); Logger.Information($"Start monitoring events on endpoint '{EndpointUrl}'. Currently monitoring {monitoredEventsCount} events."); } // init perf data Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); foreach (var unmonitoredEvent in unmonitoredEvents) { // if the session is not connected or a shutdown is in progress, we stop trying and wait for the next cycle if (ct.IsCancellationRequested || State != SessionState.Connected) { break; } NodeId currentNodeId = null; try { // update the namespace of the node if requested. there are two cases where this is requested: // 1) publishing requests via the OPC server method are raised using a NodeId format. for those // the NodeId format is converted into an ExpandedNodeId format // 2) ExpandedNodeId configuration file entries do not have at parsing time a session to get // the namespace index. this is set now. if (unmonitoredEvent.State == OpcMonitoredItemState.UnmonitoredNamespaceUpdateRequested) { if (unmonitoredEvent.ConfigType == OpcMonitoredItemConfigurationType.ExpandedNodeId) { ExpandedNodeId expandedNodeId = ExpandedNodeId.Parse(unmonitoredEvent.Id); int namespaceIndex = _namespaceTable.GetIndex(expandedNodeId.NamespaceUri); if (namespaceIndex < 0) { Logger.Information($"The namespace URI of node '{expandedNodeId.ToString()}' can be not mapped to a namespace index."); } else { unmonitoredEvent.IdAsExpandedNodeId = expandedNodeId; } } if (unmonitoredEvent.ConfigType == OpcMonitoredItemConfigurationType.NodeId) { NodeId nodeId = NodeId.Parse(unmonitoredEvent.Id); string namespaceUri = _namespaceTable.ToArray().ElementAtOrDefault(nodeId.NamespaceIndex); if (string.IsNullOrEmpty(namespaceUri)) { Logger.Information($"The namespace index of node '{nodeId.ToString()}' is invalid and the node format can not be updated."); } else { unmonitoredEvent.IdAsExpandedNodeId = new ExpandedNodeId(nodeId.Identifier, nodeId.NamespaceIndex, namespaceUri, 0); unmonitoredEvent.ConfigType = OpcMonitoredItemConfigurationType.ExpandedNodeId; } } unmonitoredEvent.State = OpcMonitoredItemState.Unmonitored; } // lookup namespace index if ExpandedNodeId format has been used and build NodeId identifier. if (unmonitoredEvent.ConfigType == OpcMonitoredItemConfigurationType.ExpandedNodeId) { ExpandedNodeId expandedNodeId = ExpandedNodeId.Parse(unmonitoredEvent.Id); int namespaceIndex = _namespaceTable.GetIndex(expandedNodeId.NamespaceUri); if (namespaceIndex < 0) { Logger.Warning($"Syntax or namespace URI of ExpandedNodeId '{expandedNodeId.ToString()}' is invalid and will be ignored."); continue; } unmonitoredEvent.IdAsNodeId = new NodeId(expandedNodeId.Identifier, expandedNodeId.NamespaceIndex); currentNodeId = unmonitoredEvent.IdAsNodeId; } else { NodeId nodeId = NodeId.Parse(unmonitoredEvent.Id); string namespaceUri = _namespaceTable.ToArray().ElementAtOrDefault(nodeId.NamespaceIndex); if (string.IsNullOrEmpty(namespaceUri)) { Logger.Information($"The namespace index of node '{nodeId.ToString()}' is invalid and the node format can not be updated."); } else { unmonitoredEvent.IdAsExpandedNodeId = new ExpandedNodeId(nodeId.Identifier, nodeId.NamespaceIndex, namespaceUri, 0); currentNodeId = nodeId; } } // if configured, get the key for the node, otherwise use the nodeId Node node; if (string.IsNullOrEmpty(unmonitoredEvent.Key)) { if (FetchOpcNodeDisplayName == true) { node = OpcUaClientSession.ReadNode(currentNodeId); unmonitoredEvent.Key = node.DisplayName.Text ?? currentNodeId.ToString(); } else { unmonitoredEvent.Key = currentNodeId.ToString(); } } // resolve all node and namespace references in the select and where clauses EventFilter eventFilter = new EventFilter(); foreach (var selectClause in unmonitoredEvent.EventConfiguration.SelectClauses) { SimpleAttributeOperand simpleAttributeOperand = new SimpleAttributeOperand(); simpleAttributeOperand.AttributeId = selectClause.AttributeId.ResolveAttributeId(); simpleAttributeOperand.IndexRange = selectClause.IndexRange; NodeId typeId = selectClause.TypeId.ToNodeId(_namespaceTable); simpleAttributeOperand.TypeDefinitionId = new NodeId(typeId); QualifiedNameCollection browsePaths = new QualifiedNameCollection(); foreach (var browsePath in selectClause.BrowsePaths) { browsePaths.Add(QualifiedName.Parse(browsePath)); } simpleAttributeOperand.BrowsePath = browsePaths; eventFilter.SelectClauses.Add(simpleAttributeOperand); } foreach (var whereClauseElement in unmonitoredEvent.EventConfiguration.WhereClause) { ContentFilterElement contentFilterElement = new ContentFilterElement(); contentFilterElement.FilterOperator = whereClauseElement.Operator.ResolveFilterOperator(); switch (contentFilterElement.FilterOperator) { case FilterOperator.OfType: case FilterOperator.InView: if (whereClauseElement.Operands.Count != 1) { Logger.Error($"The where clause element '{whereClauseElement.ToString()}' must contain 1 operands."); continue; } FilterOperand[] filterOperands = new FilterOperand[1]; TypeInfo typeInfo = new TypeInfo(BuiltInType.NodeId, ValueRanks.Scalar); filterOperands[0] = whereClauseElement.Operands[0].GetOperand(typeInfo); eventFilter.WhereClause.Push(contentFilterElement.FilterOperator, filterOperands); break; case FilterOperator.Equals: case FilterOperator.IsNull: case FilterOperator.GreaterThan: case FilterOperator.LessThan: case FilterOperator.GreaterThanOrEqual: case FilterOperator.LessThanOrEqual: case FilterOperator.Like: case FilterOperator.Not: case FilterOperator.Between: case FilterOperator.InList: case FilterOperator.And: case FilterOperator.Or: case FilterOperator.Cast: case FilterOperator.BitwiseAnd: case FilterOperator.BitwiseOr: case FilterOperator.RelatedTo: default: Logger.Error($"The operator '{contentFilterElement.FilterOperator.ToString()}' is not supported."); break; } } // add the new monitored event. IOpcUaMonitoredItem monitoredItem = new OpcUaMonitoredItem() { StartNodeId = currentNodeId, AttributeId = Attributes.EventNotifier, DisplayName = unmonitoredEvent.Key, MonitoringMode = unmonitoredEvent.MonitoringMode, SamplingInterval = 0, QueueSize = unmonitoredEvent.QueueSize, DiscardOldest = unmonitoredEvent.DiscardOldest, Filter = eventFilter }; monitoredItem.Notification += unmonitoredEvent.NotificationEventHandler; opcEventSubscription.OpcUaClientSubscription.AddItem(monitoredItem); unmonitoredEvent.OpcUaClientMonitoredItem = monitoredItem; unmonitoredEvent.State = OpcMonitoredItemState.Monitored; unmonitoredEvent.EndpointUrl = EndpointUrl; Logger.Verbose($"Created monitored event for node '{currentNodeId.ToString()}' in subscription with id '{opcEventSubscription.OpcUaClientSubscription.Id}' on endpoint '{EndpointUrl}' (version: {NodeConfigVersion:X8})"); if (unmonitoredEvent.RequestedSamplingInterval != monitoredItem.SamplingInterval) { Logger.Information($"Sampling interval: requested: {unmonitoredEvent.RequestedSamplingInterval}; revised: {monitoredItem.SamplingInterval}"); unmonitoredEvent.SamplingInterval = monitoredItem.SamplingInterval; } if (additionalMonitoredEventsCount % 10000 == 0) { Logger.Information($"Now monitoring {monitoredEventsCount + additionalMonitoredEventsCount} events in subscription with id '{opcEventSubscription.OpcUaClientSubscription.Id}'"); } } catch (Exception e) when(e.GetType() == typeof(ServiceResultException)) { ServiceResultException sre = (ServiceResultException)e; switch ((uint)sre.Result.StatusCode) { case StatusCodes.BadSessionIdInvalid: { Logger.Information($"Session with Id {OpcUaClientSession.SessionId} is no longer available on endpoint '{EndpointUrl}'. Cleaning up."); // clean up the session InternalDisconnect(); break; } case StatusCodes.BadNodeIdInvalid: case StatusCodes.BadNodeIdUnknown: { Logger.Error($"Failed to monitor node '{currentNodeId}' on endpoint '{EndpointUrl}'."); Logger.Error($"OPC UA ServiceResultException is '{sre.Result}'. Please check your publisher configuration for this node."); break; } default: { Logger.Error($"Unhandled OPC UA ServiceResultException '{sre.Result}' when monitoring node '{currentNodeId}' on endpoint '{EndpointUrl}'. Continue."); break; } } } catch (Exception e) { Logger.Error(e, $"Failed to monitor node '{currentNodeId}' on endpoint '{EndpointUrl}'"); } } opcEventSubscription.OpcUaClientSubscription.SetPublishingMode(true); opcEventSubscription.OpcUaClientSubscription.ApplyChanges(); stopWatch.Stop(); if (haveUnmonitoredEvents == true) { monitoredEventsCount = opcEventSubscription.OpcMonitoredItems.Count(i => (i.State == OpcMonitoredItemState.Monitored)); Logger.Information($"Done processing unmonitored events on endpoint '{EndpointUrl}' took {stopWatch.ElapsedMilliseconds} msec. Now monitoring {monitoredEventsCount} events in subscription with id '{opcEventSubscription.OpcUaClientSubscription.Id}'."); } } } catch (Exception e) { Logger.Error(e, "Exception"); } finally { if (sessionLocked) { ReleaseSession(); } } }