smithtek-mako-rf 2.9.10 → 3.0.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/package.json +25 -25
- package/smithtek-mako-rf.js +35 -41
- package/smithtek-mako-rf-2.9.10.tgz +0 -0
- package/smithtek-mako-rf-2.9.9.tgz +0 -0
package/package.json
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "smithtek-mako-rf",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Smithtek dedicated node for communicating with the Mako PLC over RS485 or RF",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"node-red",
|
|
7
|
-
"smithtek",
|
|
8
|
-
"modbus",
|
|
9
|
-
"rtu",
|
|
10
|
-
"rs485",
|
|
11
|
-
"lora"
|
|
12
|
-
],
|
|
13
|
-
"license": "GPL-3.0-only",
|
|
14
|
-
"author": "Smithtek",
|
|
15
|
-
"homepage": "https://www.smithtek.com.au",
|
|
16
|
-
"dependencies": {
|
|
17
|
-
"modbus-serial": "8.0.23-no-serial-port",
|
|
18
|
-
"serialport": "10.4.0"
|
|
19
|
-
},
|
|
20
|
-
"node-red": {
|
|
21
|
-
"nodes": {
|
|
22
|
-
"smithtek-mako-rf": "smithtek-mako-rf.js"
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "smithtek-mako-rf",
|
|
3
|
+
"version": "3.0.1",
|
|
4
|
+
"description": "Smithtek dedicated node for communicating with the Mako PLC over RS485 or RF",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"node-red",
|
|
7
|
+
"smithtek",
|
|
8
|
+
"modbus",
|
|
9
|
+
"rtu",
|
|
10
|
+
"rs485",
|
|
11
|
+
"lora"
|
|
12
|
+
],
|
|
13
|
+
"license": "GPL-3.0-only",
|
|
14
|
+
"author": "Smithtek",
|
|
15
|
+
"homepage": "https://www.smithtek.com.au",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"modbus-serial": "8.0.23-no-serial-port",
|
|
18
|
+
"serialport": "10.4.0"
|
|
19
|
+
},
|
|
20
|
+
"node-red": {
|
|
21
|
+
"nodes": {
|
|
22
|
+
"smithtek-mako-rf": "smithtek-mako-rf.js"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
package/smithtek-mako-rf.js
CHANGED
|
@@ -185,12 +185,17 @@ state._rssiCleanup = cleanup;
|
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
// Scale supports:
|
|
188
|
-
// - numeric => multiply
|
|
189
|
-
// - operators like ">100", "<<2", "==1", etc.
|
|
188
|
+
// - numeric => multiply (including 0)
|
|
189
|
+
// - operators like "*2", "/10", "+5", "-7", ">100", "<<2", "==1", etc.
|
|
190
190
|
const scalingOps = {
|
|
191
|
+
"*": (v, o) => v * o,
|
|
192
|
+
"/": (v, o) => v / o,
|
|
193
|
+
"+": (v, o) => v + o,
|
|
194
|
+
"-": (v, o) => v - o,
|
|
195
|
+
|
|
191
196
|
">": (v, o) => v > o,
|
|
192
197
|
"<": (v, o) => v < o,
|
|
193
|
-
"==": (v, o) => v
|
|
198
|
+
"==": (v, o) => v === o,
|
|
194
199
|
"!=": (v, o) => v != o,
|
|
195
200
|
"%": (v, o) => v % o,
|
|
196
201
|
"<<": (v, o) => v << o,
|
|
@@ -198,16 +203,19 @@ state._rssiCleanup = cleanup;
|
|
|
198
203
|
">>>": (v, o) => v >>> o,
|
|
199
204
|
};
|
|
200
205
|
|
|
201
|
-
|
|
206
|
+
// allow optional leading operator, otherwise a plain number
|
|
207
|
+
const scalerRegex = /^\s*([*\/+\-]|[<>!=%]{1,3}|<<|>>>|>>)\s*(-?\d+(\.\d+)?)\s*$/;
|
|
202
208
|
|
|
203
209
|
function parseScaler(scale) {
|
|
204
210
|
if (scale == null) return null;
|
|
205
211
|
const s = String(scale).trim();
|
|
206
|
-
if (!s
|
|
212
|
+
if (!s) return null;
|
|
207
213
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
214
|
+
// Plain numeric means multiply (0 is valid!)
|
|
215
|
+
const asNum = Number(s);
|
|
216
|
+
if (!Number.isNaN(asNum)) return { op: "*", val: asNum };
|
|
217
|
+
|
|
218
|
+
// Operator form: "*2" "/10" "+5" etc
|
|
211
219
|
const m = s.match(scalerRegex);
|
|
212
220
|
if (m && m[1] && !Number.isNaN(Number(m[2])) && scalingOps[m[1]]) {
|
|
213
221
|
return { op: m[1], val: Number(m[2]) };
|
|
@@ -222,7 +230,7 @@ state._rssiCleanup = cleanup;
|
|
|
222
230
|
// Read a typed value using your Mako dropdown names.
|
|
223
231
|
// Offset is BYTES (0,2,4...) like your original decoder.
|
|
224
232
|
// For 32-bit values, auto-detect word/byte order once per decode pass.
|
|
225
|
-
function readTyped(regs, type, byteOffset, offsetBit
|
|
233
|
+
function readTyped(regs, type, byteOffset, offsetBit) {
|
|
226
234
|
const t = String(type || "").toLowerCase();
|
|
227
235
|
const regIndex = Math.floor(byteOffset / 2);
|
|
228
236
|
|
|
@@ -251,34 +259,11 @@ state._rssiCleanup = cleanup;
|
|
|
251
259
|
return Buffer.from([b & 0xff, (b >> 8) & 0xff, a & 0xff, (a >> 8) & 0xff]);
|
|
252
260
|
}
|
|
253
261
|
|
|
254
|
-
function pick32OrderIfNeeded(a, b) {
|
|
255
|
-
if (state32.order) return state32.order;
|
|
256
|
-
|
|
257
|
-
const candidates = [
|
|
258
|
-
{ o: "ABCD", v: bytesABCD(a, b).readFloatBE(0) },
|
|
259
|
-
{ o: "CDAB", v: bytesCDAB(a, b).readFloatBE(0) },
|
|
260
|
-
{ o: "BADC", v: bytesBADC(a, b).readFloatBE(0) },
|
|
261
|
-
{ o: "DCBA", v: bytesDCBA(a, b).readFloatBE(0) },
|
|
262
|
-
];
|
|
263
|
-
|
|
264
|
-
// Sane heuristic: finite, not denormal-tiny, not insane huge
|
|
265
|
-
for (const c of candidates) {
|
|
266
|
-
if (Number.isFinite(c.v) && Math.abs(c.v) > 1e-6 && Math.abs(c.v) < 1e9) {
|
|
267
|
-
state32.order = c.o;
|
|
268
|
-
return c.o;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
262
|
|
|
272
|
-
state32.order = "ABCD";
|
|
273
|
-
return state32.order;
|
|
274
|
-
}
|
|
275
263
|
|
|
276
264
|
function get32Bytes(a, b) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
if (order === "CDAB") return bytesCDAB(a, b);
|
|
280
|
-
if (order === "BADC") return bytesBADC(a, b);
|
|
281
|
-
return bytesDCBA(a, b);
|
|
265
|
+
// LOCKED CDAB (matches your confirmed raw->decoded results)
|
|
266
|
+
return bytesCDAB(a, b);
|
|
282
267
|
}
|
|
283
268
|
|
|
284
269
|
switch (t) {
|
|
@@ -343,7 +328,7 @@ state._rssiCleanup = cleanup;
|
|
|
343
328
|
if (!Array.isArray(regArray) || !Array.isArray(items) || !items.length) return decoded;
|
|
344
329
|
|
|
345
330
|
// Auto-detect 32-bit order on first 32-bit value, then reuse for all
|
|
346
|
-
|
|
331
|
+
// const state32 = { order: null };
|
|
347
332
|
|
|
348
333
|
for (let i = 0; i < items.length; i++) {
|
|
349
334
|
const it = items[i] || {};
|
|
@@ -359,18 +344,27 @@ state._rssiCleanup = cleanup;
|
|
|
359
344
|
|
|
360
345
|
let val;
|
|
361
346
|
try {
|
|
362
|
-
val = readTyped(regArray, type, byteOffset, offsetBit
|
|
347
|
+
val = readTyped(regArray, type, byteOffset, offsetBit);
|
|
363
348
|
} catch (_e) {
|
|
364
349
|
continue;
|
|
365
350
|
}
|
|
366
351
|
|
|
352
|
+
// Read scale from multiple possible editor field names (prevents silent "never scales")
|
|
353
|
+
const scaleRaw = (it.scale != null) ? it.scale
|
|
354
|
+
: (it.scaler != null) ? it.scaler
|
|
355
|
+
: (it.scaleFactor != null) ? it.scaleFactor
|
|
356
|
+
: null;
|
|
357
|
+
|
|
358
|
+
// Apply mask ONLY if value is an integer (otherwise it will trash floats)
|
|
367
359
|
const mask = parseMask(it.mask);
|
|
368
|
-
if (mask && typeof val === "number"
|
|
360
|
+
if (mask && typeof val === "number" && Number.isInteger(val)) {
|
|
361
|
+
val = (val & mask) >>> 0; // keep it sane as unsigned
|
|
362
|
+
}
|
|
369
363
|
|
|
370
|
-
const scaler = parseScaler(
|
|
371
|
-
if (scaler) {
|
|
372
|
-
|
|
373
|
-
|
|
364
|
+
const scaler = parseScaler(scaleRaw);
|
|
365
|
+
if (scaler && typeof val === "number") {
|
|
366
|
+
const fn = scalingOps[scaler.op] || scalingOps["*"];
|
|
367
|
+
val = fn(val, scaler.val);
|
|
374
368
|
}
|
|
375
369
|
|
|
376
370
|
setObjectProperty(decoded, name, val, "=>");
|
|
Binary file
|
|
Binary file
|