node-red-contrib-modbus-modpackqt 1.1.85 → 2.0.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.
@@ -0,0 +1,118 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType('modpackqt-traffic', {
3
+ category: 'ModPackQT',
4
+ color: '#22c55e',
5
+ defaults: {
6
+ name: { value: '' },
7
+ server: { value: '', type: 'modpackqt-config', required: true },
8
+ filterDirection: { value: 'any' },
9
+ filterKind: { value: 'any' },
10
+ filterFc: { value: 'any' },
11
+ filterTarget: { value: '' },
12
+ format: { value: 'object' },
13
+ alsoLog: { value: false }
14
+ },
15
+ inputs: 0,
16
+ outputs: 1,
17
+ icon: 'font-awesome/fa-eye',
18
+ label: function () {
19
+ const parts = [];
20
+ if (this.filterKind !== 'any') parts.push(this.filterKind);
21
+ if (this.filterDirection !== 'any') parts.push(this.filterDirection);
22
+ if (this.filterFc !== 'any') parts.push('FC' + this.filterFc);
23
+ if (this.filterTarget) parts.push(this.filterTarget);
24
+ return this.name || ('traffic' + (parts.length ? ' · ' + parts.join(' ') : ''));
25
+ },
26
+ paletteLabel: 'modbus traffic'
27
+ });
28
+ </script>
29
+
30
+ <script type="text/html" data-template-name="modpackqt-traffic">
31
+ <div class="form-row"><label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
32
+ <input type="text" id="node-input-name" placeholder="Name"></div>
33
+ <div class="form-row"><label for="node-input-server"><i class="fa fa-cog"></i> Runtime</label>
34
+ <input type="text" id="node-input-server"></div>
35
+
36
+ <h4 style="margin-top:14px">Filters (optional)</h4>
37
+ <div class="form-row"><label for="node-input-filterKind"><i class="fa fa-server"></i> Kind</label>
38
+ <select id="node-input-filterKind">
39
+ <option value="any">Any</option>
40
+ <option value="master">Master only</option>
41
+ <option value="slave">Slave only</option>
42
+ </select>
43
+ </div>
44
+ <div class="form-row"><label for="node-input-filterDirection"><i class="fa fa-exchange"></i> Direction</label>
45
+ <select id="node-input-filterDirection">
46
+ <option value="any">Any</option>
47
+ <option value="read">Read only</option>
48
+ <option value="write">Write only</option>
49
+ </select>
50
+ </div>
51
+ <div class="form-row"><label for="node-input-filterFc"><i class="fa fa-list"></i> Function Code</label>
52
+ <select id="node-input-filterFc">
53
+ <option value="any">Any</option>
54
+ <option value="1">FC1 — Read Coils</option>
55
+ <option value="2">FC2 — Read Discrete Inputs</option>
56
+ <option value="3">FC3 — Read Holding</option>
57
+ <option value="4">FC4 — Read Input</option>
58
+ <option value="5">FC5 — Write Coil</option>
59
+ <option value="6">FC6 — Write Register</option>
60
+ <option value="15">FC15 — Write Coils</option>
61
+ <option value="16">FC16 — Write Registers</option>
62
+ </select>
63
+ </div>
64
+ <div class="form-row"><label for="node-input-filterTarget"><i class="fa fa-filter"></i> Target contains</label>
65
+ <input type="text" id="node-input-filterTarget" placeholder="e.g. 192.168.1.10 or rtu:/dev/ttyUSB0"></div>
66
+
67
+ <h4 style="margin-top:14px">Output</h4>
68
+ <div class="form-row"><label for="node-input-format"><i class="fa fa-code"></i> Payload</label>
69
+ <select id="node-input-format">
70
+ <option value="object">Full event object</option>
71
+ <option value="summary">One-line summary string</option>
72
+ </select>
73
+ </div>
74
+ <div class="form-row"><label for="node-input-alsoLog" style="width:auto">
75
+ <input type="checkbox" id="node-input-alsoLog" style="width:auto;vertical-align:middle">
76
+ Also write to Node-RED log
77
+ </label></div>
78
+
79
+ <div class="form-tips">
80
+ <b>Passive monitor.</b> No input wire — just listens to all Modbus ops on the runtime
81
+ and emits one message each. Free.
82
+ </div>
83
+ </script>
84
+
85
+ <script type="text/html" data-help-name="modpackqt-traffic">
86
+ <p>
87
+ Passive Modbus traffic monitor. Subscribes to all Modbus operations performed by any
88
+ master or slave node sharing the same runtime config and emits one message per op.
89
+ </p>
90
+
91
+ <h3>Output payload (object format)</h3>
92
+ <pre>{
93
+ ts: "2026-05-09T14:23:01.234Z",
94
+ direction: "read" | "write",
95
+ kind: "master" | "slave",
96
+ target: "192.168.1.10:502" | "local:1502" | "rtu:/dev/ttyUSB0",
97
+ unitId: 1,
98
+ fc: 3, // null for slave events
99
+ registerType: "holding",// only set for slave events
100
+ address: 100,
101
+ quantity: 2,
102
+ values: [16828, 0], // raw register values
103
+ durationMs: 12,
104
+ ok: true,
105
+ error: null // populated on failure
106
+ }</pre>
107
+
108
+ <h3>Use cases</h3>
109
+ <ul>
110
+ <li><b>Debug:</b> wire to a Debug node — see exactly what every Modbus op did.</li>
111
+ <li><b>Alerting:</b> filter <code>ok=false</code> → email/Slack on errors.</li>
112
+ <li><b>Charting:</b> count ops/sec, response-time histograms, per-target latency.</li>
113
+ <li><b>Audit trail:</b> store every op in a database for compliance.</li>
114
+ </ul>
115
+
116
+ <h3>Pricing</h3>
117
+ <p><b>Free</b> — visibility costs nothing. Traffic events themselves don't count toward your daily op limit.</p>
118
+ </script>
@@ -0,0 +1,68 @@
1
+ /**
2
+ * ModPackQT Traffic — passive Modbus traffic monitor.
3
+ *
4
+ * Subscribes to the config node's traffic event bus and emits one message
5
+ * per Modbus operation (master read/write or slave read/write).
6
+ *
7
+ * Free for everyone — debug visibility costs nothing.
8
+ */
9
+ module.exports = function (RED) {
10
+ function ModPackQTTrafficNode(config) {
11
+ RED.nodes.createNode(this, config);
12
+ const node = this;
13
+ const server = RED.nodes.getNode(config.server);
14
+
15
+ node.filterDirection = config.filterDirection || 'any'; // any | read | write
16
+ node.filterKind = config.filterKind || 'any'; // any | master | slave
17
+ node.filterFc = config.filterFc || 'any'; // any | 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16
18
+ node.filterTarget = (config.filterTarget || '').trim(); // substring match
19
+ node.format = config.format || 'object'; // object | summary
20
+ node.alsoLog = config.alsoLog === true || config.alsoLog === 'true';
21
+
22
+ let count = 0;
23
+
24
+ if (!server) {
25
+ node.status({ fill: 'red', shape: 'ring', text: 'no config' });
26
+ return;
27
+ }
28
+
29
+ function summarize(evt) {
30
+ const tag = evt.kind === 'slave'
31
+ ? `slave ${evt.direction} ${evt.registerType}@${evt.address}×${evt.quantity}`
32
+ : `${evt.direction} FC${evt.fc} ${evt.target} u${evt.unitId} @${evt.address}×${evt.quantity}`;
33
+ const status = evt.ok ? `${evt.durationMs}ms` : `ERR ${evt.error || ''}`;
34
+ return `${tag} · ${status}`;
35
+ }
36
+
37
+ function onTraffic(evt) {
38
+ // Filters
39
+ if (node.filterDirection !== 'any' && evt.direction !== node.filterDirection) return;
40
+ if (node.filterKind !== 'any' && evt.kind !== node.filterKind) return;
41
+ if (node.filterFc !== 'any' && String(evt.fc) !== String(node.filterFc)) return;
42
+ if (node.filterTarget && !(evt.target || '').includes(node.filterTarget)) return;
43
+
44
+ count += 1;
45
+ const summary = summarize(evt);
46
+ node.status({
47
+ fill: evt.ok ? 'green' : 'red', shape: 'dot',
48
+ text: `modpackqt · ${count} ops · ${summary.slice(0, 50)}`
49
+ });
50
+
51
+ if (node.alsoLog) node.log(summary);
52
+
53
+ const msg = (node.format === 'summary')
54
+ ? { payload: summary, traffic: evt }
55
+ : { payload: evt };
56
+ msg.topic = `modpackqt/traffic/${evt.kind}/${evt.direction}`;
57
+ node.send(msg);
58
+ }
59
+
60
+ server.traffic.on('op', onTraffic);
61
+ node.status({ fill: 'grey', shape: 'ring', text: 'modpackqt · waiting…' });
62
+
63
+ node.on('close', function () {
64
+ try { server.traffic.removeListener('op', onTraffic); } catch (_) {}
65
+ });
66
+ }
67
+ RED.nodes.registerType('modpackqt-traffic', ModPackQTTrafficNode);
68
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "node-red-contrib-modbus-modpackqt",
3
- "version": "1.1.85",
4
- "description": "Node-RED nodes for ModPackQT read/write Modbus TCP registers (master & slave) via the ModPackQT Gateway app. Supports FC1/FC2/FC3/FC4 reads and FC5/FC6/FC15/FC16 writes.",
3
+ "version": "2.0.0",
4
+ "description": "Node-RED Modbus nodes — embedded Modbus TCP/RTU master + slave server, no extra apps required. By ModPackQT. FC1/FC2/FC3/FC4 reads, FC5/FC6/FC15/FC16 writes, a built-in slave register store, and a free passive traffic monitor for debugging.",
5
5
  "keywords": [
6
6
  "node-red",
7
7
  "modbus",
@@ -10,22 +10,39 @@
10
10
  "modbus-rtu",
11
11
  "modbus-master",
12
12
  "modbus-slave",
13
+ "modbus-server",
13
14
  "industrial",
14
15
  "automation",
15
16
  "holding-register",
16
17
  "coil",
17
18
  "iiot",
19
+ "scada",
18
20
  "plc"
19
21
  ],
20
22
  "license": "MIT",
21
23
  "author": "ModPackQT",
22
- "homepage": "https://modpackqt.com",
24
+ "homepage": "https://modpackqt.com/nodered",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/modpackqt/node-red-contrib-modbus-modpackqt.git"
28
+ },
23
29
  "bugs": {
24
- "url": "https://modpackqt.com/contact"
30
+ "url": "https://github.com/modpackqt/node-red-contrib-modbus-modpackqt/issues",
31
+ "email": "support@modpackqt.com"
25
32
  },
26
33
  "engines": {
27
34
  "node": ">=14"
28
35
  },
36
+ "files": [
37
+ "nodes/",
38
+ "examples/",
39
+ "README.md",
40
+ "LICENSE",
41
+ "CHANGELOG.md",
42
+ "DISCLAIMER.md",
43
+ "SECURITY.md",
44
+ "CONTRIBUTING.md"
45
+ ],
29
46
  "node-red": {
30
47
  "version": ">=2.0.0",
31
48
  "nodes": {
@@ -33,10 +50,11 @@
33
50
  "modpackqt-master-read": "nodes/modpackqt-master-read.js",
34
51
  "modpackqt-master-write": "nodes/modpackqt-master-write.js",
35
52
  "modpackqt-slave-read": "nodes/modpackqt-slave-read.js",
36
- "modpackqt-slave-write": "nodes/modpackqt-slave-write.js"
53
+ "modpackqt-slave-write": "nodes/modpackqt-slave-write.js",
54
+ "modpackqt-traffic": "nodes/modpackqt-traffic.js"
37
55
  }
38
56
  },
39
57
  "dependencies": {
40
- "axios": "^1.6.0"
58
+ "modbus-serial": "^8.0.0"
41
59
  }
42
60
  }