如何在Ignition中实现串口端口数据持续监听
Ignition平台串口持续监听实现方案
核心实现逻辑
Ignition对自定义线程有生命周期托管机制,自行创建的无托管常驻异步线程会在会话回收、资源卸载时被强制终止,还容易引发串口句柄泄漏,不需要自己手写循环监听逻辑,直接用平台内置的串口事件能力即可。
1. 基于内置串口事件回调实现持续监听(推荐方案)
system.serial 系列函数自带托管式事件监听能力,不需要自行维护线程循环,平台会自动在串口收到数据时触发绑定的回调函数:
- 配置代码放在窗口打开事件中执行,示例如下:
# 串口参数按实际设备配置修改 port = "COM3" baud = 9600 # 打开串口 system.serial.openSerialPort( port=port, baudRate=baud, dataBits=8, stopBits=1, parity="N" ) # 提前在窗口自定义一个名为serialRecvBuffer的属性,初始值设为空字节串/空字符串,用于处理分包 win = system.gui.getParentWindow(event) win.serialRecvBuffer = b"" # 定义数据接收回调 def onDataArrive(recvPort, recvData): # 新收到的数据拼入缓冲区 win.serialRecvBuffer += recvData # 按设备协议做帧切分,以下示例以帧尾\r\n为分割标识,按实际协议替换 while b"\r\n" in win.serialRecvBuffer: fullFrame, win.serialRecvBuffer = win.serialRecvBuffer.split(b"\r\n", 1) # 完整帧写入标签 system.tag.writeBlocking("[default]设备最新上报值", fullFrame.decode("utf-8")) # 更新UI组件 def updateUI(): win.getComponent("DataDisplay").text = fullFrame.decode("utf-8") system.util.invokeLater(updateUI) # 绑定串口接收事件,绑定完成后平台自动维持监听,无需额外写循环 system.serial.setSerialPortEventListener(port=port, listener=onDataArrive)
- 回调函数运行在平台托管的线程池内,不要在回调里写耗时超过100ms的逻辑(比如数据库慢查询、大批量标签写入),否则会堵塞其他串口事件触发。
- 禁止直接在串口回调线程里修改Swing UI组件属性,所有UI更新操作必须通过
system.util.invokeLater抛到UI线程执行,否则会随机抛出线程冲突异常,导致客户端崩溃。 - 分包缓冲区必须绑定在窗口自定义属性或者全局会话变量上,不要定义成回调内的局部变量,否则每次回调触发时缓冲区会被重置,无法拼接分多次传输的完整帧。
2. 监听进程终止逻辑(绑定窗口生命周期)
直接在窗口关闭事件中添加资源释放代码即可,不需要自行维护线程中断标志,能彻底避免窗口关闭后残留进程占用串口:
port = "COM3" # 移除串口监听回调 system.serial.removeSerialPortEventListener(port) # 关闭串口释放句柄 system.serial.closeSerialPort(port)
3. 自写异步线程方案的常见问题
- 自行通过
system.util.invokeAsynchronous创建的循环读线程属于非托管线程,Ignition在窗口卸载、会话超时回收资源时不会感知到这类线程,轻则监听随机中断,重则串口句柄泄漏,必须重启客户端才能重新打开串口。 - 用阻塞式读方法(如
system.serial.readBytes)写死循环等待数据时,无法实现优雅终止,关窗口时大概率抛出端口占用异常。
内容的提问来源于stack exchange,提问作者ss2
相关产品推荐
相关产品推荐

