Пример #1
0
 /// <summary>
 /// Returns value of the field name containing the event type.
 /// </summary>
 public object GetFieldValue(
     EventFieldList eventFields, 
     NodeId         eventTypeId, 
     QualifiedName  browseName)
 {
     QualifiedNameCollection browsePath = new QualifiedNameCollection();
     browsePath.Add(browseName);
     return GetFieldValue(eventFields, eventTypeId, browsePath, Attributes.Value);
 }
Пример #2
0
        /// <summary>
        /// Collects the fields for the instance node.
        /// </summary>
        /// <param name="session">The session.</param>
        /// <param name="nodeId">The node id.</param>
        /// <param name="parentPath">The parent path.</param>
        /// <param name="eventFields">The event fields.</param>
        /// <param name="foundNodes">The table of found nodes.</param>
        private void CollectFields(
            Session session,
            NodeId nodeId,
            QualifiedNameCollection parentPath,
            SimpleAttributeOperandCollection eventFields,
            Dictionary <NodeId, QualifiedNameCollection> foundNodes)
        {
            // find all of the children of the field.
            BrowseDescription nodeToBrowse = new BrowseDescription();

            nodeToBrowse.NodeId          = nodeId;
            nodeToBrowse.BrowseDirection = BrowseDirection.Forward;
            nodeToBrowse.ReferenceTypeId = ReferenceTypeIds.Aggregates;
            nodeToBrowse.IncludeSubtypes = true;
            nodeToBrowse.NodeClassMask   = (uint)(NodeClass.Object | NodeClass.Variable);
            nodeToBrowse.ResultMask      = (uint)BrowseResultMask.All;

            ReferenceDescriptionCollection children = FormUtils.Browse(session, nodeToBrowse, false);

            if (children == null)
            {
                return;
            }

            // process the children.
            for (int ii = 0; ii < children.Count; ii++)
            {
                ReferenceDescription child = children[ii];

                if (child.NodeId.IsAbsolute)
                {
                    continue;
                }

                // construct browse path.
                QualifiedNameCollection browsePath = new QualifiedNameCollection(parentPath);
                browsePath.Add(child.BrowseName);

                // check if the browse path is already in the list.
                if (!ContainsPath(eventFields, browsePath))
                {
                    SimpleAttributeOperand field = new SimpleAttributeOperand();

                    field.TypeDefinitionId = ObjectTypeIds.BaseEventType;
                    field.BrowsePath       = browsePath;
                    field.AttributeId      = (child.NodeClass == NodeClass.Variable)?Attributes.Value:Attributes.NodeId;

                    eventFields.Add(field);
                }

                // recusively find all of the children.
                NodeId targetId = (NodeId)child.NodeId;

                // need to guard against loops.
                if (!foundNodes.ContainsKey(targetId))
                {
                    foundNodes.Add(targetId, browsePath);
                    CollectFields(session, (NodeId)child.NodeId, browsePath, eventFields, foundNodes);
                }
            }
        }
Пример #3
0
        /// <summary>
        /// Reads an QualifiedName array from the stream.
        /// </summary>
        public QualifiedNameCollection ReadQualifiedNameArray(string fieldName)
        {
            bool isNil = false;

            QualifiedNameCollection values = new QualifiedNameCollection();
                                    
            if (BeginField(fieldName, true, out isNil))
            {                                
                PushNamespace(Namespaces.OpcUaXsd);
                
                while (MoveToElement("QualifiedName"))
                {
                    values.Add(ReadQualifiedName("QualifiedName"));
                }

                // check the length.
                if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count)
                {
                    throw new ServiceResultException(StatusCodes.BadEncodingLimitsExceeded);
                }

                PopNamespace();

                EndField(fieldName);
                return values;
            }

            if (isNil)
            {
                return null;
            }

            return values;
        }
 /// <summary>
 /// Returns value of the field name containing the event type.
 /// </summary>
 public object GetFieldValue(
     EventFieldList eventFields, 
     NodeId         eventTypeId, 
     QualifiedName  browseName)
 {
     QualifiedNameCollection browsePath = new QualifiedNameCollection();
     browsePath.Add(browseName);
     return GetFieldValue(eventFields, eventTypeId, browsePath, Attributes.Value);
 }
Пример #5
0
        /// <summary>
        /// Parses a browse path.
        /// </summary>
        public static QualifiedNameCollection Parse(string browsePath)
        {
            QualifiedNameCollection browseNames = new QualifiedNameCollection();

            if (String.IsNullOrEmpty(browsePath))
            {
                return browseNames;
            }
                       
            StringBuilder buffer = new StringBuilder();

            bool escaped = false;

            for (int ii = 0; ii < browsePath.Length; ii++)
            {
                char ch = browsePath[ii];

                if (escaped)
                {
                    buffer.Append(ch);
                    escaped = false;
                    continue;
                }

                if (ch == '&')
                {
                    escaped = true;
                    continue;
                }

                if (ch == '/')
                {
                    if (buffer.Length > 0)
                    {
                        QualifiedName browseName = QualifiedName.Parse(buffer.ToString());
                        browseNames.Add(browseName);
                    }

                    buffer.Length = 0;
                    continue;
                }

                buffer.Append(ch);
            }

            if (buffer.Length > 0)
            {
                QualifiedName browseName = QualifiedName.Parse(buffer.ToString());
                browseNames.Add(browseName);
            }

            return browseNames;
        }
Пример #6
0
        /// <summary>
        /// Creates an operand that references a component/property of a type.
        /// </summary>
        public SimpleAttributeOperand(
            NodeId typeId,
            QualifiedName browsePath)
        {
            m_typeDefinitionId = typeId;
            m_browsePath       = new QualifiedNameCollection();
            m_attributeId      = Attributes.Value;
            m_indexRange       = null;

            m_browsePath.Add(browsePath);
        }
        /// <summary>
        /// Collects the fields for the instance node.
        /// </summary>
        /// <param name="session">The session.</param>
        /// <param name="nodeId">The node id.</param>
        /// <param name="parentPath">The parent path.</param>
        /// <param name="fields">The event fields.</param>
        /// <param name="fieldNodeIds">The node id for the declaration of the field.</param>
        /// <param name="foundNodes">The table of found nodes.</param>
        private static void CollectFields(
            Session session,
            NodeId nodeId,
            QualifiedNameCollection parentPath,
            SimpleAttributeOperandCollection fields,
            List<NodeId> fieldNodeIds,
            Dictionary<NodeId, QualifiedNameCollection> foundNodes)
        {
            // find all of the children of the field.
            BrowseDescription nodeToBrowse = new BrowseDescription();

            nodeToBrowse.NodeId = nodeId;
            nodeToBrowse.BrowseDirection = BrowseDirection.Forward;
            nodeToBrowse.ReferenceTypeId = ReferenceTypeIds.Aggregates;
            nodeToBrowse.IncludeSubtypes = true;
            nodeToBrowse.NodeClassMask = (uint)(NodeClass.Object | NodeClass.Variable);
            nodeToBrowse.ResultMask = (uint)BrowseResultMask.All;

            ReferenceDescriptionCollection children = ClientUtils.Browse(session, nodeToBrowse, false);

            if (children == null)
            {
                return;
            }

            // process the children.
            for (int ii = 0; ii < children.Count; ii++)
            {
                ReferenceDescription child = children[ii];

                if (child.NodeId.IsAbsolute)
                {
                    continue;
                }

                // construct browse path.
                QualifiedNameCollection browsePath = new QualifiedNameCollection(parentPath);
                browsePath.Add(child.BrowseName);

                // check if the browse path is already in the list.
                int index = ContainsPath(fields, browsePath);

                if (index < 0)
                {
                    SimpleAttributeOperand field = new SimpleAttributeOperand();

                    field.TypeDefinitionId = ObjectTypeIds.BaseEventType;
                    field.BrowsePath = browsePath;
                    field.AttributeId = (child.NodeClass == NodeClass.Variable) ? Attributes.Value : Attributes.NodeId;

                    fields.Add(field);
                    fieldNodeIds.Add((NodeId)child.NodeId);
                }

                // recusively find all of the children.
                NodeId targetId = (NodeId)child.NodeId;

                // need to guard against loops.
                if (!foundNodes.ContainsKey(targetId))
                {
                    foundNodes.Add(targetId, browsePath);
                    CollectFields(session, (NodeId)child.NodeId, browsePath, fields, fieldNodeIds, foundNodes);
                }
            }
        }
Пример #8
0
        /// <summary>
        /// Reads an QualifiedName array from the stream.
        /// </summary>
        public QualifiedNameCollection ReadQualifiedNameArray(string fieldName)
        {
            int length = ReadArrayLength();

            if (length == -1)
            {
                return null;
            }

            QualifiedNameCollection values = new QualifiedNameCollection(length);

            for (int ii = 0; ii < length; ii++)
            {
                values.Add(ReadQualifiedName(null));
            }

            return values;
        }
 public void OneItemInCollectionReturnsQualifiedNameInToString()
 {
     qualifiedNames.Add(new QualifiedName("root", "ns", "a"));
     Assert.AreEqual("a:root [ns]", qualifiedNames.ToString());
 }
        /// <summary>
        /// Reads an QualifiedName array from the stream.
        /// </summary>
        public QualifiedNameCollection ReadQualifiedNameArray(string fieldName)
        {
            var values = new QualifiedNameCollection();

            List<object> token = null;

            if (!ReadArrayField(fieldName, out token))
            {
                return values;
            }

            for (int ii = 0; ii < token.Count; ii++)
            {
                try
                {
                    m_stack.Push(token[ii]);
                    var element = ReadQualifiedName(null);
                    values.Add(element);
                }
                finally
                {
                    m_stack.Pop();
                }
            }

            return values;
        }
Пример #11
0
 public void AddElement(QualifiedName elementName)
 {
     _elements.Add(elementName);
 }
Пример #12
0
 public void EqualsReturnsFalseWhenQualifiedNamesAreDifferent()
 {
     lhs.Add(new QualifiedName("name1", "ns1", "prefix1"));
     rhs.Add(new QualifiedName("name2", "ns2", "prefix2"));
     Assert.IsFalse(lhs.Equals(rhs));
 }
Пример #13
0
 public void EqualReturnsFalseWhenOneCollectionHasAnItemAndTheOtherDoesNot()
 {
     lhs.Add(new QualifiedName("root", "ns", "a"));
     Assert.IsFalse(lhs.Equals(rhs));
 }
Пример #14
0
        /// <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();
                }
            }
        }