node-red-contrib-symi-modbus 2.9.1 → 2.9.3

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 CHANGED
@@ -888,39 +888,29 @@ HomeKit网桥节点无需输入消息,自动同步主站配置和状态。
888
888
 
889
889
  ## 版本信息
890
890
 
891
- **当前版本**: v2.9.1 (2025-12-13)
892
-
893
- **v2.9.1 更新内容**:
894
- - **重要修复**:RS-485开关指示灯同步问题
895
- - 修复TCP粘包导致的协议帧解析失败问题
896
- - 新增`parseAllFrames`函数,正确处理多个帧粘在一起的情况
897
- - 根据协议的`dataLen`字段精确提取帧边界,确保每个帧都被正确解析
898
- - **重要修复**:Modbus控制看板部署失效问题
899
- - 修复主站更新后重新部署导致看板无法正确显示的问题
900
- - 部署时自动清空并重新初始化状态缓存
901
- - 新增`refresh` API,点击刷新按钮从主站获取真实继电器状态
902
- - **优化**:控制看板刷新功能
903
- - 刷新按钮现在会主动从主站获取最新的继电器状态
904
- - 确保显示的状态与实际继电器状态同步
905
- - **优化**:指示灯反馈队列间隔调整为50ms
906
- - TCP/串口写入间隔从40ms调整为50ms
907
- - 触发源面板优先处理,确保按键响应流畅
908
- - 按线圈顺序陆续反馈所有有动作的继电器指示灯
909
-
910
- **v2.9.0 更新内容**:
911
- - **重要修复**:一个按键现在可以控制多个不同从站的继电器
912
- - 修复了全局防抖机制导致的问题:之前一个开关配置控制多个从站时,只有第一个从站会执行
913
- - 现在支持:一个物理按键同时控制从站0A和0B的同一线圈,两个都会正确执行
914
- - 优化防抖key设计,包含目标从站地址,确保不同目标互不影响
915
- - RS-485开关、Mesh开关、Mesh场景模式均已修复
916
-
917
- **v2.8.9 更新内容**:
918
- - 新增Mesh开关无线模式支持,适用于场景触发按键
919
- - 修复Mesh开关群控LED反馈问题,确保状态完全同步
920
- - 优化LED反馈防抖机制(100ms),智能合并多次状态变化
921
- - 群控多个继电器时,Mesh开关只发送一次LED反馈,包含所有按钮的最新状态
922
- - 修复串口永久连接机制,彻底解决部署时串口锁定问题
923
- - RS-485开关功能完全不受影响,保持稳定运行
891
+ **当前版本**: v2.9.3 (2025-12-15)
892
+
893
+ **v2.9.3 更新内容**:
894
+ - **重要修复**:场景按钮LED反馈不同步问题
895
+ - 修复场景按钮帧使用特殊通道(如0x0F)导致LED反馈失效的问题
896
+ - 场景模式保持使用配置计算的按键通道,不被帧中的特殊标识覆盖
897
+ - 解决"部署后场景按钮可以同步一次,点击其他开关后不再同步"的问题
898
+ - **优化**:场景按钮匹配逻辑
899
+ - 增加场景模式帧的特殊匹配:通道0x0F或isSceneMode标识
900
+ - 同一面板的场景按钮现在能正确识别和处理
901
+
902
+ **v2.9.2 更新内容**:
903
+ - **稳定性增强**:全局防抖缓存定期清理机制
904
+ - 每10分钟自动清理超过1分钟未使用的防抖记录
905
+ - 防止长期运行时内存缓慢增长
906
+ - **稳定性增强**:Mesh无线模式按键注册清理
907
+ - 节点关闭时正确清理meshWirelessButtons全局Map
908
+ - 避免重新部署时残留无效注册信息
909
+ - **代码审查**:全面检查所有节点代码
910
+ - 确认所有定时器在节点关闭时正确清理
911
+ - 确认所有事件监听器正确注销
912
+ - 确认所有连接资源正确释放
913
+ - 支持断电断网后自动恢复运行
924
914
 
925
915
  **核心特性**:
926
916
  - 支持Modbus RTU/TCP协议,兼容标准Modbus设备和TCP转RS485网关
@@ -30,6 +30,27 @@ module.exports = function(RED) {
30
30
  let meshLedFeedbackQueueTimer = null;
31
31
  let meshLedFeedbackGlobalLock = 0; // 全局锁定时间戳:在此时间之前,忽略所有Mesh面板的状态上报
32
32
 
33
+ // 全局防抖缓存定期清理(每10分钟清理超过1分钟未使用的条目,防止内存泄漏)
34
+ let globalDebounceCacheCleanupTimer = null;
35
+ function startGlobalDebounceCacheCleanup() {
36
+ if (globalDebounceCacheCleanupTimer) return;
37
+ globalDebounceCacheCleanupTimer = setInterval(() => {
38
+ const now = Date.now();
39
+ const expireThreshold = 60 * 1000; // 1分钟未使用的条目将被清理
40
+ let cleanedCount = 0;
41
+ for (const [key, timestamp] of globalDebounceCache.entries()) {
42
+ if (now - timestamp > expireThreshold) {
43
+ globalDebounceCache.delete(key);
44
+ cleanedCount++;
45
+ }
46
+ }
47
+ if (cleanedCount > 0) {
48
+ RED.log.debug(`全局防抖缓存清理: 删除${cleanedCount}条过期记录,剩余${globalDebounceCache.size}条`);
49
+ }
50
+ }, 10 * 60 * 1000); // 每10分钟执行一次
51
+ }
52
+ startGlobalDebounceCacheCleanup();
53
+
33
54
  // 初始化Mesh设备持久化存储
34
55
  const meshPersistDir = path.join(RED.settings.userDir || os.homedir() + '/.node-red', 'mesh-devices-persist');
35
56
  let meshStorageInitialized = false;
@@ -774,16 +795,26 @@ module.exports = function(RED) {
774
795
  // 检查是否是我们监听的开关面板和按钮
775
796
  // switchId对应本地地址(物理面板地址)
776
797
  // buttonNumber对应实际按键编号(1-8)
777
- if (buttonEvent.raw.localAddr === node.config.switchId && actualButtonNumber === node.config.buttonNumber) {
778
- // 保存原始的deviceAddr和channel,用于LED反馈
779
- const oldDeviceAddr = node.buttonDeviceAddr;
780
- const oldChannel = node.buttonChannel;
781
- node.buttonDeviceAddr = buttonEvent.deviceAddr;
782
- node.buttonChannel = buttonEvent.channel;
783
-
784
- // 输出调试日志,对比计算值和实际值
785
- if (oldDeviceAddr !== buttonEvent.deviceAddr || oldChannel !== buttonEvent.channel) {
786
- node.log(`按键事件更新LED反馈地址:计算值(设备${oldDeviceAddr} 通道${oldChannel}) → 实际值(设备${buttonEvent.deviceAddr} 通道${buttonEvent.channel})`);
798
+ //
799
+ // 重要:场景按钮的帧通道可能是特殊标识(如0x0F),而不是实际按键通道
800
+ // 因此需要额外判断:如果是场景模式且localAddr匹配,也应该处理
801
+ const isSceneModeFrame = buttonEvent.isSceneMode || buttonEvent.channel === 0x0F;
802
+ const isMatchedByButtonNumber = (buttonEvent.raw.localAddr === node.config.switchId && actualButtonNumber === node.config.buttonNumber);
803
+ const isMatchedBySceneMode = (buttonEvent.raw.localAddr === node.config.switchId && node.config.buttonType === 'scene' && isSceneModeFrame);
804
+
805
+ if (isMatchedByButtonNumber || isMatchedBySceneMode) {
806
+ // 只有开关模式才更新deviceAddrchannel为实际值
807
+ // 场景模式保持使用配置计算的值,因为场景帧的通道(如0x0F)是特殊标识,不是LED反馈通道
808
+ if (!isSceneModeFrame && node.config.buttonType !== 'scene') {
809
+ const oldDeviceAddr = node.buttonDeviceAddr;
810
+ const oldChannel = node.buttonChannel;
811
+ node.buttonDeviceAddr = buttonEvent.deviceAddr;
812
+ node.buttonChannel = buttonEvent.channel;
813
+
814
+ // 输出调试日志,对比计算值和实际值
815
+ if (oldDeviceAddr !== buttonEvent.deviceAddr || oldChannel !== buttonEvent.channel) {
816
+ node.log(`按键事件更新LED反馈地址:计算值(设备${oldDeviceAddr} 通道${oldChannel}) → 实际值(设备${buttonEvent.deviceAddr} 通道${buttonEvent.channel})`);
817
+ }
787
818
  }
788
819
 
789
820
  // 判断按钮类型:优先使用协议解析结果,其次使用配置
@@ -1478,6 +1509,15 @@ module.exports = function(RED) {
1478
1509
  clearTimeout(meshLedDebounceTimers.get(meshAddr).timer);
1479
1510
  meshLedDebounceTimers.delete(meshAddr);
1480
1511
  }
1512
+
1513
+ // 清理meshWirelessButtons中当前节点的按键注册
1514
+ if (node.config.meshWirelessMode === true && meshWirelessButtons.has(meshAddr)) {
1515
+ meshWirelessButtons.get(meshAddr).delete(node.config.meshButtonNumber);
1516
+ // 如果该设备没有其他无线按键了,删除整个条目
1517
+ if (meshWirelessButtons.get(meshAddr).size === 0) {
1518
+ meshWirelessButtons.delete(meshAddr);
1519
+ }
1520
+ }
1481
1521
  }
1482
1522
 
1483
1523
  // 清理队列(释放内存)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-symi-modbus",
3
- "version": "2.9.1",
3
+ "version": "2.9.3",
4
4
  "description": "Node-RED Modbus节点,支持TCP/串口通信、串口自动搜索、多设备轮询、可选MQTT集成(支持纯本地模式和MQTT模式)、Home Assistant自动发现、HomeKit网桥、可视化控制看板、自定义协议转换和物理开关面板双向同步,工控机长期稳定运行",
5
5
  "main": "nodes/modbus-master.js",
6
6
  "scripts": {