Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
assert(context instanceof SessionContext);
assert(callback instanceof Function);
const nodeId = nodeToRead.nodeId;
const indexRange = nodeToRead.indexRange;
const dataEncoding = nodeToRead.dataEncoding;
const continuationPoint = nodeToRead.continuationPoint;
timestampsToReturn = (_.isObject(timestampsToReturn)) ? timestampsToReturn : TimestampsToReturn.Neither;
const obj = this.__findObject(nodeId) as UAVariable;
if (!obj) {
// may be return BadNodeIdUnknown in dataValue instead ?
// Object Not Found
callback(null, new HistoryReadResult({ statusCode: StatusCodes.BadNodeIdUnknown }));
return;
} else {
if (!obj.historyRead) {
// note : Object and View may also support historyRead to provide Event historical data
// todo implement historyRead for Object and View
const msg = " this node doesn't provide historyRead! probably not a UAVariable\n "
+ obj.nodeId.toString() + " " + obj.browseName.toString() + "\n"
+ "with " + nodeToRead.toString() + "\n"
+ "HistoryReadDetails " + historyReadDetails.toString();
if (doDebug) {
console.log(chalk.cyan("ServerEngine#_historyReadSingleNode "),
chalk.white.bold(msg));
}
const err = new Error(msg);
});
return callback(null, result);
}
} else if (isMinDate(historyReadRawModifiedDetails.startTime!)) {
// start time is not specified
// end time is specified
maxNumberToExtract = historyReadRawModifiedDetails.numValuesPerNode;
isReversed = true;
reverseDataValue = false;
if (historyReadRawModifiedDetails.numValuesPerNode === 0) {
// when start time is not specified
// and end time is specified
// numValuesPerNode shall be greater than 0
const result = new HistoryReadResult({
statusCode: StatusCodes.BadHistoryOperationUnsupported // should be an error
});
return callback(null, result);
}
} else {
// start time is specified
// end time is specified
if (historyReadRawModifiedDetails.endTime!.getTime() < historyReadRawModifiedDetails.startTime!.getTime()) {
reverseDataValue = true;
const tmp = historyReadRawModifiedDetails.endTime;
historyReadRawModifiedDetails.endTime = historyReadRawModifiedDetails.startTime;
historyReadRawModifiedDetails.startTime = tmp;
}
}
node._historyReadRawAsync(
(err: Error | null, dataValues?: DataValue[]) => {
if (err || !dataValues) {
return callback(err);
}
// now make sure that only the requested number of value is returned
if (historyReadRawModifiedDetails.numValuesPerNode >= 1) {
if (dataValues.length === 0) {
const result1 = new HistoryReadResult({
historyData: new HistoryData({ dataValues: [] }),
statusCode: StatusCodes.GoodNoData
});
return callback(null, result1);
} else {
const remaining = dataValues;
dataValues = remaining.splice(0, historyReadRawModifiedDetails.numValuesPerNode);
if (remaining.length > 0 && !isMinDate(historyReadRawModifiedDetails.endTime)) {
continuationPoint = createContinuationPoint();
context.continuationPoints = context.continuationPoints || {};
context.continuationPoints[continuationPoint.toString("hex")] = {
dataValues: remaining
};
}
}
if (!cnt) {
// invalid continuation point
const result1 = new HistoryReadResult({
historyData: new HistoryData({ dataValues: [] }),
statusCode: StatusCodes.BadContinuationPointInvalid
});
return callback(null, result1);
}
const dataValues = cnt.dataValues.splice(0, historyReadRawModifiedDetails.numValuesPerNode);
if (cnt.dataValues.length > 0) {
//
} else {
context.continuationPoints[continuationPoint.toString("hex")] = null;
continuationPoint = null;
}
const result2 = new HistoryReadResult({
continuationPoint: continuationPoint || undefined,
historyData: new HistoryData({ dataValues }),
statusCode: StatusCodes.Good
});
return callback(null, result2);
}
// todo add special treatment for when startTime > endTime
// ( in this case series must be return in reverse order )
let maxNumberToExtract = 0;
let isReversed = false;
let reverseDataValue = false;
if (isMinDate(historyReadRawModifiedDetails.endTime!)) {
// end time is not specified
maxNumberToExtract = historyReadRawModifiedDetails.numValuesPerNode;
// values with the same timestamp should be from the most recent to oldest modification
// timestamp, if startTime is less than or equal to endTime. If endTime is less than startTime,
// then the order of the returned values will be from the oldest modification timestamp to the
// most recent. It is Server dependent whether multiple modifications are kept or only the most
// recent.
// A Server does not have to create a modification record for data when it is first added to the
// historical collection. If it does then it shall set the ExtraData bit and the Client can read the
// modification record using a ReadModified call. If the data is subsequently modified the Server
// shall create a second modification record which is returned along with the original
// modification record whenever a Client uses the ReadModified call if the Server supports
// multiple modification records per timestamp.
// If the requested TimestampsToReturn is not supported for a Node then the operation shall
// return the BadTimestampNotSupported StatusCode.
// todo : provide correct implementation
const result = new HistoryReadResult({
historyData: new HistoryData({ dataValues: [] }),
statusCode: StatusCodes.BadUnexpectedError
});
return callback(null, result);
}
// For cases where there are multiple values for a given timestamp, all but the most recent are
// considered to be Modified values and the Server shall return the most recent value. If the
// Server returns a value which hides other values at a timestamp then it shall set the ExtraData
// bit in the StatusCode associated with that value. If the Server contains additional information
// regarding a value then the ExtraData bit shall also be set. It indicates that ModifiedValues are
// available for retrieval, see 6.4.3.3.
//
// If the requested TimestampsToReturn is not supported for a Node, the operation shall return
// the Bad_TimestampNotSupported StatusCode.
if (continuationPoint) {
const cnt = context.continuationPoints
? context.continuationPoints[continuationPoint.toString("hex")] : null;
if (!cnt) {
// invalid continuation point
const result1 = new HistoryReadResult({
historyData: new HistoryData({ dataValues: [] }),
statusCode: StatusCodes.BadContinuationPointInvalid
});
return callback(null, result1);
}
const dataValues = cnt.dataValues.splice(0, historyReadRawModifiedDetails.numValuesPerNode);
if (cnt.dataValues.length > 0) {
//
} else {
context.continuationPoints[continuationPoint.toString("hex")] = null;
continuationPoint = null;
}
const result2 = new HistoryReadResult({
continuationPoint: continuationPoint || undefined,
historyData: new HistoryData({ dataValues }),
statusCode: StatusCodes.Good
(err: Error | null, result?: any) => {
if (err && !result) {
result = new HistoryReadResult({ statusCode: StatusCodes.BadInternalError });
}
historyData.push(result);
async.setImmediate(cbNode);
// it's not guaranteed that the historical read process is really asynchronous
});
}, (err?: Error | null) => {
// follow the same rules as the standard Interpolated Aggregate as outlined in Part 13.
// If the useSimpleBounds flag is True and Interpolation is required then simple bounding values
// will be used to calculate the data value. If useSimpleBounds is False and Interpolation is
// required then interpolated bounding values will be used to calculate the data value. See
// Part 13 for the definition of simple bounding values and interpolated bounding values.
// If a value is found for the specified timestamp, then the Server will set the StatusCode
// InfoBits to be Raw. If the value is Interpolated from the surrounding values, then the Server
// will set the StatusCode InfoBits to be Interpolated.
// If the read request is taking a long time to calculate then the Server may return zero results
// with a ContinuationPoint that allows the Server to resume the calculation on the next Client
// HistoryRead call.
// If the requested TimestampsToReturn is not supported for a Node, then the operation shall
// return the Bad_TimestampNotSupported StatusCode.
// todo provide correct implementation
const result = new HistoryReadResult({
historyData: new HistoryData({ dataValues: [] }),
statusCode: StatusCodes.BadHistoryOperationUnsupported
});
return callback(null, result);
} else {
const result = new HistoryReadResult({
historyData: new HistoryData({ dataValues: [] }),
statusCode: StatusCodes.BadHistoryOperationUnsupported
});
return callback(null, result);
}
}
// that falls directly on the timestamp ending the interval. Thus, each value shall be included
// only once in the calculation. If the time domain is in reverse order then we consider the later
// timestamp to be the one beginning the sub interval, and the earlier timestamp to be the one
// ending it. Note that this means that simply swapping the start and end times will not result in
// getting the same values back in reverse order as the intervals being requested in the two
// cases are not the same.
// If an Aggregate is taking a long time to calculate then the Server can return partial results
// with a continuation point. This might be done if the calculation is going to take more time th an
// the Client timeout hint. In some cases it may take longer than the Client timeout hint to
// calculate even one Aggregate result. Then the Server may return zero results with a
// continuation point that allows the Server to resume the calculation on the next Client read
// call.
// todo provide correct implementation
const result = new HistoryReadResult({
historyData: new HistoryData({ dataValues: [] }),
statusCode: StatusCodes.BadHistoryOperationUnsupported
});
return callback(null, result);
} else if (historyReadDetails instanceof ReadAtTimeDetails) {
// Release 1.03 28 OPC Unified Architecture, Part 11
// The ReadAtTimeDetails structure reads the values and qualities from the history database for
// the specified timestamps for one or more HistoricalDataNodes. This function is intended to
// provide values to correlate with other values with a known timestamp. For example, a Client
// may need to read the values of sensors when lab samples were collected.
// The order of the values and qualities returned shall match the order of the timestamps
// supplied in the request.
// When no value exists for a specified timestamp, a value shall be Interpolated from the
// surrounding values to represent the value at the specified timestamp. The interpolation will
// follow the same rules as the standard Interpolated Aggregate as outlined in Part 13.
// considered to be Modified values and the Server shall return the most recent value. If the
// Server returns a value which hides other values at a timestamp then it shall set the ExtraData
// bit in the StatusCode associated with that value. If the Server contains additional information
// regarding a value then the ExtraData bit shall also be set. It indicates that ModifiedValues are
// available for retrieval, see 6.4.3.3.
//
// If the requested TimestampsToReturn is not supported for a Node, the operation shall return
// the Bad_TimestampNotSupported StatusCode.
if (continuationPoint) {
const cnt = context.continuationPoints
? context.continuationPoints[continuationPoint.toString("hex")] : null;
if (!cnt) {
// invalid continuation point
const result1 = new HistoryReadResult({
historyData: new HistoryData({ dataValues: [] }),
statusCode: StatusCodes.BadContinuationPointInvalid
});
return callback(null, result1);
}
const dataValues = cnt.dataValues.splice(0, historyReadRawModifiedDetails.numValuesPerNode);
if (cnt.dataValues.length > 0) {
//
} else {
context.continuationPoints[continuationPoint.toString("hex")] = null;
continuationPoint = null;
}
const result2 = new HistoryReadResult({
continuationPoint: continuationPoint || undefined,
historyData: new HistoryData({ dataValues }),
statusCode: StatusCodes.Good
});