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.
@@ -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
- node.error(`TCP连接错误: ${err.message}`);
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
- node.log(`TCP连接已关闭${hadError ? '(有错误)' : ''}`);
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
- node.log(`${delay/1000}秒后尝试重新连接TCP(第${node.reconnectAttempts}次)...`);
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
- node.error(`串口打开失败: ${err.message}`);
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
- node.log(`${delay/1000}秒后尝试重新打开串口(第${node.reconnectAttempts}次)...`);
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
- node.error(`数据监听器错误: ${err.message}`);
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
- node.error(`串口错误: ${err.message}`);
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
- if (err) {
208
- node.log(`串口已关闭(错误: ${err.message})`);
209
- } else {
210
- node.log('串口已关闭');
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
- node.log(`检测到串口断开,${delay/1000}秒后尝试重新连接(第${node.reconnectAttempts}次)...`);
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.7",
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": {