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);
}
}
});
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
};
}
}
}
const result = new HistoryReadResult({
continuationPoint: continuationPoint || undefined,
historyData: new HistoryData({ dataValues }),
statusCode: StatusCodes.Good
});
callback(null, result);
});