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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,7 +1,13 @@
1
1
  ## Changelog
2
2
 
3
+ ### Version 1.1.2
4
+ - Fixes wrong delta supressed error
5
+ - Fixes early timeout error
6
+ - Fixes lang type error (missing config node)
7
+ - Added node & output number validation
8
+
3
9
  ### Version 1.1.1
4
- - Fix wrong V2 output number
10
+ - Fixes wrong V2 output number
5
11
 
6
12
  ### Version 1.1.0
7
13
  - Change to variable message size (similar to CoE V2 approach)
package/README.de.md CHANGED
@@ -88,13 +88,13 @@ Auf der CMI unter **Einstellungen → Ausgänge → CoE**:
88
88
  - **Eingang**: CAN-Bus Eingang (z.B. CAN 1)
89
89
  - **IP**: IP-Adresse von Node-RED
90
90
  - **Knoten**: Knoten-Nummer des Input Nodes
91
- - **Netzwerkausgang**: Nummer des Ausgangs (1-32)
91
+ - **Netzwerkausgang**: Nummer des Ausgangs (1-32 in CoE V1 / 1-64 in CoE V2)
92
92
  - **Sendebedingungen**: Unterdrückung kleiner & häufiger Änderungen, Intervall für wiederholtes Senden (nach Bedarf)
93
93
 
94
94
  #### Für Senden an CMI (CoE Output):
95
95
  Auf dem Regler: CAN-Eingang konfigurieren
96
96
  - **Knoten**: Wert aus "Node Number" des Output Nodes
97
- - **Ausgangsnummer**: Nummer des Ausgangs (1-32)
97
+ - **Ausgangsnummer**: Nummer des Ausgangs (1-32 in CoE V1 / 1-64 in CoE V2)
98
98
  - **Messgröße**: "Automatisch" für Unit von Node-RED
99
99
 
100
100
  ## Node Typen
@@ -109,7 +109,6 @@ Empfängt Werte von der CMI.
109
109
  payload: 22.5, // Der Wert
110
110
  topic: "coe/10/analog/1", // Format: coe/{node}/{type}/{output}
111
111
  coe: {
112
- timestamp: 2026-01-08T // Eingangszeit
113
112
  sourceIP: "192.168.1.100", // IP der CMI
114
113
  nodeNumber: 10, // CAN Knoten-Nummer
115
114
  dataType: "analog", // Datentyp
@@ -118,7 +117,8 @@ Empfängt Werte von der CMI.
118
117
  state: 22.5, // Wert oder digitaler Zustand
119
118
  unit: 1, // Unit ID (z.B. 1 = °C)
120
119
  unitName: "Temperatur °C", // Unit Name
121
- unitSymbol: "°C°" // Unit Symbol
120
+ unitSymbol: "°C°", // Unit Symbol
121
+ timestamp: 2026-01-08T11:04 // Eingangszeit
122
122
  }
123
123
  }
124
124
  ```
package/README.md CHANGED
@@ -87,13 +87,13 @@ On the CMI under **Settings → Outputs → CoE**:
87
87
  - **Input**: CAN bus input (e.g., CAN 1)
88
88
  - **IP**: IP address of Node-RED
89
89
  - **Node**: Node Number of the input node
90
- - **Network Output**: Number of the output (1-32)
90
+ - **Network Output**: Number of the output (1-32 in CoE V1 / 1-64 in CoE V2)
91
91
  - **Sending Conditions**: Suppression of small & frequent changes, interval for repeated sending (as required)
92
92
 
93
93
  #### For sending to the CMI (CoE Output):
94
94
  On the controller: Configure the CAN input
95
95
  - **Node**: Value from the "Node Number" of the output node
96
- - **Output Number**: Number of the output (1-32)
96
+ - **Output Number**: Number of the output (1-32 in CoE V1 / 1-64 in CoE V2)
97
97
  - **Measured Unit**: "Automatic" for Node-RED Unit
98
98
 
99
99
  ## Node Types
@@ -108,7 +108,6 @@ Receives values ​​from the CMI.
108
108
  payload: 22.5, // The value
109
109
  topic: "coe/10/analog/1", // Format: coe/{node}/{type}/{output}
110
110
  coe: {
111
- timestamp: 2026-01-08T // Reception Time
112
111
  sourceIP: "192.168.1.100", // IP of the CMI
113
112
  nodeNumber: 10, // CAN Node Number
114
113
  dataType: "analog", // Data Type
@@ -118,6 +117,7 @@ Receives values ​​from the CMI.
118
117
  unit: 1, // Unit ID (z.B. 1 = °C)
119
118
  unitName: "Temperature °C", // Unit name
120
119
  unitSymbol: "°C°", // Unit symbol
120
+ timestamp: 2026-01-08T11:04 // Reception Time
121
121
  }
122
122
  }
123
123
  ```
@@ -45,7 +45,7 @@
45
45
  </div>
46
46
  <div class="form-row">
47
47
  <label for="node-input-outputNumber"><i class="fa fa-arrow-down"></i> <span data-i18n="coe-input.label.outputNumber"></span></label>
48
- <input type="number" id="node-input-outputNumber" placeholder="1" min="1" max="32">
48
+ <input type="number" id="node-input-outputNumber" placeholder="1" min="1" max="64">
49
49
  </div>
50
50
  <div class="form-row">
51
51
  <label for="node-input-dataType"><i class="fa fa-list"></i> <span data-i18n="coe-input.label.dataType"></span></label>
package/coe/coe-input.js CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  module.exports = function(RED) {
10
10
  'use strict';
11
- const { getUnitInfo, getDigitalStateKey, mergeNodeData, createEmptyState } = require('../lib/utils');
11
+ const { Validate, getUnitInfo, getDigitalStateKey, mergeNodeData, createEmptyState } = require('../lib/utils');
12
12
 
13
13
  // CoE Input Node (receiving values)
14
14
  function CoEInputNode(config) {
@@ -19,16 +19,17 @@ module.exports = function(RED) {
19
19
  node.cmiConfig = RED.nodes.getNode(config.cmiconfig);
20
20
 
21
21
  if (!node.cmiConfig) {
22
- node.error("CMI Configuration missing");
22
+ node.error("No CMI config assigned to CoE Input node.");
23
+ node.status({fill:"red", shape:"ring", text:"coe-input.status.noconfig"});
23
24
  return;
24
25
  }
25
26
 
26
- node.cmiAddress = node.cmiConfig.address;
27
+ node.cmiAddress = node.cmiConfig.address || "";
27
28
  node.coeVersion = node.cmiConfig.coeVersion || 1;
28
- node.lang = node.cmiConfig.lang;
29
- node.nodeNumber = parseInt(config.nodeNumber) || 0;
30
- node.outputNumber = parseInt(config.outputNumber) || 1;
31
- node.dataType = config.dataType || 'analog';
29
+ node.lang = node.cmiConfig?.lang || "en";
30
+ node.nodeNumber = Validate.node(config.nodeNumber, true);
31
+ node.outputNumber = Validate.output(config.outputNumber, node.coeVersion);
32
+ node.dataType = Validate.type(config.dataType);
32
33
 
33
34
  // State management for LKGVs (Last Known Good Values) per block
34
35
  node.canNodeState = {};
@@ -59,7 +60,7 @@ module.exports = function(RED) {
59
60
  }
60
61
 
61
62
  // Filter dataType number
62
- if (canNode.dataType !== node.dataType) {
63
+ if (canNode.dataType !== node.dataType.long) {
63
64
  continue;
64
65
  }
65
66
 
@@ -81,7 +82,7 @@ module.exports = function(RED) {
81
82
  continue;
82
83
  }
83
84
 
84
- if (node.dataType === 'analog') {
85
+ if (node.dataType.long === 'analog') {
85
86
  value = output.value;
86
87
  unit = output.unit;
87
88
  state = value;
@@ -96,17 +97,17 @@ module.exports = function(RED) {
96
97
  const unitInfo = getUnitInfo(unit, node.lang);
97
98
  const msg = {
98
99
  payload: value,
99
- topic: `coe/${node.nodeNumber || mergedCanNode.nodeNumber}/${node.dataType}/${node.outputNumber}`,
100
+ topic: `coe/${node.nodeNumber || mergedCanNode.nodeNumber}/${node.dataType.long}/${node.outputNumber}`,
100
101
  coe: {
101
- timestamp: received.timestamp,
102
102
  sourceIP: received.sourceIP,
103
103
  nodeNumber: mergedCanNode.nodeNumber,
104
- dataType: node.dataType,
104
+ dataType: node.dataType.long,
105
105
  outputNumber: node.outputNumber,
106
106
  state: state,
107
107
  unit: unit,
108
108
  unitName: unitInfo.name,
109
- unitSymbol: unitInfo.symbol
109
+ unitSymbol: unitInfo.symbol,
110
+ timestamp: received.timestamp
110
111
  // raw: received.rawBuffer ? received.rawBuffer.toString('hex').toUpperCase() : null
111
112
  }
112
113
  };
@@ -115,7 +116,7 @@ module.exports = function(RED) {
115
116
  node.send(msg);
116
117
  }
117
118
 
118
- currentNodeText = `${state} ${unitInfo.symbol || ''} [v${node.coeVersion}]` // Caching last Node text
119
+ currentNodeText = `${state} ${unitInfo.symbol || ''}` // Caching last Node text
119
120
 
120
121
  node.status({
121
122
  fill: "green",
@@ -131,24 +132,34 @@ module.exports = function(RED) {
131
132
  };
132
133
 
133
134
  node.cmiConfig.registerListener(listener);
135
+
136
+ resetTimeout(); // Set initial timeout
134
137
 
135
138
  // Reset CoE Timeout
136
139
  function resetTimeout() {
137
140
  if (timeoutTimer) clearTimeout(timeoutTimer);
141
+
142
+ // Fallback status text (initial timeout)
143
+ const statusText = currentNodeText ? `${currentNodeText} (Timeout)` : node._("coe-input.status.initialTimeout");
144
+
138
145
  timeoutTimer = setTimeout(() => {
139
- node.status({ fill: "red", shape: "dot", text: `${currentNodeText} (Timeout)` });
146
+ node.status({ fill: "red", shape: "dot", text: statusText });
140
147
  }, timeoutMs);
141
148
  }
142
149
 
143
150
  // Status information, including if filtered
144
151
  if (node.nodeNumber === 0) {
145
- node.status({fill: "yellow", shape: "ring", text: "coe-input.status.waitingAny"});
152
+ node.status({fill: "yellow", shape: "ring", text: node._("coe-input.status.waitingAny") + ` [v${node.coeVersion}]`});
146
153
  } else {
147
- node.status({fill: "grey", shape: "ring", text: "coe-input.status.waiting"});
154
+ node.status({fill: "grey", shape: "ring", text: "ᐅ" + node.dataType.short + " " + node.nodeNumber + "/" + node.outputNumber + " " + node._("coe-input.status.waiting") + ` [v${node.coeVersion}]`});
148
155
  }
149
156
 
150
157
  node.on('close', function() {
151
158
  node.cmiConfig.unregisterListener(listener);
159
+
160
+ if (timeoutTimer) {
161
+ clearTimeout(timeoutTimer);
162
+ }
152
163
  });
153
164
 
154
165
  }
@@ -48,7 +48,7 @@
48
48
  <div class="form-row">
49
49
  <label for="node-input-filterDataType"><i class="fa fa-filter"></i> <span data-i18n="coe-monitor.label.filterDataType"></span></label>
50
50
  <select id="node-input-filterDataType">
51
- <option value="" data-i18n="coe-monitor.label.all"></option>
51
+ <option value="all" data-i18n="coe-monitor.label.all"></option>
52
52
  <option value="analog">Analog</span></option>
53
53
  <option value="digital">Digital</span></option>
54
54
  </select>
@@ -8,7 +8,7 @@
8
8
 
9
9
  module.exports = function(RED) {
10
10
  'use strict';
11
- const { getUnitInfo, getDigitalStateKey } = require('../lib/utils')
11
+ const { Validate, getUnitInfo, getDigitalStateKey } = require('../lib/utils')
12
12
 
13
13
  function CoEMonitorNode(config) {
14
14
  RED.nodes.createNode(this, config);
@@ -16,19 +16,23 @@ module.exports = function(RED) {
16
16
  node._ = RED._;
17
17
 
18
18
  node.cmiConfig = RED.nodes.getNode(config.cmiconfig);
19
-
20
- const lang = node.cmiConfig.lang;
21
- const coeVersion = node.cmiConfig.coeVersion || 1;
22
19
 
23
20
  if (!node.cmiConfig) {
24
- node.error("CMI Configuration missing");
21
+ node.error("No CMI config assigned to CoE Monitor node.");
22
+ node.status({fill:"red", shape:"ring", text:"coe-monitor.status.noconfig"});
25
23
  return;
26
24
  }
27
25
 
28
- node.filterNodeNumber = config.filterNodeNumber ? parseInt(config.filterNodeNumber) : null;
29
- node.filterDataType = config.filterDataType || 'all';
26
+ node.lang = node.cmiConfig?.lang || "en";
27
+ node.coeVersion = node.cmiConfig.coeVersion || 1;
28
+ node.filterNodeNumber = Validate.node(config.filterNodeNumber, true);
29
+ node.filterDataType = Validate.type(config.filterDataType);
30
30
  node.includeRaw = config.includeRaw || false;
31
31
 
32
+ const isFilteringByNode = (node.filterNodeNumber !== null && node.filterNodeNumber !== 0);
33
+ const isFilteringByDataType = node.filterDataType.isDigital !== null;
34
+ const filterText = (!isFilteringByNode && !isFilteringByDataType) ? "" : "ᐅ" + node.filterDataType.short + " " + node.filterNodeNumber;
35
+
32
36
  let packetCount = 0;
33
37
  let lastUpdate = Date.now();
34
38
 
@@ -41,17 +45,15 @@ module.exports = function(RED) {
41
45
  for (let canNode of received.data) {
42
46
  if (!canNode) continue;
43
47
 
44
- if (node.filterNodeNumber !== null && // Filter by Node Number
45
- node.filterNodeNumber !== 0 &&
46
- canNode.nodeNumber !== node.filterNodeNumber) {
48
+ if (isFilteringByNode && canNode.nodeNumber !== node.filterNodeNumber) {
47
49
  continue;
48
50
  }
49
51
 
50
52
  const isDigital = (canNode.dataType === 'digital'); // Filter by Data Type
51
53
  const isAnalog = !isDigital;
52
54
 
53
- if (node.filterDataType === 'analog' && !isAnalog) continue;
54
- if (node.filterDataType === 'digital' && !isDigital) continue;
55
+ if (node.filterDataType.long === 'analog' && !isAnalog) continue;
56
+ if (node.filterDataType.long === 'digital' && !isDigital) continue;
55
57
 
56
58
  packetCount++;
57
59
  lastUpdate = Date.now();
@@ -59,12 +61,12 @@ module.exports = function(RED) {
59
61
  // Build message
60
62
  const msg = {
61
63
  payload: {
62
- timestamp: new Date().toISOString(),
63
64
  sourceIP: received.sourceIP,
64
65
  version: received.version,
65
66
  nodeNumber: canNode.nodeNumber,
66
67
  ...(canNode.blockNumber && { blockNumber: canNode.blockNumber}),
67
- dataType: canNode.dataType
68
+ dataType: canNode.dataType,
69
+ timestamp: new Date().toISOString()
68
70
  },
69
71
  topic: `coe/monitor/${canNode.nodeNumber}/block/${canNode.dataType}`
70
72
  };
@@ -74,7 +76,7 @@ module.exports = function(RED) {
74
76
 
75
77
  if (canNode.outputs) {
76
78
  Object.entries(canNode.outputs).forEach(([outputNumber, output]) => {
77
- const unitInfo = getUnitInfo(output.unit, lang);
79
+ const unitInfo = getUnitInfo(output.unit, node.lang);
78
80
  const translationKey = getDigitalStateKey(output.unit, output.value, "coe-monitor.status.");
79
81
 
80
82
  valuesDetailed[outputNumber] = {
@@ -106,7 +108,7 @@ module.exports = function(RED) {
106
108
  };
107
109
 
108
110
  node.cmiConfig.registerListener(listener);
109
- node.status({fill: "grey", shape: "ring", text: node._("coe-monitor.status.monitoring") + "..."});
111
+ node.status({fill: "grey", shape: "ring", text: node._("coe-monitor.status.monitoring") + " " + filterText + "..."});
110
112
 
111
113
  // Status Update Timer (shows last activity)
112
114
  const statusTimer = setInterval(() => {
@@ -115,7 +117,7 @@ module.exports = function(RED) {
115
117
  node.status({
116
118
  fill: "yellow",
117
119
  shape: "ring",
118
- text: node._("coe-monitor.status.monitoring") + ` ${secsSinceUpdate}s - ${packetCount} Pkt. [v${coeVersion}]`
120
+ text: node._("coe-monitor.status.monitoring") + " " + filterText + ` ${secsSinceUpdate}s - ${packetCount} Pkt. [v${node.coeVersion}]`
119
121
  });
120
122
  }
121
123
  }, 5000);
@@ -107,7 +107,7 @@
107
107
  </div>
108
108
  <div class="form-row">
109
109
  <label for="node-input-outputNumber"><i class="fa fa-arrow-up"></i> <span data-i18n="coe-output.label.outputNumber"></span></label>
110
- <input type="number" id="node-input-outputNumber" placeholder="1" min="1" max="32">
110
+ <input type="number" id="node-input-outputNumber" placeholder="1" min="1" max="64">
111
111
  </div>
112
112
  <div class="form-row">
113
113
  <label for="node-input-dataType"><i class="fa fa-list"></i> <span data-i18n="coe-output.label.dataType"></span></label>
package/coe/coe-output.js CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  module.exports = function(RED) {
10
10
  'use strict';
11
- const { getBlockInfo } = require('../lib/utils')
11
+ const { Validate, getBlockInfo } = require('../lib/utils')
12
12
  const { queueAndSend } = require('../lib/queueing');
13
13
 
14
14
  function CoEOutputNode(config) {
@@ -19,22 +19,24 @@ module.exports = function(RED) {
19
19
  node.cmiConfig = RED.nodes.getNode(config.cmiconfig);
20
20
 
21
21
  if (!node.cmiConfig) {
22
- node.error("CoE Configuration missing or invalid.");
22
+ node.error("No CMI config assigned to CoE Output node.");
23
23
  node.status({fill:"red", shape:"ring", text:"coe-output.status.noconfig"});
24
24
  return;
25
25
  }
26
26
 
27
- node.cmiAddress = node.cmiConfig.address;
27
+ node.cmiAddress = node.cmiConfig.address || "";
28
28
  node.coeVersion = node.cmiConfig.coeVersion || 1;
29
- node.nodeNumber = parseInt(config.nodeNumber) || 1;
30
- node.outputNumber = parseInt(config.outputNumber) || 1;
31
- node.dataType = config.dataType || 'analog';
29
+ node.nodeNumber = Validate.node(config.nodeNumber, false);
30
+ node.outputNumber = Validate.output(config.outputNumber, node.coeVersion);
31
+ node.dataType = Validate.type(config.dataType);
32
32
  node.unit = parseInt(config.unit) || 0;
33
33
 
34
+ node.readyText = "ᐅ" + node.dataType.short + " " + node.nodeNumber + "/" + node.outputNumber + " " + node._("coe-output.status.ready") + ` [v${node.coeVersion}]`
35
+
34
36
  if (node.coeVersion === 2) {
35
- node.queueKey = node.nodeNumber + '-' + node.dataType;
37
+ node.queueKey = node.nodeNumber + '-' + node.dataType.short;
36
38
  } else {
37
- node.block = getBlockInfo(node.dataType, node.outputNumber);
39
+ node.block = getBlockInfo(node.dataType.short, node.outputNumber);
38
40
  node.queueKey = node.nodeNumber + '-' + node.block.number;
39
41
  }
40
42
 
@@ -52,18 +54,20 @@ module.exports = function(RED) {
52
54
  node.lastInputTime = 0;
53
55
 
54
56
  node.intervalTimer = null;
57
+
58
+ node.status({fill: "grey", shape: "ring", text: node.readyText});
55
59
 
56
60
  node.on('input', function(msg) {
57
61
  node.lastInputTime = Date.now();
58
62
  const timeSinceLastSend = (node.lastInputTime - node.lastSentTime) / 1000; // Seconds
59
- const isSupressed = checkSuppressCondition(node, timeSinceLastSend);
60
- node.lastReceivedMsg = msg;
61
-
63
+
62
64
  // Prepare last received values
63
65
  const payloadValue = parseFloat(msg.payload);
64
66
  node.lastReceivedValue = isNaN(payloadValue) ? 0 : payloadValue;
65
67
  node.lastReceivedUnit = (msg.coe && msg.coe.unit !== undefined) ? parseInt(msg.coe.unit) : node.unit;
68
+ node.lastReceivedMsg = msg;
66
69
 
70
+ const isSupressed = checkSuppressCondition(node, timeSinceLastSend);
67
71
 
68
72
  // Filter based on sending conditions
69
73
  if (isSupressed > 0) {
@@ -101,8 +105,6 @@ module.exports = function(RED) {
101
105
  node.blockingTimer = null;
102
106
  }
103
107
  });
104
-
105
- node.status({fill:"grey", shape:"ring", text:node._("coe-output.status.ready") + ` [v${node.coeVersion}]`});
106
108
 
107
109
  // Start the sending interval timer
108
110
  function setIntervalTimer() {
@@ -129,7 +131,7 @@ module.exports = function(RED) {
129
131
  });
130
132
  }
131
133
  setTimeout(() => {
132
- node.status({fill: "grey", shape: "ring", text: node._("coe-output.status.ready") + ` [v${node.coeVersion}]`});
134
+ node.status({fill: "grey", shape: "ring", text: node.readyText});
133
135
  }, 5000);
134
136
 
135
137
  setIntervalTimer(); // Restart interval timer
@@ -179,9 +181,9 @@ module.exports = function(RED) {
179
181
  }
180
182
 
181
183
  // Check size of value change
182
- if (node.dataType === 'analog') {
184
+ if (node.dataType.long === 'analog' && node.minChange > 0) {
183
185
  const delta = Math.abs(node.lastReceivedValue - node.lastSentValue);
184
- if (node.minChange > 0 && delta < node.minChange) {
186
+ if (delta < node.minChange) {
185
187
  isSuppressed = 2;
186
188
  blockReason = `Δ${delta.toFixed(2)} < ${node.minChange}`;
187
189
  }
package/coe/config.js CHANGED
@@ -19,12 +19,11 @@ module.exports = function(RED) {
19
19
  RED.nodes.createNode(this, config);
20
20
  const node = this;
21
21
 
22
- let langSetting = (RED.settings.lang || "").toLowerCase();
23
- switch (true) {
24
- case langSetting.startsWith("de"):
22
+ // Determine backend language
23
+ const globalLang = (RED.settings?.lang || "en").toLowerCase();
24
+ if (globalLang.startsWith("de")) {
25
25
  node.lang = "de";
26
- break;
27
- default:
26
+ } else {
28
27
  node.lang = "en";
29
28
  }
30
29
 
@@ -3,8 +3,8 @@
3
3
  <h3>Konfiguration</h3>
4
4
  <ul>
5
5
  <li><b>Knoten:</b> CAN Knoten-Nummer (1-62, 0 = von beliebigem Knoten empfangen; nicht empfohlen in Produktivbetrieb)</li>
6
- <li><b>Eingang:</b> Nummer des Netzwerkeingangs (1-32)</li>
7
- <li><b>Datentyp:</b> Analog (1-32) oder Digital (1-32)</li>
6
+ <li><b>Eingang:</b> Nummer des Netzwerkeingangs (CoE-Version 1: 1-32, CoE-Version 2: 1-64)</li>
7
+ <li><b>Datentyp:</b> Analog oder Digital</li>
8
8
  <li><b>Timeout:</b> CAN-Bus Timeout (Wert in Minuten)</li>
9
9
  </ul>
10
10
  </script>
@@ -12,6 +12,8 @@
12
12
  "nodeNumber": "1-62, 0 = beliebig"
13
13
  },
14
14
  "status": {
15
+ "noconfig": "Nicht konfiguriert",
16
+ "initialTimeout": "Initial Timeout (keine Daten)",
15
17
  "waiting": "wartet",
16
18
  "waitingAny": "wartet (alle Knoten)",
17
19
  "open": "Offen",
@@ -12,6 +12,7 @@
12
12
  "filterNodeNumber": "0 = alle"
13
13
  },
14
14
  "status": {
15
+ "noconfig": "Nicht konfiguriert",
15
16
  "monitoring": "Lauscht",
16
17
  "node": "Knoten",
17
18
  "open": "Offen",
@@ -3,7 +3,7 @@
3
3
  <h3>Konfiguration</h3>
4
4
  <ul>
5
5
  <li><b>Knoten:</b> CAN Knoten-Nummer (1-62)</li>
6
- <li><b>Eingang:</b> Nummer des Netzwerkausgangs (Analog 1-32, Digital 1-16)</li>
6
+ <li><b>Eingang:</b> Nummer des Netzwerkausgangs (CoE-Version 1: 1-32, CoE-Version 2: 1-64)</li>
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>
@@ -15,7 +15,7 @@
15
15
  "status": {
16
16
  "noconfig": "Nicht konfiguriert",
17
17
  "queued": "Wartet",
18
- "ready": "Bereit",
18
+ "ready": "bereit",
19
19
  "merged": "Gesendet",
20
20
  "retransmit": "Erneut gesendet",
21
21
  "blocked": "Blockiert"
@@ -3,8 +3,8 @@
3
3
  <h3>Configuration</h3>
4
4
  <ul>
5
5
  <li><b>Node:</b> CAN node number (1-62, 0 = receive from any node; not recommended for production)</li>
6
- <li><b>Input:</b> Network input number (1-32)</li>
7
- <li><b>Data Type:</b> Analog (1-32) or Digital (1-32)</li>
6
+ <li><b>Input:</b> Network input number (CoE version 1: 1-32, CoE version 2: 1-64))</li>
7
+ <li><b>Data Type:</b> Analog or Digital</li>
8
8
  <li><b>Timeout:</b> CAN-Bus timeout (Value in minutes)</li>
9
9
  </ul>
10
10
  </script>
@@ -12,6 +12,8 @@
12
12
  "nodeNumber": "1-62, 0 = any"
13
13
  },
14
14
  "status": {
15
+ "noconfig": "No config",
16
+ "initialTimeout": "Initial Timeout (no data)",
15
17
  "waiting": "waiting",
16
18
  "waitingAny": "waiting (any node)",
17
19
  "open": "Open",
@@ -12,6 +12,7 @@
12
12
  "filterNodeNumber": "0 = all"
13
13
  },
14
14
  "status": {
15
+ "noconfig": "No config",
15
16
  "monitoring": "Monitoring",
16
17
  "node": "Node",
17
18
  "open": "Open",
@@ -3,7 +3,7 @@
3
3
  <h3>Configuration</h3>
4
4
  <ul>
5
5
  <li><b>Node:</b> CAN node number (1-62)</li>
6
- <li><b>Input:</b> Network output number (Analog 1-32, Digital 1-16)</li>
6
+ <li><b>Input:</b> Network output number (CoE version 1: 1-32, CoE version 2: 1-64)</li>
7
7
  <li><b>Data Type:</b> Analog (numeric) or Digital (boolean)</li>
8
8
  <li><b>Unit:</b> Measurement unit (analog only)</li>
9
9
  </ul>
@@ -15,7 +15,7 @@
15
15
  "status": {
16
16
  "noconfig": "No config",
17
17
  "queued": "queued",
18
- "ready": "Ready",
18
+ "ready": "ready",
19
19
  "merged": "Sent",
20
20
  "retransmit": "Resent",
21
21
  "blocked": "Blocked"
package/lib/queueing.js CHANGED
@@ -26,7 +26,7 @@ function getOutputState(node, queueKey) {
26
26
  if (!outputStateCache[queueKey]) {
27
27
  outputStateCache[queueKey] = {
28
28
  nodeNumber: node.nodeNumber,
29
- dataType: node.dataType,
29
+ dataType: node.dataType.long,
30
30
  outputs: {}
31
31
  }
32
32
  }
@@ -93,7 +93,7 @@ function queueAndSend(node) {
93
93
 
94
94
  const now = Date.now();
95
95
  const mergedText = node._("coe-output.status.merged");
96
- const readyText = node._("coe-output.status.ready");
96
+ const readyText = node.readyText;
97
97
 
98
98
  queuedState[queueKey].participating.forEach(participatingNode => {
99
99
  const outputNumber = participatingNode.outputNumber;
@@ -108,7 +108,7 @@ function queueAndSend(node) {
108
108
  text: `${mergedText} [v${coeVersion}]`
109
109
  });
110
110
  setTimeout(() => {
111
- participatingNode.status({fill: "grey", shape: "ring", text: `${readyText} [v${coeVersion}]`});
111
+ participatingNode.status({fill: "grey", shape: "ring", text: readyText});
112
112
  }, 5000);
113
113
 
114
114
  // Send debug output on the node outputs: [original msg, debug info]
package/lib/utils.js CHANGED
@@ -8,6 +8,33 @@
8
8
 
9
9
  const UNITS = require ('./units.js');
10
10
 
11
+ // CAN Node, Output & Type validator
12
+ const Validate = {
13
+ node: function(value, allowZero) {
14
+ const min = allowZero ? 0 : 1;
15
+ const max = 62;
16
+ const num = parseInt(value, 10) || min;
17
+ return Math.min(Math.max(num, min), max);
18
+ },
19
+
20
+ output: function(value, version) {
21
+ const max = (version === 2 || version === "2") ? 64 : 32;
22
+ const num = parseInt(value, 10) || 1;
23
+ return Math.min(Math.max(num, 1), max);
24
+ },
25
+
26
+ types: {
27
+ "analog": { long: "analog", short: "A", isDigital: false },
28
+ "digital": { long: "digital", short: "D", isDigital: true },
29
+ "all": { long: "all", short: "", isDigital: null }
30
+ },
31
+
32
+ // Return a type object
33
+ type: function(input) {
34
+ return this.types[input] || this.types["analog"];
35
+ }
36
+ };
37
+
11
38
  // Utilities for unit conversion
12
39
  function convertRawToValue(rawValue, unitId, coeVersion) {
13
40
  const unitDecimals = getUnitDecimals(unitId, coeVersion);
@@ -87,21 +114,18 @@ function getUnitLanguage(lang) {
87
114
 
88
115
  // Translate output number to block position (CoE V1)
89
116
  function getBlockInfo(dataType, outputNumber) {
90
- let blockNumber, position, dType;
117
+ let blockNumber, position;
91
118
  outputNumber = parseInt(outputNumber);
92
119
 
93
120
  if (isNaN(outputNumber) || outputNumber < 1) { // Default to block 1 position 0
94
121
  blockNumber = 1;
95
122
  position = 0;
96
- dType = 'a';
97
123
  } else {
98
124
  if (dataType === 'analog') {
99
125
  // Analog: Outputs 1..32 → Blocks 1..8 (4 Outputs each)
100
126
  blockNumber = Math.floor((outputNumber - 1) / 4) + 1; // 1..8
101
127
  position = (outputNumber - 1) % 4; // 0..3
102
- dType = 'a';
103
128
  } else {
104
- dType = 'd';
105
129
  // Digital: Outputs 1..16 → Block 0, 17..32 → Block 9
106
130
  if (outputNumber <= 16) {
107
131
  blockNumber = 0;
@@ -157,6 +181,7 @@ function createEmptyState(incomingBlock) {
157
181
  }
158
182
 
159
183
  module.exports = {
184
+ Validate,
160
185
  convertRawToValue,
161
186
  convertValueToRaw,
162
187
  getUnitInfo,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-ta-cmi-coe",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Node-RED nodes for TA CMI CoE (CAN over Ethernet)",
5
5
  "author": "Florian Mayrhofer",
6
6
  "license": "Apache-2.0",