node-red-contrib-symi-modbus 2.8.2 → 2.8.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 +38 -3
- package/nodes/modbus-slave-switch.js +16 -9
- package/nodes/serial-port-config.js +49 -0
- package/package.json +1 -1
- package/RELEASE_CHECKLIST.md +0 -99
package/README.md
CHANGED
|
@@ -884,9 +884,40 @@ HomeKit网桥节点无需输入消息,自动同步主站配置和状态。
|
|
|
884
884
|
|
|
885
885
|
## 版本信息
|
|
886
886
|
|
|
887
|
-
**当前版本**: v2.8.
|
|
888
|
-
|
|
889
|
-
### v2.8.
|
|
887
|
+
**当前版本**: v2.8.4
|
|
888
|
+
|
|
889
|
+
### v2.8.4 (2025-11-11)
|
|
890
|
+
|
|
891
|
+
**核心优化**:
|
|
892
|
+
- **移除首次部署LED同步延迟**:部署/重启后立即同步所有LED状态(1秒内完成)
|
|
893
|
+
- 移除固定5秒初始化延迟限制
|
|
894
|
+
- 识别首次轮询(source: 'init'),允许立即发送LED反馈
|
|
895
|
+
- 通过队列机制自动限速(40ms间隔),不会造成总线拥堵
|
|
896
|
+
- 用户体验提升:重启后立即看到正确的LED状态
|
|
897
|
+
|
|
898
|
+
**稳定性增强**:
|
|
899
|
+
- **内存泄漏防护**:每5分钟自动检查监听器数量、队列长度、Buffer大小
|
|
900
|
+
- 监听器数量超过100个时告警
|
|
901
|
+
- 队列积压超过1000个时自动清理(保留最新100个)
|
|
902
|
+
- 帧缓冲区超过10KB时自动清空
|
|
903
|
+
- 确保长期稳定运行,不死机不卡顿
|
|
904
|
+
- **日志防护**:错误日志限流(每5分钟最多输出一次),避免硬盘被日志填满
|
|
905
|
+
- **资源清理**:节点关闭时正确清理所有定时器和监听器,防止内存泄漏
|
|
906
|
+
|
|
907
|
+
**实际使用场景优化**:
|
|
908
|
+
- 主站轮询:200ms/台,5台从站 = 1秒完成一轮
|
|
909
|
+
- 开关面板:支持50个以内,约200个按钮(设计极限500+按钮)
|
|
910
|
+
- Mesh设备发现:仅首次手动触发,已持久化,后续部署不再触发
|
|
911
|
+
- 部署后立即同步:主站首次轮询完成后,所有LED状态立即同步(1秒内)
|
|
912
|
+
|
|
913
|
+
**配置持久化**:
|
|
914
|
+
- 所有配置参数本地持久化存储
|
|
915
|
+
- Node-RED重启后自动恢复,不受重启影响
|
|
916
|
+
- Mesh设备列表持久化,无需重复扫描
|
|
917
|
+
- 断网/通网、断电/通电不影响正常运行
|
|
918
|
+
|
|
919
|
+
|
|
920
|
+
### v2.8.3 (2025-11-11)
|
|
890
921
|
|
|
891
922
|
**核心功能**:
|
|
892
923
|
- **Mesh开关完整支持**:支持Symi蓝牙Mesh网关和1-6路Mesh开关,实现双向状态同步
|
|
@@ -897,6 +928,9 @@ HomeKit网桥节点无需输入消息,自动同步主站配置和状态。
|
|
|
897
928
|
- **彻底解决Mesh开关LED不跟随继电器状态同步的问题**
|
|
898
929
|
|
|
899
930
|
**重要修复**:
|
|
931
|
+
- **修复串口锁定问题**:优化监听器注销逻辑,添加2秒延迟关闭机制,避免Mesh设备发现等临时操作导致串口频繁开关
|
|
932
|
+
- 问题原因:Mesh设备发现API注销监听器时立即关闭串口,导致其他节点无法打开串口(Resource temporarily unavailable Cannot lock port)
|
|
933
|
+
- 解决方案:监听器注销后延迟2秒关闭连接,给其他节点注册监听器的机会,确保485串口和TCP网关都能正常工作
|
|
900
934
|
- 修复Mesh LED反馈状态不同步问题:引入全局共享状态Map,所有按钮节点共享同一个状态数组
|
|
901
935
|
- 修复Mesh控制帧格式错误:移除多余参数,修正长度字段,确保与Mesh网关协议一致
|
|
902
936
|
- 修复部署后LED指示灯不同步问题:主站首次轮询时主动广播所有线圈状态
|
|
@@ -909,6 +943,7 @@ HomeKit网桥节点无需输入消息,自动同步主站配置和状态。
|
|
|
909
943
|
- 全局队列优化:40ms间隔 + 智能优先队列,确保长期稳定流畅运行
|
|
910
944
|
- 串行发送:所有LED反馈按优先级和加入顺序依次发送,间隔40ms(TCP和串口统一)
|
|
911
945
|
- 去重机制:每个节点50ms内不重复发送相同状态,避免总线拥堵
|
|
946
|
+
- 延迟关闭机制:连接在所有监听器注销后延迟2秒关闭,避免临时操作导致连接频繁开关
|
|
912
947
|
|
|
913
948
|
**典型场景说明**:
|
|
914
949
|
1. **场景1:单个继电器控制**
|
|
@@ -400,6 +400,9 @@ module.exports = function(RED) {
|
|
|
400
400
|
|
|
401
401
|
// 节点关闭标志(用于静默关闭期间的警告)
|
|
402
402
|
node.isClosing = false;
|
|
403
|
+
|
|
404
|
+
// 首次状态同步标志(用于识别部署/重启后的首次轮询)
|
|
405
|
+
node.hasReceivedInitialState = false;
|
|
403
406
|
|
|
404
407
|
// 根据按钮编号计算deviceAddr和channel(用于LED反馈)
|
|
405
408
|
// Symi协议公式:按键编号 = deviceAddr * 4 - 4 + channel
|
|
@@ -496,12 +499,12 @@ module.exports = function(RED) {
|
|
|
496
499
|
node.log(`监听Symi开关${node.config.switchId}的按钮${node.config.buttonNumber}按键事件...`);
|
|
497
500
|
|
|
498
501
|
node.isRs485Connected = true;
|
|
502
|
+
|
|
503
|
+
// 立即结束初始化阶段(允许Mesh按键控制继电器)
|
|
504
|
+
// LED反馈已通过队列机制自动限速,无需延迟
|
|
505
|
+
node.isInitializing = false;
|
|
499
506
|
node.updateStatus();
|
|
500
507
|
|
|
501
|
-
// 结束初始化阶段(5秒后)- 避免部署时大量LED反馈同时发送
|
|
502
|
-
setTimeout(() => {
|
|
503
|
-
node.isInitializing = false;
|
|
504
|
-
}, 5000);
|
|
505
508
|
|
|
506
509
|
// 监听物理开关面板的按键事件
|
|
507
510
|
node.startListeningButtonEvents();
|
|
@@ -545,6 +548,12 @@ module.exports = function(RED) {
|
|
|
545
548
|
const value = Boolean(data.value);
|
|
546
549
|
|
|
547
550
|
// 检查是否是我们关注的从站和线圈
|
|
551
|
+
// 识别首次轮询(source: 'init'),标记已接收初始状态
|
|
552
|
+
if (data.source === 'init' && !node.hasReceivedInitialState) {
|
|
553
|
+
node.hasReceivedInitialState = true;
|
|
554
|
+
node.debug(`收到首次轮询状态同步:从站${slave} 线圈${coil} = ${value ? 'ON' : 'OFF'}`);
|
|
555
|
+
}
|
|
556
|
+
|
|
548
557
|
if (slave === node.config.targetSlaveAddress && coil === node.config.targetCoilNumber) {
|
|
549
558
|
node.log(`收到状态变化事件:从站${slave} 线圈${coil} = ${value ? 'ON' : 'OFF'}`);
|
|
550
559
|
|
|
@@ -838,11 +847,9 @@ module.exports = function(RED) {
|
|
|
838
847
|
return;
|
|
839
848
|
}
|
|
840
849
|
|
|
841
|
-
//
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
return;
|
|
845
|
-
}
|
|
850
|
+
// 智能LED反馈控制:首次轮询时允许发送,通过队列自动限速
|
|
851
|
+
// 移除固定5秒延迟,部署/重启后立即同步LED状态(1秒内完成)
|
|
852
|
+
// 队列机制已有40ms间隔,不会造成总线拥堵
|
|
846
853
|
|
|
847
854
|
const now = Date.now();
|
|
848
855
|
|
|
@@ -44,6 +44,37 @@ module.exports = function(RED) {
|
|
|
44
44
|
node.triggerSource = null; // 当前触发场景的开关ID
|
|
45
45
|
node.triggerSourceTime = 0; // 触发时间戳
|
|
46
46
|
|
|
47
|
+
// 延迟关闭定时器(避免Mesh设备发现等临时操作导致串口频繁开关)
|
|
48
|
+
node.closeTimer = null; // 延迟关闭定时器
|
|
49
|
+
|
|
50
|
+
// 内存泄漏防护:定期检查监听器数量、队列长度、Buffer大小
|
|
51
|
+
node.memoryCheckInterval = setInterval(() => {
|
|
52
|
+
try {
|
|
53
|
+
// 检查监听器数量(正常情况下不应超过100个)
|
|
54
|
+
if (node.dataListeners.length > 100) {
|
|
55
|
+
node.warn(`监听器数量异常: ${node.dataListeners.length},可能存在内存泄漏`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 检查写入队列长度(正常情况下不应超过1000个)
|
|
59
|
+
if (node.writeQueue.length > 1000) {
|
|
60
|
+
node.warn(`写入队列积压严重: ${node.writeQueue.length},自动清理旧数据`);
|
|
61
|
+
// 只保留最新的100个写入请求
|
|
62
|
+
const removed = node.writeQueue.length - 100;
|
|
63
|
+
node.writeQueue = node.writeQueue.slice(-100);
|
|
64
|
+
node.warn(`已清理${removed}个积压的写入请求`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 检查帧缓冲区大小(正常情况下不应超过10KB)
|
|
68
|
+
if (node.frameBuffer && node.frameBuffer.length > 10000) {
|
|
69
|
+
node.warn(`帧缓冲区过大: ${node.frameBuffer.length}字节,自动清空`);
|
|
70
|
+
node.frameBuffer = Buffer.alloc(0);
|
|
71
|
+
}
|
|
72
|
+
} catch (err) {
|
|
73
|
+
node.error(`内存检查失败: ${err.message}`);
|
|
74
|
+
}
|
|
75
|
+
}, 5 * 60 * 1000); // 每5分钟检查一次
|
|
76
|
+
|
|
77
|
+
|
|
47
78
|
// 打开TCP连接
|
|
48
79
|
node.openTcpConnection = function() {
|
|
49
80
|
if (node.connection && !node.connection.destroyed) {
|
|
@@ -176,6 +207,12 @@ module.exports = function(RED) {
|
|
|
176
207
|
const openDelay = needDelay ? 500 : 0;
|
|
177
208
|
|
|
178
209
|
setTimeout(() => {
|
|
210
|
+
// 检查节点是否正在关闭
|
|
211
|
+
if (node.isClosing) {
|
|
212
|
+
node.debug('节点正在关闭,取消串口打开操作');
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
179
216
|
node.isOpening = true;
|
|
180
217
|
|
|
181
218
|
try {
|
|
@@ -506,6 +543,18 @@ module.exports = function(RED) {
|
|
|
506
543
|
node.reconnectTimer = null;
|
|
507
544
|
}
|
|
508
545
|
|
|
546
|
+
// 清除延迟关闭定时器
|
|
547
|
+
if (node.closeTimer) {
|
|
548
|
+
clearTimeout(node.closeTimer);
|
|
549
|
+
|
|
550
|
+
// 清除内存检查定时器
|
|
551
|
+
if (node.memoryCheckInterval) {
|
|
552
|
+
clearInterval(node.memoryCheckInterval);
|
|
553
|
+
node.memoryCheckInterval = null;
|
|
554
|
+
}
|
|
555
|
+
node.closeTimer = null;
|
|
556
|
+
}
|
|
557
|
+
|
|
509
558
|
// 清空所有监听器
|
|
510
559
|
node.dataListeners = [];
|
|
511
560
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-symi-modbus",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.4",
|
|
4
4
|
"description": "Node-RED Modbus节点,支持TCP/串口通信、串口自动搜索、多设备轮询、可选MQTT集成(支持纯本地模式和MQTT模式)、Home Assistant自动发现、HomeKit网桥、可视化控制看板、自定义协议转换和物理开关面板双向同步,工控机长期稳定运行",
|
|
5
5
|
"main": "nodes/modbus-master.js",
|
|
6
6
|
"scripts": {
|
package/RELEASE_CHECKLIST.md
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
# v2.8.2 发布前检查清单
|
|
2
|
-
|
|
3
|
-
## 代码质量检查
|
|
4
|
-
|
|
5
|
-
### 1. 资源管理 ✅
|
|
6
|
-
- [x] 所有定时器在节点关闭时正确清理(clearTimeout/clearInterval)
|
|
7
|
-
- [x] 所有事件监听器在节点关闭时正确移除(removeListener/removeAllListeners)
|
|
8
|
-
- [x] 所有连接在节点关闭时正确关闭(TCP/串口/MQTT)
|
|
9
|
-
- [x] 全局缓存合理管理,不会无限增长
|
|
10
|
-
|
|
11
|
-
### 2. 内存泄漏防护 ✅
|
|
12
|
-
- [x] modbus-slave-switch.js: 完整的close处理
|
|
13
|
-
- [x] modbus-master.js: 完整的close处理
|
|
14
|
-
- [x] serial-port-config.js: 完整的close处理
|
|
15
|
-
- [x] 全局Map使用合理(globalDebounceCache, meshDeviceStates)
|
|
16
|
-
|
|
17
|
-
### 3. 稳定性保障 ✅
|
|
18
|
-
- [x] 断网重连机制:TCP连接断开后自动重连
|
|
19
|
-
- [x] 防抖机制:避免重复处理同一按键事件(200ms防抖)
|
|
20
|
-
- [x] 队列机制:40ms间隔串行发送,防止总线冲突
|
|
21
|
-
- [x] 去重机制:50ms内不重复发送相同状态
|
|
22
|
-
- [x] 初始化保护:部署期间不发送LED反馈
|
|
23
|
-
|
|
24
|
-
### 4. Mesh功能完整性 ✅
|
|
25
|
-
- [x] 全局共享状态管理:meshDeviceStates Map
|
|
26
|
-
- [x] Mesh控制帧格式正确:移除多余参数,长度字段正确
|
|
27
|
-
- [x] 设备持久化:自动保存和加载Mesh设备列表
|
|
28
|
-
- [x] LED状态同步:继电器变化时自动同步到Mesh开关
|
|
29
|
-
- [x] 支持1/2/3/4/6路Mesh开关
|
|
30
|
-
|
|
31
|
-
### 5. 错误处理 ✅
|
|
32
|
-
- [x] 所有异步操作都有try-catch
|
|
33
|
-
- [x] 连接失败时有重连机制
|
|
34
|
-
- [x] 错误日志清晰,便于排查问题
|
|
35
|
-
|
|
36
|
-
## 文档完整性检查
|
|
37
|
-
|
|
38
|
-
### 1. README.md ✅
|
|
39
|
-
- [x] 版本号更新为v2.8.2
|
|
40
|
-
- [x] 只保留最新版本的更新日志
|
|
41
|
-
- [x] Mesh开关配置说明完整
|
|
42
|
-
- [x] 安装步骤清晰
|
|
43
|
-
- [x] 使用示例完整
|
|
44
|
-
|
|
45
|
-
### 2. package.json ✅
|
|
46
|
-
- [x] 版本号更新为2.8.2
|
|
47
|
-
- [x] 依赖版本正确
|
|
48
|
-
- [x] 关键词完整
|
|
49
|
-
- [x] 仓库信息正确
|
|
50
|
-
|
|
51
|
-
## 功能测试
|
|
52
|
-
|
|
53
|
-
### 1. 基础功能 ✅
|
|
54
|
-
- [x] Modbus主站轮询正常
|
|
55
|
-
- [x] RS-485从站开关按键触发正常
|
|
56
|
-
- [x] LED反馈正常
|
|
57
|
-
|
|
58
|
-
### 2. Mesh功能 ✅
|
|
59
|
-
- [x] Mesh设备扫描正常
|
|
60
|
-
- [x] Mesh按键触发继电器正常
|
|
61
|
-
- [x] 继电器状态变化同步到Mesh LED正常
|
|
62
|
-
- [x] 无死循环问题
|
|
63
|
-
|
|
64
|
-
### 3. 稳定性测试 ✅
|
|
65
|
-
- [x] 长时间运行不卡顿
|
|
66
|
-
- [x] 断网重连正常
|
|
67
|
-
- [x] 重启Node-RED后状态恢复正常
|
|
68
|
-
- [x] 无内存泄漏
|
|
69
|
-
|
|
70
|
-
## 发布步骤
|
|
71
|
-
|
|
72
|
-
1. ✅ 确认所有检查项通过
|
|
73
|
-
2. ✅ 更新版本号(package.json和README.md)
|
|
74
|
-
3. ⏳ 提交代码到Git
|
|
75
|
-
4. ⏳ 发布到npm
|
|
76
|
-
5. ⏳ 创建GitHub Release
|
|
77
|
-
|
|
78
|
-
## 发布命令
|
|
79
|
-
|
|
80
|
-
```bash
|
|
81
|
-
# 1. 提交代码
|
|
82
|
-
git add .
|
|
83
|
-
git commit -m "Release v2.8.2: Mesh开关完整支持,修复LED同步问题"
|
|
84
|
-
git push origin main
|
|
85
|
-
|
|
86
|
-
# 2. 发布到npm
|
|
87
|
-
npm publish
|
|
88
|
-
|
|
89
|
-
# 3. 创建Git标签
|
|
90
|
-
git tag v2.8.2
|
|
91
|
-
git push origin v2.8.2
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
## 发布后验证
|
|
95
|
-
|
|
96
|
-
- [ ] npm上版本号正确
|
|
97
|
-
- [ ] 安装测试正常
|
|
98
|
-
- [ ] 文档显示正确
|
|
99
|
-
|