Example #1
0
        /// <summary>
        /// Releases any continuation points.
        /// </summary>
        private void ReleaseContinuationPoints()
        {
            if (m_details != null)
            {
                HistoryReadValueIdCollection nodesToRead = new HistoryReadValueIdCollection();
                nodesToRead.Add(m_nodeToRead);

                HistoryReadResultCollection results         = null;
                DiagnosticInfoCollection    diagnosticInfos = null;

                m_session.HistoryRead(
                    null,
                    new ExtensionObject(m_details),
                    TimestampsToReturn.Neither,
                    true,
                    nodesToRead,
                    out results,
                    out diagnosticInfos);

                Session.ValidateResponse(results, nodesToRead);
                Session.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);

                NextBTN.Visible = false;
                StopBTN.Enabled = false;
                GoBTN.Visible   = true;
                m_details       = null;
                m_nodeToRead    = null;
            }
        }
Example #2
0
        /// <summary>
        /// Starts a new read operation.
        /// </summary>
        private void ReadFirst()
        {
            ResultsLV.ClearEventHistory();

            // set up the request parameters.
            ReadEventDetails details = new ReadEventDetails();

            details.StartTime        = DateTime.MinValue;
            details.EndTime          = DateTime.MinValue;
            details.NumValuesPerNode = 0;
            details.Filter           = m_filter.GetFilter();

            if (StartTimeCK.Checked)
            {
                details.StartTime = StartTimeDP.Value.ToUniversalTime();
            }

            if (EndTimeCK.Checked)
            {
                details.EndTime = EndTimeDP.Value.ToUniversalTime();
            }

            if (MaxReturnValuesCK.Checked)
            {
                details.NumValuesPerNode = (uint)MaxReturnValuesNP.Value;
            }

            // read the events from the server.
            HistoryReadValueId nodeToRead = new HistoryReadValueId();

            nodeToRead.NodeId = m_areaId;

            ReadNext(details, nodeToRead);
        }
        private ExtensionObject ReadEventDetails()
        {
            ReadEventDetails details = new ReadEventDetails {
                NumValuesPerNode = 10,
                StartTime        = DateTime.UtcNow.AddSeconds(30),
                EndTime          = DateTime.UtcNow.AddHours(-1),
            };

            return(new ExtensionObject(details));
        }
Example #4
0
        /// <summary>
        /// Continues a read operation.
        /// </summary>
        private void ReadNext(ReadEventDetails details, HistoryReadValueId nodeToRead)
        {
            HistoryReadValueIdCollection nodesToRead = new HistoryReadValueIdCollection();

            nodesToRead.Add(nodeToRead);

            HistoryReadResultCollection results         = null;
            DiagnosticInfoCollection    diagnosticInfos = null;

            ResponseHeader responseHeader = m_session.HistoryRead(
                null,
                new ExtensionObject(details),
                TimestampsToReturn.Neither,
                false,
                nodesToRead,
                out results,
                out diagnosticInfos);

            Session.ValidateResponse(results, nodesToRead);
            Session.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);

            if (StatusCode.IsBad(results[0].StatusCode))
            {
                throw ServiceResultException.Create(results[0].StatusCode, 0, diagnosticInfos, responseHeader.StringTable);
            }

            // display results.
            HistoryEvent data = ExtensionObject.ToEncodeable(results[0].HistoryData) as HistoryEvent;

            ResultsLV.AddEventHistory(data);

            // check if a continuation point exists.
            if (results[0].ContinuationPoint != null && results[0].ContinuationPoint.Length > 0)
            {
                nodeToRead.ContinuationPoint = results[0].ContinuationPoint;

                NextBTN.Visible = true;
                StopBTN.Enabled = true;
                GoBTN.Visible   = false;
                m_details       = details;
                m_nodeToRead    = nodeToRead;
            }

            // all done.
            else
            {
                NextBTN.Visible = false;
                StopBTN.Enabled = false;
                GoBTN.Visible   = true;
                m_details       = null;
                m_nodeToRead    = null;
            }
        }
Example #5
0
        /// <summary>
        /// Fetches the recent history.
        /// </summary>
        private void ReadHistory(ReadEventDetails details, NodeId areaId)
        {
            HistoryReadValueIdCollection nodesToRead = new HistoryReadValueIdCollection();
            HistoryReadValueId           nodeToRead  = new HistoryReadValueId();

            nodeToRead.NodeId = areaId;
            nodesToRead.Add(nodeToRead);

            HistoryReadResultCollection results         = null;
            DiagnosticInfoCollection    diagnosticInfos = null;

            m_session.HistoryRead(
                null,
                new ExtensionObject(details),
                TimestampsToReturn.Neither,
                false,
                nodesToRead,
                out results,
                out diagnosticInfos);

            ClientBase.ValidateResponse(results, nodesToRead);
            ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);

            if (StatusCode.IsBad(results[0].StatusCode))
            {
                throw new ServiceResultException(results[0].StatusCode);
            }

            HistoryEvent events = ExtensionObject.ToEncodeable(results[0].HistoryData) as HistoryEvent;

            foreach (HistoryEventFieldList e in events.Events)
            {
                DisplayEvent(e.EventFields);
            }

            // release continuation points.
            if (results[0].ContinuationPoint != null && results[0].ContinuationPoint.Length > 0)
            {
                nodeToRead.ContinuationPoint = results[0].ContinuationPoint;

                m_session.HistoryRead(
                    null,
                    new ExtensionObject(details),
                    TimestampsToReturn.Neither,
                    true,
                    nodesToRead,
                    out results,
                    out diagnosticInfos);
            }
        }
Example #6
0
        /// <summary>
        /// Fetches the recent history.
        /// </summary>
        private void ReadRecentHistory()
        {
            // check if session is active.
            if (m_session != null)
            {
                // check if area supports history.
                IObject area = m_session.NodeCache.Find(m_areaId) as IObject;

                if (area != null && ((area.EventNotifier & EventNotifiers.HistoryRead) != 0))
                {
                    // get the last hour or 10 events.
                    ReadEventDetails details = new ReadEventDetails();
                    details.StartTime        = DateTime.UtcNow.AddSeconds(30);
                    details.EndTime          = details.StartTime.AddHours(-1);
                    details.NumValuesPerNode = 10;
                    details.Filter           = m_filter.GetFilter();

                    // read the history.
                    ReadHistory(details, m_areaId);
                }
            }
        }
        /// <summary>
        /// Returns the UTC timestamp of the first event in the archive.
        /// </summary>
        private DateTime ReadFirstDate()
        {
            // read the time of the first event in the archive.
            ReadEventDetails details = new ReadEventDetails();
            details.StartTime = new DateTime(1970, 1, 1);
            details.EndTime = DateTime.MinValue;
            details.NumValuesPerNode = 1;
            details.Filter = new EventFilter();
            details.Filter.AddSelectClause(Opc.Ua.ObjectTypeIds.BaseEventType, Opc.Ua.BrowseNames.Time);

            HistoryReadValueId nodeToRead = new HistoryReadValueId();
            nodeToRead.NodeId = m_areaId;

            HistoryReadValueIdCollection nodesToRead = new HistoryReadValueIdCollection();
            nodesToRead.Add(nodeToRead);

            HistoryReadResultCollection results = null;
            DiagnosticInfoCollection diagnosticInfos = null;

            m_session.HistoryRead(
                null,
                new ExtensionObject(details),
                TimestampsToReturn.Neither,
                false,
                nodesToRead,
                out results,
                out diagnosticInfos);

            Session.ValidateResponse(results, nodesToRead);
            Session.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);

            if (StatusCode.IsBad(results[0].StatusCode))
            {
                throw new ServiceResultException(results[0].StatusCode);
            }

            // get the data.
            HistoryEvent data = ExtensionObject.ToEncodeable(results[0].HistoryData) as HistoryEvent;

            // release the continuation point.
            if (results[0].ContinuationPoint != null)
            {
                nodeToRead.ContinuationPoint = results[0].ContinuationPoint;

                m_session.HistoryRead(
                    null,
                    new ExtensionObject(details),
                    TimestampsToReturn.Neither,
                    true,
                    nodesToRead,
                    out results,
                    out diagnosticInfos);

                Session.ValidateResponse(results, nodesToRead);
                Session.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);
            }

            // check if an event found.
            if (data == null || data.Events.Count == 0 || data.Events[0].EventFields.Count == 0)
            {
                throw new ServiceResultException(StatusCodes.BadNoDataAvailable);
            }

            // get the event time.
            DateTime? eventTime = data.Events[0].EventFields[0].Value as DateTime?;

            if (eventTime == null)
            {
                throw new ServiceResultException(StatusCodes.BadTypeMismatch);
            }

            // return time as UTC value.
            return eventTime.Value;
        }
        /// <summary>
        /// Releases any continuation points.
        /// </summary>
        private void ReleaseContinuationPoints()
        {
            if (m_details != null)
            {
                HistoryReadValueIdCollection nodesToRead = new HistoryReadValueIdCollection();
                nodesToRead.Add(m_nodeToRead);

                HistoryReadResultCollection results = null;
                DiagnosticInfoCollection diagnosticInfos = null;

                m_session.HistoryRead(
                    null,
                    new ExtensionObject(m_details),
                    TimestampsToReturn.Neither,
                    true,
                    nodesToRead,
                    out results,
                    out diagnosticInfos);

                Session.ValidateResponse(results, nodesToRead);
                Session.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);

                NextBTN.Visible = false;
                StopBTN.Enabled = false;
                GoBTN.Visible = true;
                m_details = null;
                m_nodeToRead = null;
            }
        }
        /// <summary>
        /// Reads history events.
        /// </summary>
        protected virtual void HistoryReadEvents(
            ServerSystemContext context,
            ReadEventDetails details,
            TimestampsToReturn timestampsToReturn,
            IList<HistoryReadValueId> nodesToRead,
            IList<HistoryReadResult> results,
            IList<ServiceResult> errors,
            List<NodeHandle> nodesToProcess,
            IDictionary<NodeId, NodeState> cache)
        {
            for (int ii = 0; ii < nodesToProcess.Count; ii++)
            {
                NodeHandle handle = nodesToProcess[ii];

                // validate node.
                NodeState source = ValidateNode(context, handle, cache);

                if (source == null)
                {
                    continue;
                }

                errors[handle.Index] = StatusCodes.BadHistoryOperationUnsupported;
            }
        }
        /// <summary>
        /// Creates a new history request.
        /// </summary>
        private HistoryReadRequest CreateHistoryReadRequest(
            ServerSystemContext context,
            ReadEventDetails details,
            NodeHandle handle,
            HistoryReadValueId nodeToRead)
        {
            FilterContext filterContext        = new FilterContext(context.NamespaceUris, context.TypeTable, context.PreferredLocales);
            LinkedList <BaseEventState> events = new LinkedList <BaseEventState>();

            for (ReportType ii = ReportType.FluidLevelTest; ii <= ReportType.InjectionTest; ii++)
            {
                DataView view = null;

                if (handle.Node is WellState)
                {
                    view = m_generator.ReadHistoryForWellId(
                        ii,
                        (string)handle.Node.NodeId.Identifier,
                        details.StartTime,
                        details.EndTime);
                }
                else
                {
                    view = m_generator.ReadHistoryForArea(
                        ii,
                        handle.Node.NodeId.Identifier as string,
                        details.StartTime,
                        details.EndTime);
                }

                LinkedListNode <BaseEventState> pos = events.First;
                bool sizeLimited = (details.StartTime == DateTime.MinValue || details.EndTime == DateTime.MinValue);

                foreach (DataRowView row in view)
                {
                    // check if reached max results.
                    if (sizeLimited)
                    {
                        if (events.Count >= details.NumValuesPerNode)
                        {
                            break;
                        }
                    }

                    BaseEventState e = m_generator.GetReport(context, NamespaceIndex, ii, row.Row);

                    if (details.Filter.WhereClause != null && details.Filter.WhereClause.Elements.Count > 0)
                    {
                        if (!details.Filter.WhereClause.Evaluate(filterContext, e))
                        {
                            continue;
                        }
                    }

                    bool inserted = false;

                    for (LinkedListNode <BaseEventState> jj = pos; jj != null; jj = jj.Next)
                    {
                        if (jj.Value.Time.Value > e.Time.Value)
                        {
                            events.AddBefore(jj, e);
                            pos      = jj;
                            inserted = true;
                            break;
                        }
                    }

                    if (!inserted)
                    {
                        events.AddLast(e);
                        pos = null;
                    }
                }
            }

            HistoryReadRequest request = new HistoryReadRequest();

            request.Events            = events;
            request.TimeFlowsBackward = details.StartTime == DateTime.MinValue || (details.EndTime != DateTime.MinValue && details.EndTime < details.StartTime);
            request.NumValuesPerNode  = details.NumValuesPerNode;
            request.Filter            = details.Filter;
            request.FilterContext     = filterContext;
            return(request);
        }
        /// <summary>
        /// Reads history events.
        /// </summary>
        protected override void HistoryReadEvents(
            ServerSystemContext context,
            ReadEventDetails details,
            TimestampsToReturn timestampsToReturn,
            IList <HistoryReadValueId> nodesToRead,
            IList <HistoryReadResult> results,
            IList <ServiceResult> errors,
            List <NodeHandle> nodesToProcess,
            IDictionary <NodeId, NodeState> cache)
        {
            for (int ii = 0; ii < nodesToProcess.Count; ii++)
            {
                NodeHandle         handle     = nodesToProcess[ii];
                HistoryReadValueId nodeToRead = nodesToRead[handle.Index];
                HistoryReadResult  result     = results[handle.Index];

                HistoryReadRequest request = null;

                // load an exising request.
                if (nodeToRead.ContinuationPoint != null)
                {
                    request = LoadContinuationPoint(context, nodeToRead.ContinuationPoint);

                    if (request == null)
                    {
                        errors[handle.Index] = StatusCodes.BadContinuationPointInvalid;
                        continue;
                    }
                }

                // create a new request.
                else
                {
                    request = CreateHistoryReadRequest(
                        context,
                        details,
                        handle,
                        nodeToRead);
                }

                // process events until the max is reached.
                HistoryEvent events = new HistoryEvent();

                while (request.NumValuesPerNode == 0 || events.Events.Count < request.NumValuesPerNode)
                {
                    if (request.Events.Count == 0)
                    {
                        break;
                    }

                    BaseEventState e = null;

                    if (request.TimeFlowsBackward)
                    {
                        e = request.Events.Last.Value;
                        request.Events.RemoveLast();
                    }
                    else
                    {
                        e = request.Events.First.Value;
                        request.Events.RemoveFirst();
                    }

                    events.Events.Add(GetEventFields(request, e));
                }

                errors[handle.Index] = ServiceResult.Good;

                // check if a continuation point is requred.
                if (request.Events.Count > 0)
                {
                    // only set if both end time and start time are specified.
                    if (details.StartTime != DateTime.MinValue && details.EndTime != DateTime.MinValue)
                    {
                        result.ContinuationPoint = SaveContinuationPoint(context, request);
                    }
                }

                // check if no data returned.
                else
                {
                    errors[handle.Index] = StatusCodes.GoodNoData;
                }

                // return the data.
                result.HistoryData = new ExtensionObject(events);
            }
        }
        /// <summary>
        /// Creates a new history request.
        /// </summary>
        private HistoryReadRequest CreateHistoryReadRequest(
            ServerSystemContext context,
            ReadEventDetails details,
            NodeHandle handle,
            HistoryReadValueId nodeToRead)
        {
            FilterContext filterContext = new FilterContext(context.NamespaceUris, context.TypeTable, context.PreferredLocales);
            LinkedList<BaseEventState> events = new LinkedList<BaseEventState>();

            for (ReportType ii = ReportType.FluidLevelTest; ii <= ReportType.InjectionTest; ii++)
            {
                DataView view = null;

                if (handle.Node is WellState)
                {
                    view = m_generator.ReadHistoryForWellId(
                        ii,
                        (string)handle.Node.NodeId.Identifier,
                        details.StartTime,
                        details.EndTime);
                }
                else
                {
                    view = m_generator.ReadHistoryForArea(
                        ii,
                        handle.Node.NodeId.Identifier as string,
                        details.StartTime,
                        details.EndTime);
                }

                LinkedListNode<BaseEventState> pos = events.First;
                bool sizeLimited = (details.StartTime == DateTime.MinValue || details.EndTime == DateTime.MinValue);

                foreach (DataRowView row in view)
                {
                    // check if reached max results.
                    if (sizeLimited)
                    {
                        if (events.Count >= details.NumValuesPerNode)
                        {
                            break;
                        }
                    }

                    BaseEventState e = m_generator.GetReport(context, NamespaceIndex, ii, row.Row);

                    if (details.Filter.WhereClause != null && details.Filter.WhereClause.Elements.Count > 0)
                    {
                        if (!details.Filter.WhereClause.Evaluate(filterContext, e))
                        {
                            continue;
                        }
                    }

                    bool inserted = false;

                    for (LinkedListNode<BaseEventState> jj = pos; jj != null; jj = jj.Next)
                    {
                        if (jj.Value.Time.Value > e.Time.Value)
                        {
                            events.AddBefore(jj, e);
                            pos = jj;
                            inserted = true;
                            break;
                        }
                    }

                    if (!inserted)
                    {
                        events.AddLast(e);
                        pos = null;
                    }
                }
            }

            HistoryReadRequest request = new HistoryReadRequest();
            request.Events = events;
            request.TimeFlowsBackward = details.StartTime == DateTime.MinValue || (details.EndTime != DateTime.MinValue && details.EndTime < details.StartTime);
            request.NumValuesPerNode = details.NumValuesPerNode;
            request.Filter = details.Filter;
            request.FilterContext = filterContext;
            return request;
        }
Example #13
0
        /// <summary>
        /// Fetches the recent history.
        /// </summary>
        private void ReadRecentHistory()
        {
            // check if session is active.
            if (m_session != null)
            {
                // check if area supports history.
                IObject area = m_session.NodeCache.Find(m_areaId) as IObject;

                if (area != null && ((area.EventNotifier & EventNotifiers.HistoryRead) != 0))
                {
                    // get the last hour or 10 events.
                    ReadEventDetails details = new ReadEventDetails();
                    details.StartTime = DateTime.UtcNow.AddSeconds(30);
                    details.EndTime = details.StartTime.AddHours(-1);
                    details.NumValuesPerNode = 10;
                    details.Filter = m_filter.GetFilter();

                    // read the history.
                    ReadHistory(details, m_areaId);
                }
            }
        }
        /// <summary>
        /// Starts a new read operation.
        /// </summary>
        private void ReadFirst()
        {
            ResultsLV.ClearEventHistory();

            // set up the request parameters.
            ReadEventDetails details = new ReadEventDetails();
            details.StartTime = DateTime.MinValue;
            details.EndTime = DateTime.MinValue;
            details.NumValuesPerNode = 0;
            details.Filter = m_filter.GetFilter();

            if (StartTimeCK.Checked)
            {
                details.StartTime = StartTimeDP.Value.ToUniversalTime();
            }

            if (EndTimeCK.Checked)
            {
                details.EndTime = EndTimeDP.Value.ToUniversalTime();
            }

            if (MaxReturnValuesCK.Checked)
            {
                details.NumValuesPerNode = (uint)MaxReturnValuesNP.Value;
            }
            
            // read the events from the server.
            HistoryReadValueId nodeToRead = new HistoryReadValueId();
            nodeToRead.NodeId = m_areaId;

            ReadNext(details, nodeToRead);
        }
Example #15
0
        /// <summary>
        /// Fetches the recent history.
        /// </summary>
        private void ReadHistory(ReadEventDetails details, NodeId areaId)
        {
            HistoryReadValueIdCollection nodesToRead = new HistoryReadValueIdCollection();
            HistoryReadValueId nodeToRead = new HistoryReadValueId();
            nodeToRead.NodeId = areaId;
            nodesToRead.Add(nodeToRead);

            HistoryReadResultCollection results = null;
            DiagnosticInfoCollection diagnosticInfos = null;

            m_session.HistoryRead(
                null,
                new ExtensionObject(details),
                TimestampsToReturn.Neither,
                false,
                nodesToRead,
                out results,
                out diagnosticInfos);

            ClientBase.ValidateResponse(results, nodesToRead);
            ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);

            if (StatusCode.IsBad(results[0].StatusCode))
            {
                throw new ServiceResultException(results[0].StatusCode);
            }

            HistoryEvent events = ExtensionObject.ToEncodeable(results[0].HistoryData) as HistoryEvent;
            AddEventHistory(events);

            // release continuation points.
            if (results[0].ContinuationPoint != null && results[0].ContinuationPoint.Length > 0)
            {
                nodeToRead.ContinuationPoint = results[0].ContinuationPoint;

                m_session.HistoryRead(
                    null,
                    new ExtensionObject(details),
                    TimestampsToReturn.Neither,
                    true,
                    nodesToRead,
                    out results,
                    out diagnosticInfos);
            }
        }
Example #16
0
        /// <summary>
        /// Returns the UTC timestamp of the first event in the archive.
        /// </summary>
        private DateTime ReadFirstDate()
        {
            // read the time of the first event in the archive.
            ReadEventDetails details = new ReadEventDetails();

            details.StartTime        = new DateTime(1970, 1, 1);
            details.EndTime          = DateTime.MinValue;
            details.NumValuesPerNode = 1;
            details.Filter           = new EventFilter();
            details.Filter.AddSelectClause(Opc.Ua.ObjectTypeIds.BaseEventType, Opc.Ua.BrowseNames.Time);

            HistoryReadValueId nodeToRead = new HistoryReadValueId();

            nodeToRead.NodeId = m_areaId;

            HistoryReadValueIdCollection nodesToRead = new HistoryReadValueIdCollection();

            nodesToRead.Add(nodeToRead);

            HistoryReadResultCollection results         = null;
            DiagnosticInfoCollection    diagnosticInfos = null;

            m_session.HistoryRead(
                null,
                new ExtensionObject(details),
                TimestampsToReturn.Neither,
                false,
                nodesToRead,
                out results,
                out diagnosticInfos);

            Session.ValidateResponse(results, nodesToRead);
            Session.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);

            if (StatusCode.IsBad(results[0].StatusCode))
            {
                throw new ServiceResultException(results[0].StatusCode);
            }

            // get the data.
            HistoryEvent data = ExtensionObject.ToEncodeable(results[0].HistoryData) as HistoryEvent;

            // release the continuation point.
            if (results[0].ContinuationPoint != null)
            {
                nodeToRead.ContinuationPoint = results[0].ContinuationPoint;

                m_session.HistoryRead(
                    null,
                    new ExtensionObject(details),
                    TimestampsToReturn.Neither,
                    true,
                    nodesToRead,
                    out results,
                    out diagnosticInfos);

                Session.ValidateResponse(results, nodesToRead);
                Session.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);
            }

            // check if an event found.
            if (data == null || data.Events.Count == 0 || data.Events[0].EventFields.Count == 0)
            {
                throw new ServiceResultException(StatusCodes.BadNoDataAvailable);
            }

            // get the event time.
            DateTime?eventTime = data.Events[0].EventFields[0].Value as DateTime?;

            if (eventTime == null)
            {
                throw new ServiceResultException(StatusCodes.BadTypeMismatch);
            }

            // return time as UTC value.
            return(eventTime.Value);
        }
Example #17
0
        /// <summary>
        /// Encode historic events
        /// </summary>
        /// <param name="ct"></param>
        /// <returns></returns>
        private async Task EncodeHistoricEventsAsync(CancellationToken ct)
        {
            EventFilter filter = null;

            try {
                filter = await ReadEventFilterAsync(ct);
            }
            catch (Exception ex) {
                _logger.Error(ex, "Failed to retrieve event filter for {nodeId}", _nodeId);
            }

            if (filter == null)
            {
                // Nothing to do without event filter
                // TODO: use generates events?  What other way to get event field structure?
                return;
            }

            var details = new ReadEventDetails {
                NumValuesPerNode = (uint)_maxValues,
                // Read from today backward
                EndTime   = _startTime,
                StartTime = _endTime,
                Filter    = filter
            };

            //
            // Read first and retry with lower number of nodes to fix issues with
            // misbehaving servers such as reference stack server.
            //
            byte[] continuationToken = null;
            while (true)
            {
                ct.ThrowIfCancellationRequested();
                try {
                    var result = await ReadHistoryAsync <HistoryEvent>(details, ct);

                    if (result.history?.Events != null)
                    {
                        _logger.Verbose("  {count} events...",
                                        result.history.Events.Count);
                        foreach (var data in result.history.Events)
                        {
                            _encoder.WriteEncodeable(null, data, data.GetType());
                            _count++;
                        }
                    }
                    continuationToken = result.continuationToken;
                    break;
                }
                catch (FormatException) {
                    if (details.NumValuesPerNode == 0)
                    {
                        details.NumValuesPerNode = ushort.MaxValue;
                    }
                    else
                    {
                        details.NumValuesPerNode /= 2;
                    }
                }
            }
            // Continue reading
            while (continuationToken != null && _count < _maxValues)
            {
                // Continue reading history
                ct.ThrowIfCancellationRequested();
                var result = await ReadHistoryAsync <HistoryEvent>(details, ct,
                                                                   continuationToken);

                if (result.history?.Events != null)
                {
                    _logger.Verbose("+ {count} events...",
                                    result.history.Events.Count);
                    foreach (var data in result.history.Events)
                    {
                        _encoder.WriteEncodeable(null, data, data.GetType());
                        _count++;
                    }
                }
                continuationToken = result.continuationToken;
            }
        }
        /// <summary>
        /// Continues a read operation.
        /// </summary>
        private void ReadNext(ReadEventDetails details, HistoryReadValueId nodeToRead)
        {
            HistoryReadValueIdCollection nodesToRead = new HistoryReadValueIdCollection();
            nodesToRead.Add(nodeToRead);

            HistoryReadResultCollection results = null;
            DiagnosticInfoCollection diagnosticInfos = null;

            ResponseHeader responseHeader = m_session.HistoryRead(
                null,
                new ExtensionObject(details),
                TimestampsToReturn.Neither,
                false,
                nodesToRead,
                out results,
                out diagnosticInfos);

            Session.ValidateResponse(results, nodesToRead);
            Session.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);

            if (StatusCode.IsBad(results[0].StatusCode))
            {
                throw ServiceResultException.Create(results[0].StatusCode, 0, diagnosticInfos, responseHeader.StringTable);
            }
            
            // display results.
            HistoryEvent data = ExtensionObject.ToEncodeable(results[0].HistoryData) as HistoryEvent;
            ResultsLV.AddEventHistory(data);

            // check if a continuation point exists.
            if (results[0].ContinuationPoint != null && results[0].ContinuationPoint.Length > 0)
            {
                nodeToRead.ContinuationPoint = results[0].ContinuationPoint;

                NextBTN.Visible = true;
                StopBTN.Enabled = true;
                GoBTN.Visible = false;
                m_details = details;
                m_nodeToRead = nodeToRead;
            }

            // all done.
            else
            {
                NextBTN.Visible = false;
                StopBTN.Enabled = false;
                GoBTN.Visible = true;
                m_details = null;
                m_nodeToRead = null;
            }
        }
        /// <summary>
        /// Reads history events.
        /// </summary>
        protected override void HistoryReadEvents(
            ServerSystemContext context,
            ReadEventDetails details,
            TimestampsToReturn timestampsToReturn,
            IList<HistoryReadValueId> nodesToRead,
            IList<HistoryReadResult> results,
            IList<ServiceResult> errors,
            List<NodeHandle> nodesToProcess,
            IDictionary<NodeId, NodeState> cache)
        {
            for (int ii = 0; ii < nodesToProcess.Count; ii++)
            {
                NodeHandle handle = nodesToProcess[ii];
                HistoryReadValueId nodeToRead = nodesToRead[handle.Index];
                HistoryReadResult result = results[handle.Index];

                HistoryReadRequest request = null;

                // load an exising request.
                if (nodeToRead.ContinuationPoint != null)
                {
                    request = LoadContinuationPoint(context, nodeToRead.ContinuationPoint);

                    if (request == null)
                    {
                        errors[handle.Index] = StatusCodes.BadContinuationPointInvalid;
                        continue;
                    }
                }

                // create a new request.
                else
                {
                    request = CreateHistoryReadRequest(
                        context,
                        details,
                        handle,
                        nodeToRead);
                }

                // process events until the max is reached.
                HistoryEvent events = new HistoryEvent();

                while (request.NumValuesPerNode == 0 || events.Events.Count < request.NumValuesPerNode)
                {
                    if (request.Events.Count == 0)
                    {
                        break;
                    }

                    BaseEventState e = null;

                    if (request.TimeFlowsBackward)
                    {
                        e = request.Events.Last.Value;
                        request.Events.RemoveLast();
                    }
                    else
                    {
                        e = request.Events.First.Value;
                        request.Events.RemoveFirst();
                    }

                    events.Events.Add(GetEventFields(request, e));
                }

                errors[handle.Index] = ServiceResult.Good;

                // check if a continuation point is requred.
                if (request.Events.Count > 0)
                {
                    // only set if both end time and start time are specified.
                    if (details.StartTime != DateTime.MinValue && details.EndTime != DateTime.MinValue)
                    {
                        result.ContinuationPoint = SaveContinuationPoint(context, request);
                    }
                }

                // check if no data returned.
                else
                {
                    errors[handle.Index] = StatusCodes.GoodNoData;
                }

                // return the data.
                result.HistoryData = new ExtensionObject(events);
            }
        }