node-red-contrib-3dm-space 2.2.0 → 2.2.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/3dm-cloud-in.js +4 -2
- package/3dm-cloud-join.js +3 -1
- package/3dm-cloud-out.js +15 -2
- package/3dm-config-store.js +5 -3
- package/CHANGELOG.md +8 -0
- package/README.md +8 -1
- package/package.json +43 -34
- package/payload-precision.js +38 -0
- package/node-red-contrib-3dm-space-2.0.0.tgz +0 -0
package/3dm-cloud-in.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module.exports = function (RED) {
|
|
2
|
-
"use strict";
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
var roundPayload = require("./payload-precision");
|
|
3
5
|
|
|
4
6
|
/*
|
|
5
7
|
* Hidden cloud subscribe settings.
|
|
@@ -53,7 +55,7 @@ module.exports = function (RED) {
|
|
|
53
55
|
return;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
var data = parsePayload(payload);
|
|
58
|
+
var data = roundPayload(parsePayload(payload));
|
|
57
59
|
|
|
58
60
|
if (node.keyFilter) {
|
|
59
61
|
if (typeof data !== "object" || data === null || Array.isArray(data)) {
|
package/3dm-cloud-join.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
module.exports = function (RED) {
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
|
+
var roundPayload = require("./payload-precision");
|
|
5
|
+
|
|
4
6
|
function isPlainObject(value) {
|
|
5
7
|
return value &&
|
|
6
8
|
typeof value === "object" &&
|
|
@@ -79,7 +81,7 @@ module.exports = function (RED) {
|
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
var outMsg = node.firstMsg || {};
|
|
82
|
-
outMsg.payload = clonePayloadObject(node.buffer);
|
|
84
|
+
outMsg.payload = roundPayload(clonePayloadObject(node.buffer));
|
|
83
85
|
|
|
84
86
|
node.send(outMsg);
|
|
85
87
|
|
package/3dm-cloud-out.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module.exports = function (RED) {
|
|
2
|
-
"use strict";
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
var roundPayload = require("./payload-precision");
|
|
3
5
|
|
|
4
6
|
var fs = require("fs");
|
|
5
7
|
var path = require("path");
|
|
@@ -234,7 +236,7 @@ module.exports = function (RED) {
|
|
|
234
236
|
|
|
235
237
|
return {
|
|
236
238
|
ts: ts,
|
|
237
|
-
values: payload
|
|
239
|
+
values: roundPayload(payload)
|
|
238
240
|
};
|
|
239
241
|
}
|
|
240
242
|
|
|
@@ -253,6 +255,8 @@ module.exports = function (RED) {
|
|
|
253
255
|
return;
|
|
254
256
|
}
|
|
255
257
|
|
|
258
|
+
packet = Object.assign({}, packet, { values: roundPayload(packet.values) });
|
|
259
|
+
|
|
256
260
|
node.server.publish(
|
|
257
261
|
TELEMETRY_TOPIC,
|
|
258
262
|
packet,
|
|
@@ -283,6 +287,15 @@ module.exports = function (RED) {
|
|
|
283
287
|
}
|
|
284
288
|
|
|
285
289
|
function sendResult(msg, send, done) {
|
|
290
|
+
if (typeof msg.payload === "string") {
|
|
291
|
+
try {
|
|
292
|
+
msg.payload = JSON.stringify(roundPayload(JSON.parse(msg.payload)));
|
|
293
|
+
} catch (err) {
|
|
294
|
+
// Preserve non-JSON text on rejected messages.
|
|
295
|
+
}
|
|
296
|
+
} else {
|
|
297
|
+
msg.payload = roundPayload(msg.payload);
|
|
298
|
+
}
|
|
286
299
|
send(msg);
|
|
287
300
|
|
|
288
301
|
if (done) {
|
package/3dm-config-store.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module.exports = function (RED) {
|
|
2
|
-
"use strict";
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
var roundPayload = require("./payload-precision");
|
|
3
5
|
|
|
4
6
|
var fs = require("fs");
|
|
5
7
|
var path = require("path");
|
|
@@ -694,9 +696,9 @@ module.exports = function (RED) {
|
|
|
694
696
|
topic: "configStore",
|
|
695
697
|
event: eventName || "configStore",
|
|
696
698
|
payload: {
|
|
697
|
-
configStore: latestStore
|
|
699
|
+
configStore: roundPayload(latestStore)
|
|
698
700
|
},
|
|
699
|
-
configStore: latestStore
|
|
701
|
+
configStore: roundPayload(latestStore)
|
|
700
702
|
};
|
|
701
703
|
|
|
702
704
|
node.send(outputs);
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 2.2.1 — 2026-10-01
|
|
4
|
+
|
|
5
|
+
- Round numeric output payload values to a maximum of five decimal places, including nested objects and arrays.
|
|
6
|
+
- Apply consistent precision to cloud telemetry, queue replay, local telemetry results, incoming attributes, joined messages, and config snapshots.
|
|
7
|
+
- Preserve numeric types, integers, text, stored configuration precision, and internal control comparisons.
|
|
8
|
+
- Include the shared precision helper in the npm package and exclude old release archives.
|
package/README.md
CHANGED
|
@@ -23,6 +23,13 @@ The package is intended for Smithtek PassPort gateway projects using 3DM.space,
|
|
|
23
23
|
|
|
24
24
|
---
|
|
25
25
|
|
|
26
|
+
|
|
27
|
+
## Numeric output precision
|
|
28
|
+
|
|
29
|
+
From version 2.2.1, numeric output payload values are rounded to a maximum of five decimal places. This applies recursively to nested objects and arrays in telemetry, incoming attributes, joined payloads, and config snapshots. For example, `12.12345678` becomes `12.12346`.
|
|
30
|
+
|
|
31
|
+
Values remain numbers; trailing zeros are not added. Integers, strings (including numeric strings), booleans, and null are unchanged. JSON telemetry strings are parsed and their numeric values are rounded before publishing; the local result payload remains a JSON string. Live and queued telemetry use the same precision, including older packets replayed from the queue. Stored configuration and internal control comparisons retain their original precision.
|
|
32
|
+
|
|
26
33
|
## Requirements
|
|
27
34
|
|
|
28
35
|
To use these nodes, you need:
|
|
@@ -53,7 +60,7 @@ The credentials are copied from 3DM.space into Node-RED in this order:
|
|
|
53
60
|
|
|
54
61
|
## Nodes Included
|
|
55
62
|
|
|
56
|
-
This package includes
|
|
63
|
+
This package includes five nodes:
|
|
57
64
|
|
|
58
65
|
- **3DM Cloud Login**
|
|
59
66
|
- **3DM Out**
|
package/package.json
CHANGED
|
@@ -1,34 +1,43 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "node-red-contrib-3dm-space",
|
|
3
|
-
"version": "2.2.
|
|
4
|
-
"description": "Node-RED nodes for 3DM.space SCADA Cloud telemetry, attributes, and local config storage",
|
|
5
|
-
"main": "3dm-cloud-config.js",
|
|
6
|
-
"license": "GPL-3.0-or-later",
|
|
7
|
-
"keywords": [
|
|
8
|
-
"node-red",
|
|
9
|
-
"node-red-contrib",
|
|
10
|
-
"3dm",
|
|
11
|
-
"3dm.space",
|
|
12
|
-
"smithtek",
|
|
13
|
-
"scada",
|
|
14
|
-
"telemetry",
|
|
15
|
-
"store-and-forward",
|
|
16
|
-
"config-store",
|
|
17
|
-
"scheduler"
|
|
18
|
-
],
|
|
19
|
-
"node-red": {
|
|
20
|
-
"nodes": {
|
|
21
|
-
"3dm-cloud-config": "3dm-cloud-config.js",
|
|
22
|
-
"3dm-cloud-out": "3dm-cloud-out.js",
|
|
23
|
-
"3dm-cloud-in": "3dm-cloud-in.js",
|
|
24
|
-
"3dm-config-store": "3dm-config-store.js",
|
|
25
|
-
"3dm-cloud-join": "3dm-cloud-join.js"
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
"dependencies": {
|
|
29
|
-
"mqtt": "~4.3.7"
|
|
30
|
-
},
|
|
31
|
-
"optionalDependencies": {
|
|
32
|
-
"node-persistent-queue": "^0.3.1"
|
|
33
|
-
}
|
|
34
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "node-red-contrib-3dm-space",
|
|
3
|
+
"version": "2.2.1",
|
|
4
|
+
"description": "Node-RED nodes for 3DM.space SCADA Cloud telemetry, attributes, and local config storage",
|
|
5
|
+
"main": "3dm-cloud-config.js",
|
|
6
|
+
"license": "GPL-3.0-or-later",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"node-red",
|
|
9
|
+
"node-red-contrib",
|
|
10
|
+
"3dm",
|
|
11
|
+
"3dm.space",
|
|
12
|
+
"smithtek",
|
|
13
|
+
"scada",
|
|
14
|
+
"telemetry",
|
|
15
|
+
"store-and-forward",
|
|
16
|
+
"config-store",
|
|
17
|
+
"scheduler"
|
|
18
|
+
],
|
|
19
|
+
"node-red": {
|
|
20
|
+
"nodes": {
|
|
21
|
+
"3dm-cloud-config": "3dm-cloud-config.js",
|
|
22
|
+
"3dm-cloud-out": "3dm-cloud-out.js",
|
|
23
|
+
"3dm-cloud-in": "3dm-cloud-in.js",
|
|
24
|
+
"3dm-config-store": "3dm-config-store.js",
|
|
25
|
+
"3dm-cloud-join": "3dm-cloud-join.js"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"mqtt": "~4.3.7"
|
|
30
|
+
},
|
|
31
|
+
"optionalDependencies": {
|
|
32
|
+
"node-persistent-queue": "^0.3.1"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"3dm-*.js",
|
|
36
|
+
"3dm-*.html",
|
|
37
|
+
"payload-precision.js",
|
|
38
|
+
"icons/",
|
|
39
|
+
"icon.png",
|
|
40
|
+
"README.md",
|
|
41
|
+
"CHANGELOG.md"
|
|
42
|
+
]
|
|
43
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Round numeric payload values without changing strings or the source objects.
|
|
4
|
+
function roundPayload(value, seen) {
|
|
5
|
+
if (typeof value === "number") {
|
|
6
|
+
return Number.isFinite(value) && !Number.isInteger(value)
|
|
7
|
+
? Number(value.toFixed(5))
|
|
8
|
+
: value;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (!value || typeof value !== "object") {
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
var prototype = Object.getPrototypeOf(value);
|
|
16
|
+
if (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null) {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
seen = seen || new WeakMap();
|
|
21
|
+
if (seen.has(value)) {
|
|
22
|
+
return seen.get(value);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
var output = Array.isArray(value) ? new Array(value.length) : Object.create(prototype);
|
|
26
|
+
seen.set(value, output);
|
|
27
|
+
Object.keys(value).forEach(function (key) {
|
|
28
|
+
Object.defineProperty(output, key, {
|
|
29
|
+
value: roundPayload(value[key], seen),
|
|
30
|
+
enumerable: true,
|
|
31
|
+
writable: true,
|
|
32
|
+
configurable: true
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
return output;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = roundPayload;
|
|
Binary file
|