node-red-contrib-symi-modbus 2.7.8 → 2.8.0
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 +39 -33
- package/examples/basic-flow.json +17 -6
- package/nodes/modbus-master.js +2 -2
- package/nodes/modbus-slave-switch.js +67 -154
- package/nodes/serial-port-config.js +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -267,7 +267,7 @@ node-red-restart
|
|
|
267
267
|
- 长期稳定运行,反复控制不会造成内存增加、卡顿或死机
|
|
268
268
|
|
|
269
269
|
4. **技术细节**:
|
|
270
|
-
- 写入队列间隔:
|
|
270
|
+
- 写入队列间隔:30ms(确保总线稳定)
|
|
271
271
|
- 轮询恢复时间:20ms(快速响应)
|
|
272
272
|
- 锁等待超时:100ms(快速检测异常)
|
|
273
273
|
- 队列自动处理,无需手动干预
|
|
@@ -883,38 +883,44 @@ HomeKit网桥节点无需输入消息,自动同步主站配置和状态。
|
|
|
883
883
|
|
|
884
884
|
## 版本信息
|
|
885
885
|
|
|
886
|
-
**当前版本**: v2.
|
|
887
|
-
|
|
888
|
-
### v2.
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
-
|
|
892
|
-
-
|
|
893
|
-
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
-
|
|
898
|
-
-
|
|
899
|
-
-
|
|
900
|
-
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
-
|
|
913
|
-
-
|
|
914
|
-
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
-
|
|
886
|
+
**当前版本**: v2.8.0
|
|
887
|
+
|
|
888
|
+
### v2.8.0 (2025-11-10)
|
|
889
|
+
|
|
890
|
+
**重要更新**:
|
|
891
|
+
- 优化指示灯反馈稳定性,统一队列间隔为30ms(TCP和串口)
|
|
892
|
+
- 修复高频控制场景下指示灯反馈丢失问题
|
|
893
|
+
- 提升批量控制时的总线稳定性
|
|
894
|
+
|
|
895
|
+
**队列处理机制**(全局统一队列):
|
|
896
|
+
- **全局队列**:所有从站开关节点共享同一个全局队列(由serial-port-config管理)
|
|
897
|
+
- **串行发送**:所有LED反馈按加入队列的顺序依次发送,间隔30ms(TCP和串口统一)
|
|
898
|
+
- **完整性保证**:所有LED反馈都能正确发送,不会遗漏
|
|
899
|
+
- **去重机制**:每个节点50ms内不重复发送相同状态,避免总线拥堵
|
|
900
|
+
- **性能优化**:400个按键(50个8键开关)反馈耗时约12秒(30ms×400)
|
|
901
|
+
|
|
902
|
+
**典型场景说明**:
|
|
903
|
+
1. **场景1:单个继电器控制**
|
|
904
|
+
- 按下开关 → 继电器动作 → LED反馈加入全局队列 → 30ms后发送
|
|
905
|
+
|
|
906
|
+
2. **场景2:批量控制(如17通道全开,16个继电器)**
|
|
907
|
+
- 触发17通道 → 1-16路继电器全开 → 所有绑定的LED反馈加入全局队列
|
|
908
|
+
- 假设每个继电器绑定2个开关面板 = 32个LED反馈
|
|
909
|
+
- 按加入队列的顺序依次发送,耗时约960ms(30ms×32)
|
|
910
|
+
|
|
911
|
+
3. **场景3:快速切换(如17通道全开后立即18通道全关)**
|
|
912
|
+
- 17通道触发 → 1-16路全开 → 32个LED反馈加入队列
|
|
913
|
+
- 18通道触发 → 1-16路全关 → 32个LED反馈加入队列
|
|
914
|
+
- 全局队列按顺序处理,确保所有面板最终显示正确状态
|
|
915
|
+
|
|
916
|
+
4. **场景4:双控开关(1个继电器绑定2个不同ID的开关面板)**
|
|
917
|
+
- 继电器状态变化 → 2个LED反馈加入全局队列
|
|
918
|
+
- 按顺序发送,间隔30ms,确保所有面板LED状态同步
|
|
919
|
+
|
|
920
|
+
5. **场景5:大规模部署(50个8键开关 = 400个按键)**
|
|
921
|
+
- 批量控制触发 → 所有LED反馈加入全局队列
|
|
922
|
+
- 按部署时的节点顺序依次发送,耗时约12秒(30ms×400)
|
|
923
|
+
- 确保总线稳定,不会冲突或丢失
|
|
918
924
|
|
|
919
925
|
## 许可证
|
|
920
926
|
|
package/examples/basic-flow.json
CHANGED
|
@@ -2,11 +2,22 @@
|
|
|
2
2
|
{
|
|
3
3
|
"id": "modbus_server_config_1",
|
|
4
4
|
"type": "modbus-server-config",
|
|
5
|
-
"name": "Modbus
|
|
5
|
+
"name": "Modbus服务器(TCP)",
|
|
6
6
|
"connectionType": "tcp",
|
|
7
|
-
"tcpHost": "
|
|
7
|
+
"tcpHost": "192.168.1.100",
|
|
8
8
|
"tcpPort": "502"
|
|
9
9
|
},
|
|
10
|
+
{
|
|
11
|
+
"id": "serial_port_config_1",
|
|
12
|
+
"type": "serial-port-config",
|
|
13
|
+
"name": "RS-485串口",
|
|
14
|
+
"connectionType": "serial",
|
|
15
|
+
"serialPort": "/dev/ttyUSB0",
|
|
16
|
+
"baudRate": "9600",
|
|
17
|
+
"dataBits": "8",
|
|
18
|
+
"parity": "none",
|
|
19
|
+
"stopBits": "1"
|
|
20
|
+
},
|
|
10
21
|
{
|
|
11
22
|
"id": "modbus_master_1",
|
|
12
23
|
"type": "modbus-master",
|
|
@@ -124,14 +135,14 @@
|
|
|
124
135
|
"id": "switch_node_1",
|
|
125
136
|
"type": "modbus-slave-switch",
|
|
126
137
|
"name": "面板1按键1(开关模式)",
|
|
127
|
-
"serialPortConfig": "",
|
|
138
|
+
"serialPortConfig": "serial_port_config_1",
|
|
128
139
|
"mqttServer": "",
|
|
129
140
|
"switchBrand": "symi",
|
|
130
141
|
"buttonType": "switch",
|
|
131
142
|
"switchId": "1",
|
|
132
143
|
"buttonNumber": "1",
|
|
133
144
|
"targetSlaveAddress": "10",
|
|
134
|
-
"targetCoilNumber": "
|
|
145
|
+
"targetCoilNumber": "0",
|
|
135
146
|
"x": 340,
|
|
136
147
|
"y": 300,
|
|
137
148
|
"wires": [["debug_2"]]
|
|
@@ -194,14 +205,14 @@
|
|
|
194
205
|
"id": "switch_node_2",
|
|
195
206
|
"type": "modbus-slave-switch",
|
|
196
207
|
"name": "面板1按键2(场景模式)",
|
|
197
|
-
"serialPortConfig": "",
|
|
208
|
+
"serialPortConfig": "serial_port_config_1",
|
|
198
209
|
"mqttServer": "",
|
|
199
210
|
"switchBrand": "symi",
|
|
200
211
|
"buttonType": "scene",
|
|
201
212
|
"switchId": "1",
|
|
202
213
|
"buttonNumber": "2",
|
|
203
214
|
"targetSlaveAddress": "10",
|
|
204
|
-
"targetCoilNumber": "
|
|
215
|
+
"targetCoilNumber": "1",
|
|
205
216
|
"x": 340,
|
|
206
217
|
"y": 360,
|
|
207
218
|
"wires": [["debug_3"]]
|
package/nodes/modbus-master.js
CHANGED
|
@@ -150,7 +150,7 @@ module.exports = function(RED) {
|
|
|
150
150
|
// 写入队列机制(确保所有写入操作串行执行,避免锁竞争)
|
|
151
151
|
node.writeQueue = []; // 写入队列
|
|
152
152
|
node.isProcessingWrite = false; // 是否正在处理写入队列
|
|
153
|
-
node.writeQueueInterval =
|
|
153
|
+
node.writeQueueInterval = 30; // 写入队列处理间隔(30ms,确保总线稳定,避免数据丢失)
|
|
154
154
|
|
|
155
155
|
// 定期清理机制(每小时清理一次,防止内存泄漏)
|
|
156
156
|
node.cleanupTimer = setInterval(() => {
|
|
@@ -1197,7 +1197,7 @@ module.exports = function(RED) {
|
|
|
1197
1197
|
node.warn(`队列任务失败,继续处理下一个任务: ${err.message}`);
|
|
1198
1198
|
}
|
|
1199
1199
|
|
|
1200
|
-
// 等待一段时间再处理下一个任务(
|
|
1200
|
+
// 等待一段时间再处理下一个任务(30ms间隔,确保总线稳定)
|
|
1201
1201
|
if (node.writeQueue.length > 0) {
|
|
1202
1202
|
await new Promise(resolve => setTimeout(resolve, node.writeQueueInterval));
|
|
1203
1203
|
}
|
|
@@ -379,12 +379,11 @@ module.exports = function(RED) {
|
|
|
379
379
|
node.commandQueue = [];
|
|
380
380
|
node.isProcessingCommand = false;
|
|
381
381
|
|
|
382
|
-
//
|
|
383
|
-
node.
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
node.queueTimeout = 3000;
|
|
382
|
+
// LED反馈去重:记录最后一次发送的状态和时间戳(防止重复发送)
|
|
383
|
+
node.lastSentLedState = {
|
|
384
|
+
value: null,
|
|
385
|
+
timestamp: 0
|
|
386
|
+
};
|
|
388
387
|
|
|
389
388
|
// 防死循环:记录最后一次状态变化的时间戳和值
|
|
390
389
|
node.lastStateChange = {
|
|
@@ -392,12 +391,6 @@ module.exports = function(RED) {
|
|
|
392
391
|
value: false
|
|
393
392
|
};
|
|
394
393
|
|
|
395
|
-
// 防止LED反馈重复发送:记录最后一次LED反馈的时间戳和值
|
|
396
|
-
node.lastLedFeedback = {
|
|
397
|
-
timestamp: 0,
|
|
398
|
-
value: false
|
|
399
|
-
};
|
|
400
|
-
|
|
401
394
|
// 节点初始化标志(用于静默初始化期间的警告)
|
|
402
395
|
node.isInitializing = true;
|
|
403
396
|
|
|
@@ -504,8 +497,6 @@ module.exports = function(RED) {
|
|
|
504
497
|
// 结束初始化阶段(5秒后)- 避免部署时大量LED反馈同时发送
|
|
505
498
|
setTimeout(() => {
|
|
506
499
|
node.isInitializing = false;
|
|
507
|
-
// 初始化完成后,处理积累的LED反馈队列
|
|
508
|
-
node.processLedFeedbackQueue();
|
|
509
500
|
}, 5000);
|
|
510
501
|
|
|
511
502
|
// 监听物理开关面板的按键事件
|
|
@@ -797,7 +788,8 @@ module.exports = function(RED) {
|
|
|
797
788
|
node.isProcessingCommand = false;
|
|
798
789
|
};
|
|
799
790
|
|
|
800
|
-
//
|
|
791
|
+
// 发送控制指令到物理开关面板(控制指示灯等)
|
|
792
|
+
// 直接发送到全局队列,由serial-port-config统一管理(20ms间隔串行发送)
|
|
801
793
|
node.sendCommandToPanel = function(state) {
|
|
802
794
|
// 检查连接状态
|
|
803
795
|
if (!node.serialPortConfig || !node.serialPortConfig.connection) {
|
|
@@ -808,161 +800,83 @@ module.exports = function(RED) {
|
|
|
808
800
|
return;
|
|
809
801
|
}
|
|
810
802
|
|
|
811
|
-
//
|
|
803
|
+
// 初始化期间不发送LED反馈(避免部署时大量LED同时发送)
|
|
804
|
+
if (node.isInitializing) {
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
|
|
812
808
|
const now = Date.now();
|
|
813
|
-
const timeSinceLastFeedback = now - node.lastLedFeedback.timestamp;
|
|
814
|
-
const stateChanged = (state !== node.lastLedFeedback.value);
|
|
815
809
|
|
|
816
|
-
|
|
817
|
-
|
|
810
|
+
// 防止重复发送:如果状态相同且时间间隔小于50ms,跳过
|
|
811
|
+
if (node.lastSentLedState.value === state && (now - node.lastSentLedState.timestamp) < 50) {
|
|
812
|
+
node.debug(`跳过重复LED反馈(状态未变化,间隔${now - node.lastSentLedState.timestamp}ms)`);
|
|
818
813
|
return;
|
|
819
814
|
}
|
|
820
815
|
|
|
821
|
-
//
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
816
|
+
// 更新最后发送状态
|
|
817
|
+
node.lastSentLedState.value = state;
|
|
818
|
+
node.lastSentLedState.timestamp = now;
|
|
819
|
+
|
|
820
|
+
// 构建LED反馈协议帧
|
|
821
|
+
let command;
|
|
822
|
+
|
|
823
|
+
if (node.config.buttonType === 'mesh') {
|
|
824
|
+
// Mesh模式:发送Mesh控制帧
|
|
825
|
+
command = meshProtocol.buildSwitchControlFrame(
|
|
826
|
+
node.config.meshShortAddress,
|
|
827
|
+
node.config.meshButtonNumber,
|
|
828
|
+
node.config.meshTotalButtons,
|
|
829
|
+
state,
|
|
830
|
+
node.meshCurrentStates || null
|
|
831
|
+
);
|
|
832
|
+
|
|
833
|
+
if (!command) {
|
|
834
|
+
node.error(`构建Mesh控制帧失败`);
|
|
828
835
|
return;
|
|
836
|
+
}
|
|
837
|
+
} else {
|
|
838
|
+
// RS-485模式:使用轻量级协议
|
|
839
|
+
const deviceAddr = node.buttonDeviceAddr;
|
|
840
|
+
const channel = node.buttonChannel;
|
|
841
|
+
|
|
842
|
+
// 根据按钮类型选择协议类型
|
|
843
|
+
if (node.config.buttonType === 'scene') {
|
|
844
|
+
// 场景模式:使用REPORT协议
|
|
845
|
+
command = protocol.buildSingleLightReport(
|
|
846
|
+
node.config.switchId,
|
|
847
|
+
deviceAddr,
|
|
848
|
+
channel,
|
|
849
|
+
state
|
|
850
|
+
);
|
|
829
851
|
} else {
|
|
830
|
-
//
|
|
831
|
-
|
|
832
|
-
|
|
852
|
+
// 开关模式(默认):使用SET协议
|
|
853
|
+
command = protocol.buildSingleLightCommand(
|
|
854
|
+
node.config.switchId,
|
|
855
|
+
deviceAddr,
|
|
856
|
+
channel,
|
|
857
|
+
state
|
|
858
|
+
);
|
|
833
859
|
}
|
|
834
860
|
}
|
|
835
861
|
|
|
836
|
-
//
|
|
837
|
-
node.
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
// 处理LED反馈队列(20ms间隔串行发送)
|
|
844
|
-
node.processLedFeedbackQueue = async function() {
|
|
845
|
-
if (node.isProcessingLedFeedback || node.ledFeedbackQueue.length === 0) {
|
|
846
|
-
return;
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
// 初始化期间不处理LED反馈(避免部署时大量LED同时发送)
|
|
850
|
-
if (node.isInitializing) {
|
|
851
|
-
return;
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
node.isProcessingLedFeedback = true;
|
|
855
|
-
|
|
856
|
-
// 清理过期队列项
|
|
857
|
-
const now = Date.now();
|
|
858
|
-
node.ledFeedbackQueue = node.ledFeedbackQueue.filter(item => (now - item.timestamp) < node.queueTimeout);
|
|
859
|
-
|
|
860
|
-
// 记录队列开始处理时间
|
|
861
|
-
const queueStartTime = Date.now();
|
|
862
|
-
const queueLength = node.ledFeedbackQueue.length;
|
|
863
|
-
|
|
864
|
-
while (node.ledFeedbackQueue.length > 0) {
|
|
865
|
-
const item = node.ledFeedbackQueue.shift();
|
|
866
|
-
|
|
867
|
-
// 再次检查是否过期
|
|
868
|
-
if (Date.now() - item.timestamp >= node.queueTimeout) {
|
|
869
|
-
node.warn(`丢弃过期LED反馈(${Date.now() - item.timestamp}ms)`);
|
|
870
|
-
continue;
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
const state = item.state;
|
|
874
|
-
|
|
875
|
-
try {
|
|
876
|
-
let command;
|
|
877
|
-
|
|
862
|
+
// 直接发送到全局队列(由serial-port-config统一管理,20ms间隔串行发送)
|
|
863
|
+
node.serialPortConfig.write(command, (err) => {
|
|
864
|
+
if (err) {
|
|
865
|
+
node.error(`LED反馈失败: ${err.message}`);
|
|
866
|
+
} else {
|
|
867
|
+
// 输出调试日志,确认LED反馈已发送(包含协议帧十六进制)
|
|
868
|
+
const hexStr = command.toString('hex').toUpperCase().match(/.{1,2}/g).join(' ');
|
|
878
869
|
if (node.config.buttonType === 'mesh') {
|
|
879
|
-
|
|
880
|
-
command = meshProtocol.buildSwitchControlFrame(
|
|
881
|
-
node.config.meshShortAddress,
|
|
882
|
-
node.config.meshButtonNumber,
|
|
883
|
-
node.config.meshTotalButtons,
|
|
884
|
-
state,
|
|
885
|
-
node.meshCurrentStates || null
|
|
886
|
-
);
|
|
887
|
-
|
|
888
|
-
if (!command) {
|
|
889
|
-
node.error(`构建Mesh控制帧失败`);
|
|
890
|
-
continue;
|
|
891
|
-
}
|
|
870
|
+
node.log(`Mesh LED反馈已发送:MAC=${node.config.meshMacAddress} 按钮${node.config.meshButtonNumber} = ${state ? 'ON' : 'OFF'} [${hexStr}]`);
|
|
892
871
|
} else {
|
|
893
|
-
// RS-485模式:使用轻量级协议
|
|
894
|
-
// 使用初始化时计算的deviceAddr和channel
|
|
895
|
-
// 当物理按键按下时,会更新为实际的deviceAddr和channel
|
|
896
872
|
const deviceAddr = node.buttonDeviceAddr;
|
|
897
873
|
const channel = node.buttonChannel;
|
|
898
|
-
|
|
899
|
-
// 根据按钮类型选择协议类型
|
|
900
|
-
// 开关模式:使用SET协议(0x03),面板LED需要接收SET指令
|
|
901
|
-
// 场景模式:使用REPORT协议(0x04),面板LED需要接收REPORT指令
|
|
902
|
-
if (node.config.buttonType === 'scene') {
|
|
903
|
-
// 场景模式:使用REPORT协议
|
|
904
|
-
command = protocol.buildSingleLightReport(
|
|
905
|
-
node.config.switchId, // 本地地址(面板地址)
|
|
906
|
-
deviceAddr, // 设备地址(从按键事件中获取)
|
|
907
|
-
channel, // 通道(从按键事件中获取)
|
|
908
|
-
state
|
|
909
|
-
);
|
|
910
|
-
} else {
|
|
911
|
-
// 开关模式(默认):使用SET协议
|
|
912
|
-
command = protocol.buildSingleLightCommand(
|
|
913
|
-
node.config.switchId, // 本地地址(面板地址)
|
|
914
|
-
deviceAddr, // 设备地址(从按键事件中获取)
|
|
915
|
-
channel, // 通道(从按键事件中获取)
|
|
916
|
-
state
|
|
917
|
-
);
|
|
918
|
-
}
|
|
874
|
+
node.log(`LED反馈已发送:面板${node.config.switchId} 按钮${node.config.buttonNumber} 设备${deviceAddr} 通道${channel} = ${state ? 'ON' : 'OFF'} (${node.config.buttonType === 'scene' ? 'REPORT' : 'SET'}) [${hexStr}]`);
|
|
919
875
|
}
|
|
920
|
-
|
|
921
|
-
// 发送到RS-485总线或Mesh网关(使用共享连接配置)
|
|
922
|
-
if (node.serialPortConfig) {
|
|
923
|
-
node.serialPortConfig.write(command, (err) => {
|
|
924
|
-
if (err) {
|
|
925
|
-
node.error(`LED反馈失败: ${err.message}`);
|
|
926
|
-
} else {
|
|
927
|
-
// 记录最后一次LED反馈的时间戳和值
|
|
928
|
-
node.lastLedFeedback.timestamp = Date.now();
|
|
929
|
-
node.lastLedFeedback.value = state;
|
|
930
|
-
|
|
931
|
-
// 输出调试日志,确认LED反馈已发送(包含协议帧十六进制)
|
|
932
|
-
const hexStr = command.toString('hex').toUpperCase().match(/.{1,2}/g).join(' ');
|
|
933
|
-
if (node.config.buttonType === 'mesh') {
|
|
934
|
-
node.log(`Mesh LED反馈已发送:MAC=${node.config.meshMacAddress} 按钮${node.config.meshButtonNumber} = ${state ? 'ON' : 'OFF'} [${hexStr}]`);
|
|
935
|
-
} else {
|
|
936
|
-
const deviceAddr = node.buttonDeviceAddr;
|
|
937
|
-
const channel = node.buttonChannel;
|
|
938
|
-
node.log(`LED反馈已发送:面板${node.config.switchId} 按钮${node.config.buttonNumber} 设备${deviceAddr} 通道${channel} = ${state ? 'ON' : 'OFF'} (${node.config.buttonType === 'scene' ? 'REPORT' : 'SET'}) [${hexStr}]`);
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
});
|
|
942
|
-
} else {
|
|
943
|
-
node.warn('连接未配置');
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
// 队列间隔20ms(优化速度,确保200个节点能快速反馈)
|
|
947
|
-
// 20ms × 200节点 = 4秒完成所有反馈
|
|
948
|
-
if (node.ledFeedbackQueue.length > 0) {
|
|
949
|
-
await new Promise(resolve => setTimeout(resolve, 20));
|
|
950
|
-
}
|
|
951
|
-
} catch (err) {
|
|
952
|
-
node.error(`发送LED反馈失败: ${err.message}`);
|
|
953
|
-
// LED反馈失败不中断队列,继续处理下一个
|
|
954
876
|
}
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
// 队列处理完成,输出统计信息
|
|
958
|
-
const queueDuration = Date.now() - queueStartTime;
|
|
959
|
-
if (queueLength > 1) {
|
|
960
|
-
node.debug(`LED反馈队列处理完成:${queueLength}个任务,耗时${queueDuration}ms`);
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
node.isProcessingLedFeedback = false;
|
|
877
|
+
});
|
|
964
878
|
};
|
|
965
|
-
|
|
879
|
+
|
|
966
880
|
// 更新节点状态显示
|
|
967
881
|
node.updateStatus = function() {
|
|
968
882
|
const rs485Status = node.isRs485Connected ? 'OK' : 'ERR';
|
|
@@ -1290,7 +1204,6 @@ module.exports = function(RED) {
|
|
|
1290
1204
|
|
|
1291
1205
|
// 清理队列(释放内存)
|
|
1292
1206
|
node.commandQueue = [];
|
|
1293
|
-
node.ledFeedbackQueue = [];
|
|
1294
1207
|
node.frameBuffer = Buffer.alloc(0);
|
|
1295
1208
|
|
|
1296
1209
|
// 注销RS-485数据监听器(不关闭连接,由配置节点管理)
|
|
@@ -405,9 +405,9 @@ module.exports = function(RED) {
|
|
|
405
405
|
});
|
|
406
406
|
});
|
|
407
407
|
|
|
408
|
-
// TCP写入间隔(
|
|
408
|
+
// TCP写入间隔(30ms,避免网关处理不过来)
|
|
409
409
|
if (node.writeQueue.length > 0) {
|
|
410
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
410
|
+
await new Promise(resolve => setTimeout(resolve, 30));
|
|
411
411
|
}
|
|
412
412
|
} else {
|
|
413
413
|
if (!node.connection.isOpen) {
|
|
@@ -439,9 +439,9 @@ module.exports = function(RED) {
|
|
|
439
439
|
});
|
|
440
440
|
});
|
|
441
441
|
|
|
442
|
-
// 串口写入间隔(
|
|
442
|
+
// 串口写入间隔(30ms,确保指示灯反馈稳定)
|
|
443
443
|
if (node.writeQueue.length > 0) {
|
|
444
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
444
|
+
await new Promise(resolve => setTimeout(resolve, 30));
|
|
445
445
|
}
|
|
446
446
|
}
|
|
447
447
|
} catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-symi-modbus",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.0",
|
|
4
4
|
"description": "Node-RED Modbus节点,支持TCP/串口通信、串口自动搜索、多设备轮询、可选MQTT集成(支持纯本地模式和MQTT模式)、Home Assistant自动发现、HomeKit网桥、可视化控制看板、自定义协议转换和物理开关面板双向同步,工控机长期稳定运行",
|
|
5
5
|
"main": "nodes/modbus-master.js",
|
|
6
6
|
"scripts": {
|