node-red-contrib-symi-modbus 2.5.2 → 2.5.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/README.md +395 -445
- package/RELEASE_CHECKLIST.md +106 -0
- package/examples/basic-flow.json +243 -0
- package/nodes/lightweight-protocol.js +12 -6
- package/nodes/modbus-master.html +13 -185
- package/nodes/modbus-master.js +453 -148
- package/nodes/modbus-server-config.html +174 -0
- package/nodes/modbus-server-config.js +18 -0
- package/nodes/modbus-slave-switch.html +33 -161
- package/nodes/modbus-slave-switch.js +208 -317
- package/nodes/serial-port-config.html +191 -0
- package/nodes/serial-port-config.js +270 -0
- package/package.json +9 -5
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType('serial-port-config', {
|
|
3
|
+
category: 'config',
|
|
4
|
+
defaults: {
|
|
5
|
+
name: { value: "" },
|
|
6
|
+
connectionType: { value: "serial" },
|
|
7
|
+
// TCP配置
|
|
8
|
+
tcpHost: { value: "192.168.2.110" },
|
|
9
|
+
tcpPort: { value: 1031, validate: RED.validators.number() },
|
|
10
|
+
// 串口配置
|
|
11
|
+
serialPort: { value: "/dev/ttyUSB0" },
|
|
12
|
+
baudRate: { value: 9600, validate: RED.validators.number() },
|
|
13
|
+
dataBits: { value: 8, validate: RED.validators.number() },
|
|
14
|
+
stopBits: { value: 1, validate: RED.validators.number() },
|
|
15
|
+
parity: { value: "none" }
|
|
16
|
+
},
|
|
17
|
+
label: function() {
|
|
18
|
+
if (this.name) return this.name;
|
|
19
|
+
if (this.connectionType === 'tcp') {
|
|
20
|
+
return `TCP ${this.tcpHost}:${this.tcpPort}`;
|
|
21
|
+
} else {
|
|
22
|
+
return `串口 ${this.serialPort} ${this.baudRate} ${this.dataBits}${this.parity.charAt(0).toUpperCase()}${this.stopBits}`;
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
oneditprepare: function() {
|
|
26
|
+
var node = this;
|
|
27
|
+
|
|
28
|
+
// 连接类型切换
|
|
29
|
+
function updateConnectionTypeVisibility() {
|
|
30
|
+
var connectionType = $('#node-config-input-connectionType').val();
|
|
31
|
+
if (connectionType === 'tcp') {
|
|
32
|
+
$('.config-tcp-row').show();
|
|
33
|
+
$('.config-serial-row').hide();
|
|
34
|
+
} else {
|
|
35
|
+
$('.config-tcp-row').hide();
|
|
36
|
+
$('.config-serial-row').show();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 初始化显示
|
|
41
|
+
updateConnectionTypeVisibility();
|
|
42
|
+
|
|
43
|
+
// 监听连接类型变化
|
|
44
|
+
$('#node-config-input-connectionType').on('change', function() {
|
|
45
|
+
updateConnectionTypeVisibility();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// 串口搜索功能
|
|
49
|
+
$('#node-config-search-serial').on('click', function() {
|
|
50
|
+
var btn = $(this);
|
|
51
|
+
btn.prop('disabled', true).text('搜索中...');
|
|
52
|
+
|
|
53
|
+
$.ajax({
|
|
54
|
+
url: 'serial-ports',
|
|
55
|
+
type: 'GET',
|
|
56
|
+
success: function(data) {
|
|
57
|
+
if (data && data.length > 0) {
|
|
58
|
+
var select = $('<select id="serial-port-select" style="width:100%; margin-top:10px;"></select>');
|
|
59
|
+
data.forEach(function(port) {
|
|
60
|
+
select.append(`<option value="${port.path}">${port.path}${port.manufacturer ? ' (' + port.manufacturer + ')' : ''}</option>`);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// 显示选择框
|
|
64
|
+
$('#serial-port-list').html(select);
|
|
65
|
+
|
|
66
|
+
// 监听选择
|
|
67
|
+
select.on('change', function() {
|
|
68
|
+
$('#node-config-input-serialPort').val($(this).val());
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// 自动填充第一个
|
|
72
|
+
if (data.length > 0) {
|
|
73
|
+
$('#node-config-input-serialPort').val(data[0].path);
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
$('#serial-port-list').html('<div style="color:#999; margin-top:10px;">未找到可用串口</div>');
|
|
77
|
+
}
|
|
78
|
+
btn.prop('disabled', false).text('搜索串口');
|
|
79
|
+
},
|
|
80
|
+
error: function() {
|
|
81
|
+
$('#serial-port-list').html('<div style="color:#f00; margin-top:10px;">搜索失败</div>');
|
|
82
|
+
btn.prop('disabled', false).text('搜索串口');
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
</script>
|
|
89
|
+
|
|
90
|
+
<script type="text/x-red" data-template-name="serial-port-config">
|
|
91
|
+
<div class="form-row">
|
|
92
|
+
<label for="node-config-input-name"><i class="fa fa-tag"></i> 名称</label>
|
|
93
|
+
<input type="text" id="node-config-input-name" placeholder="RS-485连接配置">
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<!-- 连接类型选择 -->
|
|
97
|
+
<div class="form-row">
|
|
98
|
+
<label for="node-config-input-connectionType"><i class="fa fa-exchange"></i> 连接类型</label>
|
|
99
|
+
<select id="node-config-input-connectionType" style="width:70%">
|
|
100
|
+
<option value="tcp">TCP网关</option>
|
|
101
|
+
<option value="serial">串口</option>
|
|
102
|
+
</select>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<!-- TCP配置 -->
|
|
106
|
+
<div class="form-row config-tcp-row">
|
|
107
|
+
<label for="node-config-input-tcpHost"><i class="fa fa-server"></i> TCP主机</label>
|
|
108
|
+
<input type="text" id="node-config-input-tcpHost" placeholder="192.168.2.110" style="width:70%">
|
|
109
|
+
</div>
|
|
110
|
+
<div class="form-row config-tcp-row">
|
|
111
|
+
<label for="node-config-input-tcpPort"><i class="fa fa-plug"></i> TCP端口</label>
|
|
112
|
+
<input type="number" id="node-config-input-tcpPort" placeholder="1031" style="width:70%">
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<!-- 串口配置 -->
|
|
116
|
+
<div class="form-row config-serial-row">
|
|
117
|
+
<label for="node-config-input-serialPort"><i class="fa fa-plug"></i> 串口路径</label>
|
|
118
|
+
<input type="text" id="node-config-input-serialPort" placeholder="/dev/ttyUSB0" style="width:calc(70% - 90px);">
|
|
119
|
+
<button type="button" id="node-config-search-serial" class="red-ui-button" style="width:80px; margin-left:5px;">
|
|
120
|
+
<i class="fa fa-search"></i> 搜索
|
|
121
|
+
</button>
|
|
122
|
+
</div>
|
|
123
|
+
<div class="form-row config-serial-row">
|
|
124
|
+
<div id="serial-port-list" style="margin-left:110px;"></div>
|
|
125
|
+
</div>
|
|
126
|
+
<div class="form-row config-serial-row">
|
|
127
|
+
<label for="node-config-input-baudRate"><i class="fa fa-tachometer"></i> 波特率</label>
|
|
128
|
+
<select id="node-config-input-baudRate" style="width:70%">
|
|
129
|
+
<option value="9600">9600</option>
|
|
130
|
+
<option value="19200">19200</option>
|
|
131
|
+
<option value="38400">38400</option>
|
|
132
|
+
<option value="57600">57600</option>
|
|
133
|
+
<option value="115200">115200</option>
|
|
134
|
+
</select>
|
|
135
|
+
</div>
|
|
136
|
+
<div class="form-row config-serial-row">
|
|
137
|
+
<label for="node-config-input-dataBits"><i class="fa fa-database"></i> 数据位</label>
|
|
138
|
+
<select id="node-config-input-dataBits" style="width:70%">
|
|
139
|
+
<option value="5">5</option>
|
|
140
|
+
<option value="6">6</option>
|
|
141
|
+
<option value="7">7</option>
|
|
142
|
+
<option value="8" selected>8</option>
|
|
143
|
+
</select>
|
|
144
|
+
</div>
|
|
145
|
+
<div class="form-row config-serial-row">
|
|
146
|
+
<label for="node-config-input-stopBits"><i class="fa fa-stop"></i> 停止位</label>
|
|
147
|
+
<select id="node-config-input-stopBits" style="width:70%">
|
|
148
|
+
<option value="1" selected>1</option>
|
|
149
|
+
<option value="2">2</option>
|
|
150
|
+
</select>
|
|
151
|
+
</div>
|
|
152
|
+
<div class="form-row config-serial-row">
|
|
153
|
+
<label for="node-config-input-parity"><i class="fa fa-check-square-o"></i> 校验位</label>
|
|
154
|
+
<select id="node-config-input-parity" style="width:70%">
|
|
155
|
+
<option value="none" selected>无 (N)</option>
|
|
156
|
+
<option value="even">偶校验 (E)</option>
|
|
157
|
+
<option value="odd">奇校验 (O)</option>
|
|
158
|
+
</select>
|
|
159
|
+
</div>
|
|
160
|
+
</script>
|
|
161
|
+
|
|
162
|
+
<script type="text/x-red" data-help-name="serial-port-config">
|
|
163
|
+
<p>RS-485连接配置节点,支持TCP网关和串口两种连接方式。</p>
|
|
164
|
+
|
|
165
|
+
<h3>配置参数</h3>
|
|
166
|
+
<dl class="message-properties">
|
|
167
|
+
<dt>名称 <span class="property-type">string</span></dt>
|
|
168
|
+
<dd>配置节点的显示名称</dd>
|
|
169
|
+
|
|
170
|
+
<dt>连接类型 <span class="property-type">string</span></dt>
|
|
171
|
+
<dd>选择TCP网关或串口连接</dd>
|
|
172
|
+
|
|
173
|
+
<dt class="optional">TCP主机 <span class="property-type">string</span></dt>
|
|
174
|
+
<dd>TCP网关的IP地址(仅TCP模式)</dd>
|
|
175
|
+
|
|
176
|
+
<dt class="optional">TCP端口 <span class="property-type">number</span></dt>
|
|
177
|
+
<dd>TCP网关的端口号(仅TCP模式)</dd>
|
|
178
|
+
|
|
179
|
+
<dt class="optional">串口路径 <span class="property-type">string</span></dt>
|
|
180
|
+
<dd>串口设备路径,支持自动搜索(仅串口模式)</dd>
|
|
181
|
+
|
|
182
|
+
<dt class="optional">波特率 <span class="property-type">number</span></dt>
|
|
183
|
+
<dd>串口通信波特率,默认9600(仅串口模式)</dd>
|
|
184
|
+
</dl>
|
|
185
|
+
|
|
186
|
+
<h3>说明</h3>
|
|
187
|
+
<p>此配置节点管理一个共享的RS-485连接,允许多个从站开关节点使用同一个连接。</p>
|
|
188
|
+
<p>连接只会被打开一次,所有使用此配置的节点共享同一个连接。</p>
|
|
189
|
+
<p>支持最多500个从站开关节点共享同一个连接。</p>
|
|
190
|
+
</script>
|
|
191
|
+
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RS-485连接配置节点
|
|
3
|
+
* 支持TCP网关和串口两种连接方式
|
|
4
|
+
* 用于多个从站开关节点共享同一个连接
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
module.exports = function(RED) {
|
|
8
|
+
const { SerialPort } = require('serialport');
|
|
9
|
+
const net = require('net');
|
|
10
|
+
|
|
11
|
+
function SerialPortConfigNode(config) {
|
|
12
|
+
RED.nodes.createNode(this, config);
|
|
13
|
+
const node = this;
|
|
14
|
+
|
|
15
|
+
// 配置参数
|
|
16
|
+
node.connectionType = config.connectionType || 'serial';
|
|
17
|
+
node.tcpHost = config.tcpHost;
|
|
18
|
+
node.tcpPort = parseInt(config.tcpPort) || 1031;
|
|
19
|
+
node.serialPort = config.serialPort;
|
|
20
|
+
node.baudRate = parseInt(config.baudRate) || 9600;
|
|
21
|
+
node.dataBits = parseInt(config.dataBits) || 8;
|
|
22
|
+
node.stopBits = parseInt(config.stopBits) || 1;
|
|
23
|
+
node.parity = config.parity || 'none';
|
|
24
|
+
|
|
25
|
+
// 共享的连接实例
|
|
26
|
+
node.connection = null; // TCP socket 或 SerialPort
|
|
27
|
+
node.isOpening = false; // 标记是否正在打开连接
|
|
28
|
+
|
|
29
|
+
// 数据监听器列表(每个从站开关节点注册一个)
|
|
30
|
+
node.dataListeners = [];
|
|
31
|
+
|
|
32
|
+
// 打开TCP连接
|
|
33
|
+
node.openTcpConnection = function() {
|
|
34
|
+
if (node.connection && !node.connection.destroyed) {
|
|
35
|
+
node.log('TCP连接已打开,跳过重复打开');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
node.connection = new net.Socket();
|
|
41
|
+
|
|
42
|
+
node.connection.connect(node.tcpPort, node.tcpHost, () => {
|
|
43
|
+
node.log(`TCP连接已建立: ${node.tcpHost}:${node.tcpPort}`);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// 监听数据(分发给所有注册的监听器)
|
|
47
|
+
node.connection.on('data', (data) => {
|
|
48
|
+
// 广播给所有监听器
|
|
49
|
+
node.dataListeners.forEach(listener => {
|
|
50
|
+
try {
|
|
51
|
+
listener(data);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
node.error(`数据监听器错误: ${err.message}`);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// 监听错误
|
|
59
|
+
node.connection.on('error', (err) => {
|
|
60
|
+
node.error(`TCP连接错误: ${err.message}`);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// 监听关闭
|
|
64
|
+
node.connection.on('close', () => {
|
|
65
|
+
node.log('TCP连接已关闭');
|
|
66
|
+
// 自动重连
|
|
67
|
+
setTimeout(() => {
|
|
68
|
+
if (node.dataListeners.length > 0) {
|
|
69
|
+
node.log('尝试重新连接TCP...');
|
|
70
|
+
node.openTcpConnection();
|
|
71
|
+
}
|
|
72
|
+
}, 5000);
|
|
73
|
+
});
|
|
74
|
+
} catch (err) {
|
|
75
|
+
node.error(`TCP连接初始化失败: ${err.message}`);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// 打开串口连接
|
|
80
|
+
node.openSerialConnection = function() {
|
|
81
|
+
if (node.connection && node.connection.isOpen) {
|
|
82
|
+
node.log('串口已打开,跳过重复打开');
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (node.isOpening) {
|
|
87
|
+
node.log('串口正在打开中,跳过重复打开');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
node.isOpening = true;
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
node.connection = new SerialPort({
|
|
95
|
+
path: node.serialPort,
|
|
96
|
+
baudRate: node.baudRate,
|
|
97
|
+
dataBits: node.dataBits,
|
|
98
|
+
parity: node.parity,
|
|
99
|
+
stopBits: node.stopBits,
|
|
100
|
+
autoOpen: false
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// 打开串口
|
|
104
|
+
node.connection.open((err) => {
|
|
105
|
+
node.isOpening = false;
|
|
106
|
+
|
|
107
|
+
if (err) {
|
|
108
|
+
node.error(`串口打开失败: ${err.message}`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
node.log(`串口已打开: ${node.serialPort} @ ${node.baudRate}bps ${node.dataBits}${node.parity.charAt(0).toUpperCase()}${node.stopBits}`);
|
|
113
|
+
|
|
114
|
+
// 监听数据(分发给所有注册的监听器)
|
|
115
|
+
node.connection.on('data', (data) => {
|
|
116
|
+
// 广播给所有监听器
|
|
117
|
+
node.dataListeners.forEach(listener => {
|
|
118
|
+
try {
|
|
119
|
+
listener(data);
|
|
120
|
+
} catch (err) {
|
|
121
|
+
node.error(`数据监听器错误: ${err.message}`);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// 监听错误
|
|
127
|
+
node.connection.on('error', (err) => {
|
|
128
|
+
node.error(`串口错误: ${err.message}`);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// 监听关闭
|
|
132
|
+
node.connection.on('close', () => {
|
|
133
|
+
node.log('串口已关闭');
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
} catch (err) {
|
|
137
|
+
node.error(`串口初始化失败: ${err.message}`);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// 注册数据监听器
|
|
142
|
+
node.registerDataListener = function(listener) {
|
|
143
|
+
if (typeof listener !== 'function') {
|
|
144
|
+
node.error('监听器必须是函数');
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
node.dataListeners.push(listener);
|
|
149
|
+
node.log(`注册数据监听器,当前监听器数量: ${node.dataListeners.length}`);
|
|
150
|
+
|
|
151
|
+
// 如果连接未打开,现在打开
|
|
152
|
+
if (node.connectionType === 'tcp') {
|
|
153
|
+
if (!node.connection || node.connection.destroyed) {
|
|
154
|
+
node.openTcpConnection();
|
|
155
|
+
}
|
|
156
|
+
} else {
|
|
157
|
+
if (!node.connection || !node.connection.isOpen) {
|
|
158
|
+
node.openSerialConnection();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// 注销数据监听器
|
|
164
|
+
node.unregisterDataListener = function(listener) {
|
|
165
|
+
const index = node.dataListeners.indexOf(listener);
|
|
166
|
+
if (index > -1) {
|
|
167
|
+
node.dataListeners.splice(index, 1);
|
|
168
|
+
node.log(`注销数据监听器,当前监听器数量: ${node.dataListeners.length}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// 如果没有监听器了,关闭连接
|
|
172
|
+
if (node.dataListeners.length === 0 && node.connection) {
|
|
173
|
+
if (node.connectionType === 'tcp') {
|
|
174
|
+
if (!node.connection.destroyed) {
|
|
175
|
+
node.connection.destroy();
|
|
176
|
+
node.log('所有监听器已注销,TCP连接已关闭');
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
if (node.connection.isOpen) {
|
|
180
|
+
node.connection.close((err) => {
|
|
181
|
+
if (err) {
|
|
182
|
+
node.error(`串口关闭失败: ${err.message}`);
|
|
183
|
+
} else {
|
|
184
|
+
node.log('所有监听器已注销,串口已关闭');
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// 写入数据
|
|
193
|
+
node.write = function(data, callback) {
|
|
194
|
+
if (!node.connection) {
|
|
195
|
+
const err = new Error('连接未建立');
|
|
196
|
+
if (callback) callback(err);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (node.connectionType === 'tcp') {
|
|
201
|
+
if (node.connection.destroyed) {
|
|
202
|
+
const err = new Error('TCP连接已断开');
|
|
203
|
+
if (callback) callback(err);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
node.connection.write(data, (err) => {
|
|
207
|
+
if (err) {
|
|
208
|
+
node.error(`TCP写入失败: ${err.message}`);
|
|
209
|
+
}
|
|
210
|
+
if (callback) callback(err);
|
|
211
|
+
});
|
|
212
|
+
} else {
|
|
213
|
+
if (!node.connection.isOpen) {
|
|
214
|
+
const err = new Error('串口未打开');
|
|
215
|
+
if (callback) callback(err);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
node.connection.write(data, (err) => {
|
|
219
|
+
if (err) {
|
|
220
|
+
node.error(`串口写入失败: ${err.message}`);
|
|
221
|
+
}
|
|
222
|
+
if (callback) callback(err);
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// 节点关闭时清理
|
|
228
|
+
node.on('close', function(done) {
|
|
229
|
+
// 清空所有监听器
|
|
230
|
+
node.dataListeners = [];
|
|
231
|
+
|
|
232
|
+
// 关闭连接
|
|
233
|
+
if (node.connection) {
|
|
234
|
+
if (node.connectionType === 'tcp') {
|
|
235
|
+
if (!node.connection.destroyed) {
|
|
236
|
+
node.connection.destroy();
|
|
237
|
+
}
|
|
238
|
+
done();
|
|
239
|
+
} else {
|
|
240
|
+
if (node.connection.isOpen) {
|
|
241
|
+
node.connection.close((err) => {
|
|
242
|
+
if (err) {
|
|
243
|
+
node.error(`串口关闭失败: ${err.message}`);
|
|
244
|
+
}
|
|
245
|
+
done();
|
|
246
|
+
});
|
|
247
|
+
} else {
|
|
248
|
+
done();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
} else {
|
|
252
|
+
done();
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
RED.nodes.registerType('serial-port-config', SerialPortConfigNode);
|
|
258
|
+
|
|
259
|
+
// 提供串口搜索API
|
|
260
|
+
RED.httpAdmin.get('/serial-ports', async (req, res) => {
|
|
261
|
+
try {
|
|
262
|
+
const { SerialPort } = require('serialport');
|
|
263
|
+
const ports = await SerialPort.list();
|
|
264
|
+
res.json(ports);
|
|
265
|
+
} catch (err) {
|
|
266
|
+
res.status(500).json({ error: err.message });
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
};
|
|
270
|
+
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-symi-modbus",
|
|
3
|
-
"version": "2.5.
|
|
4
|
-
"description": "Node-RED Modbus节点,支持TCP/串口通信、串口自动搜索、多设备轮询、智能MQTT连接(自动fallback HassOS/Docker环境)、Home Assistant
|
|
3
|
+
"version": "2.5.4",
|
|
4
|
+
"description": "Node-RED Modbus节点,支持TCP/串口通信、串口自动搜索、多设备轮询、智能MQTT连接(自动fallback HassOS/Docker环境)、Home Assistant自动发现和物理开关面板双向同步,工控机长期稳定运行",
|
|
5
5
|
"main": "nodes/modbus-master.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
@@ -34,12 +34,16 @@
|
|
|
34
34
|
"nodes": {
|
|
35
35
|
"modbus-master": "nodes/modbus-master.js",
|
|
36
36
|
"modbus-slave-switch": "nodes/modbus-slave-switch.js",
|
|
37
|
-
"
|
|
37
|
+
"modbus-server-config": "nodes/modbus-server-config.js",
|
|
38
|
+
"mqtt-server-config": "nodes/mqtt-server-config.js",
|
|
39
|
+
"serial-port-config": "nodes/serial-port-config.js"
|
|
38
40
|
}
|
|
39
41
|
},
|
|
40
42
|
"dependencies": {
|
|
41
|
-
"modbus-serial": "^8.0.
|
|
42
|
-
"mqtt": "^5.
|
|
43
|
+
"modbus-serial": "^8.0.23",
|
|
44
|
+
"mqtt": "^5.14.1",
|
|
45
|
+
"node-red-contrib-symi-modbus": "file:node-red-contrib-symi-modbus-2.5.4.tgz",
|
|
46
|
+
"serialport": "^12.0.0"
|
|
43
47
|
},
|
|
44
48
|
"repository": {
|
|
45
49
|
"type": "git",
|