node-opcua-aggregates 2.53.0 → 2.56.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mocharc.yml +7 -7
- package/LICENSE +20 -20
- package/bin/sample_aggregate_server.js +14 -14
- package/dist/aggregates.js +2 -2
- package/dist/aggregates.js.map +1 -1
- package/dist/average.js +6 -5
- package/dist/average.js.map +1 -1
- package/dist/common.js +12 -2
- package/dist/common.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interpolate.d.ts +1 -1
- package/dist/interpolate.js +8 -12
- package/dist/interpolate.js.map +1 -1
- package/dist/minmax.js +3 -2
- package/dist/minmax.js.map +1 -1
- package/dist/read_processed_details.d.ts +2 -2
- package/dist/read_processed_details.js +41 -38
- package/dist/read_processed_details.js.map +1 -1
- package/nyc.config.js +16 -16
- package/package.json +17 -15
- package/source/aggregates.ts +284 -277
- package/source/average.ts +9 -12
- package/source/common.ts +23 -10
- package/source/index.ts +11 -17
- package/source/interpolate.ts +14 -20
- package/source/interval.ts +3 -3
- package/source/minmax.ts +10 -10
- package/source/read_processed_details.ts +149 -139
package/source/common.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module node-opca-aggregates
|
|
3
3
|
*/
|
|
4
|
-
import { SessionContext, UAVariable } from "node-opcua-address-space";
|
|
4
|
+
import { SessionContext, UAVariable, ContinuationPointManager, ContinuationPoint } from "node-opcua-address-space";
|
|
5
5
|
import { NodeClass } from "node-opcua-data-model";
|
|
6
6
|
import { DataValue } from "node-opcua-data-value";
|
|
7
7
|
import { HistoryData, HistoryReadResult, ReadRawModifiedDetails } from "node-opcua-service-history";
|
|
8
8
|
import { StatusCode } from "node-opcua-status-code";
|
|
9
|
+
import { coerceNodeId } from "node-opcua-nodeid";
|
|
9
10
|
|
|
10
11
|
import { getAggregateConfiguration } from "./aggregates";
|
|
11
12
|
import { getInterval, Interval, AggregateConfigurationOptionsEx } from "./interval";
|
|
@@ -56,7 +57,6 @@ function processAggregateData(
|
|
|
56
57
|
setImmediate(() => {
|
|
57
58
|
callback(null, results);
|
|
58
59
|
});
|
|
59
|
-
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
export function getAggregateData(
|
|
@@ -66,8 +66,7 @@ export function getAggregateData(
|
|
|
66
66
|
endDate: Date,
|
|
67
67
|
lambda: (interval: Interval, aggregateConfiguration: AggregateConfigurationOptionsEx) => DataValue,
|
|
68
68
|
callback: (err: Error | null, dataValues?: DataValue[]) => void
|
|
69
|
-
) {
|
|
70
|
-
|
|
69
|
+
): void {
|
|
71
70
|
/* istanbul ignore next */
|
|
72
71
|
if (node.nodeClass !== NodeClass.Variable) {
|
|
73
72
|
throw new Error("node must be UAVariable");
|
|
@@ -78,17 +77,30 @@ export function getAggregateData(
|
|
|
78
77
|
throw new Error("Invalid processing interval, shall be greater than 0");
|
|
79
78
|
}
|
|
80
79
|
|
|
81
|
-
const
|
|
80
|
+
const continuationPointManager = new ContinuationPointManager();
|
|
81
|
+
const context = new SessionContext({
|
|
82
|
+
session: {
|
|
83
|
+
continuationPointManager,
|
|
84
|
+
getSessionId: () => coerceNodeId("i=0")
|
|
85
|
+
}
|
|
86
|
+
});
|
|
82
87
|
const historyReadDetails = new ReadRawModifiedDetails({
|
|
83
88
|
endTime: endDate,
|
|
84
89
|
startTime: startDate,
|
|
90
|
+
isReadModified: false,
|
|
91
|
+
numValuesPerNode: 0
|
|
92
|
+
// returnBounds: true,
|
|
85
93
|
});
|
|
86
94
|
const indexRange = null;
|
|
87
95
|
const dataEncoding = null;
|
|
88
|
-
const continuationPoint = null;
|
|
89
|
-
node.historyRead(
|
|
96
|
+
const continuationPoint: ContinuationPoint | null = null;
|
|
97
|
+
node.historyRead(
|
|
98
|
+
context,
|
|
99
|
+
historyReadDetails,
|
|
100
|
+
indexRange,
|
|
101
|
+
dataEncoding,
|
|
102
|
+
{ continuationPoint },
|
|
90
103
|
(err: Error | null, result?: HistoryReadResult) => {
|
|
91
|
-
|
|
92
104
|
/* istanbul ignore next */
|
|
93
105
|
if (err) {
|
|
94
106
|
return callback(err);
|
|
@@ -98,10 +110,11 @@ export function getAggregateData(
|
|
|
98
110
|
const dataValues = historyData.dataValues || [];
|
|
99
111
|
|
|
100
112
|
processAggregateData(node, processingInterval, startDate, endDate, dataValues, lambda, callback);
|
|
101
|
-
}
|
|
113
|
+
}
|
|
114
|
+
);
|
|
102
115
|
}
|
|
103
116
|
|
|
104
|
-
export function interpolateValue(dataValue1: DataValue, dataValue2: DataValue, date: Date) {
|
|
117
|
+
export function interpolateValue(dataValue1: DataValue, dataValue2: DataValue, date: Date): DataValue {
|
|
105
118
|
const t0 = dataValue1.sourceTimestamp!.getTime();
|
|
106
119
|
const t = date.getTime();
|
|
107
120
|
const t1 = dataValue2.sourceTimestamp!.getTime();
|
package/source/index.ts
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module node-opca-aggregates
|
|
3
|
-
*/
|
|
4
|
-
export {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export * from "./
|
|
10
|
-
export * from "./
|
|
11
|
-
export
|
|
12
|
-
export * from "./common";
|
|
13
|
-
export * from "./average";
|
|
14
|
-
export * from "./read_processed_details";
|
|
15
|
-
export {
|
|
16
|
-
AggregateFunction
|
|
17
|
-
} from "node-opcua-constants";
|
|
1
|
+
/**
|
|
2
|
+
* @module node-opca-aggregates
|
|
3
|
+
*/
|
|
4
|
+
export { addAggregateSupport, installAggregateConfigurationOptions, getAggregateConfiguration } from "./aggregates";
|
|
5
|
+
export * from "./interpolate";
|
|
6
|
+
export * from "./minmax";
|
|
7
|
+
export * from "./interval";
|
|
8
|
+
export * from "./common";
|
|
9
|
+
export * from "./average";
|
|
10
|
+
export * from "./read_processed_details";
|
|
11
|
+
export { AggregateFunction } from "node-opcua-constants";
|
package/source/interpolate.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @module node-
|
|
2
|
+
* @module node-opcua-aggregates
|
|
3
3
|
*/
|
|
4
4
|
// excerpt from OPC Unified Architecture, Part 13 21 Release 1.04
|
|
5
5
|
//
|
|
@@ -47,7 +47,8 @@ import {
|
|
|
47
47
|
_findGoodDataValueBefore,
|
|
48
48
|
adjustProcessingOptions,
|
|
49
49
|
AggregateConfigurationOptionsEx,
|
|
50
|
-
Interval,
|
|
50
|
+
Interval,
|
|
51
|
+
isBad,
|
|
51
52
|
isGood
|
|
52
53
|
} from "./interval";
|
|
53
54
|
|
|
@@ -62,11 +63,10 @@ import {
|
|
|
62
63
|
If for an interval neither ratio applies then that interval is Uncertain_DataSubNormal.
|
|
63
64
|
*/
|
|
64
65
|
export function interpolatedValue(interval: Interval, options: AggregateConfigurationOptionsEx): DataValue {
|
|
65
|
-
|
|
66
66
|
options = adjustProcessingOptions(options);
|
|
67
67
|
|
|
68
|
-
assert(Object.prototype.hasOwnProperty.call(options,"useSlopedExtrapolation"));
|
|
69
|
-
assert(Object.prototype.hasOwnProperty.call(options,"treatUncertainAsBad"));
|
|
68
|
+
assert(Object.prototype.hasOwnProperty.call(options, "useSlopedExtrapolation"));
|
|
69
|
+
assert(Object.prototype.hasOwnProperty.call(options, "treatUncertainAsBad"));
|
|
70
70
|
|
|
71
71
|
const bTreatUncertainAsBad = options.treatUncertainAsBad!;
|
|
72
72
|
|
|
@@ -77,10 +77,9 @@ export function interpolatedValue(interval: Interval, options: AggregateConfigur
|
|
|
77
77
|
const interpValue = new DataValue({
|
|
78
78
|
sourceTimestamp: interval.startTime,
|
|
79
79
|
statusCode: StatusCodes.Bad,
|
|
80
|
-
value: previousDataValue.value
|
|
80
|
+
value: previousDataValue.value
|
|
81
81
|
});
|
|
82
|
-
interpValue.statusCode =
|
|
83
|
-
StatusCode.makeStatusCode(StatusCodes.UncertainDataSubNormal, "HistorianInterpolated");
|
|
82
|
+
interpValue.statusCode = StatusCode.makeStatusCode(StatusCodes.UncertainDataSubNormal, "HistorianInterpolated");
|
|
84
83
|
return interpValue;
|
|
85
84
|
};
|
|
86
85
|
|
|
@@ -93,7 +92,7 @@ export function interpolatedValue(interval: Interval, options: AggregateConfigur
|
|
|
93
92
|
return new DataValue({
|
|
94
93
|
sourceTimestamp: interval.startTime,
|
|
95
94
|
statusCode: StatusCodes.BadNoData,
|
|
96
|
-
value: undefined
|
|
95
|
+
value: undefined
|
|
97
96
|
});
|
|
98
97
|
}
|
|
99
98
|
if (!options.useSlopedExtrapolation) {
|
|
@@ -111,13 +110,12 @@ export function interpolatedValue(interval: Interval, options: AggregateConfigur
|
|
|
111
110
|
// tslint:disable:no-bitwise
|
|
112
111
|
if (prev2.index + 1 < prev1.index || prev1.index < interval.dataValues.length - 1) {
|
|
113
112
|
// some bad data exist in between = change status code
|
|
114
|
-
const mask =
|
|
113
|
+
const mask = 0x0000ffffff;
|
|
115
114
|
const extraBits = interpVal.statusCode.value & mask;
|
|
116
115
|
interpVal.statusCode = StatusCode.makeStatusCode(StatusCodes.UncertainDataSubNormal, extraBits);
|
|
117
116
|
}
|
|
118
117
|
|
|
119
118
|
return interpVal;
|
|
120
|
-
|
|
121
119
|
}
|
|
122
120
|
|
|
123
121
|
/* istanbul ignore next */
|
|
@@ -166,16 +164,12 @@ export function interpolatedValue(interval: Interval, options: AggregateConfigur
|
|
|
166
164
|
// x is a value at ‘x’ and Tx is the timestamp associated with Vx.
|
|
167
165
|
const interpolatedDataValue = interpolateValue(before.dataValue, next.dataValue, interval.startTime);
|
|
168
166
|
|
|
169
|
-
if (before.index + 1 < next.index
|
|
170
|
-
|| !isGood(next.dataValue.statusCode)
|
|
171
|
-
|| !isGood(before.dataValue.statusCode)
|
|
172
|
-
) {
|
|
167
|
+
if (before.index + 1 < next.index || !isGood(next.dataValue.statusCode) || !isGood(before.dataValue.statusCode)) {
|
|
173
168
|
// tslint:disable:no-bitwise
|
|
174
169
|
// some bad data exist in between = change status code
|
|
175
|
-
const mask =
|
|
170
|
+
const mask = 0x0000ffffff;
|
|
176
171
|
const extraBits = interpolatedDataValue.statusCode.value & mask;
|
|
177
|
-
interpolatedDataValue.statusCode =
|
|
178
|
-
StatusCode.makeStatusCode(StatusCodes.UncertainDataSubNormal, extraBits);
|
|
172
|
+
interpolatedDataValue.statusCode = StatusCode.makeStatusCode(StatusCodes.UncertainDataSubNormal, extraBits);
|
|
179
173
|
}
|
|
180
174
|
// check if uncertain or bad value exist between before/next
|
|
181
175
|
// todo
|
|
@@ -196,6 +190,6 @@ export function getInterpolatedData(
|
|
|
196
190
|
startDate: Date,
|
|
197
191
|
endDate: Date,
|
|
198
192
|
callback: (err: Error | null, dataValues?: DataValue[]) => void
|
|
199
|
-
) {
|
|
200
|
-
|
|
193
|
+
): void {
|
|
194
|
+
getAggregateData(node, processingInterval, startDate, endDate, interpolatedValue, callback);
|
|
201
195
|
}
|
package/source/interval.ts
CHANGED
|
@@ -117,7 +117,7 @@ export class Interval {
|
|
|
117
117
|
/**
|
|
118
118
|
* returns true if a raw data exists at start
|
|
119
119
|
*/
|
|
120
|
-
public hasRawDataAsStart() {
|
|
120
|
+
public hasRawDataAsStart(): boolean {
|
|
121
121
|
const index = this.index;
|
|
122
122
|
if (index < 0) {
|
|
123
123
|
return false;
|
|
@@ -131,11 +131,11 @@ export class Interval {
|
|
|
131
131
|
* just preceding this interval
|
|
132
132
|
* @returns {*}
|
|
133
133
|
*/
|
|
134
|
-
public beforeStartDataValue(bTreatUncertainAsBad: boolean) {
|
|
134
|
+
public beforeStartDataValue(bTreatUncertainAsBad: boolean): DataValueWithIndex {
|
|
135
135
|
return _findGoodDataValueBefore(this.dataValues, this.index, bTreatUncertainAsBad);
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
public nextStartDataValue(bTreatUncertainAsBad: boolean) {
|
|
138
|
+
public nextStartDataValue(bTreatUncertainAsBad: boolean): DataValueWithIndex {
|
|
139
139
|
return _findGoodDataValueAfter(this.dataValues, this.index, bTreatUncertainAsBad);
|
|
140
140
|
}
|
|
141
141
|
|
package/source/minmax.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module node-opca-aggregates
|
|
3
3
|
*/
|
|
4
|
-
//
|
|
4
|
+
// excerpt from OPC Unified Architecture, Part 13 21 Release 1.04
|
|
5
5
|
// 5.4.3.10 Minimum
|
|
6
6
|
// The Minimum Aggregate defined in Table 21 retrieves the minimum Good raw value within the
|
|
7
7
|
// interval, and returns that value with the timestamp at the start of the interval. Note that if the
|
|
@@ -60,12 +60,12 @@ import { Variant } from "node-opcua-variant";
|
|
|
60
60
|
import { getAggregateData } from "./common";
|
|
61
61
|
import { AggregateConfigurationOptions, Interval, isGood } from "./interval";
|
|
62
62
|
|
|
63
|
+
// eslint-disable-next-line max-statements
|
|
63
64
|
function calculateIntervalMinOrMaxValue(
|
|
64
65
|
interval: Interval,
|
|
65
66
|
options: AggregateConfigurationOptions,
|
|
66
67
|
predicate: (a: Variant, b: Variant) => "equal" | "select" | "reject"
|
|
67
68
|
): DataValue {
|
|
68
|
-
|
|
69
69
|
// console.log(interval.toString());
|
|
70
70
|
|
|
71
71
|
const indexStart = interval.index;
|
|
@@ -143,15 +143,15 @@ function calculateIntervalMinOrMaxValue(
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
export function calculateIntervalMinValue(interval: Interval, options: AggregateConfigurationOptions): DataValue {
|
|
146
|
-
return calculateIntervalMinOrMaxValue(interval, options,
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
return calculateIntervalMinOrMaxValue(interval, options, (a: Variant, b: Variant) =>
|
|
147
|
+
a.value > b.value ? "select" : a.value === b.value ? "equal" : "reject"
|
|
148
|
+
);
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
export function calculateIntervalMaxValue(interval: Interval, options: AggregateConfigurationOptions): DataValue {
|
|
152
|
-
return calculateIntervalMinOrMaxValue(interval, options,
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
return calculateIntervalMinOrMaxValue(interval, options, (a: Variant, b: Variant) =>
|
|
153
|
+
a.value < b.value ? "select" : a.value === b.value ? "equal" : "reject"
|
|
154
|
+
);
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
// From OPC Unified Architecture, Part 13 26 Release 1.04
|
|
@@ -216,7 +216,7 @@ export function getMinData(
|
|
|
216
216
|
startDate: Date,
|
|
217
217
|
endDate: Date,
|
|
218
218
|
callback: (err: Error | null, dataValues?: DataValue[]) => void
|
|
219
|
-
) {
|
|
219
|
+
): void {
|
|
220
220
|
return getAggregateData(node, processingInterval, startDate, endDate, calculateIntervalMinValue, callback);
|
|
221
221
|
}
|
|
222
222
|
|
|
@@ -226,6 +226,6 @@ export function getMaxData(
|
|
|
226
226
|
startDate: Date,
|
|
227
227
|
endDate: Date,
|
|
228
228
|
callback: (err: Error | null, dataValues?: DataValue[]) => void
|
|
229
|
-
) {
|
|
229
|
+
): void {
|
|
230
230
|
return getAggregateData(node, processingInterval, startDate, endDate, calculateIntervalMaxValue, callback);
|
|
231
231
|
}
|
|
@@ -1,139 +1,149 @@
|
|
|
1
|
-
import * as async from "async";
|
|
2
|
-
import { AggregateFunction } from "node-opcua-constants";
|
|
3
|
-
import { ISessionContext, ContinuationPoint, UAVariable } from "node-opcua-address-space";
|
|
4
|
-
import { NumericRange } from "node-opcua-numeric-range";
|
|
5
|
-
import { QualifiedNameLike } from "node-opcua-data-model";
|
|
6
|
-
import { CallbackT, StatusCodes } from "node-opcua-status-code";
|
|
7
|
-
import { DataValue } from "node-opcua-data-value";
|
|
8
|
-
import { ObjectIds } from "node-opcua-constants";
|
|
9
|
-
import { NodeId } from "node-opcua-nodeid";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
1
|
+
import * as async from "async";
|
|
2
|
+
import { AggregateFunction } from "node-opcua-constants";
|
|
3
|
+
import { ISessionContext, ContinuationPoint, UAVariable, ContinuationData } from "node-opcua-address-space";
|
|
4
|
+
import { NumericRange } from "node-opcua-numeric-range";
|
|
5
|
+
import { QualifiedNameLike } from "node-opcua-data-model";
|
|
6
|
+
import { CallbackT, StatusCodes } from "node-opcua-status-code";
|
|
7
|
+
import { DataValue } from "node-opcua-data-value";
|
|
8
|
+
import { ObjectIds } from "node-opcua-constants";
|
|
9
|
+
import { NodeId } from "node-opcua-nodeid";
|
|
10
|
+
import {
|
|
11
|
+
HistoryData,
|
|
12
|
+
HistoryReadResult,
|
|
13
|
+
ReadAtTimeDetails,
|
|
14
|
+
ReadEventDetails,
|
|
15
|
+
ReadProcessedDetails,
|
|
16
|
+
ReadRawModifiedDetails
|
|
17
|
+
} from "node-opcua-service-history";
|
|
18
|
+
|
|
19
|
+
import { getMinData, getMaxData } from "./minmax";
|
|
20
|
+
|
|
21
|
+
import { getInterpolatedData } from "./interpolate";
|
|
22
|
+
import { getAverageData } from "./average";
|
|
23
|
+
|
|
24
|
+
function _buildResult(err: Error | null, dataValues: DataValue[] | undefined, callback2: CallbackT<HistoryReadResult>) {
|
|
25
|
+
if (err) {
|
|
26
|
+
return callback2(null, new HistoryReadResult({ statusCode: StatusCodes.BadInternalError }));
|
|
27
|
+
}
|
|
28
|
+
const result = new HistoryReadResult({
|
|
29
|
+
historyData: new HistoryData({
|
|
30
|
+
dataValues
|
|
31
|
+
}),
|
|
32
|
+
statusCode: StatusCodes.Good
|
|
33
|
+
});
|
|
34
|
+
return callback2(null, result);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function applyAggregate(
|
|
38
|
+
variable: UAVariable,
|
|
39
|
+
processingInterval: number,
|
|
40
|
+
startTime: Date,
|
|
41
|
+
endTime: Date,
|
|
42
|
+
aggregateType: NodeId,
|
|
43
|
+
callback2: CallbackT<HistoryReadResult>
|
|
44
|
+
) {
|
|
45
|
+
if (!startTime || !endTime) {
|
|
46
|
+
return _buildResult(new Error("Invalid date time"), undefined, callback2);
|
|
47
|
+
}
|
|
48
|
+
const buildResult = (err: Error | null, dataValues: DataValue[] | undefined) => {
|
|
49
|
+
_buildResult(err, dataValues, callback2);
|
|
50
|
+
};
|
|
51
|
+
switch (aggregateType.value) {
|
|
52
|
+
case AggregateFunction.Minimum:
|
|
53
|
+
getMinData(variable, processingInterval, startTime, endTime, buildResult);
|
|
54
|
+
break;
|
|
55
|
+
case AggregateFunction.Maximum:
|
|
56
|
+
getMaxData(variable, processingInterval, startTime, endTime, buildResult);
|
|
57
|
+
break;
|
|
58
|
+
case AggregateFunction.Interpolative:
|
|
59
|
+
getInterpolatedData(variable, processingInterval, startTime, endTime, buildResult);
|
|
60
|
+
break;
|
|
61
|
+
case AggregateFunction.Average:
|
|
62
|
+
getAverageData(variable, processingInterval, startTime, endTime, buildResult);
|
|
63
|
+
break;
|
|
64
|
+
case AggregateFunction.Count:
|
|
65
|
+
default:
|
|
66
|
+
// todo provide correct implementation
|
|
67
|
+
return callback2(null, new HistoryReadResult({ statusCode: StatusCodes.BadAggregateNotSupported }));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export function readProcessedDetails(
|
|
71
|
+
variable: UAVariable,
|
|
72
|
+
context: ISessionContext,
|
|
73
|
+
historyReadDetails: ReadProcessedDetails,
|
|
74
|
+
indexRange: NumericRange | null,
|
|
75
|
+
dataEncoding: QualifiedNameLike | null,
|
|
76
|
+
continuationData: ContinuationData,
|
|
77
|
+
callback: CallbackT<HistoryReadResult>
|
|
78
|
+
): void {
|
|
79
|
+
// OPC Unified Architecture, Part 11 27 Release 1.03
|
|
80
|
+
//
|
|
81
|
+
// This structure is used to compute Aggregate values, qualities, and timestamps from data in
|
|
82
|
+
// the history database for the specified time domain for one or more HistoricalDataNodes. The
|
|
83
|
+
// time domain is divided into intervals of duration ProcessingInterval. The specified Aggregate
|
|
84
|
+
// Type is calculated for each interval beginning with startTime by using the data within the next
|
|
85
|
+
// ProcessingInterval.
|
|
86
|
+
// For example, this function can provide hourly statistics such as Maximum, Minimum , and
|
|
87
|
+
// Average for each item during the specified time domain when ProcessingInterval is 1 hour.
|
|
88
|
+
// The domain of the request is defined by startTime, endTime, and ProcessingInterval. All three
|
|
89
|
+
// shall be specified. If endTime is less than startTime then the data shall be returned in reverse
|
|
90
|
+
// order with the later data coming first. If startTime and endTime are the same then the Server
|
|
91
|
+
// shall return Bad_InvalidArgument as there is no meaningful way to interpret such a case. If
|
|
92
|
+
// the ProcessingInterval is specified as 0 then Aggregates shall be calculated using one interval
|
|
93
|
+
// starting at startTime and ending at endTime.
|
|
94
|
+
// The aggregateType[] parameter allows a Client to request multiple Aggregate calculations per
|
|
95
|
+
// requested NodeId. If multiple Aggregates are requested then a corresponding number of
|
|
96
|
+
// entries are required in the NodesToRead array.
|
|
97
|
+
// For example, to request Min Aggregate for NodeId FIC101, FIC102, and both Min and Max
|
|
98
|
+
// Aggregates for NodeId FIC103 would require NodeId FIC103 to appear twice in the
|
|
99
|
+
// NodesToRead array request parameter.
|
|
100
|
+
// aggregateType[] NodesToRead[]
|
|
101
|
+
// Min FIC101
|
|
102
|
+
// Min FIC102
|
|
103
|
+
// Min FIC103
|
|
104
|
+
// Max FIC103
|
|
105
|
+
// If the array of Aggregates does not match the array of NodesToRead then the Server shall
|
|
106
|
+
// return a StatusCode of Bad_AggregateListMismatch.
|
|
107
|
+
// The aggregateConfiguration parameter allows a Client to override the Aggregate configuration
|
|
108
|
+
// settings supplied by the AggregateConfiguration Object on a per call basis. See Part 13 for
|
|
109
|
+
// more information on Aggregate configurations. If the Server does not support the ability to
|
|
110
|
+
// override the Aggregate configuration settings then it shall return a StatusCode of Bad_
|
|
111
|
+
// AggregateConfigurationRejected. If the Aggregate is not valid for the Node then the
|
|
112
|
+
// StatusCode shall be Bad_AggregateNotSupported.
|
|
113
|
+
// The values used in computing the Aggregate for each interval shall include any value that
|
|
114
|
+
// falls exactly on the timestamp at the beginning of the interval, but shall not include any value
|
|
115
|
+
// that falls directly on the timestamp ending the interval. Thus, each value shall be included
|
|
116
|
+
// only once in the calculation. If the time domain is in reverse order then we consider the later
|
|
117
|
+
// timestamp to be the one beginning the sub interval, and the earlier timestamp to be the one
|
|
118
|
+
// ending it. Note that this means that simply swapping the start and end times will not result in
|
|
119
|
+
// getting the same values back in reverse order as the intervals being requested in the two
|
|
120
|
+
// cases are not the same.
|
|
121
|
+
// If an Aggregate is taking a long time to calculate then the Server can return partial results
|
|
122
|
+
// with a continuation point. This might be done if the calculation is going to take more time th an
|
|
123
|
+
// the Client timeout hint. In some cases it may take longer than the Client timeout hint to
|
|
124
|
+
// calculate even one Aggregate result. Then the Server may return zero results with a
|
|
125
|
+
// continuation point that allows the Server to resume the calculation on the next Client read
|
|
126
|
+
// call.
|
|
127
|
+
const startTime = historyReadDetails.startTime;
|
|
128
|
+
const endTime = historyReadDetails.endTime;
|
|
129
|
+
if (!startTime || !endTime) {
|
|
130
|
+
return callback(null, new HistoryReadResult({ statusCode: StatusCodes.BadInvalidArgument }));
|
|
131
|
+
}
|
|
132
|
+
if (startTime.getTime() === endTime.getTime()) {
|
|
133
|
+
// Start = End Int = Anything No intervals. Returns a Bad_InvalidArgument StatusCode,
|
|
134
|
+
// regardless of whether there is data at the specified time or not
|
|
135
|
+
return callback(null, new HistoryReadResult({ statusCode: StatusCodes.BadInvalidArgument }));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const aggregateTypes: NodeId[] = historyReadDetails.aggregateType || [];
|
|
139
|
+
|
|
140
|
+
// If the ProcessingInterval is specified as 0 then Aggregates shall be calculated using one interval
|
|
141
|
+
// starting at startTime and ending at endTime.
|
|
142
|
+
const processingInterval = historyReadDetails.processingInterval || endTime.getTime() - startTime.getTime();
|
|
143
|
+
|
|
144
|
+
// tslint:disable-next-line: prefer-for-of
|
|
145
|
+
if (historyReadDetails.aggregateType?.length !== 1) {
|
|
146
|
+
return callback(null, new HistoryReadResult({ statusCode: StatusCodes.BadInternalError }));
|
|
147
|
+
}
|
|
148
|
+
return applyAggregate(variable, processingInterval, startTime, endTime, aggregateTypes[0], callback);
|
|
149
|
+
}
|