You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP串口读取超时:lepiaf\SerialPort读取阻塞循环终止如何解决?

问题根源

你遇到的循环卡住是因为 lepiaf\SerialPort 库的 read() 方法默认使用阻塞模式读取串口,只要串口没有返回新数据,程序会一直停在 $data2 = $this->serialPort->read(); 这行,不会执行后续的超时判断逻辑。你原有代码里的超时判断就算被触发,也没有加break跳出循环,会继续执行死循环。

解决方案

1. 开启串口非阻塞读取

打开串口后,手动设置底层流为非阻塞模式,这样没有数据时read()会直接返回空字符串,不会卡住程序:

// 你原有打开串口的代码
$serialDevice = config('app.device_type') === 'usb' ? '/dev/ttyACM0' : '/dev/ttyAMA0';
$this->serialPort->open($serialDevice);
// 新增:设置串口流为非阻塞模式
stream_set_blocking($this->serialPort->getStream(), false);

2. 调整循环逻辑

修改循环,增加空数据判断、超时后跳出循环、以及短休眠避免CPU被空循环占满:

while (true) {
    $data2 = $this->serialPort->read();

    // 只有读到非空数据才做匹配判断
    if (!empty($data2) && Str::contains($data2, "Whatever I want to check for")) {
        // 处理数据逻辑
        break;
    }

    // 超时判断
    if (time() >= $timeout) {
        $this->serialPort->write("C,STOP\n"); // 停止售卖
        $aborted = true;
        $this->alert("vending sequence stopped");
        // 新增:超时后跳出循环
        break;
    }

    // 休眠100毫秒,避免空循环占用100% CPU
    usleep(100000);
}

3. 增加超时回滚逻辑

跳出循环后判断是否为超时终止,直接调用回滚接口即可,可以用Laravel自带的Http客户端实现:

if ($aborted) {
    // 示例:调用回滚API
    \Http::post('你的回滚接口地址', [
        'order_sn' => $this->currentOrderSn,
        'status' => 'timeout_cancel'
    ]);
    // 其他本地回滚逻辑,比如修改数据库订单状态等
}

4. 兜底异常处理

因为你用supervisord托管进程,可以在supervisord对应任务的配置中增加startsecs=30参数,限制单次任务最多运行30秒,超时后自动杀掉进程。同时可以注册信号处理函数,捕获进程被终止的信号做兜底回滚:

// 在命令逻辑开头注册信号处理函数
pcntl_signal(SIGTERM, function () {
    // 进程被强制杀死时的兜底回滚逻辑
    if (isset($this->serialPort) && $this->serialPort->isOpen()) {
        $this->serialPort->write("C,STOP\n");
    }
    // 调用回滚接口
    exit;
});

内容的提问来源于stack exchange,提问作者Robin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 01:06:03