/// <summary> /// Initializes the node manager. /// </summary> public FileSystemNodeManager(Opc.Ua.Server.IServerInternal server, string systemRoot) : base(server) { List <string> namespaceUris = new List <string>(); namespaceUris.Add(Namespaces.FileSystem); namespaceUris.Add(Namespaces.FileSystem + "/Instance"); NamespaceUris = namespaceUris; m_cache = new NodeIdDictionary <NodeState>(); // create the directory. string absolutePath = Utils.GetAbsoluteDirectoryPath(systemRoot, true, false, true); // create the system. m_system = new FileSystemMonitor( SystemContext, absolutePath, server.NamespaceUris.GetIndexOrAppend(namespaceUris[1]), Lock); // update the default context. SystemContext.SystemHandle = m_system; }
/// <summary> /// Initializes the test with session, configuration and logger. /// </summary> public TestBase( string name, Session session, ServerTestConfiguration configuration, ReportMessageEventHandler reportMessage, ReportProgressEventHandler reportProgress, TestBase template) { m_name = name; m_session = session; m_configuration = configuration; m_reportMessage = reportMessage; m_reportProgress = reportProgress; if (template != null && Object.ReferenceEquals(session, template.m_session)) { m_blockSize = template.BlockSize; m_availableNodes = template.m_availableNodes; m_writeableVariables = template.m_writeableVariables; } else { m_blockSize = 1000; m_availableNodes = new NodeIdDictionary <Node>(); m_writeableVariables = new List <VariableNode>(); } }
/// <summary> /// Creates an empty hierarchy. /// </summary> public InstanceDeclarationHierarchy() { m_declarations = new NodeIdDictionary <HierarchyBrowsePath>(); m_instances = new NodeIdDictionary <INode>(); m_browsePaths = new SortedDictionary <string, HierarchyBrowsePath>(); m_references = new List <HierarchyReference>(); }
/// <summary> /// Initializes the node manager. /// </summary> public FileSystemNodeManager(Opc.Ua.Server.IServerInternal server, string systemRoot) : base(server) { List<string> namespaceUris = new List<string>(); namespaceUris.Add(Namespaces.FileSystem); namespaceUris.Add(Namespaces.FileSystem + "/Instance"); NamespaceUris = namespaceUris; m_cache = new NodeIdDictionary<NodeState>(); // create the directory. string absolutePath = Utils.GetAbsoluteDirectoryPath(systemRoot, true, false, true); // create the system. m_system = new FileSystemMonitor( SystemContext, absolutePath, server.NamespaceUris.GetIndexOrAppend(namespaceUris[1]), Lock); // update the default context. SystemContext.SystemHandle = m_system; }
/// <summary> /// Initializes the test with session, configuration and logger. /// </summary> public TestBase( string name, Session session, ServerTestConfiguration configuration, ReportMessageEventHandler reportMessage, ReportProgressEventHandler reportProgress, TestBase template) { m_name = name; m_session = session; m_configuration = configuration; m_reportMessage = reportMessage; m_reportProgress = reportProgress; if (template != null && Object.ReferenceEquals(session, template.m_session)) { m_blockSize = template.BlockSize; m_availableNodes = template.m_availableNodes; m_writeableVariables = template.m_writeableVariables; } else { m_blockSize = 1000; m_availableNodes = new NodeIdDictionary<Node>(); m_writeableVariables = new List<VariableNode>(); } }
/// <summary> /// Initializes the object with default values. /// </summary> /// <param name="namespaceUris">The namespace URIs.</param> public TypeTable(NamespaceTable namespaceUris) { m_namespaceUris = namespaceUris; m_referenceTypes = new SortedDictionary <QualifiedName, TypeInfo>(); m_nodes = new NodeIdDictionary <TypeInfo>(); m_encodings = new NodeIdDictionary <TypeInfo>(); }
/// <summary> /// Initializes the object with default values. /// </summary> /// <param name="namespaceUris">The namespace URIs.</param> public TypeTable(NamespaceTable namespaceUris) { m_namespaceUris = namespaceUris; m_referenceTypes = new SortedDictionary<QualifiedName,TypeInfo>(); m_nodes = new NodeIdDictionary<TypeInfo>(); m_encodings = new NodeIdDictionary<TypeInfo>(); }
/// <summary> /// Fetches the event type information from the AE server. /// </summary> public void LoadTypes(Opc.Ua.Client.Session client, IServerInternal server, NamespaceMapper mapper) { TypeNodes = new NodeIdDictionary <ReferenceDescription>(); LoadTypes(client, server, mapper, Opc.Ua.ObjectTypeIds.BaseObjectType); LoadTypes(client, server, mapper, Opc.Ua.VariableTypeIds.BaseVariableType); LoadTypes(client, server, mapper, Opc.Ua.DataTypeIds.BaseDataType); LoadTypes(client, server, mapper, Opc.Ua.ReferenceTypeIds.References); }
/// <summary> /// Fetches the event type information from the AE server. /// </summary> public void LoadTypes(Opc.Ua.Client.Session client, IServerInternal server, NamespaceMapper mapper) { TypeNodes = new NodeIdDictionary<ReferenceDescription>(); LoadTypes(client, server, mapper, Opc.Ua.ObjectTypeIds.BaseObjectType); LoadTypes(client, server, mapper, Opc.Ua.VariableTypeIds.BaseVariableType); LoadTypes(client, server, mapper, Opc.Ua.DataTypeIds.BaseDataType); LoadTypes(client, server, mapper, Opc.Ua.ReferenceTypeIds.References); }
/// <summary> /// Sets the lock for the phase. /// </summary> public void SetPhaseLock(NodeId phaseId, NodeId lockId) { if (m_mapping == null) { m_mapping = new NodeIdDictionary <NodeId>(); } m_mapping[phaseId] = lockId; }
/// <summary> /// Sets the lock for the phase. /// </summary> public void SetPhaseLock(NodeId phaseId, NodeId lockId) { if (m_mapping == null) { m_mapping = new NodeIdDictionary<NodeId>(); } m_mapping[phaseId] = lockId; }
/// <summary> /// Updates the event types in cache with the most recent info fetched from the AE server. /// </summary> public void UpdateCache(ServerSystemContext context, ushort namespaceIndex) { // clear the existing nodes. EventTypeNodes = new NodeIdDictionary <BaseObjectTypeState>(); Attributes = new Dictionary <int, int[]>(); TypeTable typeTable = context.TypeTable as TypeTable; // rebuild from the recently fetched list. for (int ii = 0; ii < EventTypes.Count; ii++) { // save the attributes for use when creating filters. if (EventTypes[ii].EventTypeMapping != EventTypeMapping.ConditionClassType && !Attributes.ContainsKey(EventTypes[ii].CategoryId)) { EventType eventType = EventTypes[ii]; int[] attributeIds = new int[eventType.Attributes.Count]; for (int jj = 0; jj < attributeIds.Length; jj++) { attributeIds[jj] = eventType.Attributes[jj].Id; } Attributes.Add(EventTypes[ii].CategoryId, attributeIds); } AeEventTypeState node = new AeEventTypeState(EventTypes[ii], namespaceIndex); BaseObjectTypeState mappingNode = null; if (!EventTypeNodes.TryGetValue(node.SuperTypeId, out mappingNode)) { mappingNode = new AeEventTypeMappingState(node.EventType.EventTypeMapping, namespaceIndex); EventTypeNodes.Add(mappingNode.NodeId, mappingNode); // ensure the mapping node is in the type table. if (typeTable != null) { if (!typeTable.IsKnown(mappingNode.NodeId)) { typeTable.AddSubtype(mappingNode.NodeId, mappingNode.SuperTypeId); } } } EventTypeNodes.Add(node.NodeId, node); // ensure the type node is in the type table. if (typeTable != null) { if (!typeTable.IsKnown(node.NodeId)) { typeTable.AddSubtype(node.NodeId, mappingNode.NodeId); } } } }
/// <summary> /// Initializes the object. /// </summary> /// <param name="namespaceUris">The namespace URIs.</param> /// <param name="serverUris">The server URIs.</param> /// <param name="typeTree">The type tree.</param> public NodeTable( NamespaceTable namespaceUris, StringTable serverUris, TypeTable typeTree) { m_namespaceUris = namespaceUris; m_serverUris = serverUris; m_typeTree = typeTree; m_localNodes = new NodeIdDictionary <ILocalNode>(); m_remoteNodes = new SortedDictionary <ExpandedNodeId, RemoteNode>(); }
/// <summary> /// Adds a subtype to the object. /// </summary> /// <param name="subType">The subtype</param> public void AddSubType(TypeInfo subType) { if (subType != null) { if (SubTypes == null) { SubTypes = new NodeIdDictionary <TypeInfo>(); } SubTypes[subType.NodeId] = subType; } }
/// <summary> /// Remove subtype. /// </summary> /// <param name="subtypeId">The subtype identifier.</param> public void RemoveSubType(NodeId subtypeId) { if (subtypeId != null && SubTypes != null) { SubTypes.Remove(subtypeId); if (SubTypes.Count == 0) { SubTypes = null; } } }
/// <summary> /// Updates the list of area filters. /// </summary> private void UpdateAreaFilter(List <NodeId> areas) { // check if monitoring all events. if (areas == null || areas.Count == 0) { MonitoredItem monitoredItem = null; if (!m_notifiers.TryGetValue(Opc.Ua.ObjectIds.Server, out monitoredItem)) { monitoredItem = CreateMonitoredItem(Opc.Ua.ObjectIds.Server); } m_notifiers.Clear(); m_notifiers[Opc.Ua.ObjectIds.Server] = monitoredItem; return; } // build table of areas to monitor. NodeIdDictionary <MonitoredItem> notifiers = new NodeIdDictionary <MonitoredItem>(); // map all of the area search strings onto NodeIds for notifiers. for (int ii = 0; ii < areas.Count; ii++) { NodeId areaId = areas[ii]; // check for existing item. MonitoredItem monitoredItem = null; if (m_notifiers.TryGetValue(areaId, out monitoredItem)) { notifiers[areaId] = monitoredItem; continue; } // check for new item. if (!notifiers.ContainsKey(areaId)) { notifiers[areaId] = CreateMonitoredItem(areaId); } } // mark unused items for deletion. foreach (MonitoredItem monitoredItem in m_notifiers.Values) { if (!notifiers.ContainsKey(monitoredItem.StartNodeId)) { m_subscription.RemoveItem(monitoredItem); } } m_notifiers = notifiers; }
/// <summary> /// Checks the isInverse flag are returns true if a specified target exists. /// </summary> /// <param name="entry">The entry.</param> /// <param name="reference">The reference.</param> /// <returns> /// <c>true</c> if the specified entry contains key; otherwise, <c>false</c>. /// </returns> private static bool ContainsKey(ReferenceTypeEntry entry, IReference reference) { // handle reference to external targets. if (reference.TargetId.IsAbsolute) { Dictionary <ExpandedNodeId, LinkedListNode <KeyValuePair <IReference, T> > > targets = null; if (reference.IsInverse) { targets = entry.InverseExternalTargets; } else { targets = entry.ForwardExternalTargets; } if (targets == null) { return(false); } return(targets.ContainsKey(reference.TargetId)); } // handle reference to internal target. else { NodeIdDictionary <LinkedListNode <KeyValuePair <IReference, T> > > targets = null; if (reference.IsInverse) { targets = entry.InverseTargets; } else { targets = entry.ForwardTargets; } if (targets == null) { return(false); } return(targets.ContainsKey((NodeId)reference.TargetId)); } }
/// <summary> /// Initializes the node manager. /// </summary> public CustomNodeManager2(IServerInternal server) { // save a reference to the server that owns the node manager. m_server = server; // create the default context. m_systemContext = m_server.DefaultSystemContext.Copy(); m_systemContext.SystemHandle = null; m_systemContext.NodeIdFactory = this; // create the table of nodes. m_predefinedNodes = new NodeIdDictionary<NodeState>(); m_rootNotifiers = new List<NodeState>(); m_sampledItems = new List<DataChangeMonitoredItem>(); m_minimumSamplingInterval = 100; }
/// <summary> /// Initializes the node manager. /// </summary> public DsatsDemoNodeManager(IServerInternal server, ApplicationConfiguration configuration) : base(server, DsatsDemo.Namespaces.DsatsDemo, DsatsDemo.Namespaces.DsatsDemo + "/Instance") { SystemContext.NodeIdFactory = this; // get the configuration for the node manager. m_configuration = configuration.ParseExtension <DsatsDemoServerConfiguration>(); // use suitable defaults if no configuration exists. if (m_configuration == null) { m_configuration = new DsatsDemoServerConfiguration(); } m_source = new DataSourceClient(configuration); m_sessionLocks = new NodeIdDictionary <List <NodeId> >(); }
/// <summary> /// Registers a type with the factory. /// </summary> /// <param name="typeDefinitionId">The type definition.</param> /// <param name="type">The system type.</param> public void RegisterType(NodeId typeDefinitionId, Type type) { if (NodeId.IsNull(typeDefinitionId)) { throw new ArgumentNullException("typeDefinitionId"); } if (type == null) { throw new ArgumentNullException("type"); } if (m_types == null) { m_types = new NodeIdDictionary <Type>(); } m_types[typeDefinitionId] = type; }
/// <summary> /// Initializes the node manager. /// </summary> public DsatsDemoNodeManager(IServerInternal server, ApplicationConfiguration configuration) : base(server, DsatsDemo.Namespaces.DsatsDemo, DsatsDemo.Namespaces.DsatsDemo +"/Instance") { SystemContext.NodeIdFactory = this; // get the configuration for the node manager. m_configuration = configuration.ParseExtension<DsatsDemoServerConfiguration>(); // use suitable defaults if no configuration exists. if (m_configuration == null) { m_configuration = new DsatsDemoServerConfiguration(); } m_source = new DataSourceClient(configuration); m_sessionLocks = new NodeIdDictionary<List<NodeId>>(); }
/// <summary> /// Initializes the object with the default values. /// </summary> public Group( Server server, object serverLock, string name, int serverHandle, int clientHandle, int updateRate, bool active, float deadband, int lcid, int timebias, Subscription subscription, NodeIdDictionary<CachedValue> cache) { // register interface for a group object as a connection point. RegisterInterface(typeof(OpcRcw.Da.IOPCDataCallback).GUID); // all the groups use the same lock as the server object. // this is necessary because the session/subscription objects are not thread safe. m_lock = serverLock; // set default values. m_server = server; m_subscription = subscription; m_session = subscription.Session; m_name = name; m_serverHandle = serverHandle; m_clientHandle = clientHandle; m_updateRate = updateRate; m_active = false; m_deadband = deadband; m_lcid = lcid; m_timebias = timebias; m_enabled = true; m_advised = false; m_cache = cache; m_defaultKeepAliveCount = subscription.KeepAliveCount; this.PublishStateChanged(active, null); }
/// <summary> /// Initializes a new instance of the <see cref="ComHdaBrowser"/> class. /// </summary> public ComAe2Subscription( ComAe2Proxy server, ComAe2ProxyConfiguration configuration, ComAeNamespaceMapper mapper, ComAe2Browser browser, AeConditionManager conditionManager) { m_server = server; m_configuration = configuration; m_mapper = mapper; m_browser = browser; m_conditionManager = conditionManager; m_filter = new AeEventFilter(m_mapper); m_queue = new Queue <AeEvent>(); m_notifiers = new NodeIdDictionary <MonitoredItem>(); m_sourceNodes = new List <NodeId>(); // set a default filters. m_filter.SetFilter(Constants.ALL_EVENTS, 0, UInt16.MaxValue, null, null); UpdateAreaFilter(null); UpdateSourceFilter(null); }
/// <summary> /// Initializes a new instance of the <see cref="ComHdaBrowser"/> class. /// </summary> public ComAe2Subscription( ComAe2Proxy server, ComAe2ProxyConfiguration configuration, ComAeNamespaceMapper mapper, ComAe2Browser browser, AeConditionManager conditionManager) { m_server = server; m_configuration = configuration; m_mapper = mapper; m_browser = browser; m_conditionManager = conditionManager; m_filter = new AeEventFilter(m_mapper); m_queue = new Queue<AeEvent>(); m_notifiers = new NodeIdDictionary<MonitoredItem>(); m_sourceNodes = new List<NodeId>(); // set a default filters. m_filter.SetFilter(Constants.ALL_EVENTS, 0, UInt16.MaxValue, null, null); UpdateAreaFilter(null); UpdateSourceFilter(null); }
/// <summary> /// Initializes the mapper. /// </summary> public void Initialize(Session session, ComAe2ProxyConfiguration configuration) { base.Initialize(session, configuration); m_session = session; // discard the table. m_eventTypes = new NodeIdDictionary <AeEventCategory>(); m_categories = new Dictionary <uint, AeEventCategory>(); m_attributes = new Dictionary <uint, AeEventAttribute>(); // load the well known types from an embedded resource. IndexWellKnownTypes(); // browse the server for additional types. if (!configuration.UseOnlyBuiltInTypes) { IndexTypesFromServer(Opc.Ua.ObjectTypeIds.BaseEventType, OpcRcw.Ae.Constants.SIMPLE_EVENT); } // check for existing category mapping. NodeIdMappingSet mappingSet = configuration.GetMappingSet("EventCategories"); // update mappings. UpdateEventTypeMappings(mappingSet); // update configuration. configuration.ReplaceMappingSet(mappingSet); // check for existing attribute mapping. mappingSet = configuration.GetMappingSet("EventAttributes"); // update mappings. UpdateEventAttributeMappings(mappingSet); // update configuration. configuration.ReplaceMappingSet(mappingSet); }
/// <summary> /// Initializes the mapper. /// </summary> public void Initialize(Session session, ComAe2ProxyConfiguration configuration) { base.Initialize(session, configuration); m_session = session; // discard the table. m_eventTypes = new NodeIdDictionary<AeEventCategory>(); m_categories = new Dictionary<uint, AeEventCategory>(); m_attributes = new Dictionary<uint, AeEventAttribute>(); // load the well known types from an embedded resource. IndexWellKnownTypes(); // browse the server for additional types. if (!configuration.UseOnlyBuiltInTypes) { IndexTypesFromServer(Opc.Ua.ObjectTypeIds.BaseEventType, OpcRcw.Ae.Constants.SIMPLE_EVENT); } // check for existing category mapping. NodeIdMappingSet mappingSet = configuration.GetMappingSet("EventCategories"); // update mappings. UpdateEventTypeMappings(mappingSet); // update configuration. configuration.ReplaceMappingSet(mappingSet); // check for existing attribute mapping. mappingSet = configuration.GetMappingSet("EventAttributes"); // update mappings. UpdateEventAttributeMappings(mappingSet); // update configuration. configuration.ReplaceMappingSet(mappingSet); }
/// <summary> /// Updates the object after a reconnect. /// </summary> public void OnSessionReconected(Session session) { ThrowIfDisposed(); lock (m_lock) { foreach (Subscription subscription in session.Subscriptions) { if (Object.ReferenceEquals(this, subscription.Handle)) { m_subscription = subscription; m_notifiers = new NodeIdDictionary <MonitoredItem>(); foreach (MonitoredItem monitoredItem in subscription.MonitoredItems) { m_notifiers[monitoredItem.StartNodeId] = monitoredItem; } break; } } } }
/// <summary> /// Creates a new set of monitored items for a set of variables. /// </summary> /// <remarks> /// This method only handles data change subscriptions. Event subscriptions are created by the SDK. /// </remarks> public override void CreateMonitoredItems( OperationContext context, uint subscriptionId, double publishingInterval, TimestampsToReturn timestampsToReturn, IList <MonitoredItemCreateRequest> itemsToCreate, IList <ServiceResult> errors, IList <MonitoringFilterResult> filterResults, IList <IMonitoredItem> monitoredItems, ref long globalIdCounter) { ServerSystemContext systemContext = _defaultSystemContext.Copy(context); IDictionary <NodeId, NodeState> operationCache = new NodeIdDictionary <NodeState>(); List <NodeHandle> nodesToValidate = new List <NodeHandle>(); List <IMonitoredItem> createdItems = new List <IMonitoredItem>(); lock (Lock) { for (int ii = 0; ii < itemsToCreate.Count; ii++) { MonitoredItemCreateRequest monitoredItemCreateRequest = itemsToCreate[ii]; // skip items that have already been processed. if (monitoredItemCreateRequest.Processed) { continue; } ReadValueId itemToMonitor = monitoredItemCreateRequest.ItemToMonitor; // check for valid handle. NodeHandle handle = GetManagerHandle(systemContext, itemToMonitor.NodeId, operationCache); if (handle == null) { continue; } // owned by this node manager. monitoredItemCreateRequest.Processed = true; // must validate node in a seperate operation. errors[ii] = StatusCodes.BadNodeIdUnknown; handle.Index = ii; nodesToValidate.Add(handle); } // check for nothing to do. if (nodesToValidate.Count == 0) { return; } } // validates the nodes (reads values from the underlying data source if required). for (int ii = 0; ii < nodesToValidate.Count; ii++) { NodeHandle handle = nodesToValidate[ii]; MonitoringFilterResult filterResult = null; IMonitoredItem monitoredItem = null; lock (Lock) { // validate node. NodeState source = ValidateNode(systemContext, handle, operationCache); if (source == null) { continue; } MonitoredItemCreateRequest itemToCreate = itemsToCreate[handle.Index]; // create monitored item. errors[handle.Index] = CreateMonitoredItem( systemContext, handle, subscriptionId, publishingInterval, context.DiagnosticsMask, timestampsToReturn, itemToCreate, ref globalIdCounter, out filterResult, out monitoredItem); } // save any filter error details. filterResults[handle.Index] = filterResult; if (ServiceResult.IsBad(errors[handle.Index])) { continue; } // save the monitored item. monitoredItems[handle.Index] = monitoredItem; createdItems.Add(monitoredItem); } // do any post processing. OnCreateMonitoredItemsComplete(systemContext, createdItems); }
/// <summary> /// Calls a method on the specified nodes. /// </summary> public override void Call( OperationContext context, IList <CallMethodRequest> methodsToCall, IList <CallMethodResult> results, IList <ServiceResult> errors) { ServerSystemContext systemContext = SystemContext.Copy(context); IDictionary <NodeId, NodeState> operationCache = new NodeIdDictionary <NodeState>(); bool didRefresh = false; for (int ii = 0; ii < methodsToCall.Count; ii++) { CallMethodRequest methodToCall = methodsToCall[ii]; bool refreshMethod = methodToCall.MethodId.Equals(Opc.Ua.MethodIds.ConditionType_ConditionRefresh) || methodToCall.MethodId.Equals(Opc.Ua.MethodIds.ConditionType_ConditionRefresh2); if (refreshMethod) { if (didRefresh) { errors[ii] = StatusCodes.BadRefreshInProgress; methodToCall.Processed = true; continue; } else { didRefresh = true; } } bool ackMethod = methodToCall.MethodId.Equals(Opc.Ua.MethodIds.AcknowledgeableConditionType_Acknowledge); bool confirmMethod = methodToCall.MethodId.Equals(Opc.Ua.MethodIds.AcknowledgeableConditionType_Confirm); bool commentMethod = methodToCall.MethodId.Equals(Opc.Ua.MethodIds.ConditionType_AddComment); bool ackConfirmMethod = ackMethod || confirmMethod || commentMethod; // Need to try to capture any calls to ConditionType::Acknowledge if (methodToCall.ObjectId.Equals(Opc.Ua.ObjectTypeIds.ConditionType) && (ackConfirmMethod)) { // Mantis Issue 6944 which is a duplicate of 5544 - result is Confirm should be Bad_NodeIdInvalid // Override any other errors that may be there, even if this is 'Processed' errors[ii] = StatusCodes.BadNodeIdInvalid; methodToCall.Processed = true; continue; } // skip items that have already been processed. if (methodToCall.Processed) { continue; } MethodState method = null; lock (Lock) { // check for valid handle. NodeHandle initialHandle = GetManagerHandle(systemContext, methodToCall.ObjectId, operationCache); if (initialHandle == null) { if (ackConfirmMethod) { // Mantis 6944 errors[ii] = StatusCodes.BadNodeIdUnknown; methodToCall.Processed = true; } continue; } // owned by this node manager. methodToCall.Processed = true; // Look for an alarm branchId to operate on. NodeHandle handle = FindBranchNodeHandle(systemContext, initialHandle, methodToCall); // validate the source node. NodeState source = ValidateNode(systemContext, handle, operationCache); if (source == null) { errors[ii] = StatusCodes.BadNodeIdUnknown; continue; } // find the method. method = source.FindMethod(systemContext, methodToCall.MethodId); if (method == null) { // check for loose coupling. if (source.ReferenceExists(ReferenceTypeIds.HasComponent, false, methodToCall.MethodId)) { method = (MethodState)FindPredefinedNode(methodToCall.MethodId, typeof(MethodState)); } if (method == null) { errors[ii] = StatusCodes.BadMethodInvalid; continue; } } } // call the method. CallMethodResult result = results[ii] = new CallMethodResult(); errors[ii] = Call( systemContext, methodToCall, method, result); } }
/// <summary> /// Updates the object after a reconnect. /// </summary> public void OnSessionReconected(Session session) { ThrowIfDisposed(); lock (m_lock) { foreach (Subscription subscription in session.Subscriptions) { if (Object.ReferenceEquals(this, subscription.Handle)) { m_subscription = subscription; m_notifiers = new NodeIdDictionary<MonitoredItem>(); foreach (MonitoredItem monitoredItem in subscription.MonitoredItems) { m_notifiers[monitoredItem.StartNodeId] = monitoredItem; } break; } } } }
/// <summary> /// Reads the value for the specified attribute. /// </summary> public override void Read( OperationContext context, double maxAge, IList <ReadValueId> nodesToRead, IList <DataValue> values, IList <ServiceResult> errors) { ServerSystemContext systemContext = SystemContext.Copy(context); IDictionary <NodeId, NodeState> operationCache = new NodeIdDictionary <NodeState>(); List <DataSourceClient.ReadRequest> readRequests = new List <DataSourceClient.ReadRequest>(); lock (Lock) { for (int ii = 0; ii < nodesToRead.Count; ii++) { ReadValueId nodeToRead = nodesToRead[ii]; // skip items that have already been processed. if (nodeToRead.Processed) { continue; } // check for valid handle. NodeHandle handle = GetManagerHandle(systemContext, nodeToRead.NodeId, operationCache); if (handle == null) { continue; } // owned by this node manager. nodeToRead.Processed = true; // create an initial value. DataValue value = values[ii] = new DataValue(); value.Value = null; value.ServerTimestamp = DateTime.UtcNow; value.SourceTimestamp = DateTime.MinValue; value.StatusCode = StatusCodes.Good; // check if the node is a area in memory. if (handle.Node == null) { errors[ii] = StatusCodes.BadNodeIdUnknown; continue; } if (nodeToRead.AttributeId == Attributes.Value) { // check if the request if for a remote node. RemoteNode remoteNode = null; if (m_remoteNodes.TryGetValue(handle.NodeId, out remoteNode)) { DataSourceClient.ReadRequest request = new DataSourceClient.ReadRequest(); request.RemoteId = remoteNode.RemoteId; request.ReadValueId = nodeToRead; request.Value = value; request.Index = ii; readRequests.Add(request); errors[ii] = StatusCodes.BadNoCommunication; continue; } } // read the attribute value. errors[ii] = handle.Node.ReadAttribute( systemContext, nodeToRead.AttributeId, nodeToRead.ParsedIndexRange, nodeToRead.DataEncoding, value); } // check for nothing to do. if (readRequests.Count == 0) { return; } } // read from the remote data source. List <ServiceResult> results = m_source.Read(readRequests); for (int ii = 0; ii < readRequests.Count; ii++) { values[readRequests[ii].Index] = readRequests[ii].Value; errors[readRequests[ii].Index] = results[ii]; } }
/// <summary> /// Subscribes or unsubscribes to events produced by all event sources. /// </summary> /// <remarks> /// This method is called when a event subscription is created or deleted. The node /// manager must start/stop reporting events for all objects that it manages. /// </remarks> public virtual ServiceResult SubscribeToAllEvents( OperationContext context, uint subscriptionId, IEventMonitoredItem monitoredItem, bool unsubscribe) { ServerSystemContext systemContext = m_systemContext.Copy(context); IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>(); lock (Lock) { // update root notifiers. for (int ii = 0; ii < m_rootNotifiers.Count; ii++) { SubscribeToAllEvents( systemContext, monitoredItem, unsubscribe, m_rootNotifiers[ii]); } return ServiceResult.Good; } }
/// <summary> /// Calls a method on the specified nodes. /// </summary> public virtual void Call( OperationContext context, IList<CallMethodRequest> methodsToCall, IList<CallMethodResult> results, IList<ServiceResult> errors) { ServerSystemContext systemContext = m_systemContext.Copy(context); IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>(); List<CallOperationState> nodesToValidate = new List<CallOperationState>(); lock (Lock) { for (int ii = 0; ii < methodsToCall.Count; ii++) { CallMethodRequest methodToCall = methodsToCall[ii]; // skip items that have already been processed. if (methodToCall.Processed) { continue; } // check for valid handle. NodeState source = GetManagerHandle(systemContext, methodToCall.ObjectId, operationCache) as NodeState; if (source == null) { continue; } // owned by this node manager. methodToCall.Processed = true; // check for valid method. MethodState method = GetManagerHandle(systemContext, methodToCall.MethodId, operationCache) as MethodState; if (method == null) { errors[ii] = StatusCodes.BadMethodInvalid; continue; } // check if method belongs to the object. if (!Object.ReferenceEquals(method.Parent, source)) { errors[ii] = StatusCodes.BadMethodInvalid; continue; } CallMethodResult result = results[ii] = new CallMethodResult(); // check if the node is ready for reading. if (source.ValidationRequired) { errors[ii] = StatusCodes.BadNodeIdUnknown; // must validate node in a seperate operation. CallOperationState operation = new CallOperationState(); operation.Source = source; operation.Method = method; operation.Index = ii; nodesToValidate.Add(operation); continue; } // call the method. errors[ii] = Call( systemContext, methodToCall, source, method, result); } // check for nothing to do. if (nodesToValidate.Count == 0) { return; } // validates the nodes (reads values from the underlying data source if required). for (int ii = 0; ii < nodesToValidate.Count; ii++) { CallOperationState operation = nodesToValidate[ii]; // validate the object. if (!ValidateNode(systemContext, operation.Source)) { continue; } // call the method. CallMethodResult result = results[operation.Index]; errors[operation.Index] = Call( systemContext, methodsToCall[operation.Index], operation.Source, operation.Method, result); } } }
/// <summary> /// Writes the value for the specified attributes. /// </summary> public virtual void Write( OperationContext context, IList<WriteValue> nodesToWrite, IList<ServiceResult> errors) { ServerSystemContext systemContext = m_systemContext.Copy(context); IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>(); List<ReadWriteOperationState> nodesToValidate = new List<ReadWriteOperationState>(); lock (Lock) { for (int ii = 0; ii < nodesToWrite.Count; ii++) { WriteValue nodeToWrite = nodesToWrite[ii]; // skip items that have already been processed. if (nodeToWrite.Processed) { continue; } // check for valid handle. NodeState source = GetManagerHandle(systemContext, nodeToWrite.NodeId, operationCache) as NodeState; if (source == null) { continue; } // owned by this node manager. nodeToWrite.Processed = true; // index range is not supported. if (!String.IsNullOrEmpty(nodeToWrite.IndexRange)) { errors[ii] = StatusCodes.BadIndexRangeInvalid; continue; } // check if the node is ready for reading. if (source.ValidationRequired) { errors[ii] = StatusCodes.BadNodeIdUnknown; // must validate node in a seperate operation. ReadWriteOperationState operation = new ReadWriteOperationState(); operation.Source = source; operation.Index = ii; nodesToValidate.Add(operation); continue; } // write the attribute value. errors[ii] = source.WriteAttribute( systemContext, nodeToWrite.AttributeId, nodeToWrite.ParsedIndexRange, nodeToWrite.Value); // updates to source finished - report changes to monitored items. source.ClearChangeMasks(systemContext, false); } // check for nothing to do. if (nodesToValidate.Count == 0) { return; } // validates the nodes (reads values from the underlying data source if required). for (int ii = 0; ii < nodesToValidate.Count; ii++) { ReadWriteOperationState operation = nodesToValidate[ii]; if (!ValidateNode(systemContext, operation.Source)) { continue; } WriteValue nodeToWrite = nodesToWrite[operation.Index]; // write the attribute value. errors[operation.Index] = operation.Source.WriteAttribute( systemContext, nodeToWrite.AttributeId, nodeToWrite.ParsedIndexRange, nodeToWrite.Value); // updates to source finished - report changes to monitored items. operation.Source.ClearChangeMasks(systemContext, false); } } }
/// <summary> /// Indexes the well known subtypes. /// </summary> private void IndexWellKnownTypes() { SystemContext context = new SystemContext(); context.EncodeableFactory = m_session.MessageContext.Factory; context.NamespaceUris = m_session.NamespaceUris; context.ServerUris = m_session.ServerUris; NodeStateCollection predefinedNodes = new NodeStateCollection(); predefinedNodes.LoadFromBinaryResource(context, "Opc.Ua.Stack.Generated.Opc.Ua.PredefinedNodes.uanodes", typeof(NodeState).Assembly, true); NodeIdDictionary <BaseTypeState> types = new NodeIdDictionary <BaseTypeState>(); // collect the instance declarations for all types. for (int ii = 0; ii < predefinedNodes.Count; ii++) { BaseTypeState type = predefinedNodes[ii] as BaseTypeState; if (type != null) { types.Add(type.NodeId, type); } } // index only those types which are subtypes of BaseEventType. foreach (BaseTypeState type in types.Values) { BaseTypeState subType = type; BaseTypeState superType = null; int eventType = 0; while (subType != null) { if (subType.NodeId == Opc.Ua.ObjectTypeIds.ConditionType || subType.SuperTypeId == Opc.Ua.ObjectTypeIds.ConditionType) { eventType = OpcRcw.Ae.Constants.CONDITION_EVENT; } else if (subType.NodeId == Opc.Ua.ObjectTypeIds.AuditEventType || subType.SuperTypeId == Opc.Ua.ObjectTypeIds.AuditEventType) { eventType = OpcRcw.Ae.Constants.TRACKING_EVENT; } else if (subType.NodeId == Opc.Ua.ObjectTypeIds.BaseEventType || subType.SuperTypeId == Opc.Ua.ObjectTypeIds.BaseEventType) { eventType = OpcRcw.Ae.Constants.SIMPLE_EVENT; } // found an event, collect the attribute and index it. if (eventType != 0) { List <AeEventAttribute> declarations = new List <AeEventAttribute>(); Dictionary <string, AeEventAttribute> map = new Dictionary <string, AeEventAttribute>(); ComAeUtils.CollectInstanceDeclarations( m_session, this, type, null, declarations, map); AeEventCategory declaration = new AeEventCategory(); declaration.TypeId = type.NodeId; declaration.SuperTypeId = type.SuperTypeId; declaration.EventType = eventType; declaration.Description = (LocalizedText.IsNullOrEmpty(type.DisplayName))?type.BrowseName.Name:type.DisplayName.Text; declaration.Attributes = declarations; m_eventTypes[declaration.TypeId] = declaration; break; } // follow the tree to the parent. if (!types.TryGetValue(subType.SuperTypeId, out superType)) { break; } subType = superType; } } // hide the built in attributes. AeEventCategory category = GetCategory(Opc.Ua.ObjectTypeIds.BaseEventType); if (category != null) { for (int ii = 0; ii < category.Attributes.Count; ii++) { switch (category.Attributes[ii].BrowsePathDisplayText) { case Opc.Ua.BrowseNames.Message: case Opc.Ua.BrowseNames.Severity: case Opc.Ua.BrowseNames.SourceName: case Opc.Ua.BrowseNames.Time: case Opc.Ua.BrowseNames.ReceiveTime: case Opc.Ua.BrowseNames.LocalTime: { category.Attributes[ii].Hidden = true; break; } } } } }
/// <summary> /// Writes the value for the specified attributes. /// </summary> public override void Write( OperationContext context, IList<WriteValue> nodesToWrite, IList<ServiceResult> errors) { ServerSystemContext systemContext = SystemContext.Copy(context); IDictionary<NodeId, NodeState> operationCache = new NodeIdDictionary<NodeState>(); List<DataSourceClient.WriteRequest> writeRequests = new List<DataSourceClient.WriteRequest>(); lock (Lock) { for (int ii = 0; ii < nodesToWrite.Count; ii++) { WriteValue nodeToWrite = nodesToWrite[ii]; // skip items that have already been processed. if (nodeToWrite.Processed) { continue; } // check for valid handle. NodeHandle handle = GetManagerHandle(systemContext, nodeToWrite.NodeId, operationCache); if (handle == null) { continue; } // owned by this node manager. nodeToWrite.Processed = true; // index range is not supported. if (nodeToWrite.AttributeId != Attributes.Value) { if (!String.IsNullOrEmpty(nodeToWrite.IndexRange)) { errors[ii] = StatusCodes.BadWriteNotSupported; continue; } } // check if the node is a area in memory. if (handle.Node == null) { errors[ii] = StatusCodes.BadNodeIdUnknown; continue; } // check if the request if for a remote node. RemoteNode remoteNode = null; if (m_remoteNodes.TryGetValue(handle.NodeId, out remoteNode)) { // check for theorectical access. BaseVariableState variable = handle.Node as BaseVariableState; if (variable == null || nodeToWrite.AttributeId != Attributes.Value || (variable.AccessLevel & AccessLevels.CurrentWrite) == 0) { errors[ii] = StatusCodes.BadNotWritable; continue; } // check for access based on current user credentials. if (!CheckWriteAccess(systemContext, remoteNode)) { errors[ii] = StatusCodes.BadUserAccessDenied; continue; } DataSourceClient.WriteRequest request = new DataSourceClient.WriteRequest(); request.RemoteId = remoteNode.RemoteId; request.WriteValue = nodeToWrite; request.Index = ii; writeRequests.Add(request); errors[ii] = StatusCodes.BadNoCommunication; continue; } // write the attribute value. errors[ii] = handle.Node.WriteAttribute( systemContext, nodeToWrite.AttributeId, nodeToWrite.ParsedIndexRange, nodeToWrite.Value); // updates to source finished - report changes to monitored items. handle.Node.ClearChangeMasks(systemContext, false); } // check for nothing to do. if (writeRequests.Count == 0) { return; } } // update the remote data source. List<ServiceResult> results = m_source.Write(writeRequests); for (int ii = 0; ii < writeRequests.Count; ii++) { errors[writeRequests[ii].Index] = results[ii]; } }
/// <summary> /// Writes the value for the specified attributes. /// </summary> public virtual void Write( OperationContext context, IList<WriteValue> nodesToWrite, IList<ServiceResult> errors) { ServerSystemContext systemContext = m_systemContext.Copy(context); IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>(); List<NodeHandle> nodesToValidate = new List<NodeHandle>(); lock (Lock) { for (int ii = 0; ii < nodesToWrite.Count; ii++) { WriteValue nodeToWrite = nodesToWrite[ii]; // skip items that have already been processed. if (nodeToWrite.Processed) { continue; } // check for valid handle. NodeHandle handle = GetManagerHandle(systemContext, nodeToWrite.NodeId, operationCache); if (handle == null) { continue; } // owned by this node manager. nodeToWrite.Processed = true; // index range is not supported. if (nodeToWrite.AttributeId != Attributes.Value) { if (!String.IsNullOrEmpty(nodeToWrite.IndexRange)) { errors[ii] = StatusCodes.BadWriteNotSupported; continue; } } // check if the node is a area in memory. if (handle.Node == null) { errors[ii] = StatusCodes.BadNodeIdUnknown; // must validate node in a seperate operation. handle.Index = ii; nodesToValidate.Add(handle); continue; } // Utils.Trace("WRITE: Value={0} Range={1}", nodeToWrite.Value.WrappedValue, nodeToWrite.IndexRange); // write the attribute value. errors[ii] = handle.Node.WriteAttribute( systemContext, nodeToWrite.AttributeId, nodeToWrite.ParsedIndexRange, nodeToWrite.Value); // updates to source finished - report changes to monitored items. handle.Node.ClearChangeMasks(systemContext, false); } // check for nothing to do. if (nodesToValidate.Count == 0) { return; } } // validates the nodes and writes the value to the underlying system. Write( systemContext, nodesToWrite, errors, nodesToValidate, operationCache); }
/// <summary> /// Recursively indexes the node and its children. /// </summary> protected virtual void AddPredefinedNode(ISystemContext context, NodeState node) { if (m_predefinedNodes == null) { m_predefinedNodes = new NodeIdDictionary<NodeState>(); } NodeState activeNode = AddBehaviourToPredefinedNode(context, node); m_predefinedNodes[activeNode.NodeId] = activeNode; BaseTypeState type = activeNode as BaseTypeState; if (type != null) { AddTypesToTypeTree(type); } List<BaseInstanceState> children = new List<BaseInstanceState>(); activeNode.GetChildren(context, children); for (int ii = 0; ii < children.Count; ii++) { AddPredefinedNode(context, children[ii]); } }
/// <summary> /// Loads a node set from a file or resource and addes them to the set of predefined nodes. /// </summary> public virtual void LoadPredefinedNodes( ISystemContext context, Assembly assembly, string resourcePath, IDictionary<NodeId, IList<IReference>> externalReferences) { if (m_predefinedNodes == null) { m_predefinedNodes = new NodeIdDictionary<NodeState>(); } // load the predefined nodes from an XML document. NodeStateCollection predefinedNodes = new NodeStateCollection(); predefinedNodes.LoadFromResource(context, resourcePath, assembly, true); // add the predefined nodes to the node manager. for (int ii = 0; ii < predefinedNodes.Count; ii++) { AddPredefinedNode(context, predefinedNodes[ii]); } // ensure the reverse refernces exist. AddReverseReferences(externalReferences); }
/// <summary> /// Reads the history for the specified nodes. /// </summary> public virtual void HistoryRead( OperationContext context, HistoryReadDetails details, TimestampsToReturn timestampsToReturn, bool releaseContinuationPoints, IList<HistoryReadValueId> nodesToRead, IList<HistoryReadResult> results, IList<ServiceResult> errors) { ServerSystemContext systemContext = m_systemContext.Copy(context); IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>(); List<NodeHandle> nodesToProcess = new List<NodeHandle>(); lock (Lock) { for (int ii = 0; ii < nodesToRead.Count; ii++) { HistoryReadValueId nodeToRead = nodesToRead[ii]; // skip items that have already been processed. if (nodeToRead.Processed) { continue; } // check for valid handle. NodeHandle handle = GetManagerHandle(systemContext, nodeToRead.NodeId, operationCache); if (handle == null) { continue; } // owned by this node manager. nodeToRead.Processed = true; // create an initial result. HistoryReadResult result = results[ii] = new HistoryReadResult(); result.HistoryData = null; result.ContinuationPoint = null; result.StatusCode = StatusCodes.Good; // check if the node is a area in memory. if (handle.Node == null) { errors[ii] = StatusCodes.BadNodeIdUnknown; // must validate node in a seperate operation handle.Index = ii; nodesToProcess.Add(handle); continue; } errors[ii] = StatusCodes.BadHistoryOperationUnsupported; // check for data history variable. BaseVariableState variable = handle.Node as BaseVariableState; if (variable != null) { if ((variable.AccessLevel & AccessLevels.HistoryRead) != 0) { handle.Index = ii; nodesToProcess.Add(handle); continue; } } // check for event history object. BaseObjectState notifier = handle.Node as BaseObjectState; if (notifier != null) { if ((notifier.EventNotifier & EventNotifiers.HistoryRead) != 0) { handle.Index = ii; nodesToProcess.Add(handle); continue; } } } // check for nothing to do. if (nodesToProcess.Count == 0) { return; } } // validates the nodes (reads values from the underlying data source if required). HistoryRead( systemContext, details, timestampsToReturn, releaseContinuationPoints, nodesToRead, results, errors, nodesToProcess, operationCache); }
/// <summary> /// Calls a method on the specified nodes. /// </summary> public virtual void Call( OperationContext context, IList<CallMethodRequest> methodsToCall, IList<CallMethodResult> results, IList<ServiceResult> errors) { ServerSystemContext systemContext = SystemContext.Copy(context); IDictionary<NodeId, NodeState> operationCache = new NodeIdDictionary<NodeState>(); for (int ii = 0; ii < methodsToCall.Count; ii++) { CallMethodRequest methodToCall = methodsToCall[ii]; // skip items that have already been processed. if (methodToCall.Processed) { continue; } MethodState method = null; lock (Lock) { // check for valid handle. NodeHandle handle = GetManagerHandle(systemContext, methodToCall.ObjectId, operationCache); if (handle == null) { continue; } // owned by this node manager. methodToCall.Processed = true; // validate the source node. NodeState source = ValidateNode(systemContext, handle, operationCache); if (source == null) { errors[ii] = StatusCodes.BadNodeIdUnknown; continue; } // find the method. method = source.FindMethod(systemContext, methodToCall.MethodId); if (method == null) { // check for loose coupling. if (source.ReferenceExists(ReferenceTypeIds.HasComponent, false, methodToCall.MethodId)) { method = (MethodState)FindPredefinedNode(methodToCall.MethodId, typeof(MethodState)); } if (method == null) { errors[ii] = StatusCodes.BadMethodInvalid; continue; } } } // call the method. CallMethodResult result = results[ii] = new CallMethodResult(); errors[ii] = Call( systemContext, methodToCall, method, result); } }
/// <summary> /// Reads the history for the specified nodes. /// </summary> public virtual void HistoryRead( OperationContext context, HistoryReadDetails details, TimestampsToReturn timestampsToReturn, bool releaseContinuationPoints, IList<HistoryReadValueId> nodesToRead, IList<HistoryReadResult> results, IList<ServiceResult> errors) { ServerSystemContext systemContext = m_systemContext.Copy(context); IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>(); List<ReadWriteOperationState> nodesToValidate = new List<ReadWriteOperationState>(); List<ReadWriteOperationState> readsToComplete = new List<ReadWriteOperationState>(); lock (Lock) { for (int ii = 0; ii < nodesToRead.Count; ii++) { HistoryReadValueId nodeToRead = nodesToRead[ii]; // skip items that have already been processed. if (nodeToRead.Processed) { continue; } // check for valid handle. NodeState source = GetManagerHandle(systemContext, nodeToRead.NodeId, operationCache) as NodeState; if (source == null) { continue; } // owned by this node manager. nodeToRead.Processed = true; // only variables supported. BaseVariableState variable = source as BaseVariableState; if (variable == null) { errors[ii] = StatusCodes.BadHistoryOperationUnsupported; continue; } results[ii] = new HistoryReadResult(); ReadWriteOperationState operation = new ReadWriteOperationState(); operation.Source = source; operation.Index = ii; // check if the node is ready for reading. if (source.ValidationRequired) { // must validate node in a seperate operation. errors[ii] = StatusCodes.BadNodeIdUnknown; nodesToValidate.Add(operation); continue; } // read the data. readsToComplete.Add(operation); } // validates the nodes (reads values from the underlying data source if required). for (int ii = 0; ii < nodesToValidate.Count; ii++) { ReadWriteOperationState operation = nodesToValidate[ii]; if (!ValidateNode(systemContext, operation.Source)) { continue; } readsToComplete.Add(operation); } } // reads the data without holding onto the lock. for (int ii = 0; ii < readsToComplete.Count; ii++) { ReadWriteOperationState operation = readsToComplete[ii]; errors[operation.Index] = HistoryRead( systemContext, operation.Source, details, timestampsToReturn, releaseContinuationPoints, nodesToRead[operation.Index], results[operation.Index]); } }
/// <summary> /// Updates the event types in cache with the most recent info fetched from the AE server. /// </summary> public void UpdateCache(ServerSystemContext context, ushort namespaceIndex) { // clear the existing nodes. EventTypeNodes = new NodeIdDictionary<BaseObjectTypeState>(); Attributes = new Dictionary<int,int[]>(); TypeTable typeTable = context.TypeTable as TypeTable; // rebuild from the recently fetched list. for (int ii = 0; ii < EventTypes.Count; ii++) { // save the attributes for use when creating filters. if (EventTypes[ii].EventTypeMapping != EventTypeMapping.ConditionClassType && !Attributes.ContainsKey(EventTypes[ii].CategoryId)) { EventType eventType = EventTypes[ii]; int[] attributeIds = new int[eventType.Attributes.Count]; for (int jj = 0; jj < attributeIds.Length; jj++) { attributeIds[jj] = eventType.Attributes[jj].Id; } Attributes.Add(EventTypes[ii].CategoryId, attributeIds); } AeEventTypeState node = new AeEventTypeState(EventTypes[ii], namespaceIndex); BaseObjectTypeState mappingNode = null; if (!EventTypeNodes.TryGetValue(node.SuperTypeId, out mappingNode)) { mappingNode = new AeEventTypeMappingState(node.EventType.EventTypeMapping, namespaceIndex); EventTypeNodes.Add(mappingNode.NodeId, mappingNode); // ensure the mapping node is in the type table. if (typeTable != null) { if (!typeTable.IsKnown(mappingNode.NodeId)) { typeTable.AddSubtype(mappingNode.NodeId, mappingNode.SuperTypeId); } } } EventTypeNodes.Add(node.NodeId, node); // ensure the type node is in the type table. if (typeTable != null) { if (!typeTable.IsKnown(node.NodeId)) { typeTable.AddSubtype(node.NodeId, mappingNode.NodeId); } } } }
/// <summary> /// Updates the history for the specified nodes. /// </summary> public virtual void HistoryUpdate( OperationContext context, Type detailsType, IList<HistoryUpdateDetails> nodesToUpdate, IList<HistoryUpdateResult> results, IList<ServiceResult> errors) { ServerSystemContext systemContext = m_systemContext.Copy(context); IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>(); List<ReadWriteOperationState> nodesToValidate = new List<ReadWriteOperationState>(); lock (Lock) { for (int ii = 0; ii < nodesToUpdate.Count; ii++) { HistoryUpdateDetails nodeToUpdate = nodesToUpdate[ii]; // skip items that have already been processed. if (nodeToUpdate.Processed) { continue; } // check for valid handle. NodeState source = GetManagerHandle(systemContext, nodeToUpdate.NodeId, operationCache) as NodeState; if (source == null) { continue; } // owned by this node manager. nodeToUpdate.Processed = true; // check if the node is ready for reading. if (source.ValidationRequired) { errors[ii] = StatusCodes.BadNodeIdUnknown; // must validate node in a seperate operation. ReadWriteOperationState operation = new ReadWriteOperationState(); operation.Source = source; operation.Index = ii; nodesToValidate.Add(operation); continue; } // historical data not available. errors[ii] = StatusCodes.BadHistoryOperationUnsupported; } // check for nothing to do. if (nodesToValidate.Count == 0) { return; } // validates the nodes (reads values from the underlying data source if required). for (int ii = 0; ii < nodesToValidate.Count; ii++) { ReadWriteOperationState operation = nodesToValidate[ii]; if (!ValidateNode(systemContext, operation.Source)) { continue; } // historical data not available. errors[ii] = StatusCodes.BadHistoryOperationUnsupported; } } }
/// <summary> /// Returns the target entry associated with the reference. /// </summary> /// <param name="key">The key.</param> /// <param name="value">The value.</param> /// <returns>The target entry associated with the reference.</returns> private bool TryGetEntry(IReference key, out KeyValuePair <IReference, T> value) { value = new KeyValuePair <IReference, T>(); // validate key. if (!ValidateReference(key, false)) { return(false); } // look up the reference type. ReferenceTypeEntry entry = null; if (!m_references.TryGetValue(key.ReferenceTypeId, out entry)) { return(false); } // handle reference to external targets. if (key.TargetId.IsAbsolute) { Dictionary <ExpandedNodeId, LinkedListNode <KeyValuePair <IReference, T> > > targets = null; if (key.IsInverse) { targets = entry.InverseExternalTargets; } else { targets = entry.ForwardExternalTargets; } if (targets == null) { return(false); } LinkedListNode <KeyValuePair <IReference, T> > node; if (targets.TryGetValue(key.TargetId, out node)) { value = node.Value; return(true); } } // handle reference to internal target. else { NodeIdDictionary <LinkedListNode <KeyValuePair <IReference, T> > > targets = null; if (key.IsInverse) { targets = entry.InverseTargets; } else { targets = entry.ForwardTargets; } if (targets == null) { return(false); } LinkedListNode <KeyValuePair <IReference, T> > node; if (targets.TryGetValue((NodeId)key.TargetId, out node)) { value = node.Value; return(true); } } return(false); }
/// <summary> /// Subscribes or unsubscribes to events produced by the specified source. /// </summary> /// <remarks> /// This method is called when a event subscription is created or deletes. The node manager /// must start/stop reporting events for the specified object and all objects below it in /// the notifier hierarchy. /// </remarks> public virtual ServiceResult SubscribeToEvents( OperationContext context, object sourceId, uint subscriptionId, IEventMonitoredItem monitoredItem, bool unsubscribe) { ServerSystemContext systemContext = m_systemContext.Copy(context); IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>(); lock (Lock) { // check for valid handle. NodeState source = IsHandleInNamespace(sourceId); if (source == null) { return StatusCodes.BadNodeIdInvalid; } // check if the object supports subscritions. BaseObjectState instance = sourceId as BaseObjectState; if (instance == null || instance.EventNotifier != EventNotifiers.SubscribeToEvents) { return StatusCodes.BadNotSupported; } MonitoredNode monitoredNode = instance.Handle as MonitoredNode; // handle unsubscribe. if (unsubscribe) { if (monitoredNode != null) { monitoredNode.UnsubscribeToEvents(systemContext, monitoredItem); // do any post processing. OnUnsubscribeToEvents(systemContext, monitoredNode, monitoredItem); } return ServiceResult.Good; } // subscribe to events. if (monitoredNode == null) { instance.Handle = monitoredNode = new MonitoredNode(m_server, this, source); } monitoredNode.SubscribeToEvents(systemContext, monitoredItem); // do any post processing. OnSubscribeToEvents(systemContext, monitoredNode, monitoredItem); return ServiceResult.Good; } }
/// <summary> /// Adds or replaces a reference. /// </summary> /// <param name="key">The key.</param> /// <param name="value">The value.</param> /// <param name="replace">if set to <c>true</c> reference is replaced.</param> private void Add(IReference key, T value, bool replace) { // validate key. ValidateReference(key, true); m_version++; // look up the reference type. ReferenceTypeEntry entry = null; if (!m_references.TryGetValue(key.ReferenceTypeId, out entry)) { entry = new ReferenceTypeEntry(); m_references.Add(key.ReferenceTypeId, entry); } // handle reference to external targets. if (key.TargetId.IsAbsolute) { Dictionary <ExpandedNodeId, LinkedListNode <KeyValuePair <IReference, T> > > targets = null; if (key.IsInverse) { if (entry.InverseExternalTargets == null) { entry.InverseExternalTargets = new Dictionary <ExpandedNodeId, LinkedListNode <KeyValuePair <IReference, T> > >(); } targets = entry.InverseExternalTargets; } else { if (entry.ForwardExternalTargets == null) { entry.ForwardExternalTargets = new Dictionary <ExpandedNodeId, LinkedListNode <KeyValuePair <IReference, T> > >(); } targets = entry.ForwardExternalTargets; } // create a new target. LinkedListNode <KeyValuePair <IReference, T> > node = new LinkedListNode <KeyValuePair <IReference, T> >(new KeyValuePair <IReference, T>(key, value)); // check if target already exists. LinkedListNode <KeyValuePair <IReference, T> > existingNode = null; if (!targets.TryGetValue(key.TargetId, out existingNode)) { existingNode = node; m_list.AddLast(node); } // need to replace reference in linked linked as well as the target list. else { if (!replace) { throw new ArgumentException("Key already exists in dictionary.", "key"); } m_list.AddAfter(existingNode, node); m_list.Remove(existingNode); } targets[key.TargetId] = node; } // handle reference to internal target. else { NodeIdDictionary <LinkedListNode <KeyValuePair <IReference, T> > > targets = null; if (key.IsInverse) { if (entry.InverseTargets == null) { entry.InverseTargets = new NodeIdDictionary <LinkedListNode <KeyValuePair <IReference, T> > >(); } targets = entry.InverseTargets; } else { if (entry.ForwardTargets == null) { entry.ForwardTargets = new NodeIdDictionary <LinkedListNode <KeyValuePair <IReference, T> > >(); } targets = entry.ForwardTargets; } NodeId targetId = (NodeId)key.TargetId; // create a new target. LinkedListNode <KeyValuePair <IReference, T> > node = new LinkedListNode <KeyValuePair <IReference, T> >(new KeyValuePair <IReference, T>(key, value)); // check if target already exists. LinkedListNode <KeyValuePair <IReference, T> > existingNode = null; if (!targets.TryGetValue(targetId, out existingNode)) { existingNode = node; m_list.AddLast(node); } // need to replace reference in linked linked as well as the target list. else { if (!replace) { throw new ArgumentException("Key already exists in dictionary.", "key"); } m_list.AddAfter(existingNode, node); m_list.Remove(existingNode); } targets[targetId] = node; } }
/// <summary> /// Creates a new set of monitored items for a set of variables. /// </summary> /// <remarks> /// This method only handles data change subscriptions. Event subscriptions are created by the SDK. /// </remarks> public virtual void CreateMonitoredItems( OperationContext context, uint subscriptionId, double publishingInterval, TimestampsToReturn timestampsToReturn, IList<MonitoredItemCreateRequest> itemsToCreate, IList<ServiceResult> errors, IList<MonitoringFilterResult> filterErrors, IList<IMonitoredItem> monitoredItems, ref long globalIdCounter) { ServerSystemContext systemContext = m_systemContext.Copy(context); IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>(); List<ReadWriteOperationState> nodesToValidate = new List<ReadWriteOperationState>(); lock (Lock) { for (int ii = 0; ii < itemsToCreate.Count; ii++) { MonitoredItemCreateRequest itemToCreate = itemsToCreate[ii]; // skip items that have already been processed. if (itemToCreate.Processed) { continue; } ReadValueId itemToMonitor = itemToCreate.ItemToMonitor; // check for valid handle. NodeState source = GetManagerHandle(systemContext, itemToMonitor.NodeId, operationCache) as NodeState; if (source == null) { continue; } // owned by this node manager. itemToCreate.Processed = true; // check if the node is ready for reading. if (source.ValidationRequired) { errors[ii] = StatusCodes.BadNodeIdUnknown; // must validate node in a seperate operation. ReadWriteOperationState operation = new ReadWriteOperationState(); operation.Source = source; operation.Index = ii; nodesToValidate.Add(operation); continue; } MonitoringFilterResult filterError = null; IMonitoredItem monitoredItem = null; errors[ii] = CreateMonitoredItem( systemContext, source, subscriptionId, publishingInterval, context.DiagnosticsMask, timestampsToReturn, itemToCreate, ref globalIdCounter, out filterError, out monitoredItem); // save any filter error details. filterErrors[ii] = filterError; if (ServiceResult.IsBad(errors[ii])) { continue; } // save the monitored item. monitoredItems[ii] = monitoredItem; } // check for nothing to do. if (nodesToValidate.Count == 0) { return; } // validates the nodes (reads values from the underlying data source if required). for (int ii = 0; ii < nodesToValidate.Count; ii++) { ReadWriteOperationState operation = nodesToValidate[ii]; // validate the object. if (!ValidateNode(systemContext, operation.Source)) { continue; } MonitoredItemCreateRequest itemToCreate = itemsToCreate[operation.Index]; MonitoringFilterResult filterError = null; IMonitoredItem monitoredItem = null; errors[operation.Index] = CreateMonitoredItem( systemContext, operation.Source, subscriptionId, publishingInterval, context.DiagnosticsMask, timestampsToReturn, itemToCreate, ref globalIdCounter, out filterError, out monitoredItem); // save any filter error details. filterErrors[operation.Index] = filterError; if (ServiceResult.IsBad(errors[operation.Index])) { continue; } // save the monitored item. monitoredItems[operation.Index] = monitoredItem; } } }
/// <summary> /// Writes the value for the specified attributes. /// </summary> public override void Write( OperationContext context, IList <WriteValue> nodesToWrite, IList <ServiceResult> errors) { ServerSystemContext systemContext = SystemContext.Copy(context); IDictionary <NodeId, NodeState> operationCache = new NodeIdDictionary <NodeState>(); List <DataSourceClient.WriteRequest> writeRequests = new List <DataSourceClient.WriteRequest>(); lock (Lock) { for (int ii = 0; ii < nodesToWrite.Count; ii++) { WriteValue nodeToWrite = nodesToWrite[ii]; // skip items that have already been processed. if (nodeToWrite.Processed) { continue; } // check for valid handle. NodeHandle handle = GetManagerHandle(systemContext, nodeToWrite.NodeId, operationCache); if (handle == null) { continue; } // owned by this node manager. nodeToWrite.Processed = true; // index range is not supported. if (nodeToWrite.AttributeId != Attributes.Value) { if (!String.IsNullOrEmpty(nodeToWrite.IndexRange)) { errors[ii] = StatusCodes.BadWriteNotSupported; continue; } } // check if the node is a area in memory. if (handle.Node == null) { errors[ii] = StatusCodes.BadNodeIdUnknown; continue; } // check if the request if for a remote node. RemoteNode remoteNode = null; if (m_remoteNodes.TryGetValue(handle.NodeId, out remoteNode)) { // check for theorectical access. BaseVariableState variable = handle.Node as BaseVariableState; if (variable == null || nodeToWrite.AttributeId != Attributes.Value || (variable.AccessLevel & AccessLevels.CurrentWrite) == 0) { errors[ii] = StatusCodes.BadNotWritable; continue; } // check for access based on current user credentials. if (!CheckWriteAccess(systemContext, remoteNode)) { errors[ii] = StatusCodes.BadUserAccessDenied; continue; } DataSourceClient.WriteRequest request = new DataSourceClient.WriteRequest(); request.RemoteId = remoteNode.RemoteId; request.WriteValue = nodeToWrite; request.Index = ii; writeRequests.Add(request); errors[ii] = StatusCodes.BadNoCommunication; continue; } // write the attribute value. errors[ii] = handle.Node.WriteAttribute( systemContext, nodeToWrite.AttributeId, nodeToWrite.ParsedIndexRange, nodeToWrite.Value); // updates to source finished - report changes to monitored items. handle.Node.ClearChangeMasks(systemContext, false); } // check for nothing to do. if (writeRequests.Count == 0) { return; } } // update the remote data source. List <ServiceResult> results = m_source.Write(writeRequests); for (int ii = 0; ii < writeRequests.Count; ii++) { errors[writeRequests[ii].Index] = results[ii]; } }
void LoadDataSourceSources( ISystemContext context, ToolState tool, DsatsDemo.DataSource.DataSource datasource, DsatsDemo.DataSource.DeclarationType declaration) { if (declaration == null || declaration.Sources == null) { return; } // need to ensure the server's tables are not updated when loading this file. ServerSystemContext context2 = new ServerSystemContext(this.Server); context2.NamespaceUris = new NamespaceTable(); context2.ServerUris = new StringTable(); context2.NodeIdFactory = this; context2.NamespaceUris.Append(context.ServerUris.GetString(0)); context2.ServerUris.Append(context.ServerUris.GetString(0)); foreach (DsatsDemo.DataSource.SourceType source in declaration.Sources) { BaseInstanceState child = tool.FindChildBySymbolicName(context, source.Path); if (child == null) { continue; } if (source.DefaultValue != null) { BaseVariableState variable = child as BaseVariableState; if (variable != null) { try { Variant value = datasource.Read(context, source.DefaultValue); variable.WrappedValue = value; } catch (Exception) { Utils.Trace("Could not read Variant in file. {0}", source.DefaultValue.InnerXml); } } } if (source.RemoteId != null) { ExpandedNodeId remoteId = datasource.ReadExpandedNodeId(context2, source.RemoteId); if (m_remoteNodes == null) { m_remoteNodes = new NodeIdDictionary <RemoteNode>(); } RemoteNode remoteNode = new RemoteNode(); remoteNode.Tool = tool; remoteNode.ServerUrl = context2.ServerUris.GetString(remoteId.ServerIndex); remoteNode.LocalNode = child; remoteNode.RemoteId = remoteId; m_remoteNodes.Add(child.NodeId, remoteNode); } } }
/// <summary> /// Creates an empty dictionary. /// </summary> public IReferenceDictionary() { m_version = 0; m_references = new NodeIdDictionary <ReferenceTypeEntry>(); m_list = new LinkedList <KeyValuePair <IReference, T> >(); }
/// <summary> /// Updates the list of area filters. /// </summary> private void UpdateAreaFilter(List<NodeId> areas) { // check if monitoring all events. if (areas == null || areas.Count == 0) { MonitoredItem monitoredItem = null; if (!m_notifiers.TryGetValue(Opc.Ua.ObjectIds.Server, out monitoredItem)) { monitoredItem = CreateMonitoredItem(Opc.Ua.ObjectIds.Server); } m_notifiers.Clear(); m_notifiers[Opc.Ua.ObjectIds.Server] = monitoredItem; return; } // build table of areas to monitor. NodeIdDictionary<MonitoredItem> notifiers = new NodeIdDictionary<MonitoredItem>(); // map all of the area search strings onto NodeIds for notifiers. for (int ii = 0; ii < areas.Count; ii++) { NodeId areaId = areas[ii]; // check for existing item. MonitoredItem monitoredItem = null; if (m_notifiers.TryGetValue(areaId, out monitoredItem)) { notifiers[areaId] = monitoredItem; continue; } // check for new item. if (!notifiers.ContainsKey(areaId)) { notifiers[areaId] = CreateMonitoredItem(areaId); } } // mark unused items for deletion. foreach (MonitoredItem monitoredItem in m_notifiers.Values) { if (!notifiers.ContainsKey(monitoredItem.StartNodeId)) { m_subscription.RemoveItem(monitoredItem); } } m_notifiers = notifiers; }
/// <summary cref="IDictionary.Remove" /> public bool Remove(IReference key) { // validate key. if (!ValidateReference(key, false)) { return(false); } m_version++; // look up the reference type. ReferenceTypeEntry entry = null; if (!m_references.TryGetValue(key.ReferenceTypeId, out entry)) { return(false); } // handle reference to external targets. if (key.TargetId.IsAbsolute) { Dictionary <ExpandedNodeId, LinkedListNode <KeyValuePair <IReference, T> > > targets = null; if (key.IsInverse) { targets = entry.InverseExternalTargets; } else { targets = entry.ForwardExternalTargets; } if (targets == null) { return(false); } LinkedListNode <KeyValuePair <IReference, T> > node; if (!targets.TryGetValue(key.TargetId, out node)) { return(false); } m_list.Remove(node); targets.Remove(key.TargetId); } // handle reference to internal target. else { NodeIdDictionary <LinkedListNode <KeyValuePair <IReference, T> > > targets = null; if (key.IsInverse) { targets = entry.InverseTargets; } else { targets = entry.ForwardTargets; } if (targets == null) { return(false); } LinkedListNode <KeyValuePair <IReference, T> > node; if (!targets.TryGetValue((NodeId)key.TargetId, out node)) { return(false); } m_list.Remove(node); targets.Remove((NodeId)key.TargetId); } // remove empty reference. if (entry.IsEmpty) { m_references.Remove(key.ReferenceTypeId); } return(true); }
/// <summary> /// Returns the target of the specified browse path fragment(s). /// </summary> /// <remarks> /// If reference exists but the node manager does not know the browse name it must /// return the NodeId as an unresolvedTargetIds. The caller will try to check the /// browse name. /// </remarks> public virtual void TranslateBrowsePath( OperationContext context, object sourceHandle, RelativePathElement relativePath, IList<ExpandedNodeId> targetIds, IList<NodeId> unresolvedTargetIds) { ServerSystemContext systemContext = m_systemContext.Copy(context); IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>(); lock (Lock) { // verify that the node exists. NodeState source = IsHandleInNamespace(sourceHandle); if (source == null) { return; } // validate node. if (!ValidateNode(systemContext, source)) { return; } // get list of references that relative path. INodeBrowser browser = source.CreateBrowser( systemContext, null, relativePath.ReferenceTypeId, relativePath.IncludeSubtypes, (relativePath.IsInverse) ? BrowseDirection.Inverse : BrowseDirection.Forward, relativePath.TargetName, null, false); // check the browse names. try { for (IReference reference = browser.Next(); reference != null; reference = browser.Next()) { // ignore unknown external references. if (reference.TargetId.IsAbsolute) { continue; } NodeState target = null; // check for local reference. NodeStateReference referenceInfo = reference as NodeStateReference; if (referenceInfo != null) { target = referenceInfo.Target; } if (target == null) { NodeId targetId = (NodeId)reference.TargetId; // the target may be a reference to a node in another node manager. if (!IsNodeIdInNamespace(targetId)) { unresolvedTargetIds.Add((NodeId)reference.TargetId); continue; } // look up the target manually. target = GetManagerHandle(systemContext, targetId, operationCache) as NodeState; if (target == null) { continue; } } // check browse name. if (target.BrowseName == relativePath.TargetName) { targetIds.Add(reference.TargetId); } } } finally { browser.Dispose(); } } }
/// <summary> /// Reads the value for the specified attribute. /// </summary> public virtual void Read( OperationContext context, double maxAge, IList<ReadValueId> nodesToRead, IList<DataValue> values, IList<ServiceResult> errors) { ServerSystemContext systemContext = m_systemContext.Copy(context); IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>(); List<ReadWriteOperationState> nodesToValidate = new List<ReadWriteOperationState>(); lock (Lock) { for (int ii = 0; ii < nodesToRead.Count; ii++) { ReadValueId nodeToRead = nodesToRead[ii]; // skip items that have already been processed. if (nodeToRead.Processed) { continue; } // check for valid handle. NodeState source = GetManagerHandle(systemContext, nodeToRead.NodeId, operationCache) as NodeState; if (source == null) { continue; } // owned by this node manager. nodeToRead.Processed = true; // create an initial value. DataValue value = values[ii] = new DataValue(); value.Value = null; value.ServerTimestamp = DateTime.UtcNow; value.SourceTimestamp = DateTime.MinValue; value.StatusCode = StatusCodes.Good; // check if the node is ready for reading. if (source.ValidationRequired) { errors[ii] = StatusCodes.BadNodeIdUnknown; // must validate node in a seperate operation. ReadWriteOperationState operation = new ReadWriteOperationState(); operation.Source = source; operation.Index = ii; nodesToValidate.Add(operation); continue; } // read the attribute value. errors[ii] = source.ReadAttribute( systemContext, nodeToRead.AttributeId, nodeToRead.ParsedIndexRange, nodeToRead.DataEncoding, value); } // check for nothing to do. if (nodesToValidate.Count == 0) { return; } // validates the nodes (reads values from the underlying data source if required). for (int ii = 0; ii < nodesToValidate.Count; ii++) { ReadWriteOperationState operation = nodesToValidate[ii]; if (!ValidateNode(systemContext, operation.Source)) { continue; } ReadValueId nodeToRead = nodesToRead[operation.Index]; DataValue value = values[operation.Index]; // update the attribute value. errors[operation.Index] = operation.Source.ReadAttribute( systemContext, nodeToRead.AttributeId, nodeToRead.ParsedIndexRange, nodeToRead.DataEncoding, value); } } }
/// <summary> /// Creates a new instance and assigns unique identifiers to all children. /// </summary> /// <param name="context">The operation context.</param> /// <param name="parentId">An optional parent identifier.</param> /// <param name="referenceTypeId">The reference type from the parent.</param> /// <param name="browseName">The browse name.</param> /// <param name="instance">The instance to create.</param> /// <returns>The new node id.</returns> public NodeId CreateNode( ServerSystemContext context, NodeId parentId, NodeId referenceTypeId, QualifiedName browseName, BaseInstanceState instance) { ServerSystemContext contextToUse = (ServerSystemContext)m_systemContext.Copy(context); lock (Lock) { if (m_predefinedNodes == null) { m_predefinedNodes = new NodeIdDictionary<NodeState>(); } instance.ReferenceTypeId = referenceTypeId; NodeState parent = null; if (parentId != null) { if (!m_predefinedNodes.TryGetValue(parentId, out parent)) { throw ServiceResultException.Create( StatusCodes.BadNodeIdUnknown, "Cannot find parent with id: {0}", parentId); } parent.AddChild(instance); } instance.Create(contextToUse, null, browseName, null, true); AddPredefinedNode(contextToUse, instance); return instance.NodeId; } }
/// <summary> /// Updates the history for the specified nodes. /// </summary> public virtual void HistoryUpdate( OperationContext context, Type detailsType, IList<HistoryUpdateDetails> nodesToUpdate, IList<HistoryUpdateResult> results, IList<ServiceResult> errors) { ServerSystemContext systemContext = m_systemContext.Copy(context); IDictionary<NodeId, NodeState> operationCache = new NodeIdDictionary<NodeState>(); List<NodeHandle> nodesToProcess = new List<NodeHandle>(); lock (Lock) { for (int ii = 0; ii < nodesToUpdate.Count; ii++) { HistoryUpdateDetails nodeToUpdate = nodesToUpdate[ii]; // skip items that have already been processed. if (nodeToUpdate.Processed) { continue; } // check for valid handle. NodeHandle handle = GetManagerHandle(systemContext, nodeToUpdate.NodeId, operationCache); if (handle == null) { continue; } // owned by this node manager. nodeToUpdate.Processed = true; // create an initial result. HistoryUpdateResult result = results[ii] = new HistoryUpdateResult(); result.StatusCode = StatusCodes.Good; // check if the node is a area in memory. if (handle.Node == null) { errors[ii] = StatusCodes.BadNodeIdUnknown; // must validate node in a seperate operation handle.Index = ii; nodesToProcess.Add(handle); continue; } errors[ii] = StatusCodes.BadHistoryOperationUnsupported; // check for data history variable. BaseVariableState variable = handle.Node as BaseVariableState; if (variable != null) { if ((variable.AccessLevel & AccessLevels.HistoryWrite) != 0) { handle.Index = ii; nodesToProcess.Add(handle); continue; } } // check for event history object. BaseObjectState notifier = handle.Node as BaseObjectState; if (notifier != null) { if ((notifier.EventNotifier & EventNotifiers.HistoryWrite) != 0) { handle.Index = ii; nodesToProcess.Add(handle); continue; } } } // check for nothing to do. if (nodesToProcess.Count == 0) { return; } } // validates the nodes and updates. HistoryUpdate( systemContext, detailsType, nodesToUpdate, results, errors, nodesToProcess, operationCache); }