@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,98 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const TransformState = require('./transformState');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Increments an accumulator by each incoming input amount.
|
|
8
|
+
*
|
|
9
|
+
* For every incoming value, the value will be added to the internal accumulator.
|
|
10
|
+
* This will happen even for repeated/non-state-changing inputs. Make sure to control the
|
|
11
|
+
* input via transforms like resample, value-increase-diff, etc.
|
|
12
|
+
*
|
|
13
|
+
* The first value encountered after becoming available will be ignored, priming the accumulator state
|
|
14
|
+
*/
|
|
15
|
+
class AccumulateFilter extends TransformState {
|
|
16
|
+
constructor({ engine, path, args: { reset = {} } }) {
|
|
17
|
+
super();
|
|
18
|
+
|
|
19
|
+
this.engine = engine;
|
|
20
|
+
this.resetExpression = reset.compiledExpression;
|
|
21
|
+
this.resetSources = engine.expressionService.expressionTriggers(`${path}.reset`);
|
|
22
|
+
|
|
23
|
+
this.defaultValue = false;
|
|
24
|
+
this.initReset();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static op = 'accumulate';
|
|
28
|
+
|
|
29
|
+
static create(args) {
|
|
30
|
+
return new AccumulateFilter(args);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static parseConfig(configUtil, defn) {
|
|
34
|
+
configUtil.requireObjectParams(defn.args, 'accumulate', {
|
|
35
|
+
optional: true,
|
|
36
|
+
availableAttributes: ['reset'],
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
let reset = defn.args.reset;
|
|
40
|
+
if (reset !== undefined) {
|
|
41
|
+
try {
|
|
42
|
+
const expression = reset.toString();
|
|
43
|
+
const compiledExpression = configUtil.compileExpression(expression, this.op);
|
|
44
|
+
reset = { expression, compiledExpression };
|
|
45
|
+
} catch (err) {
|
|
46
|
+
configUtil.throwConfigError(`Problem evaluating expression: ${err.message}`, this.op);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
defn.args = { reset };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
initReset() {
|
|
54
|
+
this.value = false;
|
|
55
|
+
this.previousValue = false;
|
|
56
|
+
this.count = 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
update(context, value, time) {
|
|
60
|
+
this.lastSuppliedValue = value;
|
|
61
|
+
this.lastSampleTime = time;
|
|
62
|
+
if (!this.available) {
|
|
63
|
+
this.available = true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
this.filter(context, +value, time);
|
|
67
|
+
this.emitUpdate(context);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
emitUpdate(context) {
|
|
71
|
+
this.count += this.value;
|
|
72
|
+
|
|
73
|
+
// While resetExpression evals true, force count to 0
|
|
74
|
+
if (this.resetExpression) {
|
|
75
|
+
const localState = { this: this.coumt };
|
|
76
|
+
const unavailTerm = _.reduce(this.resetSources, (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 && this.resetExpression.evaluate(localState)) {
|
|
83
|
+
this.count = 0;
|
|
84
|
+
this.emit('update', this.count, this.lastSampleTime, context);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this.emit('update', this.count, this.lastSampleTime, context);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
setUnavailable(context, time) {
|
|
93
|
+
super.setUnavailable(context, time);
|
|
94
|
+
this.initReset();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = AccumulateFilter;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const TransformState = require('./transformState');
|
|
5
|
+
const RingBuffer = require('./util/ringBuffer');
|
|
6
|
+
|
|
7
|
+
// TODO: Consider changing count implementation to support non-clocked input
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* For each incoming sample, emits the average of the last N samples seen.
|
|
11
|
+
*
|
|
12
|
+
* Average is based on the Simple Moving Average.
|
|
13
|
+
* See: https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average
|
|
14
|
+
*/
|
|
15
|
+
class AverageFilter extends TransformState {
|
|
16
|
+
constructor(sampleCount) {
|
|
17
|
+
super();
|
|
18
|
+
|
|
19
|
+
this.size = sampleCount;
|
|
20
|
+
this.initReset();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
static op = 'average';
|
|
24
|
+
|
|
25
|
+
static create(args) {
|
|
26
|
+
return new AverageFilter(args.args.count);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static parseConfig(configUtil, defn) {
|
|
30
|
+
const count = _.get(defn.args, 'count');
|
|
31
|
+
configUtil.requirePositive(count, this.op, this.op);
|
|
32
|
+
|
|
33
|
+
defn.args = { count };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
initReset() {
|
|
37
|
+
this.buffer = new RingBuffer(this.size);
|
|
38
|
+
this.sma = 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
filter(context, value, time) {
|
|
42
|
+
value /= this.size;
|
|
43
|
+
this.sma += value;
|
|
44
|
+
this.sma -= this.buffer.get(this.buffer.getTailIndex());
|
|
45
|
+
this.buffer.push(value);
|
|
46
|
+
|
|
47
|
+
this.commitValue(context, this.sma, time);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
setUnavailable(context, time) {
|
|
51
|
+
super.setUnavailable(context, time);
|
|
52
|
+
this.initReset();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = AverageFilter;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const TransformState = require('./transformState');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Filters out spurious high and low signals with duration less than the debounce window.
|
|
7
|
+
*
|
|
8
|
+
* Effectively "latches" a digital signal to the predominant underlying signal.
|
|
9
|
+
* This filter is implemented with the same logic as the on-delay and off-delay filters, with the
|
|
10
|
+
* particular version chosen based on the current latched signal.
|
|
11
|
+
*
|
|
12
|
+
* This transform introduces lag into the signal equal to the window size.
|
|
13
|
+
*/
|
|
14
|
+
class DebounceFilter extends TransformState {
|
|
15
|
+
constructor(window) {
|
|
16
|
+
super();
|
|
17
|
+
|
|
18
|
+
this.window = window;
|
|
19
|
+
this.defaultValue = false;
|
|
20
|
+
this.initReset();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
static op = 'debounce';
|
|
24
|
+
|
|
25
|
+
static create(args) {
|
|
26
|
+
return new DebounceFilter(args.args.window);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static parseConfig(configUtil, defn) {
|
|
30
|
+
const window = defn.args.window ?? defn.args[defn.func];
|
|
31
|
+
configUtil.requirePositive(window, this.op, this.op);
|
|
32
|
+
|
|
33
|
+
defn.args = { window };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
initReset() {
|
|
37
|
+
this.value = false;
|
|
38
|
+
this.transitionTime = null;
|
|
39
|
+
this.lowStartTime = null;
|
|
40
|
+
this.highStartTime = null;
|
|
41
|
+
this.pendingChangeTime = null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
supportsPendingChanges() {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
filter(context, value, time) {
|
|
49
|
+
if (this.value) {
|
|
50
|
+
this.highStartTime = null;
|
|
51
|
+
this.filterOff(context, value, time);
|
|
52
|
+
} else {
|
|
53
|
+
this.lowStartTime = null;
|
|
54
|
+
this.filterOn(context, value, time);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
filterOff(context, value, time) {
|
|
59
|
+
// Whenever the real signal transitions to low, record a marker at that time.
|
|
60
|
+
if (!value && this.lowStartTime === null) {
|
|
61
|
+
this.lowStartTime = time;
|
|
62
|
+
this.pendingChangeTime = time + this.window;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const lowStart = this.lowStartTime || 0;
|
|
66
|
+
const insideOffDelayWindow = lowStart + this.window >= time;
|
|
67
|
+
|
|
68
|
+
if (value || (!value && time >= this.pendingChangeTime)) {
|
|
69
|
+
this.pendingChangeTime = null;
|
|
70
|
+
} else if (insideOffDelayWindow && !value) {
|
|
71
|
+
// When incoming signal is low but has not been held low long enough, skip
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!insideOffDelayWindow && value && this.value && this.lowStartTime !== null) {
|
|
76
|
+
// If we're outside of the delay window AND both the incoming signal and held signal are high,
|
|
77
|
+
// AND we recorded a lowStartTime marker, then there is high->low transition between this sample
|
|
78
|
+
// and the previous sample that must be recognized.
|
|
79
|
+
|
|
80
|
+
const offTime = lowStart + this.window;
|
|
81
|
+
this.commitValue(context, false, offTime);
|
|
82
|
+
this.emitCommittedUpdate(context);
|
|
83
|
+
|
|
84
|
+
this.lowStartTime = null;
|
|
85
|
+
return;
|
|
86
|
+
} else if (!insideOffDelayWindow && !value && this.value && this.lowStartTime !== null) {
|
|
87
|
+
const offTime = lowStart + this.window;
|
|
88
|
+
this.commitValue(context, false, offTime);
|
|
89
|
+
this.emitCommittedUpdate(context);
|
|
90
|
+
|
|
91
|
+
this.lowStartTime = null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// When the real signal transitions to high, clear the low signal marker.
|
|
95
|
+
if (value && this.lowStartTime !== null) {
|
|
96
|
+
this.lowStartTime = null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
this.commitValue(context, value, time);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
filterOn(context, value, time) {
|
|
103
|
+
// Whenever the real signal transitions to high, record a marker at that time.
|
|
104
|
+
if (value && this.highStartTime === null) {
|
|
105
|
+
this.highStartTime = time;
|
|
106
|
+
this.pendingChangeTime = time + this.window;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const highStart = this.highStartTime || 0;
|
|
110
|
+
const insideOnDelayWindow = highStart + this.window >= time;
|
|
111
|
+
|
|
112
|
+
if (!value || (value && time >= this.pendingChangeTime)) {
|
|
113
|
+
this.pendingChangeTime = null;
|
|
114
|
+
} else if (insideOnDelayWindow && value) {
|
|
115
|
+
// When incoming signal is high but has not been held high long enough, skip
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!insideOnDelayWindow && !value && !this.value && this.highStartTime !== null) {
|
|
120
|
+
// If we're outside of the delay window AND both the incoming signal and held signal are low,
|
|
121
|
+
// AND we recorded a highStartTime marker, then there is low->high transition between this sample
|
|
122
|
+
// and the previous sample that must be recognized.
|
|
123
|
+
|
|
124
|
+
const onTime = highStart + this.window;
|
|
125
|
+
this.commitValue(context, true, onTime);
|
|
126
|
+
this.emitCommittedUpdate(context);
|
|
127
|
+
|
|
128
|
+
this.highStartTime = null;
|
|
129
|
+
return;
|
|
130
|
+
} else if (!insideOnDelayWindow && value && !this.value && this.highStartTime !== null) {
|
|
131
|
+
const onTime = highStart + this.window;
|
|
132
|
+
this.commitValue(context, true, onTime);
|
|
133
|
+
this.emitCommittedUpdate(context);
|
|
134
|
+
|
|
135
|
+
this.highStartTime = null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// When the real signal transitions to low, clear the high signal marker.
|
|
139
|
+
if (!value && this.highStartTime !== null) {
|
|
140
|
+
this.highStartTime = null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
this.commitValue(context, value, time);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
setUnavailable(context, time) {
|
|
147
|
+
super.setUnavailable(context, time);
|
|
148
|
+
this.initReset();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
module.exports = DebounceFilter;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const TransformState = require('./transformState');
|
|
4
|
+
|
|
5
|
+
// Deprecated: Use resample instead
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Takes an analog stream and downsamples it to a lower sample rate.
|
|
9
|
+
*
|
|
10
|
+
* Downsampling is calculated by taking every N-th sample seen in the stream. If N is not a whole number,
|
|
11
|
+
* new samples will be interpolated between adjacent samples.
|
|
12
|
+
*/
|
|
13
|
+
class DownsampleFilter extends TransformState {
|
|
14
|
+
constructor(factor) {
|
|
15
|
+
super();
|
|
16
|
+
|
|
17
|
+
this.factor = factor;
|
|
18
|
+
this.initReset();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static op = 'downsample';
|
|
22
|
+
|
|
23
|
+
static create(args) {
|
|
24
|
+
return new DownsampleFilter(args.args.factor);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static parseConfig(configUtil, defn) {
|
|
28
|
+
const { value: factor, usingShorthand } = configUtil.getArgOrShorthand(defn, 'factor');
|
|
29
|
+
configUtil.requirePositive(factor, usingShorthand ? this.op : 'factor', this.op);
|
|
30
|
+
|
|
31
|
+
defn.args = { factor };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
initReset() {
|
|
35
|
+
this.count = 0;
|
|
36
|
+
this.previousValue = 0;
|
|
37
|
+
this.previousTime = 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
filter(context, value, time) {
|
|
41
|
+
this.count += 1;
|
|
42
|
+
|
|
43
|
+
if (this.count === this.factor) {
|
|
44
|
+
this.commitValue(context, value, time);
|
|
45
|
+
} else if (this.count > this.factor) {
|
|
46
|
+
const lerp = this.count - this.factor;
|
|
47
|
+
const lerpValue = (this.previousValue * lerp) + (value * (1 - lerp));
|
|
48
|
+
const lerpTime = (this.previousTime * lerp) + (time * (1 - lerp));
|
|
49
|
+
this.commitValue(context, lerpValue, lerpTime);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
this.previousValue = value;
|
|
53
|
+
this.previousTime = time;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
emitUpdate(context) {
|
|
57
|
+
if (this.count >= this.factor) {
|
|
58
|
+
this.count -= this.factor;
|
|
59
|
+
super.emitCommittedUpdate(context);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
setUnavailable(context, time) {
|
|
64
|
+
super.setUnavailable(context, time);
|
|
65
|
+
this.initReset();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = DownsampleFilter;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const TransformState = require('./transformState');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Finds all edges (T->F and F->T) in a digital stream.
|
|
7
|
+
*
|
|
8
|
+
* For every incoming sample, the outgoing sample will be false. When an edge is found, an additional true value
|
|
9
|
+
* will be emitted before the false value, resulting in a 0-duration "event".
|
|
10
|
+
*/
|
|
11
|
+
class EdgeFilter extends TransformState {
|
|
12
|
+
constructor() {
|
|
13
|
+
super();
|
|
14
|
+
|
|
15
|
+
this.defaultValue = false;
|
|
16
|
+
this.value = false;
|
|
17
|
+
this.previousValue = false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static op = 'edge';
|
|
21
|
+
|
|
22
|
+
static create(_args) {
|
|
23
|
+
return new EdgeFilter();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
emitUpdate(context) {
|
|
27
|
+
if (this.valueTime === this.lastSampleTime) {
|
|
28
|
+
this.emit('update', true, this.lastSampleTime, context);
|
|
29
|
+
}
|
|
30
|
+
this.emit('update', false, this.lastSampleTime, context);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = EdgeFilter;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const TransformState = require('./transformState');
|
|
5
|
+
const ChainSource = require('./util/chainSource');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Transforms each value by a pre-parsed mathjs expression.
|
|
9
|
+
*
|
|
10
|
+
* Filter must be provided with the backing engine to have access to current variable states, which can
|
|
11
|
+
* be referenced in expressions by name. The special name 'this' refers to the input value at this stage
|
|
12
|
+
* of the transform chain.
|
|
13
|
+
*
|
|
14
|
+
* MathJS expressions are capable of dealing with any input types, and may produce output of differing type.
|
|
15
|
+
*/
|
|
16
|
+
class ExpressionFilter extends TransformState {
|
|
17
|
+
constructor({ engine, path, rootPath, baseTransform, args: { expression, compiledExpression } }) {
|
|
18
|
+
super();
|
|
19
|
+
|
|
20
|
+
if (baseTransform) {
|
|
21
|
+
this.origin = new ChainSource(this, { engine, path, rootPath }, {
|
|
22
|
+
onWatchSource: (name) => {
|
|
23
|
+
if (expression === name) {
|
|
24
|
+
this.simpleExpression = true;
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
this.engine = engine;
|
|
31
|
+
this.compiledExpression = compiledExpression;
|
|
32
|
+
this.selfSources = engine.expressionService.expressionTriggers(path);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static op = 'expression';
|
|
36
|
+
|
|
37
|
+
static create(args) {
|
|
38
|
+
return new ExpressionFilter(args);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static parseConfig(configUtil, defn) {
|
|
42
|
+
const expression = (defn.args[defn.func] ?? '').toString();
|
|
43
|
+
const compiledExpression = configUtil.compileExpression(expression, this.op);
|
|
44
|
+
|
|
45
|
+
defn.args = { expression, compiledExpression };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static getExpressions(configUtil, body, info) {
|
|
49
|
+
return configUtil.getExpressions(body, info);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
update(context, value, time) {
|
|
53
|
+
if (value === 'UNAVAILABLE') {
|
|
54
|
+
this.setUnavailable(context, time);
|
|
55
|
+
} else {
|
|
56
|
+
super.update(context, value, time);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
filter(context, value, time) {
|
|
61
|
+
if (this.constantSource || this.simpleSource) {
|
|
62
|
+
this.commitValue(context, value, time);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const localState = {};
|
|
67
|
+
const unavailTerm = _.reduce(this.selfSources, (res, name) => {
|
|
68
|
+
const state = this.engine.getState(name);
|
|
69
|
+
localState[name] = state?.value;
|
|
70
|
+
return res || !state || !state.available;
|
|
71
|
+
}, false);
|
|
72
|
+
|
|
73
|
+
if (unavailTerm) {
|
|
74
|
+
this.setUnavailable(context, time);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
if (!this.origin) {
|
|
80
|
+
localState.this = value;
|
|
81
|
+
}
|
|
82
|
+
this.commitValue(context, this.compiledExpression.evaluate(localState), time);
|
|
83
|
+
} catch (err) {
|
|
84
|
+
// Probably a type error in one of the arguments.
|
|
85
|
+
this.recordError(context, err, time);
|
|
86
|
+
this.setUnavailable(context, time);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = ExpressionFilter;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const TransformState = require('./transformState');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Finds all falling edges (T->F) in a digital stream.
|
|
8
|
+
*
|
|
9
|
+
* For every incoming sample, the outgoing sample will be false. When a falling 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 FallingEdgeFilter 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 = 'falling-edge';
|
|
28
|
+
|
|
29
|
+
static create(args) {
|
|
30
|
+
return new FallingEdgeFilter(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
|
+
emitUpdate(context) {
|
|
56
|
+
if (!this.value && this.valueTime === this.lastSampleTime) {
|
|
57
|
+
const duration = this.valueTime - this.previousValueTime;
|
|
58
|
+
const pass = true
|
|
59
|
+
&& (!this.minDwell || duration >= this.minDwell)
|
|
60
|
+
&& (!this.maxDwell || duration <= this.maxDwell);
|
|
61
|
+
|
|
62
|
+
if (pass) {
|
|
63
|
+
this.emit('update', true, this.lastSampleTime, context);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
this.emit('update', false, this.lastSampleTime, context);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = FallingEdgeFilter;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const Buffer = require('buffer/').Buffer;
|
|
5
|
+
const TransformState = require('./transformState');
|
|
6
|
+
|
|
7
|
+
const OFFSET_PATTERN = /(^|(offset=))(?<offset>\d+)/i;
|
|
8
|
+
const TYPE_PATTERN = /(type=)?(?<type>((u?int)(8|16|32|64))|(float(32|64)?)|double|string)/i;
|
|
9
|
+
const ORDER_PATTERN = /((order=)|(8|16|32|64|float|double)|,\s*|\s+)(?<order>be|le)(,|,?\s+|$)/i;
|
|
10
|
+
const LENGTH_PATTERN = /((length=)|string\s*)(?<length>\d+)/i;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Extracts a value from a buffer
|
|
14
|
+
*/
|
|
15
|
+
class FromBufferFilter extends TransformState {
|
|
16
|
+
constructor(spec) {
|
|
17
|
+
super();
|
|
18
|
+
|
|
19
|
+
this.spec = spec;
|
|
20
|
+
this.offset = +(this.matchPattern(spec, OFFSET_PATTERN, 'offset') ?? 0);
|
|
21
|
+
this.type = (this.matchPattern(spec, TYPE_PATTERN, 'type') ?? 'uint8').toUpperCase();
|
|
22
|
+
this.order = (this.matchPattern(spec, ORDER_PATTERN, 'order') ?? 'be').toUpperCase();
|
|
23
|
+
this.length = +(this.matchPattern(spec, LENGTH_PATTERN, 'length') ?? 0);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static op = 'from-buffer';
|
|
27
|
+
|
|
28
|
+
static create(args) {
|
|
29
|
+
return new FromBufferFilter(args.args.spec);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static parseConfig(configUtil, defn) {
|
|
33
|
+
const spec = _.get(defn.args, 'spec', defn.args[defn.func]);
|
|
34
|
+
|
|
35
|
+
defn.args = { spec };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
filter(context, value, time) {
|
|
39
|
+
if (!Buffer.isBuffer(value)) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const pulled = this.readValue(value);
|
|
45
|
+
this.commitValue(context, pulled, time);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
this.recordError(context, err, time);
|
|
48
|
+
this.setUnavailable(context, time);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
matchPattern(spec, pattern, group) {
|
|
53
|
+
const match = spec.match(pattern) ?? { };
|
|
54
|
+
return match?.groups?.[group];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
readValue(buf) {
|
|
58
|
+
switch (this.type) {
|
|
59
|
+
case 'INT8':
|
|
60
|
+
return buf.readInt8(this.offset);
|
|
61
|
+
case 'UINT8':
|
|
62
|
+
return buf.readUInt8(this.offset);
|
|
63
|
+
case 'INT16':
|
|
64
|
+
return this.order === 'BE' ? buf.readInt16BE(this.offset) : buf.readInt16LE(this.offset);
|
|
65
|
+
case 'UINT16':
|
|
66
|
+
return this.order === 'BE' ? buf.readUInt16BE(this.offset) : buf.readUInt16LE(this.offset);
|
|
67
|
+
case 'INT32':
|
|
68
|
+
return this.order === 'BE' ? buf.readInt32BE(this.offset) : buf.readInt32LE(this.offset);
|
|
69
|
+
case 'UINT32':
|
|
70
|
+
return this.order === 'BE' ? buf.readUInt32BE(this.offset) : buf.readUInt32LE(this.offset);
|
|
71
|
+
case 'INT64':
|
|
72
|
+
return this.order === 'BE' ? buf.readBigInt64BE(this.offset) : buf.readBigInt64LE(this.offset);
|
|
73
|
+
case 'UINT64':
|
|
74
|
+
return this.order === 'BE' ? buf.readBigUInt64BE(this.offset) : buf.readBigUInt64LE(this.offset);
|
|
75
|
+
case 'FLOAT':
|
|
76
|
+
case 'FLOAT32':
|
|
77
|
+
return this.order === 'BE' ? buf.readFloatBE(this.offset) : buf.readFloatLE(this.offset);
|
|
78
|
+
case 'DOUBLE':
|
|
79
|
+
case 'FLOAT64':
|
|
80
|
+
return this.order === 'BE' ? buf.readDoubleBE(this.offset) : buf.readDoubleLE(this.offset);
|
|
81
|
+
case 'STRING':
|
|
82
|
+
return buf.toString('utf8', this.offset, this.length > 0 ? this.offset + this.length : buf.length);
|
|
83
|
+
default:
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = FromBufferFilter;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const md5 = require('md5');
|
|
4
|
+
const sha1 = require('sha1');
|
|
5
|
+
const TransformState = require('./transformState');
|
|
6
|
+
|
|
7
|
+
const ALGOS = {
|
|
8
|
+
md5, sha1,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Calculates the hash of a value in the data stream
|
|
13
|
+
*/
|
|
14
|
+
class HashFilter extends TransformState {
|
|
15
|
+
constructor(algo) {
|
|
16
|
+
super();
|
|
17
|
+
|
|
18
|
+
this.algo = algo;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static op = 'hash';
|
|
22
|
+
|
|
23
|
+
static create(args) {
|
|
24
|
+
return new HashFilter(args.args.algo);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static parseConfig(configUtil, defn) {
|
|
28
|
+
const algo = defn.args.algorithm ?? defn.args[defn.func];
|
|
29
|
+
if (!ALGOS[algo]) {
|
|
30
|
+
configUtil.throwConfigError(`hash must be one of [${Object.keys(ALGOS).join(', ')}]`, this.op);
|
|
31
|
+
}
|
|
32
|
+
defn.args = { algo };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
filter(context, value, time) {
|
|
36
|
+
value = ALGOS[this.algo](value);
|
|
37
|
+
this.commitValue(context, value, time);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = HashFilter;
|