node环境下使用serialport对串口设备进行轮询
上一节我们学习了node的SerialPort如何配置,本节我们从底层到前端完成一个简易串口轮询功能
底层逻辑
定义持续扫描函数scanInfinite, 启动时每隔500ms扫描所有连接的串口设备 , 得到一个串口队列ports , 扫描到ports之后 , 由于不同的系统串口驱动的名称是不一样的 , 区分mac和windows系统做好处理 , 然后把当前ports 和之前缓存的posts(即scannedSP)作比对 , 若一致 , 说明没有新连接串口 , 不管他 ; 不一致 , 说明新连接了串口 , 通过回调函数返回port信息,然后停止扫描
停止扫描的意义 : SerialPort在底层开了C++扫描进程 , 由于我们的扫描间隔是500ms , 当快速插拔时 , 可能会造成端口扫描超时 , 虽然我们设置了超时看门狗 , 但是当热插拔十分频繁 , 大量的扫描超时事件会造成栈溢出 , 使scanInfinite崩溃 . 避免崩溃的方法就是停止扫描 , 然后在前端接受的回调函数的时候强制重启scanInfinite函数
底层伪代码
const SerialPort = require('serialport')
// 扫描队列
this.scanTask = null
// 扫描超时看门狗
this.scanWatchDog = null
scanInfinite(callback) {
//扫描到的串口放在这里
const scannedSP = []
const _this = this
// 开始轮询
this.scanTask = setInterval(function() {
//SerialPort.list是SerialPort内置方法,会返回连接的串口设备数组ports
SerialPort.list(function(err, ports) {
// 查询串口
for (var i = 0; i < ports.length; i++) {
var port = ports[i]
// 就是这个串口
let validPort = false
// mac 平台 ,字符串是什么要自己去查串口驱动,这里我用的是wch公司开发的驱动
if (process.platform === 'darwin') {
//mac串口分为数据接口和管理接口和其他扩展接口,可能会有多个字符串,随便找一个就行
validPort = port.comName.search('wchusbserial') !== -1
} else {
// windows平台
validPort = port.manufacturer === 'wch.cn'
}
if (validPort) {
// 将串口加到已扫描列表中,并回调通知
const found = scannedSP.findIndex((e) => {
return e.comName === port.comName
})
if (found === -1) {
scannedSP.push(port)
if (callback) {
callback(null, port)
_this.stopScan()
}
}
}
}
})
}, 500)
}
/** 停止扫描
*/
stopScan() {
if (this.scanWatchDog) {
clearTimeout(this.scanWatchDog)
this.scanWatchDog = null
}
if (this.scanTask) {
clearInterval(this.scanTask)
this.scanTask = null
}
}
前端Native逻辑
有了串口实例之后 , 就可以连接了,连接成功之后就是通信层面的socket业务函数了,结合上一节node环境下使用serialport对硬件指令进行封装 , 我们就可以与硬件进行通信了 , 这里是伪代码 , 在项目中其实是多个文件
注意SerialPort即使在没有反复插拔的的情况下长时间运行也会有一定的几率造成扫描跑飞(原因不明 , 可能是我写的程序有溢出,也可能是时这个包没有开多线程守护) , 这里我们可以自己写一个守护 , 在前端设置一个定时器来守护这个connected , 这里port的open方法中我们回调了onSuccess函数,这个函数会为前端层返回一个connected属性 , 然后使用testSp函数进行长轮询 , 在open的状态下 , 如果7次检查不到connected属性 , 那么直接重启扫描进程 , 如果open状态下打不开串口 , 我们就可以进行变砖检测了
前端Native伪代码
scanInfinite((err , comPort)=>{
// 9600是波特率 , 如果使用232协议的话 , 衰减很低 , 可以直接上最大值230400波特率
const port = new SerialPort(comPort.comName, { baudRate: 9600 })
//在全局保存port变量
SP._port=port
port.on('error', (err) => {})
port.on('close', () => {})
port.on('data', (data) => {})
port.on('open', () => {
if(this.callback.onSuccess) {
this.callback.onSuccess((connected=true)=>{
....
})
}else{
this.testBlock()
}
})
})
testSp = () => {
let wachDog = 0
_connTimer = setInterval(() => {
let {connected} = this.state
if (!connected) {
// 7次长轮询失败,说明底层串口扫描已经崩了
if (!SP._port) {
wachDog++;
console.log('watchDog is sleep')
if (wachDog > 7) {
wachDog = 0
console.log('watchDog is raking')
SP.stopScan()
SP. scanInfinite(callback)
}
} else {
console.log('已经插拔过', SP._port)
}
}
},500)
}