@machinemetrics/io-adapter-lib 2.32.0 → 2.32.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/CHANGELOG.md +7 -0
- package/lib/config/adapterConfig.js +4 -1
- package/lib/config/device/opcuaConfig.js +53 -6
- package/package.json +1 -1
- package/test/.eslintrc.json +2 -1
- package/test/conditions.test.js +31 -0
- package/test/config/opcua.test.js +19 -1
- package/test/configFiles/conditions.yml +10 -0
- package/test/configFiles/device/opcua-conditions.yml +7 -0
- package/test/configFiles/device/opcua-std.yml +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.32.2]
|
|
4
|
+
- Add support to reclassify conditions to NORMAL
|
|
5
|
+
|
|
6
|
+
## [2.32.1]
|
|
7
|
+
- Added opcua-conditions block to opcua config
|
|
8
|
+
- Fixed message overrides in conditions
|
|
9
|
+
|
|
3
10
|
## [2.32.0]
|
|
4
11
|
- Repackaged for public NPM publishing on @machinemetrics/io-adapter-lib
|
|
5
12
|
|
|
@@ -350,6 +350,9 @@ class AdapterConfig {
|
|
|
350
350
|
if (item['reclassify-fault']) {
|
|
351
351
|
source.reclassifyFault = allowDenyList.parseWildcardKeyList(item['reclassify-fault'], `conditions.${key}.reclassify-fault-codes`);
|
|
352
352
|
}
|
|
353
|
+
if (item['reclassify-normal']) {
|
|
354
|
+
source.reclassifyNormal = allowDenyList.parseWildcardKeyList(item['reclassify-normal'], `conditions.${key}.reclassify-normal-codes`);
|
|
355
|
+
}
|
|
353
356
|
|
|
354
357
|
if (item.overrides) {
|
|
355
358
|
if (!_.isArray(item.overrides)) {
|
|
@@ -379,7 +382,7 @@ class AdapterConfig {
|
|
|
379
382
|
defn.message = override['override-message'];
|
|
380
383
|
defn.messagePath = `conditions.${key}.overrides.${index}.override-message`;
|
|
381
384
|
defn.messageIsStatic = this.expressionService.isStaticExpression(defn.messagePath);
|
|
382
|
-
defn.messageResolver = this.expressionResolver(defn.
|
|
385
|
+
defn.messageResolver = this.expressionResolver(defn.message, defn.messagePath);
|
|
383
386
|
defn.messageResolverTriggers = this.expressionService.expressionTriggers(defn.messagePath);
|
|
384
387
|
this.conditionsByExpression[defn.messagePath] = key;
|
|
385
388
|
}
|
|
@@ -19,13 +19,11 @@ class OpcuaConfig {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
getIdentifiers(config = {}) {
|
|
22
|
-
|
|
23
|
-
// No tags, this should be a softer warning
|
|
24
|
-
return [];
|
|
25
|
-
}
|
|
22
|
+
const keyset = Object.keys(config.tags || {});
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
if (config['opcua-conditions']?.key) {
|
|
25
|
+
keyset.push(config['opcua-conditions'].key);
|
|
26
|
+
} else if (config['opcua-condition-key']) {
|
|
29
27
|
keyset.push(config['opcua-condition-key']);
|
|
30
28
|
}
|
|
31
29
|
|
|
@@ -55,7 +53,17 @@ class OpcuaConfig {
|
|
|
55
53
|
this.userPrivateKeyPath = config['user-private-key-path'];
|
|
56
54
|
|
|
57
55
|
this.tags = this.loadTags(config.tags);
|
|
56
|
+
|
|
57
|
+
// opcua-condition-key deprecated in favor of opcua-conditions
|
|
58
58
|
this.conditionKey = config['opcua-condition-key'];
|
|
59
|
+
this.opcuaConditions = this.loadConditions(config['opcua-conditions']);
|
|
60
|
+
|
|
61
|
+
// For backwards compatibility
|
|
62
|
+
if (this.conditionKey && !this.opcuaConditions?.key) {
|
|
63
|
+
this.opcuaConditions = { key: this.conditionKey };
|
|
64
|
+
} else if (this.opcuaConditions?.key) {
|
|
65
|
+
this.conditionKey = this.opcuaConditions.key;
|
|
66
|
+
}
|
|
59
67
|
|
|
60
68
|
this.securityMode = this.checkSecurityMode(config['security-mode']);
|
|
61
69
|
this.securityPolicy = this.checkSecurityPolicy(config['security-policy']);
|
|
@@ -106,6 +114,45 @@ class OpcuaConfig {
|
|
|
106
114
|
}).keyBy('name').value();
|
|
107
115
|
}
|
|
108
116
|
|
|
117
|
+
loadConditions(conditions) {
|
|
118
|
+
if (!conditions) {
|
|
119
|
+
return {};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
if (!conditions.key) {
|
|
124
|
+
throw new ConfigError('key is required').atAttribute('key');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const result = {
|
|
128
|
+
key: conditions.key,
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
if (_.has(conditions, 'fault-threshold')) {
|
|
132
|
+
const faultThreshold = conditions['fault-threshold'];
|
|
133
|
+
if (!_.isNumber(faultThreshold)) {
|
|
134
|
+
throw new ConfigError('fault-threshold must be a number').atAttribute('fault-threshold');
|
|
135
|
+
}
|
|
136
|
+
result.faultThreshold = faultThreshold;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (_.has(conditions, 'warning-threshold')) {
|
|
140
|
+
const warningThreshold = conditions['warning-threshold'];
|
|
141
|
+
if (!_.isNumber(warningThreshold)) {
|
|
142
|
+
throw new ConfigError('warning-threshold must be a number').atAttribute('warning-threshold');
|
|
143
|
+
}
|
|
144
|
+
result.warningThreshold = warningThreshold;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return result;
|
|
148
|
+
} catch (error) {
|
|
149
|
+
if (error instanceof ConfigError) {
|
|
150
|
+
error.atSection('opcua-conditions');
|
|
151
|
+
}
|
|
152
|
+
throw error;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
109
156
|
checkTagPath(defn) {
|
|
110
157
|
const path = defn.path;
|
|
111
158
|
if (!path) {
|
package/package.json
CHANGED
package/test/.eslintrc.json
CHANGED
package/test/conditions.test.js
CHANGED
|
@@ -72,6 +72,37 @@ describe('data items', async function () {
|
|
|
72
72
|
validateCondition(captured[10], 'cond1', 'FAULT', 5000, 'A2', 'Code A2');
|
|
73
73
|
validateCondition(captured[11], 'cond2', 'FAULT', 6000, 'B33', 'Code B33');
|
|
74
74
|
});
|
|
75
|
+
|
|
76
|
+
it('loads reclassification config', async function () {
|
|
77
|
+
expect(config.adapter.passthroughConditions).to.exist;
|
|
78
|
+
expect(config.adapter.passthroughConditions.system).to.exist;
|
|
79
|
+
expect(config.adapter.passthroughConditions.system.length).to.eq(1);
|
|
80
|
+
|
|
81
|
+
const source = config.adapter.passthroughConditions.system[0];
|
|
82
|
+
expect(source.name).to.eq('cond4');
|
|
83
|
+
expect(source.reclassifyFault).to.exist;
|
|
84
|
+
expect(source.reclassifyWarning).to.exist;
|
|
85
|
+
expect(source.reclassifyNormal).to.exist;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('reclassifies codes based on patterns', async function () {
|
|
89
|
+
const source = config.adapter.passthroughConditions.system[0];
|
|
90
|
+
|
|
91
|
+
// Test FAULT reclassification
|
|
92
|
+
expect(source.reclassifyFault[0].test('HIGH_TEMP')).to.eq(true);
|
|
93
|
+
expect(source.reclassifyFault[0].test('HIGH_PRESSURE')).to.eq(true);
|
|
94
|
+
expect(source.reclassifyFault[0].test('MEDIUM_TEMP')).to.eq(false);
|
|
95
|
+
|
|
96
|
+
// Test WARNING reclassification
|
|
97
|
+
expect(source.reclassifyWarning[0].test('MEDIUM_TEMP')).to.eq(true);
|
|
98
|
+
expect(source.reclassifyWarning[0].test('MEDIUM_PRESSURE')).to.eq(true);
|
|
99
|
+
expect(source.reclassifyWarning[0].test('LOW_TEMP')).to.eq(false);
|
|
100
|
+
|
|
101
|
+
// Test NORMAL reclassification
|
|
102
|
+
expect(source.reclassifyNormal[0].test('LOW_TEMP')).to.eq(true);
|
|
103
|
+
expect(source.reclassifyNormal[0].test('LOW_PRESSURE')).to.eq(true);
|
|
104
|
+
expect(source.reclassifyNormal[0].test('HIGH_TEMP')).to.eq(false);
|
|
105
|
+
});
|
|
75
106
|
});
|
|
76
107
|
|
|
77
108
|
function validateCondition(entry, key, value, time, code, message) {
|
|
@@ -7,9 +7,12 @@ const testUtils = require('../util/testUtils');
|
|
|
7
7
|
describe('OPC-UA config tests', function () {
|
|
8
8
|
let config;
|
|
9
9
|
let defaultConfig;
|
|
10
|
+
let conditionsConfig;
|
|
11
|
+
|
|
10
12
|
before(async () => {
|
|
11
13
|
defaultConfig = await testUtils.loadConfig('device/opcua-default.yml');
|
|
12
14
|
config = await testUtils.loadConfig('device/opcua-std.yml');
|
|
15
|
+
conditionsConfig = await testUtils.loadConfig('device/opcua-conditions.yml');
|
|
13
16
|
});
|
|
14
17
|
|
|
15
18
|
it('loads minimal config with defaults', async function () {
|
|
@@ -24,6 +27,7 @@ describe('OPC-UA config tests', function () {
|
|
|
24
27
|
expect(defaultConfig.device.userCertificatePath).to.eq(undefined);
|
|
25
28
|
expect(defaultConfig.device.userPrivateKeyPath).to.eq(undefined);
|
|
26
29
|
expect(defaultConfig.device.conditionKey).to.eq(undefined);
|
|
30
|
+
expect(defaultConfig.device.opcuaConditions).to.deep.eq({});
|
|
27
31
|
});
|
|
28
32
|
|
|
29
33
|
it('reads tags', async function () {
|
|
@@ -31,7 +35,7 @@ describe('OPC-UA config tests', function () {
|
|
|
31
35
|
});
|
|
32
36
|
|
|
33
37
|
it('loaded expression service', async function () {
|
|
34
|
-
const deviceNames = ['fan-speed', 'pump-speed', 'some-date', 'pressure'];
|
|
38
|
+
const deviceNames = ['fan-speed', 'pump-speed', 'some-date', 'pressure', 'system'];
|
|
35
39
|
const allNames = [...deviceNames, 'device-connected'];
|
|
36
40
|
expect(config.expressionService.definedNames()).to.deep.eq(allNames);
|
|
37
41
|
expect(config.expressionService.definedNames('device')).to.deep.eq(deviceNames);
|
|
@@ -40,6 +44,11 @@ describe('OPC-UA config tests', function () {
|
|
|
40
44
|
expect(config.expressionService.referencedNames('device')).to.deep.eq(['fan-speed', 'pump-speed']);
|
|
41
45
|
});
|
|
42
46
|
|
|
47
|
+
it('migrates deprecated opcua-condition-key to opcua-conditions', async function () {
|
|
48
|
+
expect(config.device.conditionKey).to.eq('system');
|
|
49
|
+
expect(config.device.opcuaConditions).to.deep.eq({ key: 'system' });
|
|
50
|
+
});
|
|
51
|
+
|
|
43
52
|
it('catches bad tag definition (bad path syntax)', async function () {
|
|
44
53
|
try {
|
|
45
54
|
await testUtils.loadConfig('device/opcua-bad-tag.yml');
|
|
@@ -94,4 +103,13 @@ describe('OPC-UA config tests', function () {
|
|
|
94
103
|
testPath('10853');
|
|
95
104
|
testPath('nsu=http://test.org/UA/Data/;i=10853');
|
|
96
105
|
});
|
|
106
|
+
|
|
107
|
+
it('loads opcua-conditions with all properties', async function () {
|
|
108
|
+
expect(conditionsConfig.device.opcuaConditions).to.deep.eq({
|
|
109
|
+
key: 'system',
|
|
110
|
+
faultThreshold: 100,
|
|
111
|
+
warningThreshold: 50,
|
|
112
|
+
});
|
|
113
|
+
expect(conditionsConfig.device.conditionKey).to.eq('system');
|
|
114
|
+
});
|
|
97
115
|
});
|
|
@@ -5,6 +5,8 @@ declare-keys:
|
|
|
5
5
|
- key2
|
|
6
6
|
- key3
|
|
7
7
|
- key4
|
|
8
|
+
- system:
|
|
9
|
+
type: condition
|
|
8
10
|
variables:
|
|
9
11
|
keymod:
|
|
10
12
|
- source: key2
|
|
@@ -35,3 +37,11 @@ conditions:
|
|
|
35
37
|
override-message: ${this} coolant=${key1}
|
|
36
38
|
- override-code: Y
|
|
37
39
|
override-message: Z
|
|
40
|
+
cond4:
|
|
41
|
+
source: system
|
|
42
|
+
reclassify-fault:
|
|
43
|
+
- HIGH*
|
|
44
|
+
reclassify-warning:
|
|
45
|
+
- MEDIUM*
|
|
46
|
+
reclassify-normal:
|
|
47
|
+
- LOW*
|