@machinemetrics/io-adapter-lib 2.32.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/.circleci/config.yml +141 -0
- package/.eslintrc.json +36 -0
- package/.gitattributes +12 -0
- package/CHANGELOG.md +544 -0
- package/README.md +2 -0
- package/index.js +17 -0
- package/lib/config/adapterConfig.js +535 -0
- package/lib/config/common/allowDenyList.js +58 -0
- package/lib/config/common/jsonPath.js +44 -0
- package/lib/config/common/labjackU3T4Common.js +227 -0
- package/lib/config/common/validations.js +142 -0
- package/lib/config/configError.js +32 -0
- package/lib/config/device/adam-6052Config.js +103 -0
- package/lib/config/device/brotherHTTPConfig.js +215 -0
- package/lib/config/device/ethernetIPConfig.js +191 -0
- package/lib/config/device/ifmIotConfig.js +245 -0
- package/lib/config/device/jsonHttpConfig.js +76 -0
- package/lib/config/device/labjackT4Config.js +39 -0
- package/lib/config/device/labjackT7Config.js +192 -0
- package/lib/config/device/labjackU3Config.js +32 -0
- package/lib/config/device/modbusTcpConfig.js +336 -0
- package/lib/config/device/mqttBaseConfig.js +70 -0
- package/lib/config/device/mqttConfig.js +113 -0
- package/lib/config/device/mqttLincolnConfig.js +62 -0
- package/lib/config/device/mtconnectAdapterConfig.js +42 -0
- package/lib/config/device/mtconnectBaseConfig.js +136 -0
- package/lib/config/device/mtconnectConfig.js +52 -0
- package/lib/config/device/mtconnectHaasConfig.js +15 -0
- package/lib/config/device/nullConfig.js +81 -0
- package/lib/config/device/opcuaConfig.js +205 -0
- package/lib/config/device/pcccConfig.js +88 -0
- package/lib/config/engineConfigV1.js +382 -0
- package/lib/config/engineConfigV2.js +106 -0
- package/lib/config/generator/counterGenConfig.js +15 -0
- package/lib/config/generator/cronGenConfig.js +33 -0
- package/lib/config/generator/dateTimeGenConfig.js +33 -0
- package/lib/config/index.js +339 -0
- package/lib/config/transformConfigUtil.js +296 -0
- package/lib/engine/dataOutput.js +357 -0
- package/lib/engine/deviceOutput.js +186 -0
- package/lib/engine/engineV1.js +480 -0
- package/lib/engine/engineV2.js +719 -0
- package/lib/engine/index.js +34 -0
- package/lib/engine/transformBuilderV1.js +111 -0
- package/lib/engine/transformBuilderV2.js +74 -0
- package/lib/expressionService.js +330 -0
- package/lib/math.js +142 -0
- package/lib/transform/accumulate.js +98 -0
- package/lib/transform/average.js +56 -0
- package/lib/transform/debounce.js +152 -0
- package/lib/transform/downsample.js +69 -0
- package/lib/transform/edge.js +34 -0
- package/lib/transform/expression.js +91 -0
- package/lib/transform/fallingEdge.js +70 -0
- package/lib/transform/fromBuffer.js +89 -0
- package/lib/transform/hash.js +41 -0
- package/lib/transform/ignoreValue.js +118 -0
- package/lib/transform/index.js +93 -0
- package/lib/transform/invert.js +25 -0
- package/lib/transform/latch.js +99 -0
- package/lib/transform/latchValue.js +115 -0
- package/lib/transform/logicAnd.js +67 -0
- package/lib/transform/logicOr.js +67 -0
- package/lib/transform/map.js +115 -0
- package/lib/transform/max.js +57 -0
- package/lib/transform/maxLength.js +39 -0
- package/lib/transform/min.js +57 -0
- package/lib/transform/minDelta.js +40 -0
- package/lib/transform/offDelay.js +89 -0
- package/lib/transform/onDelay.js +89 -0
- package/lib/transform/patternEscape.js +24 -0
- package/lib/transform/patternMatch.js +194 -0
- package/lib/transform/patternReplace.js +170 -0
- package/lib/transform/patternTest.js +85 -0
- package/lib/transform/rateOfChange.js +56 -0
- package/lib/transform/reject.js +115 -0
- package/lib/transform/resample.js +96 -0
- package/lib/transform/risingEdge.js +90 -0
- package/lib/transform/risingEdgeCounter.js +179 -0
- package/lib/transform/sampleInterval.js +12 -0
- package/lib/transform/source.js +116 -0
- package/lib/transform/state.js +201 -0
- package/lib/transform/threshold.js +52 -0
- package/lib/transform/toBuffer.js +159 -0
- package/lib/transform/toggle.js +118 -0
- package/lib/transform/transformState.js +193 -0
- package/lib/transform/trim.js +27 -0
- package/lib/transform/util/chainSource.js +96 -0
- package/lib/transform/util/ringBuffer.js +34 -0
- package/lib/transform/valueChange.js +34 -0
- package/lib/transform/valueDecrease.js +38 -0
- package/lib/transform/valueIncrease.js +38 -0
- package/lib/transform/valueIncreaseDiff.js +66 -0
- package/lib/transform/whenUnavailable.js +73 -0
- package/lib/transform/windowCount.js +86 -0
- package/lib/util/fileUtil.js +44 -0
- package/package.json +38 -0
- package/test/.eslintrc.json +15 -0
- package/test/chainedTransform.test.js +88 -0
- package/test/conditions.test.js +118 -0
- package/test/config/ab-pccc.test.js +41 -0
- package/test/config/adam-6052.test.js +16 -0
- package/test/config/adapter.test.js +109 -0
- package/test/config/brother-http.test.js +19 -0
- package/test/config/ethernet-ip.test.js +19 -0
- package/test/config/ifm-iot.test.js +20 -0
- package/test/config/json-http.test.js +47 -0
- package/test/config/labjack-t4.test.js +78 -0
- package/test/config/labjack-t7.test.js +18 -0
- package/test/config/labjack-u3.test.js +17 -0
- package/test/config/modbusTcp.test.js +87 -0
- package/test/config/mqtt.test.js +19 -0
- package/test/config/mqttLincoln.test.js +29 -0
- package/test/config/mtconnect.test.js +63 -0
- package/test/config/mtconnectAdapter.test.js +124 -0
- package/test/config/mtconnectHaas.test.js +15 -0
- package/test/config/null.test.js +16 -0
- package/test/config/opcua.test.js +97 -0
- package/test/config-tests.js +102 -0
- package/test/configFiles/conditions.yml +37 -0
- package/test/configFiles/data-items-legacy.yml +24 -0
- package/test/configFiles/data-items-shorthand.yml +14 -0
- package/test/configFiles/data-items.yml +12 -0
- package/test/configFiles/device/ab-pccc-default.yml +3 -0
- package/test/configFiles/device/ab-pccc-full.yml +13 -0
- package/test/configFiles/device/adam-6052-default.yml +2 -0
- package/test/configFiles/device/brother-http-default.yml +3 -0
- package/test/configFiles/device/ethernet-ip-default.yml +2 -0
- package/test/configFiles/device/ifm-iot-default.yml +2 -0
- package/test/configFiles/device/json-http-bad-prop.yml +13 -0
- package/test/configFiles/device/json-http-bad-prop2.yml +13 -0
- package/test/configFiles/device/json-http-default.yml +3 -0
- package/test/configFiles/device/json-http-std.yml +13 -0
- package/test/configFiles/device/labjack-t4-condition-exprs.yaml +46 -0
- package/test/configFiles/device/labjack-t4-default.yml +3 -0
- package/test/configFiles/device/labjack-t4-pins-alt.yaml +41 -0
- package/test/configFiles/device/labjack-t4-pins.yaml +40 -0
- package/test/configFiles/device/labjack-t4-v1.yaml +29 -0
- package/test/configFiles/device/labjack-t7-default.yml +2 -0
- package/test/configFiles/device/labjack-u3-default.yml +2 -0
- package/test/configFiles/device/modbus-partial.yml +4 -0
- package/test/configFiles/device/modbus-std-extended.yaml +23 -0
- package/test/configFiles/device/modbus-std.yaml +34 -0
- package/test/configFiles/device/modbus-tcp-default.yml +3 -0
- package/test/configFiles/device/mqtt-default.yml +2 -0
- package/test/configFiles/device/mqtt-lincoln-default.yml +3 -0
- package/test/configFiles/device/mqtt-lincoln-full.yml +7 -0
- package/test/configFiles/device/mtconnect-adapter-default.yml +3 -0
- package/test/configFiles/device/mtconnect-adapter-keys.yaml +18 -0
- package/test/configFiles/device/mtconnect-complex-keys.yaml +17 -0
- package/test/configFiles/device/mtconnect-default.yml +3 -0
- package/test/configFiles/device/mtconnect-duplicate-allow-keys.yaml +10 -0
- package/test/configFiles/device/mtconnect-duplicate-declare-keys.yaml +8 -0
- package/test/configFiles/device/mtconnect-duplicate-deny-keys.yaml +10 -0
- package/test/configFiles/device/mtconnect-haas-default.yml +3 -0
- package/test/configFiles/device/mtconnect-std.yaml +18 -0
- package/test/configFiles/device/null-default.yml +1 -0
- package/test/configFiles/device/opcua-bad-tag.yml +18 -0
- package/test/configFiles/device/opcua-bad-tag2.yml +18 -0
- package/test/configFiles/device/opcua-default.yml +3 -0
- package/test/configFiles/device/opcua-std.yml +18 -0
- package/test/configFiles/dump-test.yml +11 -0
- package/test/configFiles/expressionCond.yml +46 -0
- package/test/configFiles/min-config-t4.yaml +4 -0
- package/test/configFiles/min-config-u3.yaml +3 -0
- package/test/configFiles/missing-device.yaml +2 -0
- package/test/configFiles/parse-error1.yml +9 -0
- package/test/configFiles/parse-error2.yml +9 -0
- package/test/configFiles/repro/buffer-convert-repro.yml +15 -0
- package/test/configFiles/repro/chained-delay-timing-repro.yml +13 -0
- package/test/configFiles/repro/count-init-repro.yml +45 -0
- package/test/configFiles/repro/cycle-break-repro.yml +44 -0
- package/test/configFiles/repro/debounce-repro.yml +46 -0
- package/test/configFiles/repro/diff-count-repro.yml +34 -0
- package/test/configFiles/repro/engine-hang-repro.yml +9 -0
- package/test/configFiles/repro/latch-apm-repro.yml +26 -0
- package/test/configFiles/repro/lockout-count-repro.yml +33 -0
- package/test/configFiles/repro/program-extract-repro.yml +38 -0
- package/test/configFiles/repro/state-latch-repro.yml +47 -0
- package/test/configFiles/repro/ternary-repro.yml +26 -0
- package/test/configFiles/transform/debounce.yml +12 -0
- package/test/configFiles/transform/expression.yml +34 -0
- package/test/configFiles/transform/ignoreValue.yml +31 -0
- package/test/configFiles/transform/latch.yml +11 -0
- package/test/configFiles/transform/latchValue.yml +31 -0
- package/test/configFiles/transform/logicAnd.yml +14 -0
- package/test/configFiles/transform/logicOr.yml +14 -0
- package/test/configFiles/transform/map.yml +19 -0
- package/test/configFiles/transform/maxLength.yml +13 -0
- package/test/configFiles/transform/offDelay.yml +12 -0
- package/test/configFiles/transform/pattern-escape.yml +10 -0
- package/test/configFiles/transform/pattern-match.yml +57 -0
- package/test/configFiles/transform/pattern-replace.yml +34 -0
- package/test/configFiles/transform/pattern-test.yml +25 -0
- package/test/configFiles/transform/reject.yml +24 -0
- package/test/configFiles/transform/risingEdgeCounter.yml +36 -0
- package/test/configFiles/transform/source.yml +20 -0
- package/test/configFiles/transform/state.yml +56 -0
- package/test/configFiles/transform/toggle.yml +19 -0
- package/test/configFiles/transform/whenUnavailable.yml +19 -0
- package/test/dataFiles/noisy-pulse.txt +11330 -0
- package/test/dataItems.test.js +140 -0
- package/test/engine-v1-tests.js +418 -0
- package/test/engine-v2-tests.js +284 -0
- package/test/expression-tests.js +171 -0
- package/test/expressionService.test.js +154 -0
- package/test/expressionServiceCondition.test.js +130 -0
- package/test/repro/buffer-convert-repro.test.js +38 -0
- package/test/repro/chained-delay-timing-repro.test.js +34 -0
- package/test/repro/count-init-repro.test.js +46 -0
- package/test/repro/cylce-break-repro.test.js +57 -0
- package/test/repro/debounce-repro.test.js +65 -0
- package/test/repro/diff-count-repro.test.js +79 -0
- package/test/repro/engine-hang-repro.test.js +38 -0
- package/test/repro/latch-apm-repro.test.js +119 -0
- package/test/repro/lockout-count-repro.test.js +84 -0
- package/test/repro/program-extract-repro.test.js +40 -0
- package/test/repro/state-latch-repro.test.js +63 -0
- package/test/repro/ternary-repro.test.js +43 -0
- package/test/transform/accumulte.test.js +18 -0
- package/test/transform/average.test.js +22 -0
- package/test/transform/debounce.test.js +70 -0
- package/test/transform/downsample.test.js +30 -0
- package/test/transform/edge.test.js +27 -0
- package/test/transform/expression.test.js +189 -0
- package/test/transform/fallingEdge.test.js +59 -0
- package/test/transform/fromBuffer.test.js +60 -0
- package/test/transform/hash.test.js +34 -0
- package/test/transform/ignoreValue.test.js +123 -0
- package/test/transform/invert.test.js +26 -0
- package/test/transform/latch.test.js +33 -0
- package/test/transform/latchValue.test.js +126 -0
- package/test/transform/logicAnd.test.js +80 -0
- package/test/transform/logicOr.test.js +80 -0
- package/test/transform/map.test.js +42 -0
- package/test/transform/max.test.js +30 -0
- package/test/transform/maxLength.test.js +32 -0
- package/test/transform/min.test.js +30 -0
- package/test/transform/minDelta.test.js +14 -0
- package/test/transform/offDelay.test.js +123 -0
- package/test/transform/onDelay.test.js +105 -0
- package/test/transform/patternEscape.test.js +18 -0
- package/test/transform/patternMatch.test.js +177 -0
- package/test/transform/patternReplace.test.js +95 -0
- package/test/transform/patternTest.test.js +105 -0
- package/test/transform/rateOfChange.test.js +34 -0
- package/test/transform/reject.test.js +56 -0
- package/test/transform/resample.test.js +193 -0
- package/test/transform/risingEdge.test.js +60 -0
- package/test/transform/risingEdgeCounter.test.js +227 -0
- package/test/transform/sampleInterval.test.js +22 -0
- package/test/transform/source.test.js +137 -0
- package/test/transform/state.test.js +248 -0
- package/test/transform/threshold.test.js +78 -0
- package/test/transform/toBuffer.test.js +60 -0
- package/test/transform/toggle.test.js +92 -0
- package/test/transform/trim.test.js +30 -0
- package/test/transform/valueChange.test.js +14 -0
- package/test/transform/valueDecrease.test.js +32 -0
- package/test/transform/valueIncrease.test.js +32 -0
- package/test/transform/valueIncreaseDiff.test.js +32 -0
- package/test/transform/whenUnavailable.test.js +93 -0
- package/test/transform/windowCount.test.js +26 -0
- package/test/util/testUtils.js +405 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const TransformState = require('./transformState');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Replaces all instances in a string using a regex pattern.
|
|
8
|
+
*/
|
|
9
|
+
class PatternReplaceFilter extends TransformState {
|
|
10
|
+
constructor({ engine, path, args: { pattern, flags, replacement, noMatch } }) {
|
|
11
|
+
super();
|
|
12
|
+
|
|
13
|
+
this.patternPath = `${path}.pattern`;
|
|
14
|
+
this.withPath = `${path}.with`;
|
|
15
|
+
this.elsePath = `${path}.else`;
|
|
16
|
+
|
|
17
|
+
this.engine = engine;
|
|
18
|
+
this.staticPattern = engine.expressionService.isStaticExpression(this.patternPath);
|
|
19
|
+
this.patternSources = engine.expressionService.expressionTriggers(this.patternPath);
|
|
20
|
+
this.staticWith = engine.expressionService.isStaticExpression(this.withPath);
|
|
21
|
+
this.withSources = engine.expressionService.expressionTriggers(this.withPath);
|
|
22
|
+
this.staticElse = engine.expressionService.isStaticExpression(this.elsePath);
|
|
23
|
+
this.elseSources = engine.expressionService.expressionTriggers(this.elsePath);
|
|
24
|
+
|
|
25
|
+
this.flags = flags || '';
|
|
26
|
+
if (this.flags.indexOf('g') < 0) {
|
|
27
|
+
this.flags = `${flags}g`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (this.staticPattern) {
|
|
31
|
+
this.pattern = new RegExp(pattern, this.flags);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
this.replacement = replacement;
|
|
35
|
+
this.noMatch = noMatch;
|
|
36
|
+
|
|
37
|
+
if (_.isNil(this.replacement)) {
|
|
38
|
+
this.replacement = '';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static op = 'pattern-replace';
|
|
43
|
+
|
|
44
|
+
static create(args) {
|
|
45
|
+
return new PatternReplaceFilter(args);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static parseConfig(configUtil, defn) {
|
|
49
|
+
configUtil.requireObjectParams(defn.args, 'pattern-replace', {
|
|
50
|
+
optional: true,
|
|
51
|
+
availableAttributes: ['pattern', 'with', 'else'],
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const pattern = _.get(defn.args, 'pattern');
|
|
55
|
+
if (_.isEmpty(pattern)) {
|
|
56
|
+
configUtil.throwConfigError('pattern-replace must have an pattern', this.op);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const decomp = configUtil.decomposePattern(pattern);
|
|
60
|
+
configUtil.testPattern(decomp.pattern, decomp.flags, this.op);
|
|
61
|
+
|
|
62
|
+
const replacement = _.get(defn.args, 'with');
|
|
63
|
+
const noMatch = _.get(defn.args, 'else');
|
|
64
|
+
|
|
65
|
+
defn.args = { ...decomp, replacement, noMatch };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static getStringExpressions(configUtil, body, info) {
|
|
69
|
+
const patternVal = configUtil.decomposePattern(body?.pattern?.toString(), 'imusg').pattern;
|
|
70
|
+
const withVal = body?.with?.toString();
|
|
71
|
+
const elseVal = body?.else?.toString();
|
|
72
|
+
|
|
73
|
+
return [
|
|
74
|
+
...configUtil.getStringExpressions(body, patternVal, { ...info, field: 'pattern' }),
|
|
75
|
+
...configUtil.getStringExpressions(body, withVal, { ...info, field: 'with' }),
|
|
76
|
+
...configUtil.getStringExpressions(body, elseVal, { ...info, field: 'else' }),
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
filter(context, value, time) {
|
|
81
|
+
value = value?.toString() ?? '';
|
|
82
|
+
let pattern = this.pattern;
|
|
83
|
+
|
|
84
|
+
if (!this.staticPattern) {
|
|
85
|
+
const localState = { this: value };
|
|
86
|
+
const unavailTerm = _.reduce(this.patternSources, (res, name) => {
|
|
87
|
+
const state = this.engine.getState(name);
|
|
88
|
+
localState[name] = state?.value;
|
|
89
|
+
return res || !state || !state.available;
|
|
90
|
+
}, false);
|
|
91
|
+
|
|
92
|
+
if (unavailTerm) {
|
|
93
|
+
this.setUnavailable(context, time);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
pattern = this.engine.expressionService.computeStringExpression(this.patternPath, localState);
|
|
99
|
+
pattern = new RegExp(pattern, this.flags);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
this.recordError(context, err, time);
|
|
102
|
+
this.setUnavailable(context, time);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (this.noMatch && !pattern.test(value)) {
|
|
108
|
+
if (this.staticElse) {
|
|
109
|
+
this.commitValue(context, this.noMatch, time);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const localState = { this: value };
|
|
114
|
+
const unavailTerm = _.reduce(this.elseSources, (res, name) => {
|
|
115
|
+
const state = this.engine.getState(name);
|
|
116
|
+
localState[name] = state?.value;
|
|
117
|
+
return res || !state || !state.available;
|
|
118
|
+
}, false);
|
|
119
|
+
|
|
120
|
+
if (unavailTerm) {
|
|
121
|
+
this.setUnavailable(context, time);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
const result = this.engine.expressionService.computeStringExpression(this.elsePath, localState);
|
|
127
|
+
this.commitValue(context, result, time);
|
|
128
|
+
} catch (err) {
|
|
129
|
+
this.recordError(context, err, time);
|
|
130
|
+
this.setUnavailable(context, time);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const localState = { this: value };
|
|
137
|
+
const unavailTerm = _.reduce(this.withSources, (res, name) => {
|
|
138
|
+
const state = this.engine.getState(name);
|
|
139
|
+
localState[name] = state?.value;
|
|
140
|
+
return res || !state || !state.available;
|
|
141
|
+
}, false);
|
|
142
|
+
|
|
143
|
+
let replacement = null;
|
|
144
|
+
try {
|
|
145
|
+
if (this.staticWith) {
|
|
146
|
+
replacement = this.replacement;
|
|
147
|
+
} else if (unavailTerm) {
|
|
148
|
+
replacement = null;
|
|
149
|
+
} else {
|
|
150
|
+
replacement = this.engine.expressionService.computeStringExpression(this.withPath, localState);
|
|
151
|
+
}
|
|
152
|
+
} catch (err) {
|
|
153
|
+
this.recordError(context, err, time);
|
|
154
|
+
this.setUnavailable(context, time);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (replacement === null) {
|
|
159
|
+
if (this.pattern.test(value)) {
|
|
160
|
+
this.setUnavailable(context, time);
|
|
161
|
+
}
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const sub = value.replace(pattern, replacement);
|
|
166
|
+
this.commitValue(context, sub, time);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
module.exports = PatternReplaceFilter;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const TransformState = require('./transformState');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Attempts to match a pattern in a source string and return true if found and false otherwise
|
|
8
|
+
*/
|
|
9
|
+
class PatternTestFilter extends TransformState {
|
|
10
|
+
constructor({ engine, path, args: { pattern, flags } }) {
|
|
11
|
+
super();
|
|
12
|
+
|
|
13
|
+
this.patternPath = `${path}.pattern`;
|
|
14
|
+
|
|
15
|
+
this.engine = engine;
|
|
16
|
+
this.staticPattern = engine.expressionService.isStaticExpression(this.patternPath);
|
|
17
|
+
this.patternSources = engine.expressionService.expressionTriggers(this.patternPath);
|
|
18
|
+
|
|
19
|
+
this.flags = flags || '';
|
|
20
|
+
if (this.staticPattern) {
|
|
21
|
+
this.pattern = new RegExp(pattern, this.flags);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static op = 'pattern-test';
|
|
26
|
+
|
|
27
|
+
static create(args) {
|
|
28
|
+
return new PatternTestFilter(args);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static parseConfig(configUtil, defn) {
|
|
32
|
+
const pattern = defn.args.pattern ?? defn.args[defn.func];
|
|
33
|
+
if (_.isEmpty(pattern)) {
|
|
34
|
+
configUtil.throwConfigError('pattern-test must have an pattern', this.op);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
defn.args = configUtil.decomposePattern(pattern);
|
|
38
|
+
configUtil.testPattern(defn.args.pattern, defn.args.flags, this.op);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static getStringExpressions(configUtil, body, info) {
|
|
42
|
+
if (_.isString(body)) {
|
|
43
|
+
body = { pattern: body };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const patternVal = configUtil.decomposePattern(body?.pattern?.toString(), 'imusg').pattern;
|
|
47
|
+
|
|
48
|
+
return [
|
|
49
|
+
...configUtil.getStringExpressions(body, patternVal, { ...info, field: 'pattern' }),
|
|
50
|
+
];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
filter(context, value, time) {
|
|
54
|
+
value = value?.toString() ?? '';
|
|
55
|
+
let pattern = this.pattern;
|
|
56
|
+
|
|
57
|
+
if (!this.staticPattern) {
|
|
58
|
+
const localState = { this: value };
|
|
59
|
+
const unavailTerm = this.patternSources.reduce((res, name) => {
|
|
60
|
+
const state = this.engine.getState(name);
|
|
61
|
+
localState[name] = state?.value;
|
|
62
|
+
return res || !state || !state.available;
|
|
63
|
+
}, false);
|
|
64
|
+
|
|
65
|
+
if (unavailTerm) {
|
|
66
|
+
this.setUnavailable(context, time);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
pattern = this.engine.expressionService.computeStringExpression(this.patternPath, localState);
|
|
72
|
+
pattern = new RegExp(pattern, this.flags);
|
|
73
|
+
} catch (err) {
|
|
74
|
+
this.recordError(context, err, time);
|
|
75
|
+
this.setUnavailable(context, time);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const test = pattern.test(value);
|
|
81
|
+
this.commitValue(context, test, time);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = PatternTestFilter;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const TransformState = require('./transformState');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Compares each incoming sample with the previous sample and emits the difference between them divided by
|
|
7
|
+
* the time between the samples.
|
|
8
|
+
*/
|
|
9
|
+
class RateOfChange extends TransformState {
|
|
10
|
+
constructor() {
|
|
11
|
+
super();
|
|
12
|
+
this.initReset();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static op = 'rate-of-change';
|
|
16
|
+
|
|
17
|
+
static create(_args) {
|
|
18
|
+
return new RateOfChange();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
initReset() {
|
|
22
|
+
this.previousSample = null;
|
|
23
|
+
this.previousSampleTime = null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
filter(context, value, time) {
|
|
27
|
+
if (this.previousSample === null) {
|
|
28
|
+
this.capturePrevious(value, time);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const valDiff = value - this.previousSample;
|
|
33
|
+
const timeDiff = time - this.previousSampleTime;
|
|
34
|
+
|
|
35
|
+
this.capturePrevious(value, time);
|
|
36
|
+
|
|
37
|
+
if (timeDiff === 0) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const rate = valDiff / timeDiff;
|
|
42
|
+
this.commitValue(context, rate, time);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
capturePrevious(value, time) {
|
|
46
|
+
this.previousSample = value;
|
|
47
|
+
this.previousSampleTime = time;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
setUnavailable(context, time) {
|
|
51
|
+
super.setUnavailable(context, time);
|
|
52
|
+
this.initReset();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = RateOfChange;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const TransformState = require('./transformState');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Filters out any null elements from an array data value
|
|
8
|
+
*/
|
|
9
|
+
class RejectFilter extends TransformState {
|
|
10
|
+
constructor({ engine, path, args }) {
|
|
11
|
+
super();
|
|
12
|
+
|
|
13
|
+
this.engine = engine;
|
|
14
|
+
|
|
15
|
+
if (args.expression) {
|
|
16
|
+
this.compiledExpression = args.compiledExpression;
|
|
17
|
+
this.selfSources = engine.expressionService.expressionTriggers(path);
|
|
18
|
+
} else if (args.pattern) {
|
|
19
|
+
this.pattern = new RegExp(args.pattern, args.flags);
|
|
20
|
+
} else {
|
|
21
|
+
this.equalityTest = String(args.value);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static op = 'reject';
|
|
26
|
+
|
|
27
|
+
static create(args) {
|
|
28
|
+
return new RejectFilter(args);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static parseConfig(configUtil, defn) {
|
|
32
|
+
configUtil.requireObjectParams(defn.args, this.op, {
|
|
33
|
+
optional: false,
|
|
34
|
+
exclusive: true,
|
|
35
|
+
availableAttributes: ['pattern', 'expression', 'value'],
|
|
36
|
+
defaultAttribute: 'value',
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
let expression = _.get(defn.args, 'expression');
|
|
40
|
+
if (!_.isEmpty(expression)) {
|
|
41
|
+
expression = expression.toString();
|
|
42
|
+
const compiledExpression = configUtil.compileExpression(expression, this.op);
|
|
43
|
+
|
|
44
|
+
defn.args = { expression, compiledExpression };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const pattern = _.get(defn.args, 'pattern');
|
|
48
|
+
if (!_.isEmpty(pattern)) {
|
|
49
|
+
defn.args = configUtil.decomposePattern(pattern);
|
|
50
|
+
configUtil.testPattern(defn.args.pattern, defn.args.flags, 'pattern');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const value = _.get(defn.args, 'value', defn.args[defn.func]);
|
|
54
|
+
if (!_.isUndefined(value) && !_.isObject(value) && !_.isArray(value)) {
|
|
55
|
+
defn.args = { value };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
filter(context, value, time) {
|
|
60
|
+
if (!_.isArray(value)) {
|
|
61
|
+
this.commitValue(value, time);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (this.compiledExpression) {
|
|
66
|
+
this.filterExpression(context, value, time);
|
|
67
|
+
} else if (this.pattern) {
|
|
68
|
+
this.filterPattern(context, value, time);
|
|
69
|
+
} else {
|
|
70
|
+
this.filterEquality(context, value, time);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
filterExpression(context, value, time) {
|
|
75
|
+
const localState = {};
|
|
76
|
+
const unavailTerm = this.selfSources.reduce((res, name) => {
|
|
77
|
+
const state = this.engine.getState(name);
|
|
78
|
+
localState[name] = state?.value;
|
|
79
|
+
return res || !state || !state.available;
|
|
80
|
+
}, false);
|
|
81
|
+
|
|
82
|
+
if (unavailTerm) {
|
|
83
|
+
this.setUnavailable(context, time);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const filtered = _.reject(value, (item) => {
|
|
89
|
+
localState.this = item;
|
|
90
|
+
return this.compiledExpression.evaluate(localState);
|
|
91
|
+
});
|
|
92
|
+
this.commitValue(context, filtered, time);
|
|
93
|
+
} catch (err) {
|
|
94
|
+
// Probably a type error in one of the arguments.
|
|
95
|
+
this.recordError(context, err, time);
|
|
96
|
+
this.setUnavailable(context, time);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
filterPattern(context, value, time) {
|
|
101
|
+
const filtered = _.reject(value, (item) => {
|
|
102
|
+
return this.pattern.test(item);
|
|
103
|
+
});
|
|
104
|
+
this.commitValue(context, filtered, time);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
filterEquality(context, value, time) {
|
|
108
|
+
const filtered = _.reject(value, (item) => {
|
|
109
|
+
return String(item) === this.equalityTest;
|
|
110
|
+
});
|
|
111
|
+
this.commitValue(context, filtered, time);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = RejectFilter;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const TransformState = require('./transformState');
|
|
5
|
+
|
|
6
|
+
const E = 0.000001;
|
|
7
|
+
function floatLessEq(a, b) {
|
|
8
|
+
return a <= b + E;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const MAX_FILL_TIME = 300;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Takes an analog stream and resamples it to a given rate.
|
|
15
|
+
*
|
|
16
|
+
* If the resample interval is larger than the source interval, resampling is calculated by taking every
|
|
17
|
+
* Nth sample in the source where N is resample-interval / source-interval. If N is a fraction and a calculated
|
|
18
|
+
* sample falls between source samples, the older sample will be emitted.
|
|
19
|
+
*
|
|
20
|
+
* If the resample interval is smaller than the source interval, resampling is calculated by emitting N samples for
|
|
21
|
+
* every source sample, where N is source-interval / resample-interval. The previous received source sample is repeated
|
|
22
|
+
* for each of N samples until a sample's time matches or crosses the new sample time.
|
|
23
|
+
*/
|
|
24
|
+
class ResampleFilter extends TransformState {
|
|
25
|
+
constructor({ interval, limit }) {
|
|
26
|
+
super();
|
|
27
|
+
|
|
28
|
+
this.interval = interval;
|
|
29
|
+
this.limit = limit;
|
|
30
|
+
this.initReset();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static op = 'resample';
|
|
34
|
+
|
|
35
|
+
static create(args) {
|
|
36
|
+
return new ResampleFilter(args.args);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static parseConfig(configUtil, defn) {
|
|
40
|
+
configUtil.requireObjectParams(defn.args, this.op, {
|
|
41
|
+
optional: true,
|
|
42
|
+
availableAttributes: ['interval', 'interpolate', 'limit'],
|
|
43
|
+
defaultAttribute: 'interval',
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const interval = _.get(defn.args, 'interval', defn.args[defn.func]);
|
|
47
|
+
configUtil.requirePositive(interval, 'interval', this.op);
|
|
48
|
+
|
|
49
|
+
const interpolate = _.get(defn.args, 'interpolate');
|
|
50
|
+
if (!_.isUndefined(interpolate)) {
|
|
51
|
+
if (!_.isBoolean(interpolate)) {
|
|
52
|
+
configUtil.throwConfigError('interpolate must be true or false', this.op);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const limit = _.get(defn.args, 'limit', 1);
|
|
57
|
+
configUtil.requireZeroPositive(limit, 'limit', this.op);
|
|
58
|
+
|
|
59
|
+
defn.args = { interval, interpolate, limit };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
initReset() {
|
|
63
|
+
this.pendingChangeTime = null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
supportsPendingChanges() {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
nextPendingChange(time) {
|
|
71
|
+
// Safety limit. If resample hasn't been probed in at least 5 minutes, do now allow filling in
|
|
72
|
+
// the missing samples.
|
|
73
|
+
if (this.lastSampleTime > -1 && time - this.lastSampleTime >= MAX_FILL_TIME) {
|
|
74
|
+
return time - this.interval;
|
|
75
|
+
}
|
|
76
|
+
return this.pendingChangeTime;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
emitUpdate(context) {
|
|
80
|
+
if (!this.pendingChangeTime) {
|
|
81
|
+
this.pendingChangeTime = this.lastSampleTime;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (floatLessEq(this.pendingChangeTime, this.lastSampleTime)) {
|
|
85
|
+
super.emitUpdate(context);
|
|
86
|
+
this.pendingChangeTime = this.lastSampleTime + this.interval;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
setUnavailable(context, time) {
|
|
91
|
+
super.setUnavailable(context, time);
|
|
92
|
+
this.initReset();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = ResampleFilter;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const TransformState = require('./transformState');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Finds all rising edges (F->T) in a digital stream.
|
|
8
|
+
*
|
|
9
|
+
* For every incoming sample, the outgoing sample will be false. When a rising edge is found, an additional true
|
|
10
|
+
* value will be emitted before the false value, resulting in a 0-duration "event".
|
|
11
|
+
*
|
|
12
|
+
* The filter can be modified with a min-dwell and max-dwell property, which will only emit edge events if the
|
|
13
|
+
* preceding state duration was within the window of time bounded by min-dwell and max-dwell.
|
|
14
|
+
*/
|
|
15
|
+
class RisingEdgeFilter extends TransformState {
|
|
16
|
+
constructor({ minDwell, maxDwell } = {}) {
|
|
17
|
+
super();
|
|
18
|
+
|
|
19
|
+
this.defaultValue = false;
|
|
20
|
+
this.value = false;
|
|
21
|
+
this.previousValue = false;
|
|
22
|
+
|
|
23
|
+
this.minDwell = minDwell;
|
|
24
|
+
this.maxDwell = maxDwell;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static op = 'rising-edge';
|
|
28
|
+
|
|
29
|
+
static create(args) {
|
|
30
|
+
return new RisingEdgeFilter(args.args);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static parseConfig(configUtil, defn) {
|
|
34
|
+
configUtil.requireObjectParams(defn.args, this.op, {
|
|
35
|
+
optional: true,
|
|
36
|
+
availableAttributes: ['min-dwell', 'max-dwell'],
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const minDwell = defn.args['min-dwell'];
|
|
40
|
+
if (!_.isUndefined(minDwell)) {
|
|
41
|
+
configUtil.requireZeroPositive(minDwell, 'min-dwell', this.op);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const maxDwell = defn.args['max-dwell'];
|
|
45
|
+
if (!_.isUndefined(maxDwell)) {
|
|
46
|
+
configUtil.requireZeroPositive(maxDwell, 'max-dwell', this.op);
|
|
47
|
+
if (_.isNumber(minDwell) && maxDwell <= minDwell) {
|
|
48
|
+
configUtil.throwConfigError('max-dwell must be greater than min-dwell', this.op);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
defn.args = { minDwell, maxDwell };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
update(context, value, time) {
|
|
56
|
+
let skip = false;
|
|
57
|
+
if (!this.available) {
|
|
58
|
+
this.available = true;
|
|
59
|
+
this.forceEmit = true;
|
|
60
|
+
skip = true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.lastSuppliedValue = value;
|
|
64
|
+
this.lastSampleTime = time;
|
|
65
|
+
this.filter(context, value, time);
|
|
66
|
+
if (this.available) {
|
|
67
|
+
if (skip) {
|
|
68
|
+
this.emit('update', false, this.lastSampleTime, context);
|
|
69
|
+
} else {
|
|
70
|
+
this.emitUpdate(context);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
emitUpdate(context) {
|
|
76
|
+
if (this.value && this.valueTime === this.lastSampleTime) {
|
|
77
|
+
const duration = this.valueTime - this.previousValueTime;
|
|
78
|
+
const pass = true
|
|
79
|
+
&& (!this.minDwell || duration >= this.minDwell)
|
|
80
|
+
&& (!this.maxDwell || duration <= this.maxDwell);
|
|
81
|
+
|
|
82
|
+
if (pass) {
|
|
83
|
+
this.emit('update', true, this.lastSampleTime, context);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
this.emit('update', false, this.lastSampleTime, context);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = RisingEdgeFilter;
|