/// <summary> /// 读取历史数据 /// </summary> /// <param name="context"></param> /// <param name="details"></param> /// <param name="timestampsToReturn"></param> /// <param name="releaseContinuationPoints"></param> /// <param name="nodesToRead"></param> /// <param name="results"></param> /// <param name="errors"></param> public override void HistoryRead(OperationContext context, HistoryReadDetails details, TimestampsToReturn timestampsToReturn, bool releaseContinuationPoints, IList <HistoryReadValueId> nodesToRead, IList <HistoryReadResult> results, IList <ServiceResult> errors) { ReadProcessedDetails readDetail = details as ReadProcessedDetails; //假设查询历史数据 都是带上时间范围的 if (readDetail == null || readDetail.StartTime == DateTime.MinValue || readDetail.EndTime == DateTime.MinValue) { errors[0] = StatusCodes.BadHistoryOperationUnsupported; return; } for (int ii = 0; ii < nodesToRead.Count; ii++) { int sss = readDetail.StartTime.Millisecond; double res = sss + DateTime.Now.Millisecond; //这里 返回的历史数据可以是多种数据类型 请根据实际的业务来选择 Opc.Ua.KeyValuePair keyValue = new Opc.Ua.KeyValuePair() { Key = new QualifiedName(nodesToRead[ii].NodeId.Identifier.ToString()), Value = res }; results[ii] = new HistoryReadResult() { StatusCode = StatusCodes.Good, HistoryData = new ExtensionObject(keyValue) }; errors[ii] = StatusCodes.Good; //切记,如果你已处理完了读取历史数据的操作,请将Processed设为true,这样OPC-UA类库就知道你已经处理过了 //不需要再进行检查了 nodesToRead[ii].Processed = true; } }
/// <summary> /// Initializes a new instance of the <see cref="HdaHistoryReadProcessedRequest"/> class. /// </summary> /// <param name="itemId">The item id.</param> /// <param name="aggregateId">The aggregate id.</param> /// <param name="details">The details.</param> /// <param name="nodeToRead">The node to read.</param> public HdaHistoryReadProcessedRequest(string itemId, uint aggregateId, ReadProcessedDetails details, HistoryReadValueId nodeToRead) : base(itemId, details, nodeToRead) { StartTime = details.StartTime; EndTime = details.EndTime; ResampleInterval = (long)details.ProcessingInterval; m_aggregateId = aggregateId; }
/// <summary> /// Reads the processed data for an item. /// </summary> protected override void HistoryReadProcessed( UaServerContext context, ReadProcessedDetails details, TimestampsToReturn timestampsToReturn, IList <HistoryReadValueId> nodesToRead, IList <HistoryReadResult> results, IList <ServiceResult> errors, List <UaNodeHandle> nodesToProcess, IDictionary <NodeId, NodeState> cache) { throw new NotImplementedException(); }
public IEnumerable <DataValue> ReadHistoryProcessed(string tag, DateTime start, DateTime end, string aggregateFunction, double processingInterval, uint count = 1, bool containBound = false) { HistoryReadValueId m_nodeToContinue = new HistoryReadValueId() { NodeId = new NodeId(tag), }; AggregateConfiguration aggregate = new AggregateConfiguration(); NodeIdCollection aggregateTypes = new NodeIdCollection(); aggregateTypes.Add(new NodeId(aggregateFunction)); ReadProcessedDetails m_details = new ReadProcessedDetails { StartTime = start.ToUniversalTime(), EndTime = end.ToUniversalTime(), AggregateConfiguration = aggregate, AggregateType = aggregateTypes, ProcessingInterval = processingInterval, }; m_logger.Information("start {0}/end {0}", m_details.StartTime, m_details.EndTime); HistoryReadValueIdCollection nodesToRead = new HistoryReadValueIdCollection(); nodesToRead.Add(m_nodeToContinue); m_session.HistoryRead( null, new ExtensionObject(m_details), TimestampsToReturn.Both, false, nodesToRead, out HistoryReadResultCollection results, out DiagnosticInfoCollection diagnosticInfos); ClientBase.ValidateResponse(results, nodesToRead); ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead); if (StatusCode.IsBad(results[0].StatusCode)) { throw new ServiceResultException(results[0].StatusCode); } HistoryData values = ExtensionObject.ToEncodeable(results[0].HistoryData) as HistoryData; foreach (var value in values.DataValues) { yield return(value); } }
/// <summary> /// Fetches the recent history. /// </summary> private void ReadProcessed() { AvailableAggregate aggregate = (AvailableAggregate)AggregateCB.SelectedItem; ReadProcessedDetails details = new ReadProcessedDetails(); details.StartTime = StartTimeDP.Value.ToUniversalTime(); details.EndTime = EndTimeDP.Value.ToUniversalTime(); details.ProcessingInterval = (double)ResampleIntervalNP.Value; details.AggregateType.Add(aggregate.NodeId); details.AggregateConfiguration.UseServerCapabilitiesDefaults = true; HistoryReadValueIdCollection nodesToRead = new HistoryReadValueIdCollection(); HistoryReadValueId nodeToRead = new HistoryReadValueId(); nodeToRead.NodeId = m_nodeId; nodesToRead.Add(nodeToRead); HistoryReadResultCollection results = null; DiagnosticInfoCollection diagnosticInfos = null; m_session.HistoryRead( null, new ExtensionObject(details), TimestampsToReturn.Both, 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); } HistoryData values = ExtensionObject.ToEncodeable(results[0].HistoryData) as HistoryData; DisplayResults(values); // save any continuation point. SaveContinuationPoint(details, nodeToRead, results[0].ContinuationPoint); }
/// <summary> /// Reads the processed history for the variable value. /// </summary> protected virtual ServiceResult HistoryReadProcessed( ISystemContext context, BaseVariableState source, ReadProcessedDetails details, TimestampsToReturn timestampsToReturn, bool releaseContinuationPoints, HistoryReadValueId nodeToRead, HistoryReadResult result) { return StatusCodes.BadHistoryOperationUnsupported; }
private void ReadProcessed() { ReadProcessedDetails details = new ReadProcessedDetails(); details.StartTime = StartTimeDP.Value.ToUniversalTime(); details.EndTime = EndTimeDP.Value.ToUniversalTime(); details.ProcessingInterval = (double)ResampleIntervalNP.Value; NodeId aggregateId = null; switch ((string)AggregateCB.SelectedItem) { case BrowseNames.AggregateFunction_Interpolative: { aggregateId = ObjectIds.AggregateFunction_Interpolative; break; } case BrowseNames.AggregateFunction_TimeAverage: { aggregateId = ObjectIds.AggregateFunction_TimeAverage; break; } case BrowseNames.AggregateFunction_Average: { aggregateId = ObjectIds.AggregateFunction_Average; break; } case BrowseNames.AggregateFunction_Count: { aggregateId = ObjectIds.AggregateFunction_Count; break; } case BrowseNames.AggregateFunction_Maximum: { aggregateId = ObjectIds.AggregateFunction_Maximum; break; } case BrowseNames.AggregateFunction_Minimum: { aggregateId = ObjectIds.AggregateFunction_Minimum; break; } case BrowseNames.AggregateFunction_Total: { aggregateId = ObjectIds.AggregateFunction_Total; break; } } details.AggregateType.Add(aggregateId); HistoryReadValueId nodeToRead = new HistoryReadValueId(); nodeToRead.NodeId = m_nodeId; if (m_result != null) { nodeToRead.ContinuationPoint = m_result.ContinuationPoint; } HistoryReadValueIdCollection nodesToRead = new HistoryReadValueIdCollection(); nodesToRead.Add(nodeToRead); HistoryReadResultCollection results = null; DiagnosticInfoCollection diagnosticInfos = null; m_session.HistoryRead( null, new ExtensionObject(details), TimestampsToReturn.Source, 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); } m_result = results[0]; ShowResults(); }
/// <summary> /// Reads the history of an HDA item. /// </summary> private ServiceResult HistoryReadProcessedItem( ServerSystemContext context, ComHdaClient client, ReadProcessedDetails details, TimestampsToReturn timestampsToReturn, uint aggregateId, HistoryReadValueId nodeToRead, HdaParsedNodeId parsedNodeId, HistoryReadResult result) { // create the request or load it from a continuation point. HdaHistoryReadProcessedRequest request = null; if (nodeToRead.ContinuationPoint == null) { request = new HdaHistoryReadProcessedRequest(parsedNodeId.RootId, aggregateId, details, nodeToRead); } else { request = LoadContinuationPoint(context, nodeToRead.ContinuationPoint) as HdaHistoryReadProcessedRequest; if (request == null) { return StatusCodes.BadContinuationPointInvalid; } } // fetch the data. result.StatusCode = client.ReadHistory(request); // fill in the results. if (request.Results != null) { HistoryData data = new HistoryData(); data.DataValues = request.Results; result.HistoryData = new ExtensionObject(data); } // create a new continuation point. if (!request.Completed) { result.ContinuationPoint = SaveContinuationPoint(context, request); } return result.StatusCode; }
/// <summary> /// Creates a new history request. /// </summary> private HistoryReadRequest CreateHistoryReadRequest( ServerSystemContext context, ReadProcessedDetails details, NodeHandle handle, HistoryReadValueId nodeToRead, NodeId aggregateId) { bool applyIndexRangeOrEncoding = (nodeToRead.ParsedIndexRange != NumericRange.Empty || !QualifiedName.IsNull(nodeToRead.DataEncoding)); bool timeFlowsBackward = (details.EndTime < details.StartTime); ArchiveItemState item = handle.Node as ArchiveItemState; if (item == null) { throw new ServiceResultException(StatusCodes.BadNotSupported); } item.ReloadFromSource(context); LinkedList<DataValue> values = new LinkedList<DataValue>(); // read history. DataView view = item.ReadHistory(details.StartTime, details.EndTime, false); int ii = (timeFlowsBackward) ? view.Count - 1 : 0; // choose the aggregate configuration. AggregateConfiguration configuration = (AggregateConfiguration)details.AggregateConfiguration.Clone(); ReviseAggregateConfiguration(context, item, configuration); // create the aggregate calculator. IAggregateCalculator calculator = Server.AggregateManager.CreateCalculator( aggregateId, details.StartTime, details.EndTime, details.ProcessingInterval, item.ArchiveItem.Stepped, configuration); while (ii >= 0 && ii < view.Count) { try { DataValue value = (DataValue)view[ii].Row[2]; calculator.QueueRawValue(value); // queue any processed values. QueueProcessedValues( context, calculator, nodeToRead.ParsedIndexRange, nodeToRead.DataEncoding, applyIndexRangeOrEncoding, false, values); } finally { if (timeFlowsBackward) { ii--; } else { ii++; } } } // queue any processed values beyond the end of the data. QueueProcessedValues( context, calculator, nodeToRead.ParsedIndexRange, nodeToRead.DataEncoding, applyIndexRangeOrEncoding, true, values); HistoryReadRequest request = new HistoryReadRequest(); request.Values = values; request.NumValuesPerNode = 0; request.Filter = null; return request; }
/// <summary> /// Reads processed history data. /// </summary> protected override void HistoryReadProcessed( ServerSystemContext context, ReadProcessedDetails details, TimestampsToReturn timestampsToReturn, IList<HistoryReadValueId> nodesToRead, IList<HistoryReadResult> results, IList<ServiceResult> errors, List<NodeHandle> nodesToProcess, IDictionary<NodeId, NodeState> cache) { ComHdaClientManager system = (ComHdaClientManager)this.SystemContext.SystemHandle; ComHdaClient client = (ComHdaClient)system.SelectClient((ServerSystemContext)SystemContext, false); for (int ii = 0; ii < nodesToProcess.Count; ii++) { NodeHandle handle = nodesToProcess[ii]; HistoryReadValueId nodeToRead = nodesToRead[handle.Index]; HistoryReadResult result = results[handle.Index]; NodeId aggregateId = details.AggregateType[handle.Index]; // check if the node id has been parsed. HdaParsedNodeId parsedNodeId = handle.ParsedNodeId as HdaParsedNodeId; if (parsedNodeId == null) { errors[handle.Index] = StatusCodes.BadNodeIdInvalid; continue; } // validate the aggregate. uint hdaAggregateId = HdaModelUtils.HdaAggregateToUaAggregate(aggregateId, NamespaceIndex); if (hdaAggregateId == 0) { errors[handle.Index] = StatusCodes.BadAggregateNotSupported; continue; } // read the history of an item. if (parsedNodeId.RootType == HdaModelUtils.HdaItem) { errors[handle.Index] = HistoryReadProcessedItem( context, client, details, timestampsToReturn, hdaAggregateId, nodeToRead, parsedNodeId, result); continue; } errors[handle.Index] = StatusCodes.BadHistoryOperationUnsupported; } }
/// <summary> /// Reads processed history data. /// </summary> protected virtual void HistoryReadProcessed( ServerSystemContext context, ReadProcessedDetails 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; } }
static void HistoryReadProcessed(Session session) { // translate browse paths. IList<NodeOfInterest> nodeIds = GetNodeIds(session, Opc.Ua.Objects.ObjectsFolder, VariableBrowsePaths.ToArray()); DiagnosticInfoCollection diagnosticInfos; NodeId aggregateNodeId = null; RequestHeader rh = null; ViewDescription vd = null; ReferenceDescriptionCollection references; byte[] cp; //Get the list of avalilable aggregate functions: session.Browse( rh, vd, Opc.Ua.ObjectIds.Server_ServerCapabilities_AggregateFunctions, 1000, BrowseDirection.Forward, ReferenceTypeIds.Aggregates, false, 0, out cp, out references); Console.WriteLine("{0} aggregates are detected:", references.Count); //Print the list of avalible aggregates: int i = 0; foreach (ReferenceDescription rd in references) { i++; Console.WriteLine("{0}. {1} {2}", i, rd.BrowseName, rd.NodeId.Identifier.ToString()); } //Select aggregate function: Console.WriteLine("\nEnter aggregate number: "); string str = Console.ReadLine(); i = System.Int16.Parse(str); if (i > 0 && i <= references.Count) { aggregateNodeId = ExpandedNodeId.ToNodeId(references[i - 1].NodeId, session.NamespaceUris); } //Prepare arguments to pass to read processed history ReadProcessedDetails readDetails = new ReadProcessedDetails(); readDetails.StartTime = new DateTime(2008, 1, 1, 12, 0, 0); readDetails.EndTime = new DateTime(2008, 1, 1, 12, 0, 12); readDetails.AggregateType = new NodeIdCollection(nodeIds.Count); for (int x = 0; x < nodeIds.Count; x++) { readDetails.AggregateType.Add (aggregateNodeId); } readDetails.ProcessingInterval = 500; //500 milliseconds ExtensionObject eo = new ExtensionObject(readDetails.TypeId, readDetails); HistoryReadValueIdCollection idCollection = new HistoryReadValueIdCollection(); for (int ii = 0; ii < nodeIds.Count; ii++) { HistoryReadValueId readValueId = new HistoryReadValueId(); readValueId.NodeId = nodeIds[ii].NodeId; readValueId.Processed = true; idCollection.Add(readValueId); } HistoryReadResultCollection historyReadResults; //Read processed history: ResponseHeader responseHeader = session.HistoryRead(null, eo, TimestampsToReturn.Both, true, idCollection, out historyReadResults, out diagnosticInfos); //Print results: for (int ii = 0; ii < historyReadResults.Count; ii++) { HistoryReadResult historyReadResult = historyReadResults[ii]; ServiceResult result = Session.GetResult(historyReadResult.StatusCode, ii, diagnosticInfos, responseHeader); HistoryData historyData = null; DataValueCollection dataValues = null; if ( !(historyReadResult.HistoryData == null) ) { historyData = ExtensionObject.ToEncodeable(historyReadResult.HistoryData) as HistoryData; if (historyData == null) dataValues = null; else dataValues = historyData.DataValues; } Console.WriteLine("\nHistoryRead result code for {0}: {1}", VariableBrowsePaths[ii], result.StatusCode.ToString()); if (dataValues == null) { Console.WriteLine("dataValues == null"); continue; } for (int jj = 0; jj < dataValues.Count; jj++) { DataValue dataValue = dataValues[jj]; if (dataValue == null) continue; // write value. Console.WriteLine("{0}: V={1}, Q={2}, SrvT={3}, SrcT={4}", jj, dataValue.Value == null ? "null" : dataValue.Value.ToString(), dataValue.StatusCode.ToString(), dataValue.ServerTimestamp, dataValue.SourceTimestamp); } } }
public AggregateTestResult() : base() { TestDataName = String.Empty; Details = new ReadProcessedDetails(); }
static void HistoryReadProcessed(Session session) { // translate browse paths. IList <NodeOfInterest> nodeIds = GetNodeIds(session, Opc.Ua.Objects.ObjectsFolder, VariableBrowsePaths.ToArray()); DiagnosticInfoCollection diagnosticInfos; NodeId aggregateNodeId = null; RequestHeader rh = null; ViewDescription vd = null; ReferenceDescriptionCollection references; byte[] cp; //Get the list of avalilable aggregate functions: session.Browse( rh, vd, Opc.Ua.ObjectIds.Server_ServerCapabilities_AggregateFunctions, 1000, BrowseDirection.Forward, ReferenceTypeIds.Aggregates, false, 0, out cp, out references); Console.WriteLine("{0} aggregates are detected:", references.Count); //Print the list of avalible aggregates: int i = 0; foreach (ReferenceDescription rd in references) { i++; Console.WriteLine("{0}. {1} {2}", i, rd.BrowseName, rd.NodeId.Identifier.ToString()); } //Select aggregate function: Console.WriteLine("\nEnter aggregate number: "); string str = Console.ReadLine(); i = System.Int16.Parse(str); if (i > 0 && i <= references.Count) { aggregateNodeId = ExpandedNodeId.ToNodeId(references[i - 1].NodeId, session.NamespaceUris); } //Prepare arguments to pass to read processed history ReadProcessedDetails readDetails = new ReadProcessedDetails(); readDetails.StartTime = new DateTime(2008, 1, 1, 12, 0, 0); readDetails.EndTime = new DateTime(2008, 1, 1, 12, 0, 12); readDetails.AggregateType = new NodeIdCollection(nodeIds.Count); for (int x = 0; x < nodeIds.Count; x++) { readDetails.AggregateType.Add(aggregateNodeId); } readDetails.ProcessingInterval = 500; //500 milliseconds ExtensionObject eo = new ExtensionObject(readDetails.TypeId, readDetails); HistoryReadValueIdCollection idCollection = new HistoryReadValueIdCollection(); for (int ii = 0; ii < nodeIds.Count; ii++) { HistoryReadValueId readValueId = new HistoryReadValueId(); readValueId.NodeId = nodeIds[ii].NodeId; readValueId.Processed = true; idCollection.Add(readValueId); } HistoryReadResultCollection historyReadResults; //Read processed history: ResponseHeader responseHeader = session.HistoryRead(null, eo, TimestampsToReturn.Both, true, idCollection, out historyReadResults, out diagnosticInfos); //Print results: for (int ii = 0; ii < historyReadResults.Count; ii++) { HistoryReadResult historyReadResult = historyReadResults[ii]; ServiceResult result = Session.GetResult(historyReadResult.StatusCode, ii, diagnosticInfos, responseHeader); HistoryData historyData = null; DataValueCollection dataValues = null; if (!(historyReadResult.HistoryData == null)) { historyData = ExtensionObject.ToEncodeable(historyReadResult.HistoryData) as HistoryData; if (historyData == null) { dataValues = null; } else { dataValues = historyData.DataValues; } } Console.WriteLine("\nHistoryRead result code for {0}: {1}", VariableBrowsePaths[ii], result.StatusCode.ToString()); if (dataValues == null) { Console.WriteLine("dataValues == null"); continue; } for (int jj = 0; jj < dataValues.Count; jj++) { DataValue dataValue = dataValues[jj]; if (dataValue == null) { continue; } // write value. Console.WriteLine("{0}: V={1}, Q={2}, SrvT={3}, SrcT={4}", jj, dataValue.Value == null ? "null" : dataValue.Value.ToString(), dataValue.StatusCode.ToString(), dataValue.ServerTimestamp, dataValue.SourceTimestamp); } } }
/// <summary> /// Reads the processed data. /// </summary> public List<HdaReadRequest> ReadProcessed( DateTime startTime, DateTime endTime, long resampleInterval, int[] serverHandles, uint[] aggregateIds) { Session session = ThrowIfNotConnected(); // create the read requests. ReadProcessedDetails details = new ReadProcessedDetails(); List<HdaReadRequest> requests = CreateReadRequests( session, startTime, endTime, resampleInterval, serverHandles, aggregateIds, details); ExtensionObject extension = new ExtensionObject(details); // fetch all of the values. if (ReadNext(session, extension, requests, false)) { ReadNext(session, extension, requests, true); } return requests; }
/// <summary> /// Creates the read requests. /// </summary> private List<HdaReadRequest> CreateReadRequests( Session session, DateTime startTime, DateTime endTime, double resampleInterval, int[] serverHandles, uint[] aggregateIds, ReadProcessedDetails details) { // start time or end time must be specified. if (startTime == DateTime.MinValue || endTime == DateTime.MinValue || startTime == endTime || resampleInterval < 0) { throw ComUtils.CreateComException(ResultIds.E_INVALIDARG); } // check the number of intervals. if (m_configuration.MaxReturnValues > 0 && resampleInterval != 0) { long range = Math.Abs(((endTime - startTime).Ticks)); if (range/(TimeSpan.TicksPerMillisecond*resampleInterval) > m_configuration.MaxReturnValues) { throw ComUtils.CreateComException(ResultIds.E_MAXEXCEEDED); } } details.StartTime = startTime; details.EndTime = endTime; details.ProcessingInterval = resampleInterval; // build the list of requests. List<HdaReadRequest> requests = new List<HdaReadRequest>(); for (int ii = 0; ii < serverHandles.Length; ii++) { HdaReadRequest request = new HdaReadRequest(); requests.Add(request); // initialize request. request.AggregateId = aggregateIds[ii]; // look up server handle. request.Handle = m_itemManager.LookupHandle(serverHandles[ii]); if (request.Handle == null) { request.Error = ResultIds.E_INVALIDHANDLE; continue; } // set node id to use. request.NodeId = request.Handle.NodeId; request.ClientHandle = request.Handle.ClientHandle; // check aggregate. NodeId aggregateId = ComUtils.GetHdaAggregateId(aggregateIds[ii]); if (aggregateId == null) { aggregateId = m_mapper.GetRemoteIntegerIdMapping(Opc.Ua.BrowseNames.AggregateFunctions, aggregateIds[ii]); } if (aggregateId == null) { request.Error = ResultIds.E_NOT_AVAIL; continue; } details.AggregateType.Add(aggregateId); } return requests; }
/// <summary> /// Reads the processed data. /// </summary> public int[] ReadProcessed( int transactionId, DateTime startTime, DateTime endTime, long resampleInterval, int[] serverHandles, uint[] aggregateIds, out int cancelId) { Session session = ThrowIfNotConnected(); // create the read requests. ReadProcessedDetails details = new ReadProcessedDetails(); List<HdaReadRequest> requests = CreateReadRequests( session, startTime, endTime, resampleInterval, serverHandles, aggregateIds, details); // queue the transaction. int[] errors = CreateTransaction( TransationType.Read, transactionId, new ExtensionObject(details), requests, false, out cancelId); // return the initial results. return errors; }
/// <summary> /// Reads the processed data for an item. /// </summary> protected override void HistoryReadProcessed( ServerSystemContext context, ReadProcessedDetails details, TimestampsToReturn timestampsToReturn, IList<HistoryReadValueId> nodesToRead, IList<HistoryReadResult> results, IList<ServiceResult> errors, List<NodeHandle> nodesToProcess, IDictionary<NodeId, NodeState> cache) { for (int ii = 0; ii < nodesToRead.Count; ii++) { NodeHandle handle = nodesToProcess[ii]; HistoryReadValueId nodeToRead = nodesToRead[handle.Index]; HistoryReadResult result = results[handle.Index]; HistoryReadRequest request = null; try { // validate node. NodeState source = ValidateNode(context, handle, cache); if (source == null) { continue; } // 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 { // validate aggregate type. if (details.AggregateType.Count <= ii || !Server.AggregateManager.IsSupported(details.AggregateType[ii])) { errors[handle.Index] = StatusCodes.BadAggregateNotSupported; continue; } request = CreateHistoryReadRequest( context, details, handle, nodeToRead, details.AggregateType[ii]); } // process values until the max is reached. HistoryData data = new HistoryData(); while (request.NumValuesPerNode == 0 || data.DataValues.Count < request.NumValuesPerNode) { if (request.Values.Count == 0) { break; } DataValue value = request.Values.First.Value; request.Values.RemoveFirst(); data.DataValues.Add(value); } errors[handle.Index] = ServiceResult.Good; // check if a continuation point is requred. if (request.Values.Count > 0) { result.ContinuationPoint = SaveContinuationPoint(context, request); } // check if no data returned. else { errors[handle.Index] = StatusCodes.GoodNoData; } // return the data. result.HistoryData = new ExtensionObject(data); } catch (Exception e) { errors[handle.Index] = ServiceResult.Create(e, StatusCodes.BadUnexpectedError, "Unexpected error processing request."); } } }