@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,215 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const ConfigError = require('../configError');
|
|
5
|
+
|
|
6
|
+
class BrotherHTTPConfig {
|
|
7
|
+
constructor(expressionService) {
|
|
8
|
+
this.expressionService = expressionService;
|
|
9
|
+
this.macroSettings = expressionService.config['macro-settings'] || {};
|
|
10
|
+
|
|
11
|
+
this.DataCollectionModes =
|
|
12
|
+
{
|
|
13
|
+
HTTP: 'HTTP',
|
|
14
|
+
FTP: 'FTP',
|
|
15
|
+
MIXED: 'MIXED',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const mode = expressionService.config['collection-mode'];
|
|
19
|
+
const defaultMode = this.DataCollectionModes.MIXED;
|
|
20
|
+
if (mode === undefined) this.dataCollectionMode = defaultMode;
|
|
21
|
+
else if (mode.toLowerCase() === 'http') this.dataCollectionMode = this.DataCollectionModes.HTTP;
|
|
22
|
+
else if (mode.toLowerCase() === 'ftp') this.dataCollectionMode = this.DataCollectionModes.FTP;
|
|
23
|
+
else if (mode.toLowerCase() === 'mixed') this.dataCollectionMode = this.DataCollectionModes.MIXED;
|
|
24
|
+
|
|
25
|
+
if (this.dataCollectionMode === this.DataCollectionModes.FTP) {
|
|
26
|
+
this.macros = expressionService.config.macros || {};
|
|
27
|
+
} else {
|
|
28
|
+
this.macros = {};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
_.each(
|
|
32
|
+
this.getIdentifiers(_.keys(this.macros)),
|
|
33
|
+
name => this.expressionService.addName(name)
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getFixedKeys() {
|
|
38
|
+
if (this.dataCollectionMode === this.DataCollectionModes.HTTP) {
|
|
39
|
+
return [
|
|
40
|
+
'part_count',
|
|
41
|
+
'custom_part_count_1',
|
|
42
|
+
'custom_part_count_2',
|
|
43
|
+
'custom_part_count_3',
|
|
44
|
+
'custom_part_count_4',
|
|
45
|
+
'operating_status',
|
|
46
|
+
'program_number_1',
|
|
47
|
+
'program_number_2',
|
|
48
|
+
'execution',
|
|
49
|
+
'machine_name',
|
|
50
|
+
'machine_model',
|
|
51
|
+
];
|
|
52
|
+
} else if (this.dataCollectionMode === this.DataCollectionModes.MIXED) {
|
|
53
|
+
return [
|
|
54
|
+
'part_count',
|
|
55
|
+
'custom_part_count_1',
|
|
56
|
+
'custom_part_count_2',
|
|
57
|
+
'custom_part_count_3',
|
|
58
|
+
'custom_part_count_4',
|
|
59
|
+
'operating_status',
|
|
60
|
+
'program_number_1',
|
|
61
|
+
'program_number_2',
|
|
62
|
+
'execution',
|
|
63
|
+
'program_comment_1',
|
|
64
|
+
'program_comment_2',
|
|
65
|
+
'machine_name',
|
|
66
|
+
'machine_model',
|
|
67
|
+
];
|
|
68
|
+
} else if (this.dataCollectionMode === this.DataCollectionModes.FTP) {
|
|
69
|
+
return [
|
|
70
|
+
'part_count',
|
|
71
|
+
'custom_part_count_1',
|
|
72
|
+
'custom_part_count_2',
|
|
73
|
+
'custom_part_count_3',
|
|
74
|
+
'custom_part_count_4',
|
|
75
|
+
'operating_status',
|
|
76
|
+
'cutting_1',
|
|
77
|
+
'cutting_2',
|
|
78
|
+
'program_number_1',
|
|
79
|
+
'program_number_2',
|
|
80
|
+
'rapid_traverse_override',
|
|
81
|
+
'feedrate_override',
|
|
82
|
+
'spindle_override',
|
|
83
|
+
'feedrate',
|
|
84
|
+
'spindle_speed',
|
|
85
|
+
'execution',
|
|
86
|
+
'emergency_stop',
|
|
87
|
+
'program_comment_1',
|
|
88
|
+
'program_comment_2',
|
|
89
|
+
'controller_mode',
|
|
90
|
+
];
|
|
91
|
+
} else return [];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
getIdentifiers(macroKeys) {
|
|
95
|
+
return _.map(this.getFixedKeys().concat(macroKeys), (name) => {
|
|
96
|
+
return {
|
|
97
|
+
name,
|
|
98
|
+
channel: 'device',
|
|
99
|
+
defaultValue: 0,
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** For non-FTP modes a determination of whether or not part_count
|
|
105
|
+
* is available can only be made in the adapter; exclude here, added
|
|
106
|
+
* in adapter as appropriate. Do the same for program_number_2 (mixed/http) and
|
|
107
|
+
* program_comment_2 (mixed), meaningful only for two pallet machines.
|
|
108
|
+
*
|
|
109
|
+
* Leave the FTP mode alone since it is at this point at any rate considered experimental.
|
|
110
|
+
*/
|
|
111
|
+
postParse(config) {
|
|
112
|
+
if (this.dataCollectionMode === this.DataCollectionModes.HTTP ||
|
|
113
|
+
this.dataCollectionMode === this.DataCollectionModes.MIXED) {
|
|
114
|
+
_.each(this.getFixedKeys().concat(_.map(this.macros, (_value, key) => { return key; })), (key) => {
|
|
115
|
+
if (key !== 'part_count' && key !== 'program_number_2' && key !== 'program_comment_2') {
|
|
116
|
+
config.adapter.loadDataItem(key, key);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
} else {
|
|
120
|
+
_.each(this.getFixedKeys().concat(_.map(this.macros, (_value, key) => { return key; })), (key) => {
|
|
121
|
+
config.adapter.loadDataItem(key, key);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
parse(config = {}) {
|
|
127
|
+
this.endpoint = config.endpoint;
|
|
128
|
+
this.username = config.username;
|
|
129
|
+
this.password = config.password;
|
|
130
|
+
|
|
131
|
+
if (!config.endpoint) {
|
|
132
|
+
throw new ConfigError('No endpoint defined').atAttribute('endpoint');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const [host, port] = config.endpoint.split(':');
|
|
136
|
+
this.host = host;
|
|
137
|
+
this.port = port || 21;
|
|
138
|
+
|
|
139
|
+
this.scanInterval = this.checkScanInterval(config);
|
|
140
|
+
this.httpInterval = this.checkHttpInterval(config);
|
|
141
|
+
|
|
142
|
+
/** Configuration indicates how to collect data:
|
|
143
|
+
* HTTP only,
|
|
144
|
+
* FTP only (default),
|
|
145
|
+
* HTTP + FTP for program headers
|
|
146
|
+
*/
|
|
147
|
+
let mode = config['collection-mode'] ?? '';
|
|
148
|
+
if (_.isString(mode)) {
|
|
149
|
+
mode = mode.toLowerCase();
|
|
150
|
+
}
|
|
151
|
+
if (!_.includes(['http', 'ftp', 'mixed'], mode)) {
|
|
152
|
+
throw new ConfigError('collection-mode must be one of: {http, ftp, mixed}').atAttribute('macros');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
let keysThatAlreadyExist = '';
|
|
156
|
+
_.forEach(this.macros, (_value, key) => {
|
|
157
|
+
if (_.findIndex(this.getFixedKeys(), (itm) => { return itm === key; }) !== -1) {
|
|
158
|
+
keysThatAlreadyExist = `${keysThatAlreadyExist}${key}; `;
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
if (keysThatAlreadyExist !== '') {
|
|
162
|
+
throw new ConfigError(`The following macro keys are already in use: ${keysThatAlreadyExist}`).atAttribute('macros');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Macro program call control type set in the Data Bank -> G/M code macro section:
|
|
166
|
+
* 0 = Type 1
|
|
167
|
+
* 1 = Type 2
|
|
168
|
+
*/
|
|
169
|
+
if (this.macroSettings.type === undefined) this.macroSettings.type = 'type1';
|
|
170
|
+
if (!(this.macroSettings.type === 'type1' || this.macroSettings.type === 'type2')) {
|
|
171
|
+
throw new ConfigError('The value of macro-settings key type must be either type1 or type2').atAttribute('type');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (this.macroSettings.units === undefined) {
|
|
175
|
+
this.macroSettings.units = 'inches';
|
|
176
|
+
}
|
|
177
|
+
if (!(this.macroSettings.units === 'inches' || this.macroSettings.units === 'metric')) {
|
|
178
|
+
throw new ConfigError('The value of macro-settings key units must be either inches or metric').atAttribute('units');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (this.macroSettings.databank === undefined) {
|
|
182
|
+
this.macroSettings.databank = 1;
|
|
183
|
+
}
|
|
184
|
+
const dataBankNum = Number(this.macroSettings.databank);
|
|
185
|
+
if (Number.isNaN(dataBankNum) || dataBankNum < 0 || dataBankNum > 10) {
|
|
186
|
+
throw new ConfigError('The value of macro-settings key databank must be 0 <= x <= 9').atAttribute('databank');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
_.forEach(this.macros, (value, key) => {
|
|
190
|
+
if (value === '' || value === null || Number.isNaN(Number(value))) {
|
|
191
|
+
throw new ConfigError(`The value of macros key ${key} must be a valid integer`).atAttribute(key);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
checkScanInterval(config) {
|
|
197
|
+
const scanIntvl = +(config['scan-interval'] || 1.0);
|
|
198
|
+
if (scanIntvl <= 0) {
|
|
199
|
+
throw new ConfigError('scan-interval must be > 0').atAttribute('scan-interval');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return Math.ceil(scanIntvl * 1000);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
checkHttpInterval(config) {
|
|
206
|
+
const httpIntvl = +(config['http-interval'] || 0.05);
|
|
207
|
+
if (httpIntvl <= 0) {
|
|
208
|
+
throw new ConfigError('http-interval must be > 0').atAttribute('http-interval');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return Math.ceil(httpIntvl * 1000);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
module.exports = BrotherHTTPConfig;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const ConfigError = require('../configError');
|
|
5
|
+
const validations = require('../common/validations');
|
|
6
|
+
|
|
7
|
+
class EthernetIPConfig {
|
|
8
|
+
constructor(expressionService) {
|
|
9
|
+
this.expressionService = expressionService;
|
|
10
|
+
|
|
11
|
+
_.each(this.getIdentifiers(expressionService.config), name => this.expressionService.addName(name));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
getIdentifiers(config = {}) {
|
|
15
|
+
return _.map(_.keys(config.tags), (name) => {
|
|
16
|
+
return {
|
|
17
|
+
name,
|
|
18
|
+
channel: 'device',
|
|
19
|
+
defaultValue: 0,
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
parse(config = {}) {
|
|
25
|
+
this.host = this.checkHost(config);
|
|
26
|
+
this.slot = this.checkSlot(config);
|
|
27
|
+
this.scanRate = this.checkScanInterval(config);
|
|
28
|
+
this.groupRead = this.checkGroupRead(config);
|
|
29
|
+
this.reset = validations.checkReset(config);
|
|
30
|
+
this.tags = this.loadTags(config.tags);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
loadTags(tags) {
|
|
34
|
+
// Shorthand syntax: tags: { name: 'path' }
|
|
35
|
+
tags = _.mapValues(tags, (tag, name) => {
|
|
36
|
+
if (_.isString(tag)) {
|
|
37
|
+
// TODO: We'd really like to examine newlines but YAML strips them. Another yaml library could surface this.
|
|
38
|
+
if (/^path\s+/.test(tag)) {
|
|
39
|
+
throw new ConfigError('Malformed tag definition. Possible mix of shorthand and standard form.').atSection('tags').atAttribute(name);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return { path: tag };
|
|
43
|
+
}
|
|
44
|
+
return tag;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return _(tags).map((defn, name) => {
|
|
48
|
+
try {
|
|
49
|
+
const tagPath = this.checkTagPath(defn);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
name,
|
|
53
|
+
path: tagPath,
|
|
54
|
+
verboseSample: !!defn['verbose-sample'],
|
|
55
|
+
verboseEmit: !!defn['verbose-emit'],
|
|
56
|
+
};
|
|
57
|
+
} catch (error) {
|
|
58
|
+
if (error instanceof ConfigError) {
|
|
59
|
+
error.atSection(`tags.${name}`);
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}).keyBy('name').value();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
checkHost(defn) {
|
|
67
|
+
const host = defn.host;
|
|
68
|
+
if (_.isEmpty(host)) {
|
|
69
|
+
throw new ConfigError('An IP or hostname must be specified').atPath('host');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return host;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
checkSlot(defn) {
|
|
76
|
+
const slot = defn.slot || 0;
|
|
77
|
+
return slot;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
checkScanInterval(defn) {
|
|
81
|
+
const scanRate = defn['scan-interval'] || 0.2;
|
|
82
|
+
if (scanRate < 0) {
|
|
83
|
+
throw new ConfigError('Scan interval must be > 0').atPath('scan-interval');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return scanRate * 1000;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
checkGroupRead(defn) {
|
|
90
|
+
const groupRead = _.get(defn, 'group-read', true);
|
|
91
|
+
try {
|
|
92
|
+
return EthernetIPConfig.booleanCast(groupRead);
|
|
93
|
+
} catch (err) {
|
|
94
|
+
throw new ConfigError('Value must be true or false').atPath('group-read');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
checkTagPath(defn) {
|
|
99
|
+
const path = defn.path;
|
|
100
|
+
if (!path) {
|
|
101
|
+
throw new ConfigError('No tag path specified');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!EthernetIPConfig.isValidTagname(path)) {
|
|
105
|
+
throw new ConfigError('Invalid tag path').atAttribute('path');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return path;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static booleanCast(value) {
|
|
112
|
+
if (_.isString(value)) {
|
|
113
|
+
value = value.toLowerCase();
|
|
114
|
+
} else if (_.isNumber(value)) {
|
|
115
|
+
value = value.toString();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (value === true || value === 'true' || value === 'yes' || value === '1') {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
if (value === false || value === 'false' || value === 'no' || value === '0') {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
throw new Error();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* eslint prefer-template: 0 */
|
|
129
|
+
// Tag validation borrowed from ethernet-ip project
|
|
130
|
+
|
|
131
|
+
static isValidTagname(tagname) {
|
|
132
|
+
// regex components
|
|
133
|
+
const nameRegex = (captureIndex) => {
|
|
134
|
+
return `(_?[a-zA-Z]|_\\d)(?:(?=(_?[a-zA-Z0-9]))\\${captureIndex})*`;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const multDimArrayRegex = '(\\[\\d+(,\\d+){0,2}])';
|
|
138
|
+
const arrayRegex = '(\\[\\d+])';
|
|
139
|
+
const bitIndexRegex = '(\\.\\d{1,2})';
|
|
140
|
+
|
|
141
|
+
// user regex for user tags
|
|
142
|
+
const userRegex = new RegExp(
|
|
143
|
+
'^(Program:' +
|
|
144
|
+
nameRegex(3) +
|
|
145
|
+
'\\.)?' + // optional program name
|
|
146
|
+
nameRegex(5) +
|
|
147
|
+
multDimArrayRegex +
|
|
148
|
+
'?' + // tag name
|
|
149
|
+
'(\\.' +
|
|
150
|
+
nameRegex(10) +
|
|
151
|
+
arrayRegex +
|
|
152
|
+
'?)*' + // option member name
|
|
153
|
+
bitIndexRegex +
|
|
154
|
+
'?$'
|
|
155
|
+
); // optional bit index
|
|
156
|
+
// full user regex
|
|
157
|
+
// ^(Program:(_?[a-zA-Z]|_\d)(?:(?=(_?[a-zA-Z0-9]))\3)*\.)?(_?[a-zA-Z]|_\d)(?:(?=(_?[a-zA-Z0-9]))\5)*
|
|
158
|
+
// (\[\d+(,\d+){0,2}])?(\.(_?[a-zA-Z]|_\d)(?:(?=(_?[a-zA-Z0-9]))\10)*(\[\d+])?)*(\.\d{1,2})?$
|
|
159
|
+
|
|
160
|
+
// module regex for module tags
|
|
161
|
+
const moduleRegex = new RegExp(
|
|
162
|
+
'^' +
|
|
163
|
+
nameRegex(2) + // module name
|
|
164
|
+
'(:\\d{1,2})?' + // optional slot num (not required for rack optimized connections)
|
|
165
|
+
':[IOC]' + // input/output/config
|
|
166
|
+
'(\\.' +
|
|
167
|
+
nameRegex(6) +
|
|
168
|
+
arrayRegex +
|
|
169
|
+
'?)?' + // optional member with optional array index
|
|
170
|
+
bitIndexRegex +
|
|
171
|
+
'?$'
|
|
172
|
+
); // optional bit index
|
|
173
|
+
// full module regex
|
|
174
|
+
// ^(_?[a-zA-Z]|_\d)(?:(?=(_?[a-zA-Z0-9]))\2)*(:\d{1,2})?:[IOC](\.(_?[a-zA-Z]|_\d)(?:(?=(_?[a-zA-Z0-9]))\6)*
|
|
175
|
+
// (\[\d+])?)?(\.\d{1,2})?$
|
|
176
|
+
|
|
177
|
+
if (!userRegex.test(tagname) && !moduleRegex.test(tagname)) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// check segments
|
|
182
|
+
if (tagname.split(/[:.[\],]/).filter(segment => segment.length > 40).length > 0) {
|
|
183
|
+
return false; // check that all segments are <= 40 char
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// passed all tests
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
module.exports = EthernetIPConfig;
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const ConfigError = require('../configError');
|
|
5
|
+
|
|
6
|
+
class IfmIotConfig {
|
|
7
|
+
constructor(expressionService) {
|
|
8
|
+
this.expressionService = expressionService;
|
|
9
|
+
|
|
10
|
+
_.each(this.getIdentifiers(expressionService.config), name => this.expressionService.addName(name));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
parse(config = {}) {
|
|
14
|
+
if (config.device !== this.device()) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
this.endpoint = this.parseEndpoint(config.endpoint);
|
|
19
|
+
this.iolink = this.parseIOLink(config.iolink);
|
|
20
|
+
this.findIODD = this.checkFindIODD(config);
|
|
21
|
+
this.scanInterval = this.checkScanInterval(config);
|
|
22
|
+
this.deviceScanInterval = this.checkDeviceScanInterval(config);
|
|
23
|
+
this.declaredKeys = this.getDeclaredKeys(config);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
device() {
|
|
27
|
+
return 'iolink-ifm-iot';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
getIdentifiers(config = {}) {
|
|
31
|
+
const keys = [...this.getRecordKeys(config), ...this.getDeclaredKeys(config)];
|
|
32
|
+
return _.map(keys, (name) => {
|
|
33
|
+
return {
|
|
34
|
+
name,
|
|
35
|
+
channel: 'device',
|
|
36
|
+
defaultValue: 0,
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getRecordKeys(config = {}) {
|
|
42
|
+
if (!_.isObject(config.iolink)) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const ports = _.map(config.iolink, (port, name) => {
|
|
47
|
+
if (!name.match(/^port\d+$/)) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const pdata = port['process-data'];
|
|
52
|
+
if (!_.isObject(pdata)) {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return _.map(pdata, (record, rname) => {
|
|
57
|
+
return rname;
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return _.flatten(ports);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
getDeclaredKeys(config = {}) {
|
|
65
|
+
let attribute = 'declare-keys';
|
|
66
|
+
if (!config[attribute] && config.keys) {
|
|
67
|
+
attribute = 'keys';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const keys = config[attribute];
|
|
71
|
+
if (_.isEmpty(keys)) {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
if (!_.isArray(keys)) {
|
|
75
|
+
throw new ConfigError('list expected').atAttribute(attribute);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return keys;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
checkScanInterval(defn) {
|
|
82
|
+
const scanRate = defn['scan-interval'] || 1;
|
|
83
|
+
if (scanRate <= 0) {
|
|
84
|
+
throw new ConfigError('Scan interval must be > 0').atPath('scan-interval');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return scanRate * 1000;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
checkDeviceScanInterval(defn) {
|
|
91
|
+
const scanRate = defn['device-scan-interval'] || 10;
|
|
92
|
+
if (scanRate < 0) {
|
|
93
|
+
throw new ConfigError('Device scan interval must be >= 0').atPath('device-scan-interval');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return scanRate * 1000;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
parseEndpoint(endpoint) {
|
|
100
|
+
if (!endpoint) {
|
|
101
|
+
throw new ConfigError('No endpoint defined').atAttribute('endpoint');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// HTTP/S URLs are returned directly
|
|
105
|
+
if (endpoint.match(/^https?:\/\//)) {
|
|
106
|
+
return endpoint;
|
|
107
|
+
} else if (endpoint.match(/^\w+:\/\//)) {
|
|
108
|
+
throw new ConfigError('Unexpected endpoint protocol').atAttribute('endpoint');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Assume HTTP protocol by default
|
|
112
|
+
return `http://${endpoint}`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
parseIOLink(iolink) {
|
|
116
|
+
const ports = [];
|
|
117
|
+
const keys = _.keys(iolink);
|
|
118
|
+
|
|
119
|
+
_.each(keys, (key) => {
|
|
120
|
+
const match = key.match(/^port(\d+)$/);
|
|
121
|
+
if (match === null) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const portNum = match[1];
|
|
126
|
+
ports.push(this.parsePort(iolink[key], portNum));
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return ports;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
parsePort(port, portNum) {
|
|
133
|
+
return {
|
|
134
|
+
portNumber: portNum,
|
|
135
|
+
enabled: this.parseBool(port.enabled ?? true),
|
|
136
|
+
processData: this.parseProcessData(port),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
parseProcessData(defn) {
|
|
141
|
+
const data = defn['process-data'] ?? {};
|
|
142
|
+
if (!_.isObject(data)) {
|
|
143
|
+
throw new ConfigError('Invalid processd data').atAttribute('process-data');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return _.map(data, this.parseProcessDataRecord.bind(this));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
parseProcessDataRecord(record, name) {
|
|
150
|
+
return {
|
|
151
|
+
name,
|
|
152
|
+
enabled: this.parseBool(record.enabled ?? true),
|
|
153
|
+
bitOffset: this.checkBitOffset(record),
|
|
154
|
+
bitLength: this.checkBitLength(record),
|
|
155
|
+
type: this.checkType(record),
|
|
156
|
+
gradient: this.checkGradient(record),
|
|
157
|
+
offset: this.checkOffset(record),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
checkPortEnabled(defn) {
|
|
162
|
+
return this.parseBool(defn.enabled);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
checkBitOffset(defn) {
|
|
166
|
+
const offset = defn['bit-offset'];
|
|
167
|
+
if (_.isUndefined(offset)) {
|
|
168
|
+
throw new ConfigError('bit-offset must be specified').atAttribute('bit-offset');
|
|
169
|
+
}
|
|
170
|
+
if (!_.isNumber(offset) || offset < 0) {
|
|
171
|
+
throw new ConfigError('bit-offset must be >= 0').atAttribute('bit-offset');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return offset;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
checkBitLength(defn) {
|
|
178
|
+
const length = defn['bit-length'];
|
|
179
|
+
if (_.isUndefined(length)) {
|
|
180
|
+
throw new ConfigError('bit-length must be specified').atAttribute('bit-length');
|
|
181
|
+
}
|
|
182
|
+
if (!_.isNumber(length) || length < 0) {
|
|
183
|
+
throw new ConfigError('bit-length must be >= 0').atAttribute('bit-length');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return length;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
checkType(defn) {
|
|
190
|
+
let type = defn.type;
|
|
191
|
+
if (_.isString(type)) {
|
|
192
|
+
type = type.toLowerCase();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (!_.includes(['bool', 'uint', 'int', 'float', 'booleant', 'uintegert', 'integert', 'float32t'], type)) {
|
|
196
|
+
throw new ConfigError('type must be one of the supported data types: [bool, uint, int, float]').atAttribute('type');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return type;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
checkGradient(defn) {
|
|
203
|
+
const gradient = defn.gradient;
|
|
204
|
+
if (_.isUndefined(gradient)) {
|
|
205
|
+
return 1;
|
|
206
|
+
}
|
|
207
|
+
if (!_.isNumber(gradient)) {
|
|
208
|
+
throw new ConfigError('gradient must be numeric').atAttribute('gradient');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return gradient;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
checkOffset(defn) {
|
|
215
|
+
const offset = defn.offset;
|
|
216
|
+
if (_.isUndefined(offset)) {
|
|
217
|
+
return 0;
|
|
218
|
+
}
|
|
219
|
+
if (!_.isNumber(offset)) {
|
|
220
|
+
throw new ConfigError('offset must be numeric').atAttribute('offset');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return offset;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
checkFindIODD(defn) {
|
|
227
|
+
const findIODD = defn['find-iodd'];
|
|
228
|
+
if (_.isUndefined(findIODD)) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return this.parseBool(findIODD);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
parseBool(value) {
|
|
236
|
+
if (_.isString(value)) {
|
|
237
|
+
value = value.toLowerCase();
|
|
238
|
+
return value === 'true';
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return !!_.toInteger(value);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
module.exports = IfmIotConfig;
|