node-opcua-service-subscription 2.54.0 → 2.55.0
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/package.json +23 -23
- package/source/deadband_checker.ts +163 -163
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-opcua-service-subscription",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.55.0",
|
|
4
4
|
"description": "pure nodejs OPCUA SDK - module -service-subscription",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -11,29 +11,29 @@
|
|
|
11
11
|
"deprected_generate": "node ./generate"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"node-opcua-assert": "2.
|
|
15
|
-
"node-opcua-basic-types": "2.
|
|
16
|
-
"node-opcua-binary-stream": "2.
|
|
17
|
-
"node-opcua-data-value": "2.
|
|
18
|
-
"node-opcua-extension-object": "2.
|
|
19
|
-
"node-opcua-factory": "2.
|
|
20
|
-
"node-opcua-nodeid": "2.
|
|
21
|
-
"node-opcua-service-filter": "2.
|
|
22
|
-
"node-opcua-service-read": "2.
|
|
23
|
-
"node-opcua-service-secure-channel": "2.
|
|
24
|
-
"node-opcua-types": "2.
|
|
25
|
-
"node-opcua-variant": "2.
|
|
14
|
+
"node-opcua-assert": "2.55.0",
|
|
15
|
+
"node-opcua-basic-types": "2.55.0",
|
|
16
|
+
"node-opcua-binary-stream": "2.55.0",
|
|
17
|
+
"node-opcua-data-value": "2.55.0",
|
|
18
|
+
"node-opcua-extension-object": "2.55.0",
|
|
19
|
+
"node-opcua-factory": "2.55.0",
|
|
20
|
+
"node-opcua-nodeid": "2.55.0",
|
|
21
|
+
"node-opcua-service-filter": "2.55.0",
|
|
22
|
+
"node-opcua-service-read": "2.55.0",
|
|
23
|
+
"node-opcua-service-secure-channel": "2.55.0",
|
|
24
|
+
"node-opcua-types": "2.55.0",
|
|
25
|
+
"node-opcua-variant": "2.55.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"node-opcua-buffer-utils": "2.
|
|
29
|
-
"node-opcua-data-model": "2.
|
|
30
|
-
"node-opcua-debug": "2.
|
|
31
|
-
"node-opcua-numeric-range": "2.
|
|
32
|
-
"node-opcua-packet-analyzer": "2.
|
|
33
|
-
"node-opcua-secure-channel": "2.
|
|
34
|
-
"node-opcua-service-history": "2.
|
|
35
|
-
"node-opcua-service-translate-browse-path": "2.
|
|
36
|
-
"node-opcua-status-code": "2.
|
|
28
|
+
"node-opcua-buffer-utils": "2.55.0",
|
|
29
|
+
"node-opcua-data-model": "2.55.0",
|
|
30
|
+
"node-opcua-debug": "2.55.0",
|
|
31
|
+
"node-opcua-numeric-range": "2.55.0",
|
|
32
|
+
"node-opcua-packet-analyzer": "2.55.0",
|
|
33
|
+
"node-opcua-secure-channel": "2.55.0",
|
|
34
|
+
"node-opcua-service-history": "2.55.0",
|
|
35
|
+
"node-opcua-service-translate-browse-path": "2.55.0",
|
|
36
|
+
"node-opcua-status-code": "2.55.0",
|
|
37
37
|
"node-opcua-variant": "2.49.0",
|
|
38
38
|
"should": "^13.2.3"
|
|
39
39
|
},
|
|
@@ -52,5 +52,5 @@
|
|
|
52
52
|
"internet of things"
|
|
53
53
|
],
|
|
54
54
|
"homepage": "http://node-opcua.github.io/",
|
|
55
|
-
"gitHead": "
|
|
55
|
+
"gitHead": "28fdbfe1c5306fd0e69758ccacfa2c6d1722115c"
|
|
56
56
|
}
|
|
@@ -1,163 +1,163 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module node-opcua-service-subscription
|
|
3
|
-
*/
|
|
4
|
-
import { assert } from "node-opcua-assert";
|
|
5
|
-
|
|
6
|
-
import { DataType, Variant, VariantArrayType } from "node-opcua-variant";
|
|
7
|
-
|
|
8
|
-
export enum DeadbandType {
|
|
9
|
-
None = 0x00,
|
|
10
|
-
Absolute = 0x01,
|
|
11
|
-
Percent = 0x02,
|
|
12
|
-
Invalid = 0x1000
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export type NumberType = number | number[];
|
|
16
|
-
|
|
17
|
-
export interface PseudoRange {
|
|
18
|
-
low: number;
|
|
19
|
-
high: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* @returns true if the difference between value1 and value2 is greater than absoluteDeadband
|
|
24
|
-
*/
|
|
25
|
-
function _isOutsideDeadbandScalar(value1: NumberType, value2: NumberType, dataType: DataType, absoluteDeadband: number): boolean {
|
|
26
|
-
let diff;
|
|
27
|
-
if (dataType === DataType.UInt64 || dataType === DataType.Int64) {
|
|
28
|
-
// istanbul ignore next
|
|
29
|
-
if (!(value1 instanceof Array && value2 instanceof Array)) {
|
|
30
|
-
throw new Error("Invalid");
|
|
31
|
-
}
|
|
32
|
-
const h1 = value1[0]; // high
|
|
33
|
-
const h2 = value2[0];
|
|
34
|
-
if (h1 !== h2) {
|
|
35
|
-
diff = (h1 - h2) * 4294967295;
|
|
36
|
-
if (Math.abs(diff) > absoluteDeadband) {
|
|
37
|
-
return true;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
diff = value1[1] - value2[1];
|
|
41
|
-
assert(typeof diff === "number" && isFinite(diff));
|
|
42
|
-
return Math.abs(diff) > absoluteDeadband;
|
|
43
|
-
}
|
|
44
|
-
// istanbul ignore next
|
|
45
|
-
if (!(typeof value1 === "number" && typeof value2 === "number")) {
|
|
46
|
-
throw new Error(
|
|
47
|
-
"Invalid value in _isOutsideDeadbandScalar > expecting number only but got " + typeof value1 + " " + typeof value2
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
diff = value2 - value1;
|
|
51
|
-
assert(typeof diff === "number" && isFinite(diff));
|
|
52
|
-
return Math.abs(diff) > absoluteDeadband;
|
|
53
|
-
}
|
|
54
|
-
function isOutsideDeadbandVariant(v1: Variant, v2: Variant, absoluteDeadBand: number): boolean {
|
|
55
|
-
assert(isFinite(absoluteDeadBand));
|
|
56
|
-
|
|
57
|
-
if (v1.arrayType === VariantArrayType.Array) {
|
|
58
|
-
if (v1.dataType !== v2.dataType) {
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
if (v1.value.length !== v2.value.length) {
|
|
62
|
-
return true;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const n = v1.value.length;
|
|
66
|
-
let i = 0;
|
|
67
|
-
for (i = 0; i < n; i++) {
|
|
68
|
-
if (_isOutsideDeadbandScalar(v1.value[i], v2.value[i], v1.dataType, absoluteDeadBand)) {
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return false;
|
|
73
|
-
} else {
|
|
74
|
-
assert(v1.arrayType === VariantArrayType.Scalar);
|
|
75
|
-
if(v1.dataType !== v2.dataType) {
|
|
76
|
-
return true;
|
|
77
|
-
}
|
|
78
|
-
return _isOutsideDeadbandScalar(v1.value, v2.value, v1.dataType, absoluteDeadBand);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
function isOnEdgeOfRangeScalar(currentValue: NumberType, newValue: NumberType, dataType: DataType, range: PseudoRange): boolean {
|
|
82
|
-
if (dataType === DataType.UInt64 || dataType === DataType.Int64) {
|
|
83
|
-
// istanbul ignore next
|
|
84
|
-
if (!(currentValue instanceof Array && newValue instanceof Array)) {
|
|
85
|
-
throw new Error("Invalid");
|
|
86
|
-
}
|
|
87
|
-
currentValue = currentValue[1];
|
|
88
|
-
newValue = newValue[1];
|
|
89
|
-
}
|
|
90
|
-
if (/*currentValue !== range.low && */ newValue <= range.low) {
|
|
91
|
-
return true;
|
|
92
|
-
}
|
|
93
|
-
if (/*currentValue !== range.high && */ newValue >= range.high) {
|
|
94
|
-
return true;
|
|
95
|
-
}
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function isOnEdgeOfRange(currentValue: Variant, newValue: Variant, range: PseudoRange): boolean {
|
|
100
|
-
if (currentValue.arrayType === VariantArrayType.Array) {
|
|
101
|
-
const n = currentValue.value.length;
|
|
102
|
-
let i = 0;
|
|
103
|
-
for (i = 0; i < n; i++) {
|
|
104
|
-
if (isOnEdgeOfRangeScalar(currentValue.value[i], newValue.value[i], newValue.dataType, range)) {
|
|
105
|
-
return true;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return false;
|
|
109
|
-
} else {
|
|
110
|
-
assert(currentValue.arrayType === VariantArrayType.Scalar);
|
|
111
|
-
assert(currentValue.dataType === newValue.dataType);
|
|
112
|
-
return isOnEdgeOfRangeScalar(currentValue.value, newValue.value, currentValue.dataType, range);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* @method isOutsideDeadbandNone
|
|
118
|
-
* @return true if the element is in deadBand
|
|
119
|
-
*/
|
|
120
|
-
export function isOutsideDeadbandNone(variant1: Variant, variant2: Variant): boolean {
|
|
121
|
-
// No Deadband calculation should be applied.
|
|
122
|
-
return variant1.value !== variant2.value;
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* @method isOutsideDeadbandAbsolute
|
|
126
|
-
* @return true if the element is in deadBand
|
|
127
|
-
*/
|
|
128
|
-
export function isOutsideDeadbandAbsolute(variant1: Variant, variant2: Variant, deadbandValue: number): boolean {
|
|
129
|
-
// No Deadband calculation should be applied.
|
|
130
|
-
return isOutsideDeadbandVariant(variant1, variant2, deadbandValue);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* @method isOutsideDeadband
|
|
135
|
-
* @return true if the element is outside deadBand
|
|
136
|
-
*/
|
|
137
|
-
export function isOutsideDeadbandPercent(variant1: Variant, variant2: Variant, deadbandValue: number, range: PseudoRange): boolean {
|
|
138
|
-
// The range of the deadbandValue is from 0.0 to 100.0 Percent.
|
|
139
|
-
assert(deadbandValue >= 0 && deadbandValue <= 100);
|
|
140
|
-
|
|
141
|
-
if (isOnEdgeOfRange(variant1, variant2, range)) {
|
|
142
|
-
return true;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// DeadbandType = PercentDeadband
|
|
146
|
-
// For this type of deadband the deadbandValue is defined as the percentage of the EURange. That is,
|
|
147
|
-
// it applies only to AnalogItems with an EURange Property that defines the typical value range for the
|
|
148
|
-
// item. This range shall be multiplied with the deadbandValue and then compared to the actual value change
|
|
149
|
-
// to determine the need for a data change notification. The following pseudo code shows how the deadband
|
|
150
|
-
// is calculated:
|
|
151
|
-
// DataChange if (absolute value of (last cached value - current value) >
|
|
152
|
-
// (deadbandValue/100.0) * ((high-low) of EURange)))
|
|
153
|
-
//
|
|
154
|
-
// Specifying a deadbandValue outside of this range will be rejected and reported with the
|
|
155
|
-
// StatusCode BadDeadbandFilterInvalid (see Table 27).
|
|
156
|
-
// If the Value of the MonitoredItem is an array, then the deadband calculation logic shall be applied to
|
|
157
|
-
// each element of the array. If an element that requires a DataChange is found, then no further
|
|
158
|
-
// deadband checking is necessary and the entire array shall be returned.
|
|
159
|
-
const valueRange = Math.abs(range.high - range.low);
|
|
160
|
-
assert(typeof valueRange === "number");
|
|
161
|
-
const value = (deadbandValue / 100) * valueRange;
|
|
162
|
-
return isOutsideDeadbandAbsolute(variant1, variant2, value);
|
|
163
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @module node-opcua-service-subscription
|
|
3
|
+
*/
|
|
4
|
+
import { assert } from "node-opcua-assert";
|
|
5
|
+
|
|
6
|
+
import { DataType, Variant, VariantArrayType } from "node-opcua-variant";
|
|
7
|
+
|
|
8
|
+
export enum DeadbandType {
|
|
9
|
+
None = 0x00,
|
|
10
|
+
Absolute = 0x01,
|
|
11
|
+
Percent = 0x02,
|
|
12
|
+
Invalid = 0x1000
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type NumberType = number | number[];
|
|
16
|
+
|
|
17
|
+
export interface PseudoRange {
|
|
18
|
+
low: number;
|
|
19
|
+
high: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @returns true if the difference between value1 and value2 is greater than absoluteDeadband
|
|
24
|
+
*/
|
|
25
|
+
function _isOutsideDeadbandScalar(value1: NumberType, value2: NumberType, dataType: DataType, absoluteDeadband: number): boolean {
|
|
26
|
+
let diff;
|
|
27
|
+
if (dataType === DataType.UInt64 || dataType === DataType.Int64) {
|
|
28
|
+
// istanbul ignore next
|
|
29
|
+
if (!(value1 instanceof Array && value2 instanceof Array)) {
|
|
30
|
+
throw new Error("Invalid");
|
|
31
|
+
}
|
|
32
|
+
const h1 = value1[0]; // high
|
|
33
|
+
const h2 = value2[0];
|
|
34
|
+
if (h1 !== h2) {
|
|
35
|
+
diff = (h1 - h2) * 4294967295;
|
|
36
|
+
if (Math.abs(diff) > absoluteDeadband) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
diff = value1[1] - value2[1];
|
|
41
|
+
assert(typeof diff === "number" && isFinite(diff));
|
|
42
|
+
return Math.abs(diff) > absoluteDeadband;
|
|
43
|
+
}
|
|
44
|
+
// istanbul ignore next
|
|
45
|
+
if (!(typeof value1 === "number" && typeof value2 === "number")) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
"Invalid value in _isOutsideDeadbandScalar > expecting number only but got " + typeof value1 + " " + typeof value2
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
diff = value2 - value1;
|
|
51
|
+
assert(typeof diff === "number" && isFinite(diff));
|
|
52
|
+
return Math.abs(diff) > absoluteDeadband;
|
|
53
|
+
}
|
|
54
|
+
function isOutsideDeadbandVariant(v1: Variant, v2: Variant, absoluteDeadBand: number): boolean {
|
|
55
|
+
assert(isFinite(absoluteDeadBand));
|
|
56
|
+
|
|
57
|
+
if (v1.arrayType === VariantArrayType.Array) {
|
|
58
|
+
if (v1.dataType !== v2.dataType) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
if (v1.value.length !== v2.value.length) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const n = v1.value.length;
|
|
66
|
+
let i = 0;
|
|
67
|
+
for (i = 0; i < n; i++) {
|
|
68
|
+
if (_isOutsideDeadbandScalar(v1.value[i], v2.value[i], v1.dataType, absoluteDeadBand)) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
} else {
|
|
74
|
+
assert(v1.arrayType === VariantArrayType.Scalar);
|
|
75
|
+
if(v1.dataType !== v2.dataType) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
return _isOutsideDeadbandScalar(v1.value, v2.value, v1.dataType, absoluteDeadBand);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function isOnEdgeOfRangeScalar(currentValue: NumberType, newValue: NumberType, dataType: DataType, range: PseudoRange): boolean {
|
|
82
|
+
if (dataType === DataType.UInt64 || dataType === DataType.Int64) {
|
|
83
|
+
// istanbul ignore next
|
|
84
|
+
if (!(currentValue instanceof Array && newValue instanceof Array)) {
|
|
85
|
+
throw new Error("Invalid");
|
|
86
|
+
}
|
|
87
|
+
currentValue = currentValue[1];
|
|
88
|
+
newValue = newValue[1];
|
|
89
|
+
}
|
|
90
|
+
if (/*currentValue !== range.low && */ newValue <= range.low) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
if (/*currentValue !== range.high && */ newValue >= range.high) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function isOnEdgeOfRange(currentValue: Variant, newValue: Variant, range: PseudoRange): boolean {
|
|
100
|
+
if (currentValue.arrayType === VariantArrayType.Array) {
|
|
101
|
+
const n = currentValue.value.length;
|
|
102
|
+
let i = 0;
|
|
103
|
+
for (i = 0; i < n; i++) {
|
|
104
|
+
if (isOnEdgeOfRangeScalar(currentValue.value[i], newValue.value[i], newValue.dataType, range)) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
} else {
|
|
110
|
+
assert(currentValue.arrayType === VariantArrayType.Scalar);
|
|
111
|
+
assert(currentValue.dataType === newValue.dataType);
|
|
112
|
+
return isOnEdgeOfRangeScalar(currentValue.value, newValue.value, currentValue.dataType, range);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @method isOutsideDeadbandNone
|
|
118
|
+
* @return true if the element is in deadBand
|
|
119
|
+
*/
|
|
120
|
+
export function isOutsideDeadbandNone(variant1: Variant, variant2: Variant): boolean {
|
|
121
|
+
// No Deadband calculation should be applied.
|
|
122
|
+
return variant1.value !== variant2.value;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* @method isOutsideDeadbandAbsolute
|
|
126
|
+
* @return true if the element is in deadBand
|
|
127
|
+
*/
|
|
128
|
+
export function isOutsideDeadbandAbsolute(variant1: Variant, variant2: Variant, deadbandValue: number): boolean {
|
|
129
|
+
// No Deadband calculation should be applied.
|
|
130
|
+
return isOutsideDeadbandVariant(variant1, variant2, deadbandValue);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @method isOutsideDeadband
|
|
135
|
+
* @return true if the element is outside deadBand
|
|
136
|
+
*/
|
|
137
|
+
export function isOutsideDeadbandPercent(variant1: Variant, variant2: Variant, deadbandValue: number, range: PseudoRange): boolean {
|
|
138
|
+
// The range of the deadbandValue is from 0.0 to 100.0 Percent.
|
|
139
|
+
assert(deadbandValue >= 0 && deadbandValue <= 100);
|
|
140
|
+
|
|
141
|
+
if (isOnEdgeOfRange(variant1, variant2, range)) {
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// DeadbandType = PercentDeadband
|
|
146
|
+
// For this type of deadband the deadbandValue is defined as the percentage of the EURange. That is,
|
|
147
|
+
// it applies only to AnalogItems with an EURange Property that defines the typical value range for the
|
|
148
|
+
// item. This range shall be multiplied with the deadbandValue and then compared to the actual value change
|
|
149
|
+
// to determine the need for a data change notification. The following pseudo code shows how the deadband
|
|
150
|
+
// is calculated:
|
|
151
|
+
// DataChange if (absolute value of (last cached value - current value) >
|
|
152
|
+
// (deadbandValue/100.0) * ((high-low) of EURange)))
|
|
153
|
+
//
|
|
154
|
+
// Specifying a deadbandValue outside of this range will be rejected and reported with the
|
|
155
|
+
// StatusCode BadDeadbandFilterInvalid (see Table 27).
|
|
156
|
+
// If the Value of the MonitoredItem is an array, then the deadband calculation logic shall be applied to
|
|
157
|
+
// each element of the array. If an element that requires a DataChange is found, then no further
|
|
158
|
+
// deadband checking is necessary and the entire array shall be returned.
|
|
159
|
+
const valueRange = Math.abs(range.high - range.low);
|
|
160
|
+
assert(typeof valueRange === "number");
|
|
161
|
+
const value = (deadbandValue / 100) * valueRange;
|
|
162
|
+
return isOutsideDeadbandAbsolute(variant1, variant2, value);
|
|
163
|
+
}
|