node-red-contrib-ta-cmi-coe 1.0.0 → 1.1.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/CHANGELOG.md +28 -17
- package/README.de.md +225 -0
- package/README.md +134 -187
- package/coe/coe-input.js +49 -36
- package/coe/coe-monitor.html +1 -1
- package/coe/coe-monitor.js +32 -41
- package/coe/coe-output.html +67 -28
- package/coe/coe-output.js +159 -24
- package/coe/config.js +14 -7
- package/coe/locales/de/coe-input.html +2 -2
- package/coe/locales/de/coe-input.json +8 -2
- package/coe/locales/de/coe-monitor.html +1 -2
- package/coe/locales/de/coe-monitor.json +9 -4
- package/coe/locales/de/coe-output.html +8 -2
- package/coe/locales/de/coe-output.json +11 -5
- package/coe/locales/de/config.html +3 -3
- package/coe/locales/de/config.json +1 -1
- package/coe/locales/en-US/coe-input.html +2 -2
- package/coe/locales/en-US/coe-input.json +8 -2
- package/coe/locales/en-US/coe-monitor.html +1 -2
- package/coe/locales/en-US/coe-monitor.json +9 -4
- package/coe/locales/en-US/coe-output.html +8 -2
- package/coe/locales/en-US/coe-output.json +10 -4
- package/coe/locales/en-US/config.html +3 -3
- package/coe/locales/en-US/config.json +1 -1
- package/coe/units-config.js +2 -1
- package/examples/Example Flow.json +56 -0
- package/lib/coe-v1.js +152 -0
- package/lib/coe-v2.js +78 -116
- package/lib/old.js +133 -0
- package/lib/protocol.js +19 -0
- package/lib/queueing.js +92 -137
- package/lib/units.js +7 -7
- package/lib/utils.js +71 -66
- package/package.json +14 -2
- package/__tests__/blockinfo.test.js +0 -24
- package/__tests__/conversion.test.js +0 -22
- package/__tests__/udp.test.js +0 -46
- package/lib/coe.js +0 -109
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-ta-cmi-coe",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Node-RED nodes for TA CMI CoE (CAN over Ethernet)",
|
|
5
5
|
"author": "Florian Mayrhofer",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"automation"
|
|
17
17
|
],
|
|
18
18
|
"node-red": {
|
|
19
|
+
"version": ">=2.0.0",
|
|
19
20
|
"nodes": {
|
|
20
21
|
"coe-input": "coe/coe-input.js",
|
|
21
22
|
"coe-output": "coe/coe-output.js",
|
|
@@ -25,10 +26,21 @@
|
|
|
25
26
|
}
|
|
26
27
|
},
|
|
27
28
|
"dependencies": {},
|
|
28
|
-
"devDependencies": {
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"jest": "^30.2.0"
|
|
31
|
+
},
|
|
29
32
|
"engines": {
|
|
30
33
|
"node": ">=14.0.0"
|
|
31
34
|
},
|
|
35
|
+
"files": [
|
|
36
|
+
"coe/",
|
|
37
|
+
"examples/",
|
|
38
|
+
"lib/",
|
|
39
|
+
"CHANGELOG.md",
|
|
40
|
+
"LICENSE",
|
|
41
|
+
"README.de.md",
|
|
42
|
+
"README.md"
|
|
43
|
+
],
|
|
32
44
|
"repository": {
|
|
33
45
|
"type": "git",
|
|
34
46
|
"url": "https://github.com/mayflo/node-red-contrib-ta-cmi-coe.git"
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
const { getBlockInfo } = require("../lib/utils");
|
|
2
|
-
|
|
3
|
-
describe("Block Info Tests", () => {
|
|
4
|
-
test("Analog Output 1 → Block 1, Position 0", () => {
|
|
5
|
-
expect(getBlockInfo("analog", 1)).toEqual({ block: 1, position: 0 });
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
test("Analog Output 5 → Block 2, Position 0", () => {
|
|
9
|
-
expect(getBlockInfo("analog", 5)).toEqual({ block: 2, position: 0 });
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
test("Digital Output 10 → Block 0, Position 9", () => {
|
|
13
|
-
expect(getBlockInfo("digital", 10)).toEqual({ block: 0, position: 9 });
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
test("Digital Output 20 → Block 9, Position 3", () => {
|
|
17
|
-
expect(getBlockInfo("digital", 20)).toEqual({ block: 9, position: 3 });
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
test("Ungültiger Output → Default", () => {
|
|
21
|
-
expect(getBlockInfo("analog", 0)).toEqual({ block: 1, position: 0 });
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
const { convertCoEToValue, convertValueToCoE, getUnitInfo } = require("../lib/utils");
|
|
2
|
-
|
|
3
|
-
describe("CoE Conversion Utilities", () => {
|
|
4
|
-
test("°C mit Unit=1, decimals=1", () => {
|
|
5
|
-
const unitInfo = { decimals: 1 };
|
|
6
|
-
expect(convertCoEToValue(225, 1, 1)).toBe(22.5);
|
|
7
|
-
expect(convertValueToCoE(22.5, 1, 1)).toBe(225);
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
test("kW mit Unit=10, Protocol V2 override", () => {
|
|
11
|
-
// V2 überschreibt decimals=2
|
|
12
|
-
expect(convertCoEToValue(2500, 10, 2)).toBe(25.0);
|
|
13
|
-
expect(convertValueToCoE(25.0, 10, 2)).toBe(2500);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
test("Unknown Unit", () => {
|
|
17
|
-
const info = getUnitInfo(999, "de");
|
|
18
|
-
expect(info.name).toMatch(/Unknown/);
|
|
19
|
-
expect(convertCoEToValue(1234, 999, 1)).toBe(1234); // decimals=0
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
|
package/__tests__/udp.test.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
const dgram = require("dgram");
|
|
2
|
-
|
|
3
|
-
describe("UDP Integration mit Mock", () => {
|
|
4
|
-
let server;
|
|
5
|
-
let received;
|
|
6
|
-
|
|
7
|
-
beforeAll(() => {
|
|
8
|
-
return new Promise((resolve) => {
|
|
9
|
-
server = dgram.createSocket("udp4");
|
|
10
|
-
server.on("listening", resolve);
|
|
11
|
-
server.on("message", (msg) => {
|
|
12
|
-
received = msg.toString();
|
|
13
|
-
});
|
|
14
|
-
server.bind(5555); // besser eigenen Test-Port nehmen
|
|
15
|
-
});
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
afterAll(() => {
|
|
19
|
-
server.close();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test("Sendet Testpaket", () => {
|
|
23
|
-
return new Promise((resolve, reject) => {
|
|
24
|
-
const client = dgram.createSocket("udp4");
|
|
25
|
-
const payload = Buffer.from("Hallo UDP-Mock!");
|
|
26
|
-
|
|
27
|
-
client.send(payload, 5555, "127.0.0.1", (err) => {
|
|
28
|
-
if (err) {
|
|
29
|
-
client.close();
|
|
30
|
-
return reject(err);
|
|
31
|
-
}
|
|
32
|
-
client.close();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
setTimeout(() => {
|
|
36
|
-
try {
|
|
37
|
-
expect(received).toBe("Hallo UDP-Mock!");
|
|
38
|
-
resolve();
|
|
39
|
-
} catch (e) {
|
|
40
|
-
reject(e);
|
|
41
|
-
}
|
|
42
|
-
}, 200);
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
|
package/lib/coe.js
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CoE Protocol Parsing and Creation Module
|
|
3
|
-
*
|
|
4
|
-
* Copyright 2025 Florian Mayrhofer
|
|
5
|
-
* Licensed under the Apache License, Version 2.0
|
|
6
|
-
*
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const { parseCoEV2Packet, createCoEV2Packet, convertV2ToLegacyFormat, convertLegacyToV2Format } = require('./coe-v2');
|
|
10
|
-
const { convertCoEToValue, convertValueToCoE } = require('./utils');
|
|
11
|
-
|
|
12
|
-
// Parse CoE packet from buffer
|
|
13
|
-
function parseCoEPacket(buffer, version) {
|
|
14
|
-
|
|
15
|
-
// If V2 Protokoll is used
|
|
16
|
-
if (version === 2) {
|
|
17
|
-
const v2Data = parseCoEV2Packet(buffer);
|
|
18
|
-
if (!v2Data) {
|
|
19
|
-
console.warn('V2: Unable to parse packet.');
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Convert to legacy format for further processing
|
|
24
|
-
const legacyBlocks = convertV2ToLegacyFormat(v2Data);
|
|
25
|
-
|
|
26
|
-
// Return all blocks (can be multiple)
|
|
27
|
-
return legacyBlocks;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const nodeNumber = buffer.readUInt8(0);
|
|
31
|
-
const blockNumber = buffer.readUInt8(1);
|
|
32
|
-
|
|
33
|
-
let values = [];
|
|
34
|
-
let units = [];
|
|
35
|
-
|
|
36
|
-
if (blockNumber === 0 || blockNumber === 9) {
|
|
37
|
-
// digital
|
|
38
|
-
const bitField = buffer.readUInt16LE(2);
|
|
39
|
-
const unitId = buffer.readUInt8(11); // Block-wise unit id → byte 11
|
|
40
|
-
for (let i = 0; i < 16; i++) {
|
|
41
|
-
values.push((bitField >> i) & 1);
|
|
42
|
-
units.push(unitId);
|
|
43
|
-
}
|
|
44
|
-
} else {
|
|
45
|
-
// analog V1
|
|
46
|
-
for (let i = 0; i < 4; i++) {
|
|
47
|
-
const value = buffer.readInt16LE(2 + i * 2);
|
|
48
|
-
const unitId = buffer.readUInt8(10 + i);
|
|
49
|
-
|
|
50
|
-
const convertedValue = convertCoEToValue(value, unitId, 1); // V1 decimals
|
|
51
|
-
values.push(convertedValue);
|
|
52
|
-
units.push(unitId);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return [{
|
|
57
|
-
nodeNumber: nodeNumber,
|
|
58
|
-
blockNumber: blockNumber,
|
|
59
|
-
values: values,
|
|
60
|
-
units: units
|
|
61
|
-
}];
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Create CoE Packet from values
|
|
65
|
-
function createCoEPacket(nodeNumber, blockNumber, values, units, dataType, version) {
|
|
66
|
-
// If V2 Protokoll is used
|
|
67
|
-
if (version === 2) {
|
|
68
|
-
const outputs = convertLegacyToV2Format(nodeNumber, blockNumber, values, units, dataType);
|
|
69
|
-
return createCoEV2Packet(nodeNumber, outputs);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
let buffer;
|
|
73
|
-
|
|
74
|
-
if (dataType === 'digital') {
|
|
75
|
-
buffer = Buffer.alloc(14);
|
|
76
|
-
buffer.writeUInt8(nodeNumber, 0);
|
|
77
|
-
buffer.writeUInt8(blockNumber, 1);
|
|
78
|
-
|
|
79
|
-
let bitField = 0;
|
|
80
|
-
for (let i = 0; i < 16; i++) {
|
|
81
|
-
if (values[i]) {
|
|
82
|
-
bitField |= (1 << i);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
buffer.writeUInt16LE(bitField, 2);
|
|
86
|
-
buffer.fill(0, 4, buffer.length);
|
|
87
|
-
|
|
88
|
-
} else { // analog
|
|
89
|
-
buffer = Buffer.alloc(14);
|
|
90
|
-
buffer.writeUInt8(nodeNumber, 0);
|
|
91
|
-
buffer.writeUInt8(blockNumber, 1);
|
|
92
|
-
|
|
93
|
-
for (let i = 0; i < 4; i++) {
|
|
94
|
-
const unitId = units ? units[i] : 0;
|
|
95
|
-
const rawValue = convertValueToCoE(values[i], unitId, 1); // V1 Decimal places
|
|
96
|
-
|
|
97
|
-
if (rawValue > 32767 || rawValue < -32768) {
|
|
98
|
-
console.warn(`Value ${values[i]} exceeds V1 limits. Consider using V2.`);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
buffer.writeInt16LE(Math.max(-32768, Math.min(32767, rawValue)), 2 + i * 2);
|
|
102
|
-
buffer.writeUInt8(unitId, 10 + i);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return buffer;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
module.exports = { parseCoEPacket, createCoEPacket};
|