node-red-contrib-ta-cmi-coe 1.1.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.
- package/CHANGELOG.md +3 -0
- package/examples/monitor.json +42 -0
- package/lib/coe-v2.js +7 -8
- package/package.json +4 -2
- package/lib/old.js +0 -133
- /package/examples/{Example Flow.json → in-out.json} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "monitor_example",
|
|
4
|
+
"type": "coe-monitor",
|
|
5
|
+
"name": "CoE Monitor",
|
|
6
|
+
"cmiconfig": "mycmi",
|
|
7
|
+
"filterNodeNumber": 0,
|
|
8
|
+
"filterDataType": "all",
|
|
9
|
+
"includeRaw": false,
|
|
10
|
+
"x": 250,
|
|
11
|
+
"y": 100,
|
|
12
|
+
"wires": [
|
|
13
|
+
[
|
|
14
|
+
"monitor_debug"
|
|
15
|
+
]
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "monitor_debug",
|
|
20
|
+
"type": "debug",
|
|
21
|
+
"name": "Debug Monitor",
|
|
22
|
+
"active": true,
|
|
23
|
+
"tosidebar": true,
|
|
24
|
+
"console": false,
|
|
25
|
+
"tostatus": false,
|
|
26
|
+
"complete": "payload",
|
|
27
|
+
"targetType": "msg",
|
|
28
|
+
"statusVal": "",
|
|
29
|
+
"statusType": "auto",
|
|
30
|
+
"x": 450,
|
|
31
|
+
"y": 100,
|
|
32
|
+
"wires": []
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "mycmi",
|
|
36
|
+
"type": "cmiconfig",
|
|
37
|
+
"name": "My CMI",
|
|
38
|
+
"localip": "0.0.0.0",
|
|
39
|
+
"address": "192.168.0.100",
|
|
40
|
+
"coeVersion": 1
|
|
41
|
+
}
|
|
42
|
+
]
|
package/lib/coe-v2.js
CHANGED
|
@@ -38,7 +38,7 @@ function parsePacket(buffer) {
|
|
|
38
38
|
for (let i = 0; i < outputCount; i++) {
|
|
39
39
|
const offset = 4 + (i * 8);
|
|
40
40
|
const nodeNumber = buffer.readUInt8(offset);
|
|
41
|
-
const outputNumber = buffer.readUInt8(offset + 1);
|
|
41
|
+
const outputNumber = buffer.readUInt8(offset + 1) + 1;
|
|
42
42
|
const dataType = buffer.readUInt8(offset + 2);
|
|
43
43
|
const unitId = buffer.readUInt8(offset + 3);
|
|
44
44
|
const value = buffer.readInt32LE(offset + 4);
|
|
@@ -110,13 +110,15 @@ function createPacket(nodeNumber, dataType, outputs) {
|
|
|
110
110
|
for (let i = 0; i < blockCount; i++) {
|
|
111
111
|
const offset = 4 + (i * 8);
|
|
112
112
|
const output = outputState[i];
|
|
113
|
+
const isAnalog = (dataType === 'analog');
|
|
114
|
+
|
|
115
|
+
const rawNumber = output.outputNumber - 1 || 0;
|
|
113
116
|
const rawValue = convertValueToRaw(output.value, output.unitId, 2); // V2 decimals
|
|
114
117
|
|
|
115
118
|
buffer.writeUInt8(nodeNumber, offset);
|
|
116
119
|
|
|
117
|
-
|
|
118
|
-
buffer.writeUInt8(
|
|
119
|
-
buffer.writeUInt8((output.outputNumber >> 8) & 0xFF, offset + 2);
|
|
120
|
+
buffer.writeUInt8(rawNumber, offset + 1);
|
|
121
|
+
buffer.writeUInt8((isAnalog), offset + 2);
|
|
120
122
|
|
|
121
123
|
buffer.writeUInt8(output.unitId || 0, offset + 3); // Unit ID
|
|
122
124
|
buffer.writeInt32LE(rawValue, offset + 4); // Value (Int32 LE)
|
|
@@ -131,12 +133,9 @@ function convertUniformToV2Format(dataType, outputs) {
|
|
|
131
133
|
|
|
132
134
|
Object.entries(outputs).forEach(([outputKey, output]) => {
|
|
133
135
|
|
|
134
|
-
// Output > 255 = analog
|
|
135
|
-
const outputNumber = parseInt(outputKey) + !isDigital * 256;
|
|
136
|
-
|
|
137
136
|
if (output.value !== undefined) {
|
|
138
137
|
outputState.push({
|
|
139
|
-
outputNumber:
|
|
138
|
+
outputNumber: parseInt(outputKey),
|
|
140
139
|
value: isDigital? (output.value ? 1 : 0) : output.value,
|
|
141
140
|
unitId: output ? output.unit : 0
|
|
142
141
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-ta-cmi-coe",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Node-RED nodes for TA CMI CoE (CAN over Ethernet)",
|
|
5
5
|
"author": "Florian Mayrhofer",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -25,7 +25,9 @@
|
|
|
25
25
|
"units-config": "coe/units-config.js"
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
|
-
"dependencies": {
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"node-red-contrib-ta-cmi-coe": "^1.1.0"
|
|
30
|
+
},
|
|
29
31
|
"devDependencies": {
|
|
30
32
|
"jest": "^30.2.0"
|
|
31
33
|
},
|
package/lib/old.js
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CoE V2 Protocol Support Module
|
|
3
|
-
*
|
|
4
|
-
* Copyright 2025 Florian Mayrhofer
|
|
5
|
-
* Licensed under the Apache License, Version 2.0
|
|
6
|
-
*
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
// Convert V2 Data into Uniform format
|
|
10
|
-
function convertV2ToUniformFormat(v2Data) {
|
|
11
|
-
// Group Outputs by block
|
|
12
|
-
const blockMap = {};
|
|
13
|
-
|
|
14
|
-
v2Data.blocks.forEach(block => {
|
|
15
|
-
const isDigital = block.outputNumber <= 254;
|
|
16
|
-
const actualOutput = isDigital ? block.outputNumber : (block.outputNumber - 255);
|
|
17
|
-
|
|
18
|
-
// Determine block number and position
|
|
19
|
-
let blockNumber, position;
|
|
20
|
-
|
|
21
|
-
if (isDigital) {
|
|
22
|
-
// Digital: Output 1-16 → Block 0, Output 17-32 → Block 9
|
|
23
|
-
if (actualOutput <= 16) {
|
|
24
|
-
blockNumber = 0;
|
|
25
|
-
position = actualOutput - 1;
|
|
26
|
-
} else {
|
|
27
|
-
blockNumber = 9;
|
|
28
|
-
position = actualOutput - 17;
|
|
29
|
-
}
|
|
30
|
-
} else {
|
|
31
|
-
// Analog: Output 1-4 → Block 1, 5-8 → Block 2, etc.
|
|
32
|
-
blockNumber = Math.floor((actualOutput - 1) / 4) + 1;
|
|
33
|
-
position = (actualOutput - 1) % 4;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const key = `${block.nodeNumber}-${blockNumber}`;
|
|
37
|
-
|
|
38
|
-
if (!blockMap[key]) {
|
|
39
|
-
blockMap[key] = {
|
|
40
|
-
nodeNumber: block.nodeNumber,
|
|
41
|
-
blockNumber: blockNumber,
|
|
42
|
-
dataType: isDigital ? 'digital' : 'analog',
|
|
43
|
-
values: isDigital ? new Array(16).fill(undefined) : new Array(4).fill(undefined),
|
|
44
|
-
units: isDigital ? new Array(16).fill(undefined) : new Array(4).fill(undefined)
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Convert value & insert (V2 uses other decimals)
|
|
49
|
-
const convertedValue = convertRawToValue(block.value, block.unitId, 2);
|
|
50
|
-
blockMap[key].values[position] = isDigital ? (block.value ? 1 : 0) : convertedValue;
|
|
51
|
-
|
|
52
|
-
if (blockMap[key].units) {
|
|
53
|
-
blockMap[key].units[position] = block.unitId;
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
return Object.values(blockMap);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Create CoE V2 Packet
|
|
60
|
-
function createPacket(nodeNumber, dataType, outputs) {
|
|
61
|
-
// outputState: Array von {outputNumber, unit, value}
|
|
62
|
-
// Max 16 value blocks
|
|
63
|
-
const outputState = convertUniformToV2Format(dataType, outputs);
|
|
64
|
-
|
|
65
|
-
const blockCount = Math.min(outputState.length, 16);
|
|
66
|
-
const messageLength = 4 + (blockCount * 8);
|
|
67
|
-
|
|
68
|
-
const buffer = Buffer.alloc(messageLength);
|
|
69
|
-
|
|
70
|
-
// Write header
|
|
71
|
-
buffer.writeUInt8(0x02, 0); // Version Low
|
|
72
|
-
buffer.writeUInt8(0x00, 1); // Version High
|
|
73
|
-
buffer.writeUInt8(messageLength, 2); // Message Length
|
|
74
|
-
buffer.writeUInt8(blockCount, 3); // Block Count
|
|
75
|
-
|
|
76
|
-
// Write value blocks
|
|
77
|
-
for (let i = 0; i < blockCount; i++) {
|
|
78
|
-
const offset = 4 + (i * 8);
|
|
79
|
-
const output = outputState[i];
|
|
80
|
-
|
|
81
|
-
buffer.writeUInt8(nodeNumber, offset);
|
|
82
|
-
|
|
83
|
-
// Output Number (Little Endian, 2 Bytes)
|
|
84
|
-
buffer.writeUInt8(output.outputNumber & 0xFF, offset + 1);
|
|
85
|
-
buffer.writeUInt8((output.outputNumber >> 8) & 0xFF, offset + 2);
|
|
86
|
-
|
|
87
|
-
buffer.writeUInt8(output.unitId || 0, offset + 3); // Unit ID
|
|
88
|
-
buffer.writeInt32LE(output.value, offset + 4); // Value (Int32 LE)
|
|
89
|
-
}
|
|
90
|
-
return buffer;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Convert Uniform format to V2 outputs
|
|
94
|
-
// function convertUniformToV2Format(blockNumber, values, units, dataType) {
|
|
95
|
-
function convertUniformToV2Format(dataType, outputs) {
|
|
96
|
-
let outputState = [];
|
|
97
|
-
|
|
98
|
-
if (dataType === 'digital') {
|
|
99
|
-
// Digital: 16 Bits
|
|
100
|
-
const baseOutput = blockNumber === 0 ? 1 : 17;
|
|
101
|
-
for (let i = 0; i < values.length; i++) {
|
|
102
|
-
if (values[i] !== undefined) {
|
|
103
|
-
const unitId = units && units[i] !== undefined ? units[i] : 0;
|
|
104
|
-
|
|
105
|
-
outputState.push({
|
|
106
|
-
outputNumber: baseOutput + i,
|
|
107
|
-
unitId: unitId,
|
|
108
|
-
value: values[i] ? 1 : 0
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
} else {
|
|
113
|
-
// Analog: 4 Values
|
|
114
|
-
const baseOutput = (blockNumber - 1) * 4 + 1;
|
|
115
|
-
for (let i = 0; i < 4; i++) {
|
|
116
|
-
if (values[i] !== undefined) {
|
|
117
|
-
const unitId = units ? units[i] : 0;
|
|
118
|
-
const rawValue = convertValueToRaw(values[i], unitId, 2); // V2 uses other decimals
|
|
119
|
-
|
|
120
|
-
// Output > 255 = analog
|
|
121
|
-
const outputNumber = baseOutput + i + 255;
|
|
122
|
-
|
|
123
|
-
outputState.push({
|
|
124
|
-
outputNumber: outputNumber,
|
|
125
|
-
unitId: unitId,
|
|
126
|
-
value: rawValue
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return outputState;
|
|
133
|
-
}
|
|
File without changes
|