PHP速学教程(入门到精通)
PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
在使用 php 进行串口通信,特别是与外部硬件(如树莓派上的按钮输入)交互时,一个常见且关键的需求是等待特定数据的到来。然而,如果外部设备长时间没有响应,或者数据格式不符合预期,程序可能会无限期地等待串口数据,从而导致整个进程阻塞,甚至因为达到 php 的最大执行时间而终止。
原始代码尝试通过一个 while (true) 循环结合 time() 函数来手动实现超时机制:
// ... 串口初始化代码 ... $timeoutStart = time(); $timeout = $timeoutStart + 15; // 15 秒超时 $aborted = false; while (true) { $data2 = $this->serialPort->read(); // 关键的阻塞点 if (Str::contains($data2, "Whatever I want to check for")) { // 处理数据并跳出循环 break; } // 检查是否超时 if (time() >= $timeout) { $this->serialPort->write("C,STOP "); // 发送停止命令 $aborted = true; $this->alert("vending sequence stopped"); break; // 超时跳出循环 } }
尽管代码中包含了超时检查逻辑,但实际运行中发现,即使超时时间已到,循环也可能“突然停止”或根本不执行超时逻辑。这通常是因为 $this->serialPort->read() 方法本身是一个阻塞调用,它会在没有数据到来或未检测到分隔符时无限期地等待,从而阻止了 time() 检查的执行。当 read() 方法被阻塞时,PHP 脚本会暂停,直到数据到达或达到 PHP 的 max_execution_time 限制而被系统强制终止。
要解决这个问题,我们需要理解 lepiaf\SerialPort 库的内部工作机制。通过查看其源代码,特别是 SerialPort.php 文件中的 read 方法,我们可以发现:
public function read() { $this->ensureDeviceOpen(); $chars = []; do { $char = fread($this->fd, 1); // 从文件描述符读取一个字符 // ... 其他逻辑 ... } while ($char !== $this->getParser()->getSeparator()); // 循环直到找到分隔符 return $this->getParser()->parse($chars); }
尽管 lepiaf\SerialPort 库可能将串口流设置为非阻塞模式,但其 read 方法内部的 do...while 循环却是一个阻塞点。它会不断地从串口读取单个字符,直到遇到配置的分隔符为止。这意味着,如果串口长时间没有数据,或者没有发送分隔符,这个 read 方法将永远不会返回,从而导致外部的超时逻辑无法执行。
最直接有效的解决方案是修改 lepiaf\SerialPort 库的 read 方法,使其能够接受一个超时参数,并在指定时间内未接收到分隔符时返回。
请编辑 vendor/lepiaf/serialport/src/lepiaf/SerialPort/SerialPort.php 文件,找到 read 方法(通常在第 107 行左右),并将其修改为以下形式:
public function read($maxElapsed = 'infinite') { $this->ensureDeviceOpen(); $chars = []; // 计算超时时间点,使用 microtime(true) 获取微秒级时间戳 $timeout = $maxElapsed == 'infinite' ? 1.7976931348623E+308 : (microtime(true) + $maxElapsed); do { $char = fread($this->fd, 1); // 尝试读取一个字符 if ($char === '') { // 如果没有读取到字符(非阻塞模式下) if (microtime(true) > $timeout) { return false; // 超时,返回 false } usleep(100); // 短暂休眠,避免 CPU 占用过高 continue; // 继续循环等待 } $chars[] = $char; // 读取到字符,添加到缓冲区 } while ($char !== $this->getParser()->getSeparator()); // 循环直到找到分隔符 return $this->getParser()->parse($chars); // 解析并返回数据 }
修改说明:
修改库文件后,您就可以在自己的应用代码中调用带有超时参数的 read 方法,并根据其返回值进行相应的处理:
// ... 串口初始化代码 ... // 尝试读取串口数据,设置 15 秒的超时时间 $data2 = $this->serialPort->read(15); if ($data2 === false) { // 发生了超时,可以执行相应的错误处理或回滚操作 $this->serialPort->write("C,STOP "); // 发送停止命令 $aborted = true; $this->alert("vending sequence stopped due to timeout"); // 这里可以进行 API 调用来回滚之前的操作 // 例如:$this->apiClient->revertSomeAction(); } elseif (Str::contains($data2, "Whatever I want to check for")) { // 成功读取到数据,并且包含了期望的字符串 // 处理数据并继续流程 $this->alert("Received expected data: " . $data2); } else { // 成功读取到数据,但未包含期望的字符串 $this->alert("Received data, but not expected: " . $data2); // 可以选择继续等待或采取其他措施 }
通过这种方式,您的应用程序可以更健壮地处理串口通信,避免因外部设备无响应而导致的程序阻塞。
通过上述修改和实践,您可以在 PHP 应用中实现一个可靠的串口读取超时机制,有效提升系统的稳定性和用户体验。
php免费学习视频:立即学习
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!
已抢18329个
抢已抢3151个
抢已抢3366个
抢已抢5529个
抢已抢5111个
抢已抢35500个
抢