node-red-contrib-ta-cmi-coe 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +31 -17
  2. package/README.de.md +225 -0
  3. package/README.md +134 -187
  4. package/coe/coe-input.js +49 -36
  5. package/coe/coe-monitor.html +1 -1
  6. package/coe/coe-monitor.js +32 -41
  7. package/coe/coe-output.html +67 -28
  8. package/coe/coe-output.js +159 -24
  9. package/coe/config.js +14 -7
  10. package/coe/locales/de/coe-input.html +2 -2
  11. package/coe/locales/de/coe-input.json +8 -2
  12. package/coe/locales/de/coe-monitor.html +1 -2
  13. package/coe/locales/de/coe-monitor.json +9 -4
  14. package/coe/locales/de/coe-output.html +8 -2
  15. package/coe/locales/de/coe-output.json +11 -5
  16. package/coe/locales/de/config.html +3 -3
  17. package/coe/locales/de/config.json +1 -1
  18. package/coe/locales/en-US/coe-input.html +2 -2
  19. package/coe/locales/en-US/coe-input.json +8 -2
  20. package/coe/locales/en-US/coe-monitor.html +1 -2
  21. package/coe/locales/en-US/coe-monitor.json +9 -4
  22. package/coe/locales/en-US/coe-output.html +8 -2
  23. package/coe/locales/en-US/coe-output.json +10 -4
  24. package/coe/locales/en-US/config.html +3 -3
  25. package/coe/locales/en-US/config.json +1 -1
  26. package/coe/units-config.js +2 -1
  27. package/examples/in-out.json +56 -0
  28. package/examples/monitor.json +42 -0
  29. package/lib/coe-v1.js +152 -0
  30. package/lib/coe-v2.js +80 -119
  31. package/lib/protocol.js +19 -0
  32. package/lib/queueing.js +92 -137
  33. package/lib/units.js +7 -7
  34. package/lib/utils.js +71 -66
  35. package/package.json +17 -3
  36. package/__tests__/blockinfo.test.js +0 -24
  37. package/__tests__/conversion.test.js +0 -22
  38. package/__tests__/udp.test.js +0 -46
  39. package/lib/coe.js +0 -109
@@ -8,11 +8,12 @@
8
8
 
9
9
  module.exports = function(RED) {
10
10
  'use strict';
11
- const { getUnitInfo } = require('../lib/utils')
11
+ const { getUnitInfo, getDigitalStateKey } = require('../lib/utils')
12
12
 
13
13
  function CoEMonitorNode(config) {
14
14
  RED.nodes.createNode(this, config);
15
15
  const node = this;
16
+ node._ = RED._;
16
17
 
17
18
  node.cmiConfig = RED.nodes.getNode(config.cmiconfig);
18
19
 
@@ -31,22 +32,22 @@ module.exports = function(RED) {
31
32
  let packetCount = 0;
32
33
  let lastUpdate = Date.now();
33
34
 
34
- const listener = (data) => { // Listener for incoming data
35
- if (!data || !data.blocks || !Array.isArray(data.blocks)) {
35
+ const listener = (received) => { // Listener for incoming data
36
+ if (!received || !received.data || !Array.isArray(received.data)) {
36
37
  node.warn('Received invalid data format');
37
38
  return;
38
39
  }
39
40
 
40
- for (let block of data.blocks) {
41
- if (!block) continue;
41
+ for (let canNode of received.data) {
42
+ if (!canNode) continue;
42
43
 
43
44
  if (node.filterNodeNumber !== null && // Filter by Node Number
44
45
  node.filterNodeNumber !== 0 &&
45
- block.nodeNumber !== node.filterNodeNumber) {
46
+ canNode.nodeNumber !== node.filterNodeNumber) {
46
47
  continue;
47
48
  }
48
49
 
49
- const isDigital = (block.blockNumber === 0 || block.blockNumber === 9); // Filter by Data Type
50
+ const isDigital = (canNode.dataType === 'digital'); // Filter by Data Type
50
51
  const isAnalog = !isDigital;
51
52
 
52
53
  if (node.filterDataType === 'analog' && !isAnalog) continue;
@@ -58,48 +59,38 @@ module.exports = function(RED) {
58
59
  // Build message
59
60
  const msg = {
60
61
  payload: {
61
- nodeNumber: block.nodeNumber,
62
- blockNumber: block.blockNumber,
63
- dataType: isDigital ? 'digital' : 'analog',
64
- values: block.values,
65
- units: block.units,
66
- sourceIP: data.sourceIP,
67
- version: data.version,
68
62
  timestamp: new Date().toISOString(),
69
- rawBuffer: data.rawBuffer ? data.rawBuffer.toString('hex').toUpperCase() : null
63
+ sourceIP: received.sourceIP,
64
+ version: received.version,
65
+ nodeNumber: canNode.nodeNumber,
66
+ ...(canNode.blockNumber && { blockNumber: canNode.blockNumber}),
67
+ dataType: canNode.dataType
70
68
  },
71
- topic: `coe/monitor/${block.nodeNumber}/block/${block.blockNumber}`
69
+ topic: `coe/monitor/${canNode.nodeNumber}/block/${canNode.dataType}`
72
70
  };
73
71
 
74
- // Additional Details for Analog Blocks
75
- if (isAnalog && block.units) {
76
- msg.payload.valuesDetailed = block.values.map((value, idx) => {
77
- const unitInfo = getUnitInfo(block.units[idx], lang);
78
- RED.log.debug(`Unit Info for unit ${block.units[idx]}: ${JSON.stringify(unitInfo)} + version: ${coeVersion} + key: ${unitInfo.key} + symbol: ${unitInfo.symbol}`);
79
- const outputNumber = (block.blockNumber - 1) * 4 + idx + 1;
80
- return {
81
- outputNumber: outputNumber,
82
- value: value,
83
- unit: block.units[idx],
72
+ // Additional Details for Digital/Analog Blocks
73
+ const valuesDetailed = {};
74
+
75
+ if (canNode.outputs) {
76
+ Object.entries(canNode.outputs).forEach(([outputNumber, output]) => {
77
+ const unitInfo = getUnitInfo(output.unit, lang);
78
+ const translationKey = getDigitalStateKey(output.unit, output.value, "coe-monitor.status.");
79
+
80
+ valuesDetailed[outputNumber] = {
81
+ value: output.value,
82
+ ...(isDigital && { state: node._(translationKey) }),
83
+ unit: output.unit,
84
84
  unitName: unitInfo.name,
85
- unitSymbol: unitInfo.symbol
85
+ ...(isAnalog && { unitSymbol: unitInfo.symbol })
86
86
  };
87
87
  });
88
- }
89
-
90
- // Additional Details for Digital Blocks
91
- if (isDigital) {
92
- const baseOutput = block.blockNumber === 0 ? 1 : 17;
93
- msg.payload.valuesDetailed = block.values.map((value, idx) => ({
94
- outputNumber: baseOutput + idx,
95
- value: value === 1,
96
- state: value === 1 ? 'ON' : 'OFF'
97
- }));
88
+ msg.payload.values = valuesDetailed;
98
89
  }
99
90
 
100
91
  // Raw Data
101
92
  if (node.includeRaw) {
102
- msg.payload.raw = block;
93
+ msg.payload.raw = { raw: received.rawBuffer ? received.rawBuffer.toString('hex').toUpperCase() : null}
103
94
  }
104
95
 
105
96
  node.send(msg);
@@ -109,13 +100,13 @@ module.exports = function(RED) {
109
100
  node.status({
110
101
  fill: "green",
111
102
  shape: "dot",
112
- text: RED._("coe-monitor.status.node") + ` ${block.nodeNumber} B${block.blockNumber}[${dataTypeLabel}] - ${packetCount} Pkts`
103
+ text: node._("coe-monitor.status.node") + ` ${canNode.nodeNumber} ${dataTypeLabel} - ${packetCount} Pkts`
113
104
  });
114
105
  }
115
106
  };
116
107
 
117
108
  node.cmiConfig.registerListener(listener);
118
- node.status({fill: "grey", shape: "ring", text: "coe-monitor.status.monitoring"});
109
+ node.status({fill: "grey", shape: "ring", text: node._("coe-monitor.status.monitoring") + "..."});
119
110
 
120
111
  // Status Update Timer (shows last activity)
121
112
  const statusTimer = setInterval(() => {
@@ -124,7 +115,7 @@ module.exports = function(RED) {
124
115
  node.status({
125
116
  fill: "yellow",
126
117
  shape: "ring",
127
- text: RED._("coe-monitor.status.idle") + ` ${secsSinceUpdate}s - ${packetCount} Pkts [v${coeVersion}]`
118
+ text: node._("coe-monitor.status.monitoring") + ` ${secsSinceUpdate}s - ${packetCount} Pkt. [v${coeVersion}]`
128
119
  });
129
120
  }
130
121
  }, 5000);
@@ -15,7 +15,10 @@
15
15
  nodeNumber: { value: 1, validate: RED.validators.number() },
16
16
  outputNumber: { value: 1, validate: RED.validators.number() },
17
17
  dataType: { value: "analog" },
18
- unit: { value: 0, validate: RED.validators.number() }
18
+ unit: { value: 0, validate: RED.validators.number() },
19
+ minChange: { value: 1, validate: RED.validators.number() },
20
+ blockingTime: { value: 10, validate: RED.validators.number() },
21
+ maxInterval: { value: 5, validate: RED.validators.number() }
19
22
  },
20
23
  inputs: 1,
21
24
  outputs: 2,
@@ -33,37 +36,58 @@
33
36
  const langString = RED.i18n.detectLanguage() || 'en';
34
37
  const editorLanguage = (langString.toLowerCase().startsWith("de")) ? "de" : "en";
35
38
 
36
- // Populate unit dropdown
37
- $.getJSON('ta-cmi-coe/units/' + editorLanguage)
38
- .done(function(unitsData) {
39
- const selectElement = $('#node-input-unit');
40
- selectElement.empty();
39
+ function loadAndFilterUnits(dataType) {
40
+ const isDigital = dataType === 'analog' ? false : true;
41
41
 
42
- Object.keys(unitsData).forEach(function(key) {
43
- const unit = unitsData[key];
44
- selectElement.append(
45
- $('<option></option>')
46
- .attr('value', key)
47
- .text(unit.name)
48
- );
49
- });
42
+ $.getJSON('ta-cmi-coe/units/' + editorLanguage)
43
+ .done(function(unitsData) {
44
+ const selectElement = $('#node-input-unit');
45
+ selectElement.empty();
46
+ let foundUnit = false;
47
+
48
+ Object.keys(unitsData).forEach(function(key) {
49
+ const unit = unitsData[key];
50
+
51
+ if (unit.digital === isDigital) {
52
+ selectElement.append(
53
+ $('<option></option>')
54
+ .attr('value', key)
55
+ .text(unit.name)
56
+ );
57
+ if (Number(key) === Number(node.unit)) {
58
+ foundUnit = true;
59
+ }
60
+ }
61
+ });
50
62
 
51
- // Set existing value
52
- selectElement.val(node.unit);
53
- })
54
- .fail(function(jqXHR, textStatus, errorThrown) {
55
- console.error("Error loading unit list:", errorThrown);
56
- RED.notify("Error loading unit list from unit-config.js.", "error");
57
- });
63
+ if (!foundUnit) {
64
+ node.unit = selectElement.find('option:first').attr('value') || 0;
65
+ }
66
+
67
+ selectElement.val(node.unit);
68
+ })
69
+ .fail(function(jqXHR, textStatus, errorThrown) {
70
+ console.error("Error loading unit list:", errorThrown);
71
+ RED.notify("Error loading unit list from unit-config.js.", "error");
72
+ });
73
+ }
58
74
 
59
- $("#node-input-dataType").change(function() {
60
- if ($(this).val() === "analog") {
61
- $("#unit-row").show();
75
+ $("#node-input-dataType").change(function() {
76
+ const selectedDataType = $(this).val();
77
+ loadAndFilterUnits(selectedDataType);
78
+
79
+ if (selectedDataType === "analog") {
80
+ $("#minchange-row").show();
62
81
  } else {
63
- $("#unit-row").hide();
82
+ $("#minchange-row").hide();
64
83
  }
65
84
  });
66
- $("#node-input-dataType").change();
85
+
86
+ $("#node-input-dataType").change();
87
+
88
+ $("#node-input-minChange").val(
89
+ parseFloat($("#node-input-minChange").val() || 0).toFixed(5)
90
+ );
67
91
  }
68
92
  });
69
93
  </script>
@@ -92,8 +116,23 @@
92
116
  <option value="digital">Digital</span></option>
93
117
  </select>
94
118
  </div>
95
- <div class="form-row" id="unit-row" style="display:none;">
96
- <label for="node-input-unit"><i class="fa fa-ruler"></i> <span data-i18n="coe-output.label.unit"></span></label>
119
+ <div class="form-row">
120
+ <label for="node-input-unit"><i class="fa fa-thermometer-full"></i> <span data-i18n="coe-output.label.unit"></span></label>
97
121
  <select id="node-input-unit"></select>
98
122
  </div>
123
+ <div class="form-row">
124
+ <span data-i18n="coe-output.label.sendConditions"></span>
125
+ </div>
126
+ <div class="form-row" id="minchange-row" style="display:none;">
127
+ <label for="node-input-minChange"><i class="fa fa-line-chart"></i> <span data-i18n="coe-output.label.minChange"></span></label>
128
+ <input type="number" id="node-input-minChange" placeholder="1" min="0" max="1000000" step="0.001">
129
+ </div>
130
+ <div class="form-row">
131
+ <label for="node-input-blockingTime"><i class="fa fa-clock-o"></i> <span data-i18n="coe-output.label.blockingTime"></span></label>
132
+ <input type="number" id="node-input-blockingTime" placeholder="10" min="1" max="999">
133
+ </div>
134
+ <div class="form-row">
135
+ <label for="node-input-maxInterval"><i class="fa fa-repeat"></i> <span data-i18n="coe-output.label.maxInterval"></span></label>
136
+ <input type="number" id="node-input-maxInterval" placeholder="5" min="1" max="60">
137
+ </div>
99
138
  </script>
package/coe/coe-output.js CHANGED
@@ -14,6 +14,7 @@ module.exports = function(RED) {
14
14
  function CoEOutputNode(config) {
15
15
  RED.nodes.createNode(this, config);
16
16
  const node = this;
17
+ node._ = RED._;
17
18
 
18
19
  node.cmiConfig = RED.nodes.getNode(config.cmiconfig);
19
20
 
@@ -30,39 +31,173 @@ module.exports = function(RED) {
30
31
  node.dataType = config.dataType || 'analog';
31
32
  node.unit = parseInt(config.unit) || 0;
32
33
 
34
+ if (node.coeVersion === 2) {
35
+ node.queueKey = node.nodeNumber + '-' + node.dataType;
36
+ } else {
37
+ node.block = getBlockInfo(node.dataType, node.outputNumber);
38
+ node.queueKey = node.nodeNumber + '-' + node.block.number;
39
+ }
40
+
41
+ // Filter configuration parameters
42
+ node.minChange = parseFloat(config.minChange) || 1;
43
+ node.blockingTime = parseFloat(config.blockingTime) || 10; // Seconds
44
+ node.maxInterval = parseFloat(config.maxInterval) || 5; // Minutes
45
+
46
+ // State tracking per output (not global!)
47
+ node.lastReceivedMsg = null;
48
+ node.lastReceivedValue = undefined;
49
+ node.lastReceivedUnit = null;
50
+ node.lastSentValue = undefined;
51
+ node.lastSentTime = 0;
52
+ node.lastInputTime = 0;
53
+
54
+ node.intervalTimer = null;
55
+
33
56
  node.on('input', function(msg) {
34
- const blockInfo = getBlockInfo(node.dataType, node.outputNumber);
35
- let values, units;
57
+ node.lastInputTime = Date.now();
58
+ const timeSinceLastSend = (node.lastInputTime - node.lastSentTime) / 1000; // Seconds
59
+ const isSupressed = checkSuppressCondition(node, timeSinceLastSend);
60
+ node.lastReceivedMsg = msg;
36
61
 
37
- if (node.dataType === 'analog') {
38
- values = [undefined, undefined, undefined, undefined];
39
- units = [undefined, undefined, undefined, undefined];
40
-
41
- const payloadValue = parseFloat(msg.payload);
42
- values[blockInfo.position] = isNaN(payloadValue) ? 0 : payloadValue;
62
+ // Prepare last received values
63
+ const payloadValue = parseFloat(msg.payload);
64
+ node.lastReceivedValue = isNaN(payloadValue) ? 0 : payloadValue;
65
+ node.lastReceivedUnit = (msg.coe && msg.coe.unit !== undefined) ? parseInt(msg.coe.unit) : node.unit;
66
+
67
+
68
+ // Filter based on sending conditions
69
+ if (isSupressed > 0) {
70
+ if (isSupressed === 1) { // Suppressed due to blocking time
71
+ setblockingTimer(timeSinceLastSend);
72
+ }
73
+ return;
74
+ }
75
+
76
+ if (node.blockingTimer) {
77
+ clearTimeout(node.blockingTimer);
78
+ node.blockingTimer = null;
79
+ }
80
+
81
+ setIntervalTimer(); // Restart interval timer
82
+
83
+ queueAndSend(node);
84
+ node.lastSentValue = node.lastReceivedValue;
85
+ node.lastSentTime = Date.now();
86
+
87
+ node.status({ // Update to queued
88
+ fill: "yellow",
89
+ shape: "dot",
90
+ text: node._("coe-output.status.queued") + ` [v${node.coeVersion}]`
91
+ });
92
+ });
93
+
94
+ node.on('close', function() { // Clear timers on shutdown
95
+ if (node.intervalTimer) {
96
+ clearTimeout(node.intervalTimer);
97
+ node.intervalTimer = null;
98
+ }
99
+ if (node.blockingTimer) {
100
+ clearTimeout(node.blockingTimer);
101
+ node.blockingTimer = null;
102
+ }
103
+ });
104
+
105
+ node.status({fill:"grey", shape:"ring", text:node._("coe-output.status.ready") + ` [v${node.coeVersion}]`});
106
+
107
+ // Start the sending interval timer
108
+ function setIntervalTimer() {
109
+ if (node.intervalTimer) {
110
+ clearTimeout(node.intervalTimer);
111
+ node.intervalTimer = null;
112
+ }
113
+
114
+ const checkInterval = node.maxInterval * 60 * 1000;
115
+
116
+ node.intervalTimer = setTimeout(() => { // Interval elapsed, retransmit last value
117
+ const now = Date.now();
118
+ const timeSinceLastSend = (now - node.lastSentTime) / 1000 / 60;
43
119
 
44
- // Set unit for the specific output
45
- units[blockInfo.position] =
46
- (msg.coe && msg.coe.unit !== undefined)
47
- ? parseInt(msg.coe.unit)
48
- : node.unit;
120
+ queueAndSend(node);
121
+ node.lastSentValue = node.lastReceivedValue;
122
+ node.lastSentTime = Date.now();
123
+
124
+ if (node.lastReceivedValue !== undefined) {
125
+ node.status({
126
+ fill: "yellow",
127
+ shape: "dot",
128
+ text: node._("coe-output.status.retransmit") + ` (${timeSinceLastSend.toFixed(0)} Min) [v${node.coeVersion}]`
129
+ });
130
+ }
131
+ setTimeout(() => {
132
+ node.status({fill: "grey", shape: "ring", text: node._("coe-output.status.ready") + ` [v${node.coeVersion}]`});
133
+ }, 5000);
49
134
 
50
- } else { // digital
51
- values = new Array(16).fill(undefined);
52
- values[blockInfo.position] = msg.payload ? 1 : 0;
53
- units = null;
135
+ setIntervalTimer(); // Restart interval timer
136
+ }, checkInterval);
137
+
138
+ }
139
+
140
+ function setblockingTimer(timeSinceLastSend) {
141
+ if (node.blockingTimer) {
142
+ clearTimeout(node.blockingTimer);
143
+ node.blockingTimer = null;
54
144
  }
145
+
146
+ const blockingTimeMs = (node.blockingTime - timeSinceLastSend) * 1000;
147
+
148
+ node.blockingTimer = setTimeout(() => {
149
+ if (node.lastReceivedValue !== undefined) {
150
+ node.status({
151
+ fill: "yellow",
152
+ shape: "dot",
153
+ text: node._("coe-output.status.queued") + ` [v${node.coeVersion}]`
154
+ });
155
+
156
+ queueAndSend(node);
157
+ node.lastSentValue = node.lastReceivedValue;
158
+ node.lastSentTime = Date.now();
159
+ setIntervalTimer()
160
+ }
161
+ }, blockingTimeMs);
162
+ }
163
+ }
164
+
165
+ // Checks if message should be suppressed based on sending conditions
166
+ function checkSuppressCondition(node, timeSinceLastSend) {
167
+ let isSuppressed = 0;
168
+ let blockReason = "";
169
+
170
+ if (node.lastSentValue === undefined) { // First input
171
+ isSuppressed = 0;
172
+ blockReason = "Initial";
173
+ } else {
55
174
 
175
+ // Check blocking time
176
+ if (node.blockingTime > 0 && timeSinceLastSend < node.blockingTime) {
177
+ isSuppressed = 1;
178
+ blockReason = `⏱ ${timeSinceLastSend.toFixed(0)}/${node.blockingTime}s`;
179
+ }
180
+
181
+ // Check size of value change
182
+ if (node.dataType === 'analog') {
183
+ const delta = Math.abs(node.lastReceivedValue - node.lastSentValue);
184
+ if (node.minChange > 0 && delta < node.minChange) {
185
+ isSuppressed = 2;
186
+ blockReason = `Δ${delta.toFixed(2)} < ${node.minChange}`;
187
+ }
188
+ }
189
+ }
190
+
191
+ if (isSuppressed > 0) { // Update status and state
56
192
  node.status({
57
193
  fill: "yellow",
58
- shape: "dot",
59
- text: RED._("coe-output.status.queued") + `[v${node.coeVersion}]`
194
+ shape: "ring",
195
+ text: node._("coe-output.status.blocked") + `: ${blockReason} [v${node.coeVersion}]`
60
196
  });
61
-
62
- queueAndSend(node, RED._, node.nodeNumber, blockInfo.block, values, units, node.dataType, node.coeVersion, node.cmiConfig, node.cmiAddress, msg);
63
- });
64
-
65
- node.status({fill:"grey", shape:"ring", text:RED._("coe-output.status.ready") + ` [v${node.coeVersion}]`});
197
+ }
198
+
199
+ return isSuppressed;
66
200
  }
201
+
67
202
  RED.nodes.registerType("coe-output", CoEOutputNode);
68
203
  };
package/coe/config.js CHANGED
@@ -9,7 +9,7 @@
9
9
  module.exports = function(RED) {
10
10
  "use strict";
11
11
  const dgram = require('dgram');
12
- const { parseCoEPacket } = require('../lib/coe');
12
+ const { parsePacket } = require('../lib/protocol');
13
13
 
14
14
  // CoE Protocol Ports
15
15
  const COE_PORT1 = 5441; // CoE v1
@@ -19,7 +19,14 @@ module.exports = function(RED) {
19
19
  RED.nodes.createNode(this, config);
20
20
  const node = this;
21
21
 
22
- node.lang = (RED.settings.lang.toLowerCase().startsWith("de")) ? "de" : "en";
22
+ let langSetting = (RED.settings.lang || "").toLowerCase();
23
+ switch (true) {
24
+ case langSetting.startsWith("de"):
25
+ node.lang = "de";
26
+ break;
27
+ default:
28
+ node.lang = "en";
29
+ }
23
30
 
24
31
  node.address = config.address || '192.168.0.100';
25
32
  node.coeVersion = parseInt(config.coeVersion) || 1;
@@ -36,11 +43,11 @@ module.exports = function(RED) {
36
43
  });
37
44
 
38
45
  node.socket.on('message', (msg, rinfo) => {
39
- const blocks = parseCoEPacket(msg, node.coeVersion);
46
+ const data = parsePacket[node.coeVersion](msg);
40
47
 
41
- if (blocks && blocks.length > 0) {
42
- const data = { // Wrapper object for meta info
43
- blocks: blocks,
48
+ if (data && data.length > 0) {
49
+ const received = { // Wrapper object for meta info
50
+ data: data,
44
51
  sourceIP: rinfo.address,
45
52
  version: node.coeVersion,
46
53
  timestamp: Date.now(),
@@ -49,7 +56,7 @@ module.exports = function(RED) {
49
56
 
50
57
  node.listeners.forEach(listener => { // Notify all listeners
51
58
  try {
52
- listener(data);
59
+ listener(received);
53
60
  } catch(err) {
54
61
  node.error(`Listener error: ${err.message}`);
55
62
  }
@@ -2,9 +2,9 @@
2
2
  <p>Empfängt Werte von einer TA CMI via CoE (CAN over Ethernet).</p>
3
3
  <h3>Konfiguration</h3>
4
4
  <ul>
5
- <li><b>Knoten:</b> CAN Knoten-Nummer (1-62, 0 = von beliebigem Knoten empfangen, nicht empfohlen in Produktivbetrieb)</li>
5
+ <li><b>Knoten:</b> CAN Knoten-Nummer (1-62, 0 = von beliebigem Knoten empfangen; nicht empfohlen in Produktivbetrieb)</li>
6
6
  <li><b>Eingang:</b> Nummer des Netzwerkeingangs (1-32)</li>
7
7
  <li><b>Datentyp:</b> Analog (1-32) oder Digital (1-32)</li>
8
- <li><b>Timeout:</b> CAN-Bus Timeout-Wert in Minuten</li>
8
+ <li><b>Timeout:</b> CAN-Bus Timeout (Wert in Minuten)</li>
9
9
  </ul>
10
10
  </script>
@@ -2,7 +2,7 @@
2
2
  "coe-input": {
3
3
  "label": {
4
4
  "name": "Name",
5
- "cmiconfig": "CMI Konfig",
5
+ "cmiconfig": "CMI",
6
6
  "nodeNumber": "Knoten",
7
7
  "outputNumber": "Eingang",
8
8
  "dataType": "Datentyp",
@@ -13,7 +13,13 @@
13
13
  },
14
14
  "status": {
15
15
  "waiting": "wartet",
16
- "waitingAny": "wartet (alle Knoten)"
16
+ "waitingAny": "wartet (alle Knoten)",
17
+ "open": "Offen",
18
+ "closed": "Geschlossen",
19
+ "on": "Ein",
20
+ "off": "Aus",
21
+ "yes": "Ja",
22
+ "no": "Nein"
17
23
  }
18
24
  }
19
25
  }
@@ -1,6 +1,5 @@
1
1
  <script type="text/html" data-help-name="coe-monitor">
2
- <p>Empfängt und überwacht CoE (CAN over Ethernet) Pakete von allen Quellen.</p>
3
- <p>Ideal für Debugging, Logging und Überwachung des gesamten CoE-Verkehrs.</p>
2
+ <p>Empfängt und überwacht CoE (CAN over Ethernet) Pakete von allen Quellen. Ideal für Debugging, Logging und Überwachung des gesamten CoE-Verkehrs.</p>
4
3
  <h3>Konfiguration</h3>
5
4
  <ul>
6
5
  <li>Optional: Filtere nur Pakete von einem bestimmten CAN-Knoten (0 = alle)</li>
@@ -2,7 +2,7 @@
2
2
  "coe-monitor": {
3
3
  "label": {
4
4
  "name": "Name",
5
- "cmiconfig": "CMI Konfig",
5
+ "cmiconfig": "CMI",
6
6
  "filterNodeNumber": "Filter Knoten",
7
7
  "filterDataType": "Filter Typ",
8
8
  "includeRaw": "Rohdaten einschließen",
@@ -12,9 +12,14 @@
12
12
  "filterNodeNumber": "0 = alle"
13
13
  },
14
14
  "status": {
15
- "monitoring": "Lauscht...",
16
- "idle": "Inaktiv",
17
- "node": "Knoten"
15
+ "monitoring": "Lauscht",
16
+ "node": "Knoten",
17
+ "open": "Offen",
18
+ "closed": "Geschlossen",
19
+ "on": "Ein",
20
+ "off": "Aus",
21
+ "yes": "Ja",
22
+ "no": "Nein"
18
23
  }
19
24
  }
20
25
  }
@@ -7,8 +7,14 @@
7
7
  <li><b>Datentyp:</b> Analog (numerisch) oder Digital (boolean)</li>
8
8
  <li><b>Einheit:</b> Messeinheit (nur für analog)</li>
9
9
  </ul>
10
+ <h3>Sendebedingungen</h3>
11
+ <ul>
12
+ <li><b>bei Änderung >:</b> Erneut senden, wenn sich der aktuelle Wert um mehr als dieser Wert ändert (analoge Werte)</li>
13
+ <li><b>Blockierzeit:</b> Frühestens nach dieser Zeit erneut senden (Sekunden)</li>
14
+ <li><b>Intervallzeit:</b> Falls sich der Wert nicht ändert, nach diesem Intervall erneut senden (Minuten)</li>
15
+ </ul>
10
16
  <h3>Ausgänge</h3>
11
17
  <p><b>2 Ausgänge:</b> [0] Durchgeleitete Nachricht, [1] Debug-Info (Hex, Block-State, Units)</p>
12
- <h3>Hinweise</h3>
13
- <p>Werte werden 50ms gepuffert um die Nachrichtenfrequenz zu reduzieren</p>
18
+ <h3>Hinweis</h3>
19
+ <p>Werte werden 50 ms gepuffert um die Nachrichtenfrequenz zu reduzieren.</p>
14
20
  </script>
@@ -2,17 +2,23 @@
2
2
  "coe-output": {
3
3
  "label": {
4
4
  "name": "Name",
5
- "cmiconfig": "CMI Konfig",
5
+ "cmiconfig": "CMI",
6
6
  "nodeNumber": "Knoten",
7
7
  "outputNumber": "Ausgang",
8
8
  "dataType": "Datentyp",
9
- "unit": "Einheit"
9
+ "unit": "Einheit",
10
+ "sendConditions": "Sendebedingungen:",
11
+ "minChange": "bei Änderung >",
12
+ "blockingTime": "Blockierzeit (Sek)",
13
+ "maxInterval": "Intervallzeit (Min)"
10
14
  },
11
15
  "status": {
12
16
  "noconfig": "Nicht konfiguriert",
13
- "queued": "wartet",
14
- "ready": "bereit",
15
- "merged": "gesendet (Paket)"
17
+ "queued": "Wartet",
18
+ "ready": "Bereit",
19
+ "merged": "Gesendet",
20
+ "retransmit": "Erneut gesendet",
21
+ "blocked": "Blockiert"
16
22
  }
17
23
  }
18
24
  }
@@ -1,13 +1,13 @@
1
1
  <script type="text/html" data-help-name="cmiconfig">
2
2
  <p><strong>Konfiguration:</strong></p>
3
3
  <ul>
4
- <li><b> Lokale IP:</b> IP Adressbereich in dem der UDP Port empfängt (Übliche Werte sind 0.0.0.0 oder 127.0.0.1)</li>
4
+ <li><b> IP-Bereich:</b> IP Adressbereich in dem der UDP Port empfängt (Übliche Werte sind 0.0.0.0 oder 127.0.0.1)</li>
5
5
  <li><b> Adresse:</b> Für das Senden von Nachrichten verwendete IP Adresse des TA C.M.I</li>
6
6
  </ul>
7
7
  <p><strong>CoE-Version:</strong></p>
8
8
  <ul>
9
- <li><b>V1:</b> Standard-Einstellung. Wertebereich: ±32.767</li>
10
- <li><b>V2:</b> Erweitert, benötigt CMI FW ≥v1.43.1. Wertebereich: ±2.147.483.647</li>
9
+ <li><b>Version 1:</b> Standard-Einstellung. Wertebereich: ± 32.767</li>
10
+ <li><b>Version 2:</b> Erweitert, benötigt CMI FW ≥ v1.43.1. Wertebereich: ± 2.147.483.647</li>
11
11
  </ul>
12
12
  <p>Siehe Einstellungen > CAN im Menü des CMI</p>
13
13
  </script>