node-red-contrib-modbus-modpackqt 3.3.3 → 3.3.4
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 +12 -0
- package/nodes/lib/probe-runtime.js +1 -1
- package/nodes/modpackqt-slave-read.html +26 -19
- package/nodes/modpackqt-slave-read.js +10 -17
- package/nodes/modpackqt-slave-server.html +41 -48
- package/nodes/modpackqt-slave-server.js +11 -12
- package/nodes/modpackqt-slave-write.html +25 -16
- package/nodes/modpackqt-slave-write.js +9 -17
- package/package.json +1 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ 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.4] — 2026-05-10
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **`modpackqt-slave-server` is now a visible palette node** (was a hidden
|
|
12
|
+
config node). Drop it on the canvas like any other node — it shows up in
|
|
13
|
+
the ModPackQT section of the palette as **modbus slave server**.
|
|
14
|
+
- **`slave-read` and `slave-write`** now have a **Slave Server** dropdown
|
|
15
|
+
that lists all `modbus slave server` nodes on the canvas. Select one to
|
|
16
|
+
point at its register store.
|
|
17
|
+
- **`modpackqt-slave-probe` removed** from the palette (old node type gone).
|
|
18
|
+
|
|
7
19
|
## [3.3.3] — 2026-05-10
|
|
8
20
|
|
|
9
21
|
### Added
|
|
@@ -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.4';
|
|
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;
|
|
@@ -3,13 +3,12 @@
|
|
|
3
3
|
category: 'ModPackQT',
|
|
4
4
|
color: '#15803d',
|
|
5
5
|
defaults: {
|
|
6
|
-
name:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
pollInterval: { value: 0, validate: RED.validators.number() }
|
|
6
|
+
name: { value: '' },
|
|
7
|
+
slaveServerId: { value: '' }, // ID of a modpackqt-slave-server node on the canvas
|
|
8
|
+
registerType: { value: 'holding', required: true },
|
|
9
|
+
address: { value: 0, required: true, validate: RED.validators.number() },
|
|
10
|
+
quantity: { value: 1, required: true, validate: RED.validators.number() },
|
|
11
|
+
pollInterval: { value: 0, validate: RED.validators.number() }
|
|
13
12
|
},
|
|
14
13
|
inputs: 1,
|
|
15
14
|
outputs: 1,
|
|
@@ -17,7 +16,17 @@
|
|
|
17
16
|
label: function () {
|
|
18
17
|
return this.name || ('Slave Read ' + (this.registerType || 'holding') + ' @' + this.address);
|
|
19
18
|
},
|
|
20
|
-
paletteLabel: 'modbus slave read'
|
|
19
|
+
paletteLabel: 'modbus slave read',
|
|
20
|
+
oneditprepare: function () {
|
|
21
|
+
const $sel = $('#node-input-slaveServerId');
|
|
22
|
+
$sel.empty().append('<option value="">— select a slave server node —</option>');
|
|
23
|
+
RED.nodes.eachNode(function (n) {
|
|
24
|
+
if (n.type === 'modpackqt-slave-server') {
|
|
25
|
+
const label = (n.name || (':' + n.bindPort + ' #' + n.unitId));
|
|
26
|
+
$('<option>').val(n.id).text(label).appendTo($sel);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
21
30
|
});
|
|
22
31
|
</script>
|
|
23
32
|
|
|
@@ -27,8 +36,11 @@
|
|
|
27
36
|
<input type="text" id="node-input-name" placeholder="Name">
|
|
28
37
|
</div>
|
|
29
38
|
<div class="form-row">
|
|
30
|
-
<label for="node-input-
|
|
31
|
-
<
|
|
39
|
+
<label for="node-input-slaveServerId"><i class="fa fa-server"></i> Slave Server</label>
|
|
40
|
+
<select id="node-input-slaveServerId" style="width:70%"></select>
|
|
41
|
+
</div>
|
|
42
|
+
<div class="form-tips" style="font-size:11px;color:#6b7280;margin:-8px 0 12px 105px">
|
|
43
|
+
Pick a <b>modbus slave server</b> node from the canvas. Deploy the flow first if the list is empty.
|
|
32
44
|
</div>
|
|
33
45
|
<div class="form-row">
|
|
34
46
|
<label for="node-input-registerType"><i class="fa fa-list"></i> Register Type</label>
|
|
@@ -52,8 +64,7 @@
|
|
|
52
64
|
<input type="number" id="node-input-pollInterval" min="0" placeholder="0 = trigger only">
|
|
53
65
|
</div>
|
|
54
66
|
<div class="form-tips">
|
|
55
|
-
|
|
56
|
-
<b>modpackqt-slave-server</b> node. Override at runtime via
|
|
67
|
+
Reads from the slave server's register store. Override at runtime via
|
|
57
68
|
<code>msg.address</code>, <code>msg.quantity</code>, or <code>msg.registerType</code>.
|
|
58
69
|
</div>
|
|
59
70
|
<div class="form-row" style="margin-top:18px;padding-top:14px;border-top:1px solid #e5e7eb;text-align:center;">
|
|
@@ -68,16 +79,12 @@
|
|
|
68
79
|
</script>
|
|
69
80
|
|
|
70
81
|
<script type="text/html" data-help-name="modpackqt-slave-read">
|
|
71
|
-
<p>
|
|
72
|
-
Reads registers from a <b>modpackqt-slave-server</b> node's local store.
|
|
73
|
-
Useful for seeing exactly what an external Modbus master connecting to that
|
|
74
|
-
slave port will read.
|
|
75
|
-
</p>
|
|
82
|
+
<p>Reads registers from a <b>modbus slave server</b> node's local store.</p>
|
|
76
83
|
<h3>Setup</h3>
|
|
77
|
-
<p>
|
|
84
|
+
<p>Drop a <b>modbus slave server</b> node on the canvas first, then select it here.</p>
|
|
78
85
|
<h3>Outputs</h3>
|
|
79
86
|
<dl class="message-properties">
|
|
80
87
|
<dt>payload <span class="property-type">array</span></dt>
|
|
81
|
-
<dd>
|
|
88
|
+
<dd>Register values (integers for holding/input, booleans for coils/discrete).</dd>
|
|
82
89
|
</dl>
|
|
83
90
|
</script>
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ModPackQT Slave Read —
|
|
3
|
-
*
|
|
4
|
-
* Points at a modpackqt-slave-server config node (preferred) or falls back
|
|
5
|
-
* to the legacy modpackqt-config with an embedded slave (backward compat).
|
|
2
|
+
* ModPackQT Slave Read — reads from a modpackqt-slave-server node's register store.
|
|
6
3
|
*/
|
|
7
4
|
module.exports = function (RED) {
|
|
8
5
|
function ModPackQTSlaveReadNode(config) {
|
|
@@ -14,29 +11,25 @@ module.exports = function (RED) {
|
|
|
14
11
|
node.quantity = parseInt(config.quantity, 10) || 1;
|
|
15
12
|
node.pollInterval = parseInt(config.pollInterval, 10) || 0;
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
function getSlave() {
|
|
15
|
+
return RED.nodes.getNode(config.slaveServerId);
|
|
16
|
+
}
|
|
19
17
|
|
|
20
18
|
let timer = null;
|
|
21
19
|
|
|
22
20
|
function doRead(msg) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
node.
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
if (!slave.slaveEnabled) {
|
|
29
|
-
node.status({ fill: 'red', shape: 'ring', text: 'slave server not running' });
|
|
30
|
-
node.error('Slave server is not running — check the slave-server config node.', msg);
|
|
21
|
+
const slave = getSlave();
|
|
22
|
+
if (!slave || !slave.slaveEnabled) {
|
|
23
|
+
node.status({ fill: 'red', shape: 'ring', text: 'no slave server' });
|
|
24
|
+
node.error('No slave server selected or it is not running.', msg);
|
|
31
25
|
return;
|
|
32
26
|
}
|
|
33
27
|
try {
|
|
34
28
|
const registerType = (msg && msg.registerType) || node.registerType;
|
|
35
29
|
const address = (msg && msg.address !== undefined) ? parseInt(msg.address, 10) : node.address;
|
|
36
30
|
const quantity = (msg && msg.quantity !== undefined) ? parseInt(msg.quantity, 10) : node.quantity;
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
const now = new Date().toLocaleTimeString();
|
|
31
|
+
const values = slave.slaveGet(registerType, address, quantity);
|
|
32
|
+
const now = new Date().toLocaleTimeString();
|
|
40
33
|
node.status({ fill: 'green', shape: 'dot', text: `${registerType} @${address} [${values.length}] · ${now}` });
|
|
41
34
|
node.send({
|
|
42
35
|
payload: values,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script type="text/javascript">
|
|
2
2
|
RED.nodes.registerType('modpackqt-slave-server', {
|
|
3
|
-
category: '
|
|
3
|
+
category: 'ModPackQT',
|
|
4
|
+
color: '#7c3aed',
|
|
4
5
|
defaults: {
|
|
5
6
|
name: { value: '' },
|
|
6
7
|
bindHost: { value: '0.0.0.0' },
|
|
@@ -10,16 +11,18 @@
|
|
|
10
11
|
credentials: {
|
|
11
12
|
apiKey: { type: 'password' }
|
|
12
13
|
},
|
|
14
|
+
inputs: 0,
|
|
15
|
+
outputs: 0,
|
|
16
|
+
icon: 'font-awesome/fa-server',
|
|
13
17
|
label: function () {
|
|
14
18
|
return this.name || `:${this.bindPort} #${this.unitId}`;
|
|
15
19
|
},
|
|
20
|
+
paletteLabel: 'modbus slave server',
|
|
16
21
|
oneditprepare: function () {
|
|
17
22
|
const node = this;
|
|
18
23
|
|
|
19
|
-
// ── My Slaves picker
|
|
20
|
-
|
|
21
|
-
// from the web console, not here.
|
|
22
|
-
const $sel = $('#node-config-modpackqt-slave-picker');
|
|
24
|
+
// ── My Slaves picker
|
|
25
|
+
const $sel = $('#node-input-modpackqt-slave-picker');
|
|
23
26
|
const reload = function () {
|
|
24
27
|
$sel.empty().append('<option value="">— Loading… —</option>');
|
|
25
28
|
if (!node.id) {
|
|
@@ -41,15 +44,13 @@
|
|
|
41
44
|
$sel.empty().append($('<option>').val('').text('— ' + msg + ' —'));
|
|
42
45
|
});
|
|
43
46
|
};
|
|
44
|
-
$('#modpackqt-slave-server-picker-refresh').on('click', function (e) {
|
|
45
|
-
e.preventDefault(); reload();
|
|
46
|
-
});
|
|
47
|
+
$('#modpackqt-slave-server-picker-refresh').on('click', function (e) { e.preventDefault(); reload(); });
|
|
47
48
|
$sel.on('change', function () {
|
|
48
49
|
const row = $(this).find('option:selected').data('row');
|
|
49
50
|
if (!row) return;
|
|
50
|
-
if (!node.name
|
|
51
|
-
$('#node-
|
|
52
|
-
$('#node-
|
|
51
|
+
if (!node.name) $('#node-input-name').val(row.name || '');
|
|
52
|
+
$('#node-input-bindPort').val(row.port || 1502);
|
|
53
|
+
$('#node-input-unitId').val(row.unitId || 1);
|
|
53
54
|
buildLink();
|
|
54
55
|
});
|
|
55
56
|
reload();
|
|
@@ -57,15 +58,14 @@
|
|
|
57
58
|
// ── Probe deep-link
|
|
58
59
|
const buildLink = function () {
|
|
59
60
|
$.getJSON('modpackqt-probe/info')
|
|
60
|
-
.done(function (
|
|
61
|
-
.fail(function () { renderLink({}); });
|
|
61
|
+
.done(renderLink).fail(function () { renderLink({}); });
|
|
62
62
|
};
|
|
63
63
|
const renderLink = function (info) {
|
|
64
64
|
const probeHost = (info && info.host) || '127.0.0.1';
|
|
65
65
|
const probePort = (info && info.port) || 8502;
|
|
66
|
-
const bindPort = $('#node-
|
|
67
|
-
const unitId = $('#node-
|
|
68
|
-
const name = $('#node-
|
|
66
|
+
const bindPort = $('#node-input-bindPort').val() || node.bindPort || 1502;
|
|
67
|
+
const unitId = $('#node-input-unitId').val() || node.unitId || 1;
|
|
68
|
+
const name = $('#node-input-name').val() || node.name || '';
|
|
69
69
|
const url = 'https://modpackqt.com/slave'
|
|
70
70
|
+ '?probe=' + encodeURIComponent(node.id || '')
|
|
71
71
|
+ '&probeHost=' + encodeURIComponent(probeHost)
|
|
@@ -78,20 +78,20 @@
|
|
|
78
78
|
$('#modpackqt-slave-server-target').text(':' + bindPort + ' · unit ' + unitId);
|
|
79
79
|
};
|
|
80
80
|
buildLink();
|
|
81
|
-
$('#node-
|
|
81
|
+
$('#node-input-bindPort, #node-input-unitId').on('change input', buildLink);
|
|
82
82
|
}
|
|
83
83
|
});
|
|
84
84
|
</script>
|
|
85
85
|
|
|
86
86
|
<script type="text/html" data-template-name="modpackqt-slave-server">
|
|
87
87
|
<div class="form-row">
|
|
88
|
-
<label for="node-
|
|
89
|
-
<input type="text" id="node-
|
|
88
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
89
|
+
<input type="text" id="node-input-name" placeholder="e.g. Fake Inverter">
|
|
90
90
|
</div>
|
|
91
91
|
|
|
92
92
|
<div class="form-row">
|
|
93
|
-
<label for="node-
|
|
94
|
-
<input type="password" id="node-
|
|
93
|
+
<label for="node-input-apiKey"><i class="fa fa-key"></i> Account Key</label>
|
|
94
|
+
<input type="password" id="node-input-apiKey" placeholder="Paste your ModPackQT tunnel key">
|
|
95
95
|
</div>
|
|
96
96
|
<div class="form-tips" style="font-size:11px;color:#6b7280;margin:-8px 0 12px 105px">
|
|
97
97
|
Generate at <a href="https://modpackqt.com/settings" target="_blank" rel="noopener">modpackqt.com → Settings → Cloud Gateways</a>.
|
|
@@ -99,25 +99,25 @@
|
|
|
99
99
|
</div>
|
|
100
100
|
|
|
101
101
|
<div class="form-row">
|
|
102
|
-
<label for="node-
|
|
103
|
-
<select id="node-
|
|
104
|
-
<button type="button" id="modpackqt-slave-server-picker-refresh" class="red-ui-button" title="Reload
|
|
102
|
+
<label for="node-input-modpackqt-slave-picker"><i class="fa fa-cloud-download"></i> My Slaves</label>
|
|
103
|
+
<select id="node-input-modpackqt-slave-picker" style="width:65%"></select>
|
|
104
|
+
<button type="button" id="modpackqt-slave-server-picker-refresh" class="red-ui-button" title="Reload" style="margin-left:4px"><i class="fa fa-refresh"></i></button>
|
|
105
105
|
</div>
|
|
106
106
|
<div class="form-tips" style="font-size:11px;color:#6b7280;margin:-8px 0 12px 105px">
|
|
107
|
-
|
|
107
|
+
Picks a saved slave from modpackqt.com — auto-fills port and unit.
|
|
108
108
|
Register layout and limits are managed from the web console.
|
|
109
109
|
</div>
|
|
110
110
|
|
|
111
111
|
<div class="form-row">
|
|
112
|
-
<label for="node-
|
|
113
|
-
<input type="number" id="node-
|
|
112
|
+
<label for="node-input-bindPort"><i class="fa fa-hashtag"></i> Port</label>
|
|
113
|
+
<input type="number" id="node-input-bindPort" min="1024" max="65535" placeholder="1502">
|
|
114
114
|
</div>
|
|
115
115
|
<div class="form-row">
|
|
116
|
-
<label for="node-
|
|
117
|
-
<input type="number" id="node-
|
|
116
|
+
<label for="node-input-unitId"><i class="fa fa-id-card"></i> Unit ID</label>
|
|
117
|
+
<input type="number" id="node-input-unitId" min="1" max="247" placeholder="1">
|
|
118
118
|
</div>
|
|
119
119
|
|
|
120
|
-
<input type="hidden" id="node-
|
|
120
|
+
<input type="hidden" id="node-input-bindHost">
|
|
121
121
|
|
|
122
122
|
<div class="form-row" style="margin-top:18px;padding-top:14px;border-top:1px solid #e5e7eb;text-align:center;">
|
|
123
123
|
<a id="modpackqt-slave-server-link" href="https://modpackqt.com" target="_blank" rel="noopener noreferrer"
|
|
@@ -134,27 +134,20 @@
|
|
|
134
134
|
|
|
135
135
|
<script type="text/html" data-help-name="modpackqt-slave-server">
|
|
136
136
|
<p>
|
|
137
|
-
Runs a Modbus TCP slave server
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
137
|
+
Runs a Modbus TCP slave server with its own full register store.
|
|
138
|
+
Drop one on the canvas per fake device. <b>Slave-read</b> and
|
|
139
|
+
<b>slave-write</b> nodes target it by node ID to read and write
|
|
140
|
+
registers from the flow.
|
|
141
141
|
</p>
|
|
142
|
-
|
|
143
|
-
<h3>How to set it up</h3>
|
|
142
|
+
<h3>Setup</h3>
|
|
144
143
|
<ol>
|
|
145
|
-
<li>Drop
|
|
146
|
-
<li>
|
|
147
|
-
<li>
|
|
144
|
+
<li>Drop this node, set Port and Unit ID (or pick from <b>My Slaves</b>).</li>
|
|
145
|
+
<li>On your slave-read / slave-write nodes, select this node in the <b>Slave Server</b> dropdown.</li>
|
|
146
|
+
<li>Click <b>Open in ModPackQT Console</b> to manage the register layout from the web.</li>
|
|
148
147
|
</ol>
|
|
149
|
-
|
|
150
148
|
<h3>Account Key</h3>
|
|
151
|
-
<p>Generate
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
<h3>Multiple slave servers</h3>
|
|
155
|
-
<p>Create one slave-server config per port/unit combination. Slave-read and
|
|
156
|
-
slave-write nodes each pick which server to talk to.</p>
|
|
157
|
-
|
|
149
|
+
<p>Generate at <a href="https://modpackqt.com/settings" target="_blank" rel="noopener">modpackqt.com → Settings → Cloud Gateways</a>.
|
|
150
|
+
Lets the web console connect and loads your saved slave configs.</p>
|
|
158
151
|
<h3>Port note</h3>
|
|
159
|
-
<p>Use ports above 1024 (e.g. 1502, 1503) to avoid needing root privileges.</p>
|
|
152
|
+
<p>Use ports above 1024 (e.g. 1502, 1503) to avoid needing root privileges. Each server must use a unique port.</p>
|
|
160
153
|
</script>
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ModPackQT Slave Server — Modbus TCP slave with a full register store.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Drop one on the canvas per fake device. Each server:
|
|
5
5
|
* - Binds its own TCP port and listens for external Modbus masters
|
|
6
6
|
* - Owns 65 536 registers per type (coils, discrete, holding, input)
|
|
7
|
-
* - Exposes slaveGet() / slaveSet() so slave-read / slave-write
|
|
8
|
-
* push and pull values from the flow side
|
|
9
|
-
* - Registers with the probe runtime so
|
|
10
|
-
*
|
|
7
|
+
* - Exposes slaveGet() / slaveSet() so slave-read / slave-write nodes
|
|
8
|
+
* can push and pull values from the flow side
|
|
9
|
+
* - Registers with the probe runtime so modpackqt.com can open a live
|
|
10
|
+
* register editor for it
|
|
11
11
|
*
|
|
12
12
|
* Register limits and layout are managed from the modpackqt.com web
|
|
13
|
-
* console
|
|
14
|
-
* port and unit ID to bind the TCP listener.
|
|
13
|
+
* console — this node only needs port and unit ID to bind.
|
|
15
14
|
*/
|
|
16
15
|
module.exports = function (RED) {
|
|
17
16
|
const ModbusRTU = require('modbus-serial');
|
|
@@ -26,9 +25,7 @@ module.exports = function (RED) {
|
|
|
26
25
|
node.bindPort = parseInt(config.bindPort, 10) || 1502;
|
|
27
26
|
node.unitId = parseInt(config.unitId, 10) || 1;
|
|
28
27
|
|
|
29
|
-
// ── Register store
|
|
30
|
-
// Register limits / layout are defined by the modpackqt.com slave
|
|
31
|
-
// config and are managed via the web console, not here.
|
|
28
|
+
// ── Register store
|
|
32
29
|
const store = {
|
|
33
30
|
coils: new Array(65536).fill(false),
|
|
34
31
|
discrete: new Array(65536).fill(false),
|
|
@@ -36,7 +33,7 @@ module.exports = function (RED) {
|
|
|
36
33
|
input: new Array(65536).fill(0)
|
|
37
34
|
};
|
|
38
35
|
|
|
39
|
-
// ── Flow-side API — used by slave-read and slave-write nodes
|
|
36
|
+
// ── Flow-side API — used by slave-read and slave-write nodes
|
|
40
37
|
node.slaveEnabled = true;
|
|
41
38
|
|
|
42
39
|
node.slaveGet = function (type, address, quantity) {
|
|
@@ -73,11 +70,13 @@ module.exports = function (RED) {
|
|
|
73
70
|
server.on('socketError', (err) => node.warn(`[slave-server] socket: ${err.message}`));
|
|
74
71
|
server.on('serverError', (err) => node.error(`[slave-server] server: ${err.message}`));
|
|
75
72
|
node.log(`[modpackqt] slave server listening on ${node.bindHost}:${node.bindPort} #${node.unitId}`);
|
|
73
|
+
node.status({ fill: 'green', shape: 'dot', text: `listening :${node.bindPort} #${node.unitId}` });
|
|
76
74
|
} catch (err) {
|
|
77
75
|
node.error(`Failed to start slave server: ${err.message}`);
|
|
76
|
+
node.status({ fill: 'red', shape: 'ring', text: 'failed to start' });
|
|
78
77
|
}
|
|
79
78
|
|
|
80
|
-
// ── Probe runtime
|
|
79
|
+
// ── Probe runtime — lets the web console find and control this server
|
|
81
80
|
const probe = {
|
|
82
81
|
id: node.id,
|
|
83
82
|
kind: 'slave',
|
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
category: 'ModPackQT',
|
|
4
4
|
color: '#15803d',
|
|
5
5
|
defaults: {
|
|
6
|
-
name:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
address: { value: 0, required: true, validate: RED.validators.number() }
|
|
6
|
+
name: { value: '' },
|
|
7
|
+
slaveServerId: { value: '' }, // ID of a modpackqt-slave-server node on the canvas
|
|
8
|
+
registerType: { value: 'holding', required: true },
|
|
9
|
+
address: { value: 0, required: true, validate: RED.validators.number() }
|
|
11
10
|
},
|
|
12
11
|
inputs: 1,
|
|
13
12
|
outputs: 1,
|
|
@@ -15,7 +14,17 @@
|
|
|
15
14
|
label: function () {
|
|
16
15
|
return this.name || ('Slave Write ' + (this.registerType || 'holding') + ' @' + this.address);
|
|
17
16
|
},
|
|
18
|
-
paletteLabel: 'modbus slave write'
|
|
17
|
+
paletteLabel: 'modbus slave write',
|
|
18
|
+
oneditprepare: function () {
|
|
19
|
+
const $sel = $('#node-input-slaveServerId');
|
|
20
|
+
$sel.empty().append('<option value="">— select a slave server node —</option>');
|
|
21
|
+
RED.nodes.eachNode(function (n) {
|
|
22
|
+
if (n.type === 'modpackqt-slave-server') {
|
|
23
|
+
const label = (n.name || (':' + n.bindPort + ' #' + n.unitId));
|
|
24
|
+
$('<option>').val(n.id).text(label).appendTo($sel);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
19
28
|
});
|
|
20
29
|
</script>
|
|
21
30
|
|
|
@@ -25,8 +34,11 @@
|
|
|
25
34
|
<input type="text" id="node-input-name" placeholder="Name">
|
|
26
35
|
</div>
|
|
27
36
|
<div class="form-row">
|
|
28
|
-
<label for="node-input-
|
|
29
|
-
<
|
|
37
|
+
<label for="node-input-slaveServerId"><i class="fa fa-server"></i> Slave Server</label>
|
|
38
|
+
<select id="node-input-slaveServerId" style="width:70%"></select>
|
|
39
|
+
</div>
|
|
40
|
+
<div class="form-tips" style="font-size:11px;color:#6b7280;margin:-8px 0 12px 105px">
|
|
41
|
+
Pick a <b>modbus slave server</b> node from the canvas. Deploy the flow first if the list is empty.
|
|
30
42
|
</div>
|
|
31
43
|
<div class="form-row">
|
|
32
44
|
<label for="node-input-registerType"><i class="fa fa-list"></i> Register Type</label>
|
|
@@ -42,8 +54,8 @@
|
|
|
42
54
|
<input type="number" id="node-input-address" min="0" max="65535" placeholder="0">
|
|
43
55
|
</div>
|
|
44
56
|
<div class="form-tips">
|
|
45
|
-
|
|
46
|
-
connecting to that
|
|
57
|
+
Pushes values into the slave server's register store. External Modbus masters
|
|
58
|
+
connecting to that port will read whatever you last wrote.
|
|
47
59
|
Send <code>msg.payload</code> as a number, array, or JSON string.
|
|
48
60
|
</div>
|
|
49
61
|
<div class="form-row" style="margin-top:18px;padding-top:14px;border-top:1px solid #e5e7eb;text-align:center;">
|
|
@@ -58,13 +70,10 @@
|
|
|
58
70
|
</script>
|
|
59
71
|
|
|
60
72
|
<script type="text/html" data-help-name="modpackqt-slave-write">
|
|
61
|
-
<p>
|
|
62
|
-
|
|
63
|
-
External Modbus masters connecting to that slave's port will read whatever
|
|
64
|
-
was last written here.
|
|
65
|
-
</p>
|
|
73
|
+
<p>Writes values into a <b>modbus slave server</b> node's register store.
|
|
74
|
+
External Modbus masters connecting to that slave's port will read whatever was last written.</p>
|
|
66
75
|
<h3>Setup</h3>
|
|
67
|
-
<p>
|
|
76
|
+
<p>Drop a <b>modbus slave server</b> node on the canvas first, then select it here.</p>
|
|
68
77
|
<h3>Input</h3>
|
|
69
78
|
<dl class="message-properties">
|
|
70
79
|
<dt>payload <span class="property-type">number | array | string</span></dt>
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ModPackQT Slave Write —
|
|
3
|
-
*
|
|
4
|
-
* Points at a modpackqt-slave-server config node (preferred) or falls back
|
|
5
|
-
* to the legacy modpackqt-config with an embedded slave (backward compat).
|
|
6
|
-
* External Modbus masters connecting to the slave port will read whatever
|
|
7
|
-
* was last written here.
|
|
2
|
+
* ModPackQT Slave Write — writes into a modpackqt-slave-server node's register store.
|
|
3
|
+
* External Modbus masters connecting to the slave port will read whatever was last written.
|
|
8
4
|
*/
|
|
9
5
|
module.exports = function (RED) {
|
|
10
6
|
function ModPackQTSlaveWriteNode(config) {
|
|
@@ -14,18 +10,15 @@ module.exports = function (RED) {
|
|
|
14
10
|
node.registerType = config.registerType || 'holding';
|
|
15
11
|
node.address = parseInt(config.address, 10) || 0;
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
function getSlave() {
|
|
14
|
+
return RED.nodes.getNode(config.slaveServerId);
|
|
15
|
+
}
|
|
19
16
|
|
|
20
17
|
node.on('input', function (msg) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
node.
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
if (!slave.slaveEnabled) {
|
|
27
|
-
node.status({ fill: 'red', shape: 'ring', text: 'slave server not running' });
|
|
28
|
-
node.error('Slave server is not running — check the slave-server config node.', msg);
|
|
18
|
+
const slave = getSlave();
|
|
19
|
+
if (!slave || !slave.slaveEnabled) {
|
|
20
|
+
node.status({ fill: 'red', shape: 'ring', text: 'no slave server' });
|
|
21
|
+
node.error('No slave server selected or it is not running.', msg);
|
|
29
22
|
return;
|
|
30
23
|
}
|
|
31
24
|
try {
|
|
@@ -42,7 +35,6 @@ module.exports = function (RED) {
|
|
|
42
35
|
throw new Error(`Invalid payload — expected number or array, got: ${JSON.stringify(msg.payload)}`);
|
|
43
36
|
}
|
|
44
37
|
const address = msg.address !== undefined ? parseInt(msg.address, 10) : node.address;
|
|
45
|
-
|
|
46
38
|
slave.slaveSet(registerType, address, values);
|
|
47
39
|
const now = new Date().toLocaleTimeString();
|
|
48
40
|
node.status({ fill: 'green', shape: 'dot', text: `wrote ${values.length} → ${registerType} @${address} · ${now}` });
|
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.4",
|
|
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",
|
|
@@ -51,7 +51,6 @@
|
|
|
51
51
|
"modpackqt-slave-write": "nodes/modpackqt-slave-write.js",
|
|
52
52
|
"modpackqt-traffic": "nodes/modpackqt-traffic.js",
|
|
53
53
|
"modpackqt-master-probe": "nodes/modpackqt-master-probe.js",
|
|
54
|
-
"modpackqt-slave-probe": "nodes/modpackqt-slave-probe.js",
|
|
55
54
|
"modpackqt-slave-server": "nodes/modpackqt-slave-server.js"
|
|
56
55
|
}
|
|
57
56
|
},
|