node-red-contrib-symi-modbus 2.6.7 → 2.6.9
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 +286 -360
- package/examples/basic-flow.json +33 -21
- package/nodes/custom-protocol.html +276 -0
- package/nodes/custom-protocol.js +240 -0
- package/nodes/homekit-bridge.html +273 -0
- package/nodes/homekit-bridge.js +346 -0
- package/nodes/mesh-protocol.js +286 -0
- package/nodes/modbus-dashboard.html +444 -0
- package/nodes/modbus-dashboard.js +116 -0
- package/nodes/modbus-debug.js +10 -2
- package/nodes/modbus-master.js +185 -84
- package/nodes/modbus-slave-switch.html +196 -12
- package/nodes/modbus-slave-switch.js +479 -157
- package/nodes/serial-port-config.js +84 -21
- package/package.json +8 -3
|
@@ -29,6 +29,10 @@ module.exports = function(RED) {
|
|
|
29
29
|
node.reconnectTimer = null; // 重连定时器
|
|
30
30
|
node.reconnectAttempts = 0; // 重连尝试次数
|
|
31
31
|
|
|
32
|
+
// 错误日志限流(避免断网时产生大量日志)
|
|
33
|
+
node.lastErrorLog = 0; // 上次错误日志时间
|
|
34
|
+
node.errorLogInterval = 60 * 1000; // 错误日志间隔:60秒
|
|
35
|
+
|
|
32
36
|
// 数据监听器列表(每个从站开关节点注册一个)
|
|
33
37
|
node.dataListeners = [];
|
|
34
38
|
|
|
@@ -81,15 +85,23 @@ module.exports = function(RED) {
|
|
|
81
85
|
}
|
|
82
86
|
});
|
|
83
87
|
|
|
84
|
-
//
|
|
88
|
+
// 监听错误(限流日志)
|
|
85
89
|
node.connection.on('error', (err) => {
|
|
86
|
-
|
|
90
|
+
const now = Date.now();
|
|
91
|
+
if (now - node.lastErrorLog > node.errorLogInterval) {
|
|
92
|
+
node.error(`TCP连接错误: ${err.message}`);
|
|
93
|
+
node.lastErrorLog = now;
|
|
94
|
+
}
|
|
87
95
|
// 不在这里重连,在close事件中统一处理
|
|
88
96
|
});
|
|
89
97
|
|
|
90
98
|
// 监听关闭(统一的重连入口)
|
|
91
99
|
node.connection.on('close', (hadError) => {
|
|
92
|
-
|
|
100
|
+
const now = Date.now();
|
|
101
|
+
if (now - node.lastErrorLog > node.errorLogInterval) {
|
|
102
|
+
node.log(`TCP连接已关闭${hadError ? '(有错误)' : ''}`);
|
|
103
|
+
node.lastErrorLog = now;
|
|
104
|
+
}
|
|
93
105
|
|
|
94
106
|
// 清理连接对象
|
|
95
107
|
if (node.connection) {
|
|
@@ -103,7 +115,11 @@ module.exports = function(RED) {
|
|
|
103
115
|
const delay = Math.min(5000 * Math.pow(2, node.reconnectAttempts), 60000); // 指数退避,最大60秒
|
|
104
116
|
node.reconnectAttempts++;
|
|
105
117
|
|
|
106
|
-
|
|
118
|
+
const nowLog = Date.now();
|
|
119
|
+
if (nowLog - node.lastErrorLog > node.errorLogInterval) {
|
|
120
|
+
node.log(`${delay/1000}秒后尝试重新连接TCP(第${node.reconnectAttempts}次)...`);
|
|
121
|
+
node.lastErrorLog = nowLog;
|
|
122
|
+
}
|
|
107
123
|
|
|
108
124
|
node.reconnectTimer = setTimeout(() => {
|
|
109
125
|
node.reconnectTimer = null;
|
|
@@ -164,15 +180,23 @@ module.exports = function(RED) {
|
|
|
164
180
|
node.isOpening = false;
|
|
165
181
|
|
|
166
182
|
if (err) {
|
|
167
|
-
|
|
168
|
-
|
|
183
|
+
const now = Date.now();
|
|
184
|
+
if (now - node.lastErrorLog > node.errorLogInterval) {
|
|
185
|
+
node.error(`串口打开失败: ${err.message}`);
|
|
186
|
+
node.lastErrorLog = now;
|
|
187
|
+
}
|
|
188
|
+
|
|
169
189
|
// 打开失败时也要触发重连(如果有监听器在使用)
|
|
170
190
|
if (!node.isClosing && node.dataListeners.length > 0) {
|
|
171
191
|
const delay = Math.min(5000 * Math.pow(2, node.reconnectAttempts), 60000);
|
|
172
192
|
node.reconnectAttempts++;
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
193
|
+
|
|
194
|
+
const nowLog = Date.now();
|
|
195
|
+
if (nowLog - node.lastErrorLog > node.errorLogInterval) {
|
|
196
|
+
node.log(`${delay/1000}秒后尝试重新打开串口(第${node.reconnectAttempts}次)...`);
|
|
197
|
+
node.lastErrorLog = nowLog;
|
|
198
|
+
}
|
|
199
|
+
|
|
176
200
|
node.reconnectTimer = setTimeout(() => {
|
|
177
201
|
node.reconnectTimer = null;
|
|
178
202
|
node.openSerialConnection();
|
|
@@ -191,25 +215,37 @@ module.exports = function(RED) {
|
|
|
191
215
|
try {
|
|
192
216
|
listener(data);
|
|
193
217
|
} catch (err) {
|
|
194
|
-
|
|
218
|
+
const now = Date.now();
|
|
219
|
+
if (now - node.lastErrorLog > node.errorLogInterval) {
|
|
220
|
+
node.error(`数据监听器错误: ${err.message}`);
|
|
221
|
+
node.lastErrorLog = now;
|
|
222
|
+
}
|
|
195
223
|
}
|
|
196
224
|
});
|
|
197
225
|
});
|
|
198
226
|
|
|
199
|
-
//
|
|
227
|
+
// 监听错误(限流日志)
|
|
200
228
|
node.connection.on('error', (err) => {
|
|
201
|
-
|
|
229
|
+
const now = Date.now();
|
|
230
|
+
if (now - node.lastErrorLog > node.errorLogInterval) {
|
|
231
|
+
node.error(`串口错误: ${err.message}`);
|
|
232
|
+
node.lastErrorLog = now;
|
|
233
|
+
}
|
|
202
234
|
// 不在这里重连,在close事件中统一处理
|
|
203
235
|
});
|
|
204
236
|
|
|
205
237
|
// 监听关闭(统一的重连入口)
|
|
206
238
|
node.connection.on('close', (err) => {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
239
|
+
const now = Date.now();
|
|
240
|
+
if (now - node.lastErrorLog > node.errorLogInterval) {
|
|
241
|
+
if (err) {
|
|
242
|
+
node.log(`串口已关闭(错误: ${err.message})`);
|
|
243
|
+
} else {
|
|
244
|
+
node.log('串口已关闭');
|
|
245
|
+
}
|
|
246
|
+
node.lastErrorLog = now;
|
|
211
247
|
}
|
|
212
|
-
|
|
248
|
+
|
|
213
249
|
// 清理连接对象
|
|
214
250
|
if (node.connection) {
|
|
215
251
|
try {
|
|
@@ -219,14 +255,18 @@ module.exports = function(RED) {
|
|
|
219
255
|
}
|
|
220
256
|
node.connection = null;
|
|
221
257
|
}
|
|
222
|
-
|
|
258
|
+
|
|
223
259
|
// 自动重连(如果不是主动关闭且有监听器在使用)
|
|
224
260
|
if (!node.isClosing && node.dataListeners.length > 0) {
|
|
225
261
|
const delay = Math.min(5000 * Math.pow(2, node.reconnectAttempts), 60000); // 指数退避,最大60秒
|
|
226
262
|
node.reconnectAttempts++;
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
263
|
+
|
|
264
|
+
const nowLog = Date.now();
|
|
265
|
+
if (nowLog - node.lastErrorLog > node.errorLogInterval) {
|
|
266
|
+
node.log(`检测到串口断开,${delay/1000}秒后尝试重新连接(第${node.reconnectAttempts}次)...`);
|
|
267
|
+
node.lastErrorLog = nowLog;
|
|
268
|
+
}
|
|
269
|
+
|
|
230
270
|
node.reconnectTimer = setTimeout(() => {
|
|
231
271
|
node.reconnectTimer = null;
|
|
232
272
|
node.openSerialConnection();
|
|
@@ -251,6 +291,11 @@ module.exports = function(RED) {
|
|
|
251
291
|
}
|
|
252
292
|
};
|
|
253
293
|
|
|
294
|
+
// 获取连接对象(用于Mesh设备发现等场景)
|
|
295
|
+
node.getConnection = function() {
|
|
296
|
+
return node.connection;
|
|
297
|
+
};
|
|
298
|
+
|
|
254
299
|
// 注册数据监听器
|
|
255
300
|
node.registerDataListener = function(listener) {
|
|
256
301
|
if (typeof listener !== 'function') {
|
|
@@ -345,6 +390,15 @@ module.exports = function(RED) {
|
|
|
345
390
|
if (callback) callback(err);
|
|
346
391
|
reject(err);
|
|
347
392
|
} else {
|
|
393
|
+
// 广播发送的数据给所有监听器(包括debug节点)
|
|
394
|
+
node.dataListeners.forEach(listener => {
|
|
395
|
+
try {
|
|
396
|
+
listener(data, 'sent');
|
|
397
|
+
} catch (e) {
|
|
398
|
+
node.error('监听器处理发送数据失败: ' + e.message);
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
|
|
348
402
|
if (callback) callback(null);
|
|
349
403
|
resolve();
|
|
350
404
|
}
|
|
@@ -370,6 +424,15 @@ module.exports = function(RED) {
|
|
|
370
424
|
if (callback) callback(err);
|
|
371
425
|
reject(err);
|
|
372
426
|
} else {
|
|
427
|
+
// 广播发送的数据给所有监听器(包括debug节点)
|
|
428
|
+
node.dataListeners.forEach(listener => {
|
|
429
|
+
try {
|
|
430
|
+
listener(data, 'sent');
|
|
431
|
+
} catch (e) {
|
|
432
|
+
node.error('监听器处理发送数据失败: ' + e.message);
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
|
|
373
436
|
if (callback) callback(null);
|
|
374
437
|
resolve();
|
|
375
438
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-symi-modbus",
|
|
3
|
-
"version": "2.6.
|
|
4
|
-
"description": "Node-RED Modbus节点,支持TCP/串口通信、串口自动搜索、多设备轮询、可选MQTT集成(支持纯本地模式和MQTT模式)、Home Assistant
|
|
3
|
+
"version": "2.6.9",
|
|
4
|
+
"description": "Node-RED Modbus节点,支持TCP/串口通信、串口自动搜索、多设备轮询、可选MQTT集成(支持纯本地模式和MQTT模式)、Home Assistant自动发现、HomeKit网桥、可视化控制看板、自定义协议转换和物理开关面板双向同步,工控机长期稳定运行",
|
|
5
5
|
"main": "nodes/modbus-master.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
@@ -37,12 +37,17 @@
|
|
|
37
37
|
"modbus-server-config": "nodes/modbus-server-config.js",
|
|
38
38
|
"mqtt-server-config": "nodes/mqtt-server-config.js",
|
|
39
39
|
"serial-port-config": "nodes/serial-port-config.js",
|
|
40
|
-
"modbus-debug": "nodes/modbus-debug.js"
|
|
40
|
+
"modbus-debug": "nodes/modbus-debug.js",
|
|
41
|
+
"homekit-bridge": "nodes/homekit-bridge.js",
|
|
42
|
+
"modbus-dashboard": "nodes/modbus-dashboard.js",
|
|
43
|
+
"custom-protocol": "nodes/custom-protocol.js"
|
|
41
44
|
}
|
|
42
45
|
},
|
|
43
46
|
"dependencies": {
|
|
47
|
+
"hap-nodejs": "^1.2.0",
|
|
44
48
|
"modbus-serial": "^8.0.23",
|
|
45
49
|
"mqtt": "^5.14.1",
|
|
50
|
+
"node-persist": "^4.0.4",
|
|
46
51
|
"serialport": "^12.0.0"
|
|
47
52
|
},
|
|
48
53
|
"repository": {
|