/// <summary>
 /// Constructs a new instance.
 /// </summary>
 public DataChangeMonitoredItem(
     MonitoredNode source,
     uint id,
     uint attributeId,
     NumericRange indexRange,
     QualifiedName dataEncoding,
     DiagnosticsMasks diagnosticsMasks,
     TimestampsToReturn timestampsToReturn,
     MonitoringMode monitoringMode,
     uint clientHandle,
     double samplingInterval,
     bool alwaysReportUpdates)
 {
     m_source = source;
     m_id = id;
     m_attributeId = attributeId;
     m_indexRange = indexRange;
     m_dataEncoding = dataEncoding;
     m_timestampsToReturn = timestampsToReturn;
     m_diagnosticsMasks = diagnosticsMasks;
     m_monitoringMode = monitoringMode;
     m_clientHandle = clientHandle;
     m_samplingInterval = samplingInterval;
     m_nextSampleTime = DateTime.UtcNow.Ticks;
     m_readyToPublish = false;
     m_readyToTrigger = false;
     m_alwaysReportUpdates = alwaysReportUpdates;
 }
 /// <summary>
 /// Constructs a new instance.
 /// </summary>
 public DataChangeMonitoredItem(
     MonitoredNode source,
     uint id,
     uint attributeId,
     NumericRange indexRange,
     QualifiedName dataEncoding,
     DiagnosticsMasks diagnosticsMasks,
     TimestampsToReturn timestampsToReturn,
     MonitoringMode monitoringMode,
     uint clientHandle,
     double samplingInterval,
     bool alwaysReportUpdates)
 {
     m_source              = source;
     m_id                  = id;
     m_attributeId         = attributeId;
     m_indexRange          = indexRange;
     m_dataEncoding        = dataEncoding;
     m_timestampsToReturn  = timestampsToReturn;
     m_diagnosticsMasks    = diagnosticsMasks;
     m_monitoringMode      = monitoringMode;
     m_clientHandle        = clientHandle;
     m_samplingInterval    = samplingInterval;
     m_nextSampleTime      = DateTime.UtcNow.Ticks;
     m_readyToPublish      = false;
     m_readyToTrigger      = false;
     m_alwaysReportUpdates = alwaysReportUpdates;
 }
        /// <summary>
        /// Constructs a new instance.
        /// </summary>
        public DataChangeMonitoredItem(
            MonitoredNode source,
            uint id,
            uint attributeId,
            NumericRange indexRange,
            QualifiedName dataEncoding,
            DiagnosticsMasks diagnosticsMasks,
            TimestampsToReturn timestampsToReturn,
            MonitoringMode monitoringMode,
            uint clientHandle,
            double samplingInterval,
            uint queueSize,
            bool discardOldest,
            DataChangeFilter filter,
            Range range,
            bool alwaysReportUpdates)
        {
            m_source              = source;
            m_id                  = id;
            m_attributeId         = attributeId;
            m_indexRange          = indexRange;
            m_dataEncoding        = dataEncoding;
            m_timestampsToReturn  = timestampsToReturn;
            m_diagnosticsMasks    = diagnosticsMasks;
            m_monitoringMode      = monitoringMode;
            m_clientHandle        = clientHandle;
            m_samplingInterval    = samplingInterval;
            m_nextSampleTime      = DateTime.UtcNow.Ticks;
            m_readyToPublish      = false;
            m_readyToTrigger      = false;
            m_queue               = null;
            m_filter              = filter;
            m_range               = 0;
            m_alwaysReportUpdates = alwaysReportUpdates;

            if (range != null)
            {
                m_range = range.High - range.Low;
            }

            if (queueSize > 1)
            {
                m_queue = new MonitoredItemQueue(id);
                m_queue.SetQueueSize(queueSize, discardOldest, diagnosticsMasks);
                m_queue.SetSamplingInterval(samplingInterval);
            }
        }
        /// <summary>
        /// Constructs a new instance.
        /// </summary>
        public DataChangeMonitoredItem(
            MonitoredNode source,
            uint id,
            uint attributeId,
            NumericRange indexRange,
            QualifiedName dataEncoding,
            DiagnosticsMasks diagnosticsMasks,
            TimestampsToReturn timestampsToReturn,
            MonitoringMode monitoringMode,
            uint clientHandle,
            double samplingInterval,
            uint queueSize,
            bool discardOldest,
            DataChangeFilter filter,
            Range range,
            bool alwaysReportUpdates)
        {
            m_source = source;
            m_id = id;
            m_attributeId = attributeId;
            m_indexRange = indexRange;
            m_dataEncoding = dataEncoding;
            m_timestampsToReturn = timestampsToReturn;
            m_diagnosticsMasks = diagnosticsMasks;
            m_monitoringMode = monitoringMode;
            m_clientHandle = clientHandle;
            m_samplingInterval = samplingInterval;
            m_nextSampleTime = DateTime.UtcNow.Ticks;
            m_readyToPublish = false;
            m_readyToTrigger = false;
            m_queue = null;
            m_filter = filter;
            m_range = 0;
            m_alwaysReportUpdates = alwaysReportUpdates;
        
            if (range != null)
            {
                m_range = range.High  - range.Low;
            }

            if (queueSize > 1)
            {
                m_queue = new MonitoredItemQueue(id);
                m_queue.SetQueueSize(queueSize, discardOldest, diagnosticsMasks);
                m_queue.SetSamplingInterval(samplingInterval);
            }
        }
 /// <summary>
 /// Does any processing after a monitored item is created.
 /// </summary>
 protected virtual void OnSetMonitoringMode(
     ISystemContext systemContext,
     MonitoredNode monitoredNode,
     DataChangeMonitoredItem monitoredItem,
     MonitoringMode previousMode,
     MonitoringMode currentMode)
 {
     // does nothing.
 }
 /// <summary>
 /// Does any processing after a monitored item is deleted.
 /// </summary>
 protected virtual void OnDeleteMonitoredItem(
     ISystemContext systemContext,
     MonitoredNode monitoredNode,
     DataChangeMonitoredItem monitoredItem)
 {
     // does nothing.
 }
 /// <summary>
 /// Does any processing after a monitored item is created.
 /// </summary>
 protected virtual void OnModifyMonitoredItem(
     ISystemContext systemContext,
     MonitoredItemModifyRequest itemToModify,
     MonitoredNode monitoredNode,
     DataChangeMonitoredItem monitoredItem,
     double previousSamplingInterval)
 {
     // does nothing.
 }
 /// <summary>
 /// Does any processing after a monitored item is created.
 /// </summary>
 protected virtual void OnCreateMonitoredItem(
     ISystemContext systemContext,
     MonitoredItemCreateRequest itemToCreate,
     MonitoredNode monitoredNode, 
     DataChangeMonitoredItem monitoredItem)
 {
     // does nothing.
 }
        /// <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>
        protected virtual ServiceResult CreateMonitoredItem(
            ISystemContext context,
            NodeState source,
            uint subscriptionId,
            double publishingInterval,
            DiagnosticsMasks diagnosticsMasks,
            TimestampsToReturn timestampsToReturn,
            MonitoredItemCreateRequest itemToCreate,
            ref long globalIdCounter,
            out MonitoringFilterResult filterError,
            out IMonitoredItem monitoredItem)
        {
            filterError = null;
            monitoredItem = null;
            ServiceResult error = null;
            
            // read initial value.
            DataValue initialValue = new DataValue();

            initialValue.Value = null;
            initialValue.ServerTimestamp = DateTime.UtcNow;
            initialValue.SourceTimestamp = DateTime.MinValue;
            initialValue.StatusCode = StatusCodes.Good;

            error = source.ReadAttribute(
                context,
                itemToCreate.ItemToMonitor.AttributeId,
                itemToCreate.ItemToMonitor.ParsedIndexRange,
                itemToCreate.ItemToMonitor.DataEncoding,
                initialValue);

            if (ServiceResult.IsBad(error))
            {
                return error;
            }
            
            // validate parameters.
            MonitoringParameters parameters = itemToCreate.RequestedParameters;

            // validate the data change filter.
            DataChangeFilter filter = null;
            Range range = null;

            if (!ExtensionObject.IsNull(parameters.Filter))
            {
                error = ValidateDataChangeFilter(
                    context,
                    source,
                    itemToCreate.ItemToMonitor.AttributeId,
                    parameters.Filter,
                    out filter,
                    out range);

                if (ServiceResult.IsBad(error))
                {
                    return error;
                }
            }

            // create monitored node.
            MonitoredNode monitoredNode = source.Handle as MonitoredNode;

            if (monitoredNode == null)
            {
                source.Handle = monitoredNode = new MonitoredNode(m_server, this, source);
            }

            // create a globally unique identifier.
            uint monitoredItemId = Utils.IncrementIdentifier(ref globalIdCounter);

            // determine the sampling interval.
            double samplingInterval = itemToCreate.RequestedParameters.SamplingInterval;

            if (samplingInterval < 0)
            {
                samplingInterval = publishingInterval;
            }

            // check if the variable needs to be sampled.
            bool samplingRequired = false;

            if (itemToCreate.ItemToMonitor.AttributeId == Attributes.Value)
            {
                BaseVariableState variable = source as BaseVariableState;

                if (variable.MinimumSamplingInterval > 0)
                {
                    samplingInterval = CalculateSamplingInterval(variable, samplingInterval);
                    samplingRequired = true;
                }
            }

            // create the item.
            DataChangeMonitoredItem datachangeItem = monitoredNode.CreateDataChangeItem(
                context,
                monitoredItemId,
                itemToCreate.ItemToMonitor.AttributeId,
                itemToCreate.ItemToMonitor.ParsedIndexRange,
                itemToCreate.ItemToMonitor.DataEncoding,
                diagnosticsMasks,
                timestampsToReturn,
                itemToCreate.MonitoringMode,
                itemToCreate.RequestedParameters.ClientHandle,
                samplingInterval,
                itemToCreate.RequestedParameters.QueueSize,
                itemToCreate.RequestedParameters.DiscardOldest,
                filter,
                range,
                false);

            if (samplingRequired)
            {
                CreateSampledItem(samplingInterval, datachangeItem);
            }

            // report the initial value.
            datachangeItem.QueueValue(initialValue, null);

            // do any post processing.
            OnCreateMonitoredItem(context, itemToCreate, monitoredNode, datachangeItem);

            // update monitored item list.
            monitoredItem = datachangeItem;

            return ServiceResult.Good;
        }
 /// <summary>
 /// Does any processing after a monitored item is subscribed to.
 /// </summary>
 protected virtual void OnUnsubscribeToEvents(
     ISystemContext systemContext,
     MonitoredNode monitoredNode,
     IEventMonitoredItem monitoredItem)
 {
     // does nothing.
 }
        /// <summary>
        /// Subscribes/unsubscribes to all events produced by the specified node.
        /// </summary>
        protected void SubscribeToAllEvents(
            ISystemContext      systemContext, 
            IEventMonitoredItem monitoredItem, 
            bool                unsubscribe,
            NodeState           source)
        {
            MonitoredNode monitoredNode = source.Handle as MonitoredNode;

            // handle unsubscribe.
            if (unsubscribe)
            {
                if (monitoredNode != null)
                {
                    monitoredNode.UnsubscribeToEvents(systemContext, monitoredItem);

                    // do any post processing.
                    OnUnsubscribeToEvents(systemContext, monitoredNode, monitoredItem);
                }

                return;
            }

            // subscribe to events.
            if (monitoredNode == null)
            {
                source.Handle = monitoredNode = new MonitoredNode(m_server, this, source);
            }

            monitoredNode.SubscribeToEvents(systemContext, monitoredItem);

            // do any post processing.
            OnSubscribeToEvents(systemContext, monitoredNode, monitoredItem);
        }
        /// <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;
            }
        }