node-red-contrib-modbus-modpackqt 3.3.9 → 3.3.11
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 +25 -0
- package/README.md +0 -1
- package/nodes/lib/probe-runtime.js +1 -1
- package/nodes/modpackqt-config.html +27 -121
- package/package.json +1 -2
- package/nodes/modpackqt-traffic.html +0 -127
- package/nodes/modpackqt-traffic.js +0 -68
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,31 @@ All notable changes to **node-red-contrib-modbus-modpackqt** are documented
|
|
|
4
4
|
here. This project follows [Semantic Versioning](https://semver.org/) — pin a
|
|
5
5
|
major version (`^2.0.0`) in production.
|
|
6
6
|
|
|
7
|
+
## [3.3.11] — 2026-05-10
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **Config dialog is now picker-only.** Hand-edited Target Device
|
|
12
|
+
(IP / Port / Unit ID), Transport (Mode / Serial / Baud / Parity /
|
|
13
|
+
Timeout) and the collapsible embedded-slave-server section have all
|
|
14
|
+
been removed from the runtime config dialog. Everything is set by
|
|
15
|
+
the **My Connections** picker, which auto-fills hidden fields the
|
|
16
|
+
runtime still uses. Same dialog appears regardless of whether you
|
|
17
|
+
open it from a master-read, master-write, or master-probe node.
|
|
18
|
+
To run an embedded Modbus TCP slave, drop a `modpackqt-slave-server`
|
|
19
|
+
node onto the canvas (it has its own My Slaves picker).
|
|
20
|
+
|
|
21
|
+
## [3.3.10] — 2026-05-10
|
|
22
|
+
|
|
23
|
+
### Removed
|
|
24
|
+
|
|
25
|
+
- **`modpackqt-traffic` node removed.** The matching modpackqt.com web
|
|
26
|
+
console already surfaces a live, decoded view of every Modbus op for
|
|
27
|
+
any device a master or slave node is attached to — the in-flow
|
|
28
|
+
passive sniffer was redundant. Existing flows referencing
|
|
29
|
+
`modpackqt-traffic` will show an "unknown node type" warning and can
|
|
30
|
+
be deleted.
|
|
31
|
+
|
|
7
32
|
## [3.3.9] — 2026-05-10
|
|
8
33
|
|
|
9
34
|
### Added
|
package/README.md
CHANGED
|
@@ -15,7 +15,6 @@ By [ModPackQT](https://modpackqt.com).
|
|
|
15
15
|
|
|
16
16
|
- **Modbus master** — read (FC1–FC4) and write (FC5/FC6/FC15/FC16) over **TCP** or **RTU (serial)**
|
|
17
17
|
- **Embedded Modbus TCP slave server** — push values from any flow, let PLCs / SCADA / HMIs read them
|
|
18
|
-
- **Passive traffic monitor** — see every Modbus op (timing, values, errors) in real time
|
|
19
18
|
- **Cloud profile pickers** — paste your ModPackQT Account Key into the runtime config and a **My Connections** dropdown loads your saved devices, auto-filling host / port / unit. The slave server node has a matching **My Slaves** picker. No more retyping IPs across nodes.
|
|
20
19
|
- **Outputs raw register values** — pair with [`node-red-contrib-bytes-modpackqt`](https://www.npmjs.com/package/node-red-contrib-bytes-modpackqt) to decode int / float / string / bitmask
|
|
21
20
|
- **Zero external dependencies** — Modbus runs inside the Node-RED process
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
const http = require('http');
|
|
19
19
|
const { URL } = require('url');
|
|
20
20
|
|
|
21
|
-
const PALETTE_VERSION = '3.3.
|
|
21
|
+
const PALETTE_VERSION = '3.3.11';
|
|
22
22
|
const DEFAULT_PORT = parseInt(process.env.MODPACKQT_PROBE_PORT, 10) || 8502;
|
|
23
23
|
const BIND_HOST = process.env.MODPACKQT_PROBE_HOST || '127.0.0.1';
|
|
24
24
|
const PORT_RETRY = 5;
|
|
@@ -28,20 +28,6 @@
|
|
|
28
28
|
return this.name && this.name !== 'ModPackQT Device' ? `${this.name} (${t})` : `${t}${s}`;
|
|
29
29
|
},
|
|
30
30
|
oneditprepare: function () {
|
|
31
|
-
const toggleRtu = () => {
|
|
32
|
-
const isRtu = $('#node-config-input-masterMode').val() === 'rtu';
|
|
33
|
-
$('.modpackqt-rtu-only').toggle(isRtu);
|
|
34
|
-
$('.modpackqt-tcp-only').toggle(!isRtu);
|
|
35
|
-
};
|
|
36
|
-
const toggleSlave = () => {
|
|
37
|
-
const on = $('#node-config-input-slaveEnabled').is(':checked');
|
|
38
|
-
$('.modpackqt-slave-only').toggle(on);
|
|
39
|
-
};
|
|
40
|
-
$('#node-config-input-masterMode').on('change', toggleRtu);
|
|
41
|
-
$('#node-config-input-slaveEnabled').on('change', toggleSlave);
|
|
42
|
-
toggleRtu();
|
|
43
|
-
toggleSlave();
|
|
44
|
-
|
|
45
31
|
// ── My Connections picker
|
|
46
32
|
const nodeId = this.id;
|
|
47
33
|
const $sel = $('#node-config-input-modpackqt-conn-picker');
|
|
@@ -79,26 +65,9 @@
|
|
|
79
65
|
if (c.host) $('#node-config-input-targetHost').val(c.host);
|
|
80
66
|
if (c.port) $('#node-config-input-targetPort').val(c.port);
|
|
81
67
|
if (c.unitId) $('#node-config-input-unitId').val(c.unitId);
|
|
82
|
-
|
|
83
|
-
$('#node-config-input-masterMode').val('rtu').trigger('change');
|
|
84
|
-
}
|
|
68
|
+
$('#node-config-input-masterMode').val(c.connectionType === 'rtu' ? 'rtu' : 'tcp');
|
|
85
69
|
});
|
|
86
70
|
loadConnections();
|
|
87
|
-
|
|
88
|
-
// ── Embedded slave server section (collapsed by default)
|
|
89
|
-
const $slaveSection = $('.modpackqt-slave-section');
|
|
90
|
-
const $slaveToggle = $('.modpackqt-slave-section-toggle');
|
|
91
|
-
const setSlaveOpen = (open) => {
|
|
92
|
-
$slaveSection.toggle(open);
|
|
93
|
-
$slaveToggle.html(open
|
|
94
|
-
? '▾ Hide embedded slave server'
|
|
95
|
-
: '▸ Embedded slave server (only for modpackqt-slave-read / -write nodes)');
|
|
96
|
-
};
|
|
97
|
-
setSlaveOpen(this.slaveEnabled === true || this.slaveEnabled === 'true');
|
|
98
|
-
$slaveToggle.off('click.modpackqt').on('click.modpackqt', (e) => {
|
|
99
|
-
e.preventDefault();
|
|
100
|
-
setSlaveOpen($slaveSection.is(':hidden'));
|
|
101
|
-
});
|
|
102
71
|
}
|
|
103
72
|
});
|
|
104
73
|
</script>
|
|
@@ -127,86 +96,27 @@
|
|
|
127
96
|
Picks a saved connection from modpackqt.com — auto-fills Host, Port, and Unit ID below.
|
|
128
97
|
</div>
|
|
129
98
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
<
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
<
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
<
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
<h4 style="margin-top:18px">Transport</h4>
|
|
148
|
-
<div class="form-row">
|
|
149
|
-
<label for="node-config-input-masterMode"><i class="fa fa-cogs"></i> Mode</label>
|
|
150
|
-
<select id="node-config-input-masterMode">
|
|
151
|
-
<option value="tcp">Modbus TCP</option>
|
|
152
|
-
<option value="rtu">Modbus RTU (Serial)</option>
|
|
153
|
-
</select>
|
|
154
|
-
</div>
|
|
155
|
-
<div class="form-row modpackqt-rtu-only">
|
|
156
|
-
<label for="node-config-input-serialPort"><i class="fa fa-plug"></i> Serial Port</label>
|
|
157
|
-
<input type="text" id="node-config-input-serialPort" placeholder="/dev/ttyUSB0 or COM3">
|
|
158
|
-
</div>
|
|
159
|
-
<div class="form-row modpackqt-rtu-only">
|
|
160
|
-
<label for="node-config-input-baudRate"><i class="fa fa-tachometer"></i> Baud</label>
|
|
161
|
-
<input type="number" id="node-config-input-baudRate" placeholder="9600">
|
|
162
|
-
</div>
|
|
163
|
-
<div class="form-row modpackqt-rtu-only">
|
|
164
|
-
<label for="node-config-input-parity"><i class="fa fa-list"></i> Parity</label>
|
|
165
|
-
<select id="node-config-input-parity">
|
|
166
|
-
<option value="none">None</option>
|
|
167
|
-
<option value="even">Even</option>
|
|
168
|
-
<option value="odd">Odd</option>
|
|
169
|
-
</select>
|
|
170
|
-
</div>
|
|
171
|
-
<div class="form-row modpackqt-rtu-only">
|
|
172
|
-
<label for="node-config-input-dataBits"><i class="fa fa-hashtag"></i> Data Bits</label>
|
|
173
|
-
<select id="node-config-input-dataBits">
|
|
174
|
-
<option value="8">8</option><option value="7">7</option>
|
|
175
|
-
</select>
|
|
176
|
-
</div>
|
|
177
|
-
<div class="form-row modpackqt-rtu-only">
|
|
178
|
-
<label for="node-config-input-stopBits"><i class="fa fa-hashtag"></i> Stop Bits</label>
|
|
179
|
-
<select id="node-config-input-stopBits">
|
|
180
|
-
<option value="1">1</option><option value="2">2</option>
|
|
181
|
-
</select>
|
|
182
|
-
</div>
|
|
183
|
-
<div class="form-row">
|
|
184
|
-
<label for="node-config-input-timeoutMs"><i class="fa fa-clock-o"></i> Timeout (ms)</label>
|
|
185
|
-
<input type="number" id="node-config-input-timeoutMs" placeholder="3000">
|
|
186
|
-
</div>
|
|
187
|
-
|
|
188
|
-
<div class="form-row" style="margin:14px 0 4px 0;border-top:1px solid #e5e7eb;padding-top:10px">
|
|
189
|
-
<a href="#" class="modpackqt-slave-section-toggle" style="font-size:12px;color:#6b7280;text-decoration:none;cursor:pointer">▸ Embedded slave server (only for modpackqt-slave-read / -write nodes)</a>
|
|
190
|
-
</div>
|
|
191
|
-
<div class="modpackqt-slave-section" style="display:none">
|
|
192
|
-
<div class="form-row">
|
|
193
|
-
<label for="node-config-input-slaveEnabled" style="width:auto">
|
|
194
|
-
<input type="checkbox" id="node-config-input-slaveEnabled" style="width:auto;vertical-align:middle">
|
|
195
|
-
Enable embedded Modbus TCP slave server
|
|
196
|
-
</label>
|
|
197
|
-
</div>
|
|
198
|
-
<div class="form-row modpackqt-slave-only">
|
|
199
|
-
<label for="node-config-input-slaveHost"><i class="fa fa-globe"></i> Bind Host</label>
|
|
200
|
-
<input type="text" id="node-config-input-slaveHost" placeholder="0.0.0.0 (all interfaces)">
|
|
201
|
-
</div>
|
|
202
|
-
<div class="form-row modpackqt-slave-only">
|
|
203
|
-
<label for="node-config-input-slavePort"><i class="fa fa-hashtag"></i> Slave Port</label>
|
|
204
|
-
<input type="number" id="node-config-input-slavePort" placeholder="1502">
|
|
205
|
-
</div>
|
|
206
|
-
</div>
|
|
99
|
+
<!-- All connection details (host/port/unit/mode/timeout/embedded-slave) are
|
|
100
|
+
set by the My Connections picker above. They live as hidden inputs so
|
|
101
|
+
the runtime still has them, but they are no longer hand-edited here. -->
|
|
102
|
+
<input type="hidden" id="node-config-input-targetHost">
|
|
103
|
+
<input type="hidden" id="node-config-input-targetPort">
|
|
104
|
+
<input type="hidden" id="node-config-input-unitId">
|
|
105
|
+
<input type="hidden" id="node-config-input-masterMode">
|
|
106
|
+
<input type="hidden" id="node-config-input-serialPort">
|
|
107
|
+
<input type="hidden" id="node-config-input-baudRate">
|
|
108
|
+
<input type="hidden" id="node-config-input-parity">
|
|
109
|
+
<input type="hidden" id="node-config-input-dataBits">
|
|
110
|
+
<input type="hidden" id="node-config-input-stopBits">
|
|
111
|
+
<input type="hidden" id="node-config-input-timeoutMs">
|
|
112
|
+
<input type="hidden" id="node-config-input-slaveEnabled">
|
|
113
|
+
<input type="hidden" id="node-config-input-slaveHost">
|
|
114
|
+
<input type="hidden" id="node-config-input-slavePort">
|
|
207
115
|
|
|
208
|
-
<div class="form-tips">
|
|
209
|
-
<b>One
|
|
116
|
+
<div class="form-tips" style="margin-top:14px">
|
|
117
|
+
<b>One device per config.</b> Use the picker above to choose which saved
|
|
118
|
+
Modbus device this config talks to. For more devices, create another
|
|
119
|
+
config and pick a different one.
|
|
210
120
|
</div>
|
|
211
121
|
<div class="form-row" style="margin-top:18px;padding-top:14px;border-top:1px solid #e5e7eb;text-align:center;">
|
|
212
122
|
<a href="https://modpackqt.com" target="_blank" rel="noopener noreferrer"
|
|
@@ -233,16 +143,12 @@
|
|
|
233
143
|
only need to know the function code, address, and quantity.
|
|
234
144
|
</p>
|
|
235
145
|
|
|
236
|
-
<h3>
|
|
237
|
-
<ul>
|
|
238
|
-
<li><b>Modbus TCP</b> — connects to the configured Host/Port.</li>
|
|
239
|
-
<li><b>Modbus RTU</b> — opens the configured serial port. Host/Port are ignored; only Unit ID matters.</li>
|
|
240
|
-
</ul>
|
|
241
|
-
|
|
242
|
-
<h3>Embedded slave server</h3>
|
|
146
|
+
<h3>Where do the connection details come from?</h3>
|
|
243
147
|
<p>
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
148
|
+
Paste your <b>Account Key</b>, then pick a saved device from <b>My
|
|
149
|
+
Connections</b>. The host, port, unit ID, and transport mode are all
|
|
150
|
+
pulled from your modpackqt.com profile — no hand-editing needed here.
|
|
151
|
+
To run an embedded Modbus TCP slave server, drop a
|
|
152
|
+
<code>modpackqt-slave-server</code> node onto the canvas instead.
|
|
247
153
|
</p>
|
|
248
154
|
</script>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-modbus-modpackqt",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.11",
|
|
4
4
|
"description": "Modbus commissioning, testing & analysis tools for Node-RED. Embedded Modbus TCP/RTU master + slave server, FC1/FC2/FC3/FC4 reads, FC5/FC6/FC15/FC16 writes, built-in slave register store, and a passive traffic monitor for debugging. 100% free, MIT, no usage limits. By ModPackQT — open the matching web console at modpackqt.com for register decoding, simulation and AI assistance.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"node-red",
|
|
@@ -49,7 +49,6 @@
|
|
|
49
49
|
"modpackqt-master-write": "nodes/modpackqt-master-write.js",
|
|
50
50
|
"modpackqt-slave-read": "nodes/modpackqt-slave-read.js",
|
|
51
51
|
"modpackqt-slave-write": "nodes/modpackqt-slave-write.js",
|
|
52
|
-
"modpackqt-traffic": "nodes/modpackqt-traffic.js",
|
|
53
52
|
"modpackqt-master-probe": "nodes/modpackqt-master-probe.js",
|
|
54
53
|
"modpackqt-slave-server": "nodes/modpackqt-slave-server.js"
|
|
55
54
|
}
|
|
@@ -1,127 +0,0 @@
|
|
|
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
|
-
<div class="form-row" style="margin-top:18px;padding-top:14px;border-top:1px solid #e5e7eb;text-align:center;">
|
|
84
|
-
<a href="https://modpackqt.com" target="_blank" rel="noopener noreferrer"
|
|
85
|
-
style="display:inline-block;padding:8px 20px;background:#2563eb;color:#fff;
|
|
86
|
-
text-decoration:none;border-radius:6px;font-weight:600;font-size:13px;
|
|
87
|
-
box-shadow:0 1px 2px rgba(0,0,0,0.08);">
|
|
88
|
-
Premium Advanced Modbus Tester <i class="fa fa-external-link" style="margin-left:6px;"></i>
|
|
89
|
-
</a>
|
|
90
|
-
<div style="margin-top:6px;font-size:11px;color:#6b7280;">by ModPackQT — full-featured web tester, simulator & AI helper</div>
|
|
91
|
-
</div>
|
|
92
|
-
</script>
|
|
93
|
-
|
|
94
|
-
<script type="text/html" data-help-name="modpackqt-traffic">
|
|
95
|
-
<p>
|
|
96
|
-
Passive Modbus traffic monitor. Subscribes to all Modbus operations performed by any
|
|
97
|
-
master or slave node sharing the same runtime config and emits one message per op.
|
|
98
|
-
</p>
|
|
99
|
-
|
|
100
|
-
<h3>Output payload (object format)</h3>
|
|
101
|
-
<pre>{
|
|
102
|
-
ts: "2026-05-09T14:23:01.234Z",
|
|
103
|
-
direction: "read" | "write",
|
|
104
|
-
kind: "master" | "slave",
|
|
105
|
-
target: "192.168.1.10:502" | "local:1502" | "rtu:/dev/ttyUSB0",
|
|
106
|
-
unitId: 1,
|
|
107
|
-
fc: 3, // null for slave events
|
|
108
|
-
registerType: "holding",// only set for slave events
|
|
109
|
-
address: 100,
|
|
110
|
-
quantity: 2,
|
|
111
|
-
values: [16828, 0], // raw register values
|
|
112
|
-
durationMs: 12,
|
|
113
|
-
ok: true,
|
|
114
|
-
error: null // populated on failure
|
|
115
|
-
}</pre>
|
|
116
|
-
|
|
117
|
-
<h3>Use cases</h3>
|
|
118
|
-
<ul>
|
|
119
|
-
<li><b>Debug:</b> wire to a Debug node — see exactly what every Modbus op did.</li>
|
|
120
|
-
<li><b>Alerting:</b> filter <code>ok=false</code> → email/Slack on errors.</li>
|
|
121
|
-
<li><b>Charting:</b> count ops/sec, response-time histograms, per-target latency.</li>
|
|
122
|
-
<li><b>Audit trail:</b> store every op in a database for compliance.</li>
|
|
123
|
-
</ul>
|
|
124
|
-
|
|
125
|
-
<h3>Pricing</h3>
|
|
126
|
-
<p><b>Free</b> — full visibility into every Modbus operation, no usage limits.</p>
|
|
127
|
-
</script>
|
|
@@ -1,68 +0,0 @@
|
|
|
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
|
-
};
|