@machinemetrics/io-adapter-lib 2.34.0 → 2.35.1
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 +76 -1
- package/lib/transform/valueDecrease.js +9 -1
- package/lib/transform/valueIncrease.js +9 -1
- package/package.json +1 -1
- package/test/config/data-events.test.js +75 -0
- package/test/configFiles/data-events.yaml +25 -0
- package/test/configFiles/transform/value-decrease.yml +10 -0
- package/test/configFiles/transform/value-increase.yml +10 -0
- package/test/transform/valueDecrease.test.js +29 -2
- package/test/transform/valueIncrease.test.js +27 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.35.1]
|
|
4
|
+
- Fixed value-increase and value-decrease to not emit true when coming back from UNAVAILABLE
|
|
5
|
+
- Behavior change: value-decrease does not emit true on start
|
|
6
|
+
|
|
7
|
+
## [2.35.0]
|
|
8
|
+
- Added data-events config support
|
|
9
|
+
|
|
3
10
|
## [2.34.0]
|
|
4
11
|
- Added support for initializing variables from constant source or expression ops
|
|
5
12
|
- Fixed map not recognizing external identifiers in sub-ops
|
|
@@ -100,7 +100,20 @@ class AdapterConfig {
|
|
|
100
100
|
});
|
|
101
101
|
}).flattenDeep().compact().value();
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
const dataEvents = _(config['data-events']).map((event, key) => {
|
|
104
|
+
const exprs = [];
|
|
105
|
+
if (_.isString(event.payload)) {
|
|
106
|
+
exprs.push({ path: `data-events.${key}.payload`, expression: event.payload });
|
|
107
|
+
}
|
|
108
|
+
_.each(event.triggers, (trigger, i) => {
|
|
109
|
+
if (_.isString(trigger)) {
|
|
110
|
+
exprs.push({ path: `data-events.${key}.triggers.${i}`, expression: trigger });
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
return exprs;
|
|
114
|
+
}).flattenDeep().compact().value();
|
|
115
|
+
|
|
116
|
+
return [...dataItems, ...conditions, ...dataEvents];
|
|
104
117
|
}
|
|
105
118
|
|
|
106
119
|
getStringExpressions(config = {}) {
|
|
@@ -150,6 +163,7 @@ class AdapterConfig {
|
|
|
150
163
|
parse(config = {}) {
|
|
151
164
|
this.loadDataItems(config['data-items']);
|
|
152
165
|
this.loadConditions(config.conditions);
|
|
166
|
+
this.loadDataEvents(config['data-events']);
|
|
153
167
|
|
|
154
168
|
this.passthroughConditions = _(this.conditions).filter((cond) => {
|
|
155
169
|
return cond.passthroughSource;
|
|
@@ -524,6 +538,62 @@ class AdapterConfig {
|
|
|
524
538
|
};
|
|
525
539
|
}
|
|
526
540
|
|
|
541
|
+
loadDataEvents(events) {
|
|
542
|
+
this.dataEventsByExpression = {};
|
|
543
|
+
this.dataEvents = _.mapValues(events || {}, (event, key) => {
|
|
544
|
+
if (!_.isString(event.payload)) {
|
|
545
|
+
const configErr = new ConfigError(`Data event '${key}' has no payload`);
|
|
546
|
+
throw configErr.atSection(`data-events.${key}`).atAttribute('payload');
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
let payloadDefn;
|
|
550
|
+
try {
|
|
551
|
+
const payloadPath = `data-events.${key}.payload`;
|
|
552
|
+
const compiledPayload = this.expressionService.compileExpression(event.payload);
|
|
553
|
+
this.dataEventsByExpression[payloadPath] = key;
|
|
554
|
+
payloadDefn = {
|
|
555
|
+
rawExpression: event.payload,
|
|
556
|
+
expression: compiledPayload,
|
|
557
|
+
};
|
|
558
|
+
} catch (err) {
|
|
559
|
+
const configErr = new ConfigError(`Problem evaluating payload expression: ${err.message}`);
|
|
560
|
+
throw configErr.atSection(`data-events.${key}`).atAttribute('payload');
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (!_.isArray(event.triggers) || _.isEmpty(event.triggers)) {
|
|
564
|
+
const configErr = new ConfigError(`Data event '${key}' has no triggers`);
|
|
565
|
+
throw configErr.atSection(`data-events.${key}`).atAttribute('triggers');
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
const triggers = _.map(event.triggers, (trigger, i) => {
|
|
569
|
+
if (!_.isString(trigger)) {
|
|
570
|
+
const configErr = new ConfigError(`Data event '${key}' trigger at index ${i} must be a string expression`);
|
|
571
|
+
throw configErr.atSection(`data-events.${key}.triggers`);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
try {
|
|
575
|
+
const triggerPath = `data-events.${key}.triggers.${i}`;
|
|
576
|
+
const compiledTrigger = this.expressionService.compileExpression(trigger);
|
|
577
|
+
this.dataEventsByExpression[triggerPath] = key;
|
|
578
|
+
return {
|
|
579
|
+
rawExpression: trigger,
|
|
580
|
+
expression: compiledTrigger,
|
|
581
|
+
triggerVariables: this.expressionService.expressionTriggers(triggerPath),
|
|
582
|
+
};
|
|
583
|
+
} catch (err) {
|
|
584
|
+
const configErr = new ConfigError(`Problem evaluating trigger expression: ${err.message}`);
|
|
585
|
+
throw configErr.atSection(`data-events.${key}.triggers`).atAttribute(i);
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
return {
|
|
590
|
+
name: key,
|
|
591
|
+
payload: payloadDefn,
|
|
592
|
+
triggers,
|
|
593
|
+
};
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
|
|
527
597
|
triggeredDataItems(name) {
|
|
528
598
|
const triggeredPaths = this.expressionService.expressionsTriggeredBy(name);
|
|
529
599
|
return _(triggeredPaths).map(p => this.dataItemsByExpression[p]).compact().uniq().value();
|
|
@@ -533,6 +603,11 @@ class AdapterConfig {
|
|
|
533
603
|
const triggeredPaths = this.expressionService.expressionsTriggeredBy(name);
|
|
534
604
|
return _(triggeredPaths).map(p => this.conditionsByExpression[p]).compact().uniq().value();
|
|
535
605
|
}
|
|
606
|
+
|
|
607
|
+
triggeredDataEvents(name) {
|
|
608
|
+
const triggeredPaths = this.expressionService.expressionsTriggeredBy(name);
|
|
609
|
+
return _(triggeredPaths).map(p => this.dataEventsByExpression[p]).compact().uniq().value();
|
|
610
|
+
}
|
|
536
611
|
}
|
|
537
612
|
|
|
538
613
|
module.exports = AdapterConfig;
|
|
@@ -25,7 +25,7 @@ class ValueDecreaseFilter extends TransformState {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
emitUpdate(context) {
|
|
28
|
-
if (this.
|
|
28
|
+
if (this.value < this.previousValue && this.valueTime === this.lastSampleTime) {
|
|
29
29
|
this.forceEmit = false;
|
|
30
30
|
this.lastEmitValue = this.value;
|
|
31
31
|
this.lastEmitTime = this.lastSampleTime;
|
|
@@ -33,6 +33,14 @@ class ValueDecreaseFilter extends TransformState {
|
|
|
33
33
|
}
|
|
34
34
|
this.emit('update', false, this.lastSampleTime, context);
|
|
35
35
|
}
|
|
36
|
+
|
|
37
|
+
setUnavailable(context, time) {
|
|
38
|
+
this.available = false;
|
|
39
|
+
this.previousValue = this.value;
|
|
40
|
+
this.previousValueTime = this.valueTime;
|
|
41
|
+
this.valueTime = time;
|
|
42
|
+
this.emit('unavailable', time, context);
|
|
43
|
+
}
|
|
36
44
|
}
|
|
37
45
|
|
|
38
46
|
module.exports = ValueDecreaseFilter;
|
|
@@ -25,7 +25,7 @@ class ValueIncreaseFilter extends TransformState {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
emitUpdate(context) {
|
|
28
|
-
if (this.
|
|
28
|
+
if (this.value > this.previousValue && this.valueTime === this.lastSampleTime) {
|
|
29
29
|
this.forceEmit = false;
|
|
30
30
|
this.lastEmitValue = this.value;
|
|
31
31
|
this.lastEmitTime = this.lastSampleTime;
|
|
@@ -33,6 +33,14 @@ class ValueIncreaseFilter extends TransformState {
|
|
|
33
33
|
}
|
|
34
34
|
this.emit('update', false, this.lastSampleTime, context);
|
|
35
35
|
}
|
|
36
|
+
|
|
37
|
+
setUnavailable(context, time) {
|
|
38
|
+
this.available = false;
|
|
39
|
+
this.previousValue = this.value;
|
|
40
|
+
this.previousValueTime = this.valueTime;
|
|
41
|
+
this.valueTime = time;
|
|
42
|
+
this.emit('unavailable', time, context);
|
|
43
|
+
}
|
|
36
44
|
}
|
|
37
45
|
|
|
38
46
|
module.exports = ValueIncreaseFilter;
|
package/package.json
CHANGED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const expect = require('chai').expect;
|
|
5
|
+
const testUtils = require('../util/testUtils');
|
|
6
|
+
|
|
7
|
+
describe('Data events config tests', function () {
|
|
8
|
+
let config;
|
|
9
|
+
before(async () => {
|
|
10
|
+
config = await testUtils.loadConfig('data-events.yaml');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('populates dataEventsByExpression table', function () {
|
|
14
|
+
expect(_.size(config.adapter.dataEventsByExpression)).to.eq(4);
|
|
15
|
+
expect(config.adapter.dataEventsByExpression['data-events.tool-change-1.payload']).to.eq('tool-change-1');
|
|
16
|
+
expect(config.adapter.dataEventsByExpression['data-events.tool-change-1.triggers.0']).to.eq('tool-change-1');
|
|
17
|
+
expect(config.adapter.dataEventsByExpression['data-events.tool-offset-change-1.payload']).to.eq('tool-offset-change-1');
|
|
18
|
+
expect(config.adapter.dataEventsByExpression['data-events.tool-offset-change-1.triggers.0']).to.eq('tool-offset-change-1');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('parses data event structure', function () {
|
|
22
|
+
expect(_.size(config.adapter.dataEvents)).to.eq(2);
|
|
23
|
+
|
|
24
|
+
const ev1 = config.adapter.dataEvents['tool-change-1'];
|
|
25
|
+
expect(ev1.name).to.eq('tool-change-1');
|
|
26
|
+
expect(ev1.payload.rawExpression).to.eq('{}');
|
|
27
|
+
expect(ev1.payload.expression).to.exist;
|
|
28
|
+
expect(ev1.triggers).to.have.length(1);
|
|
29
|
+
expect(ev1.triggers[0].rawExpression).to.eq('tool-change-1');
|
|
30
|
+
expect(ev1.triggers[0].expression).to.exist;
|
|
31
|
+
expect(ev1.triggers[0].triggerVariables).to.deep.eq(['tool-change-1']);
|
|
32
|
+
|
|
33
|
+
const ev2 = config.adapter.dataEvents['tool-offset-change-1'];
|
|
34
|
+
expect(ev2.name).to.eq('tool-offset-change-1');
|
|
35
|
+
expect(ev2.payload.rawExpression).to.eq('{ x: XgeoT1, y: YgeoT1, z: ZgeoT1, xwear: XwearT1, ywear: YwearT1, zwear: ZwearT1 }');
|
|
36
|
+
expect(ev2.payload.expression).to.exist;
|
|
37
|
+
expect(ev2.triggers).to.have.length(1);
|
|
38
|
+
expect(ev2.triggers[0].rawExpression).to.eq('XGeoT1-chg or YGeoT1-chg or ZGeoT1-chg or XWearT1-chg or YWearT1-chg or ZWearT1-chg');
|
|
39
|
+
expect(ev2.triggers[0].expression).to.exist;
|
|
40
|
+
expect(ev2.triggers[0].triggerVariables).to.deep.eq([
|
|
41
|
+
'XGeoT1-chg', 'YGeoT1-chg', 'ZGeoT1-chg', 'XWearT1-chg', 'YWearT1-chg', 'ZWearT1-chg',
|
|
42
|
+
]);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('resolves payload trigger variables', function () {
|
|
46
|
+
const svc = config.expressionService;
|
|
47
|
+
expect(svc.expressionTriggers('data-events.tool-change-1.payload')).to.deep.eq([]);
|
|
48
|
+
expect(svc.expressionTriggers('data-events.tool-offset-change-1.payload')).to.deep.eq([
|
|
49
|
+
'XgeoT1', 'YgeoT1', 'ZgeoT1', 'XwearT1', 'YwearT1', 'ZwearT1',
|
|
50
|
+
]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('resolves triggered data events by trigger variable', function () {
|
|
54
|
+
expect(config.adapter.triggeredDataEvents('tool-change-1')).to.deep.eq(['tool-change-1']);
|
|
55
|
+
expect(config.adapter.triggeredDataEvents('XGeoT1-chg')).to.deep.eq(['tool-offset-change-1']);
|
|
56
|
+
expect(config.adapter.triggeredDataEvents('YGeoT1-chg')).to.deep.eq(['tool-offset-change-1']);
|
|
57
|
+
expect(config.adapter.triggeredDataEvents('ZGeoT1-chg')).to.deep.eq(['tool-offset-change-1']);
|
|
58
|
+
expect(config.adapter.triggeredDataEvents('XWearT1-chg')).to.deep.eq(['tool-offset-change-1']);
|
|
59
|
+
expect(config.adapter.triggeredDataEvents('YWearT1-chg')).to.deep.eq(['tool-offset-change-1']);
|
|
60
|
+
expect(config.adapter.triggeredDataEvents('ZWearT1-chg')).to.deep.eq(['tool-offset-change-1']);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('resolves triggered data events by payload variable', function () {
|
|
64
|
+
expect(config.adapter.triggeredDataEvents('XgeoT1')).to.deep.eq(['tool-offset-change-1']);
|
|
65
|
+
expect(config.adapter.triggeredDataEvents('YgeoT1')).to.deep.eq(['tool-offset-change-1']);
|
|
66
|
+
expect(config.adapter.triggeredDataEvents('ZgeoT1')).to.deep.eq(['tool-offset-change-1']);
|
|
67
|
+
expect(config.adapter.triggeredDataEvents('XwearT1')).to.deep.eq(['tool-offset-change-1']);
|
|
68
|
+
expect(config.adapter.triggeredDataEvents('YwearT1')).to.deep.eq(['tool-offset-change-1']);
|
|
69
|
+
expect(config.adapter.triggeredDataEvents('ZwearT1')).to.deep.eq(['tool-offset-change-1']);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('returns empty array for variables not referenced in any data event', function () {
|
|
73
|
+
expect(config.adapter.triggeredDataEvents('unrelated')).to.deep.eq([]);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
device: local
|
|
3
|
+
declare-keys:
|
|
4
|
+
- tool-change-1
|
|
5
|
+
- XgeoT1
|
|
6
|
+
- YgeoT1
|
|
7
|
+
- ZgeoT1
|
|
8
|
+
- XwearT1
|
|
9
|
+
- YwearT1
|
|
10
|
+
- ZwearT1
|
|
11
|
+
- XGeoT1-chg
|
|
12
|
+
- YGeoT1-chg
|
|
13
|
+
- ZGeoT1-chg
|
|
14
|
+
- XWearT1-chg
|
|
15
|
+
- YWearT1-chg
|
|
16
|
+
- ZWearT1-chg
|
|
17
|
+
data-events:
|
|
18
|
+
tool-change-1:
|
|
19
|
+
payload: "{}"
|
|
20
|
+
triggers:
|
|
21
|
+
- tool-change-1
|
|
22
|
+
tool-offset-change-1:
|
|
23
|
+
payload: "{ x: XgeoT1, y: YgeoT1, z: ZgeoT1, xwear: XwearT1, ywear: YwearT1, zwear: ZwearT1 }"
|
|
24
|
+
triggers:
|
|
25
|
+
- XGeoT1-chg or YGeoT1-chg or ZGeoT1-chg or XWearT1-chg or YWearT1-chg or ZWearT1-chg
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const ValueDecrease = require('../../lib/transform').valueDecrease;
|
|
4
|
+
const EngineV2 = require('../../lib/engine/engineV2');
|
|
5
|
+
const Builder = require('../../lib/engine/transformBuilderV2');
|
|
4
6
|
const testUtils = require('../util/testUtils');
|
|
5
7
|
|
|
6
8
|
describe('value-decrease transform tests', function () {
|
|
@@ -12,7 +14,7 @@ describe('value-decrease transform tests', function () {
|
|
|
12
14
|
[1, 2], [1, 4], [2, 6], [4, 10], [0, 14], [1, 15], [3, 16], [1, 21],
|
|
13
15
|
]);
|
|
14
16
|
filter.validate([
|
|
15
|
-
[
|
|
17
|
+
[false, 2], [false, 4], [false, 6], [false, 10], [true, 14], [false, 14],
|
|
16
18
|
[false, 15], [false, 16], [true, 21], [false, 21],
|
|
17
19
|
]);
|
|
18
20
|
});
|
|
@@ -25,8 +27,33 @@ describe('value-decrease transform tests', function () {
|
|
|
25
27
|
['1', 2], ['1', 4], ['2', 6], ['10', 10], ['0', 14], ['1', 15], ['3', 16], ['1', 21],
|
|
26
28
|
]);
|
|
27
29
|
filter.validate([
|
|
28
|
-
[
|
|
30
|
+
[false, 2], [false, 4], [false, 6], [false, 10], [true, 14], [false, 14],
|
|
29
31
|
[false, 15], [false, 16], [true, 21], [false, 21],
|
|
30
32
|
]);
|
|
31
33
|
});
|
|
32
34
|
});
|
|
35
|
+
|
|
36
|
+
describe('value-decrease full engine config file tests', function () {
|
|
37
|
+
let config;
|
|
38
|
+
before(async function () {
|
|
39
|
+
config = await testUtils.loadConfig('transform/value-decrease.yml');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('does not emit true when source sends UNAVAILABLE', function () {
|
|
43
|
+
const engine = new EngineV2(config);
|
|
44
|
+
const builder = new Builder(config);
|
|
45
|
+
builder.build(engine);
|
|
46
|
+
|
|
47
|
+
const source = testUtils.valueSource();
|
|
48
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.decreased, source);
|
|
49
|
+
|
|
50
|
+
source.sendValues('level', [
|
|
51
|
+
[1, 0], [2, 1], [0, 2], [1, 3], [2, 4], ['UNAVAILABLE', 5], [3, 6],
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
engine.validateFilter([
|
|
55
|
+
[false, 0], [false, 1], [true, 2], [false, 2], [false, 3], [false, 4],
|
|
56
|
+
[false, 6],
|
|
57
|
+
]);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const ValueIncrease = require('../../lib/transform').valueIncrease;
|
|
4
|
+
const EngineV2 = require('../../lib/engine/engineV2');
|
|
5
|
+
const Builder = require('../../lib/engine/transformBuilderV2');
|
|
4
6
|
const testUtils = require('../util/testUtils');
|
|
5
7
|
|
|
6
8
|
describe('value-increase transform tests', function () {
|
|
@@ -30,3 +32,28 @@ describe('value-increase transform tests', function () {
|
|
|
30
32
|
]);
|
|
31
33
|
});
|
|
32
34
|
});
|
|
35
|
+
|
|
36
|
+
describe('value-increase full engine config file tests', function () {
|
|
37
|
+
let config;
|
|
38
|
+
before(async function () {
|
|
39
|
+
config = await testUtils.loadConfig('transform/value-increase.yml');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('does not emit true when source sends UNAVAILABLE', function () {
|
|
43
|
+
const engine = new EngineV2(config);
|
|
44
|
+
const builder = new Builder(config);
|
|
45
|
+
builder.build(engine);
|
|
46
|
+
|
|
47
|
+
const source = testUtils.valueSource();
|
|
48
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.increased, source);
|
|
49
|
+
|
|
50
|
+
source.sendValues('level', [
|
|
51
|
+
[1, 0], [2, 1], [0, 2], [1, 3], [2, 4], ['UNAVAILABLE', 5], [2, 6], [3, 7],
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
engine.validateFilter([
|
|
55
|
+
[true, 0], [false, 0], [true, 1], [false, 1], [false, 2], [true, 3], [false, 3],
|
|
56
|
+
[true, 4], [false, 4], [false, 6], [true, 7], [false, 7],
|
|
57
|
+
]);
|
|
58
|
+
});
|
|
59
|
+
});
|