PHP 串行连接读取超时
PHP Serial connection read timeout
我正在想办法解决这个问题
我有一个 'sequence' 要通过串行端口(在 RPI 上)执行。
我在 Laravel 运行 中有一个受监督的 PHP 命令连接到 MQTT 代理。
当我向该代理发送消息时,RPI 会拾取并处理它。现在,我有时间等待用户交互。这里的问题是,有时用户不与系统交互,PI 一直“等待”串行数据。当用户按下按钮时,我会得到我可以处理的串行数据。
我尝试使用 while (true) {}
循环读取串行数据,但它突然停止了。这是一些示例代码;
$configure = new TTYConfigure();
$configure->removeOption("9600");
$configure->setOption("115200");
$this->serialPort = new SerialPort(new SeparatorParser("\n"), $configure);
$serialDevice = config('app.device_type') === 'usb' ? '/dev/ttyACM0' : '/dev/ttyAMA0';
$this->serialPort->open($serialDevice);
// this is a special one, we add an timeout here of 15 seconds, to prevent that the machine would get stuck.
$timeoutStart = time();
$timeout = $timeoutStart + 15; // 15 seconds of timeout.
$aborted = false;
while (true) {
$data2 = $this->serialPort->read();
if (Str::contains($data2, "Whatever I want to check for")) {
// Process the data and get out of this loop via a 'break;' statement
}
// check if 15 seconds have passed, if so, then we want to stop the vend sequence.
if (time() >= $timeout) {
$this->serialPort->write("C,STOP\n"); // STOP vending
$aborted = true;
$this->alert("vending sequence stopped");
}
}
当我将日志置于真正的循环中时,我看到它在循环,但突然停止循环(我敢打赌这是 $data2 = $this->serialPort->read();
只是“停止”读取或继续读取串行端口。
我希望能够终止循环并执行 API 调用以恢复在该操作之前发生的一些更改。
这可能吗?如果有怎么办?
我使用的软件包:
- Laravel流明
- PhpMqtt
- lepiaf\SerialPort
如果您查看 lepiaf\SerialPort 的源代码,您会发现它将流设置为非阻塞模式,但是 read 方法执行无限循环,直到找到分隔符。这意味着它永远不会 return 除非收到分隔符,并且根据您的配置,一旦达到 php 最大执行时间,您的脚本将被终止。由于库非常简单,更好的选择是编辑 read 方法并添加超时参数。编辑文件“lepiaf/SerialPort/SerialPort.php”,向下滚动到读取方法(第107行)并更改如下:
public function read($maxElapsed = 'infinite')
{
$this->ensureDeviceOpen();
$chars = [];
$timeout = $maxElapsed == 'infinite' ? 1.7976931348623E+308 : (microtime(true) + $maxElapsed);
do {
$char = fread($this->fd, 1);
if ($char === '') {
if (microtime(true) > $timeout) return false;
usleep(100); //Why waste CPU?
continue;
}
$chars[] = $char;
} while ($char !== $this->getParser()->getSeparator());
return $this->getParser()->parse($chars);
}
然后在您的代码中将方法调用为:
$data2 = $this->serialPort->read(15);
if ($data2 === false) {
//Timeout occurred
} elseif (Str::contains($data2, "Whatever I want to check for")) {
//String found
} else {
//Data received but string not found
}
if (Str::contains($data2, "Whatever I want to check for"))
以上代码是你的罪魁祸首。
$data2 = $this->serialPort->read();
可能不会一次读取整个字符串,它会在进入读取缓冲区时提供数据。所以最好在内部缓冲区中收集数据并根据您的情况检查缓冲区。
$data2 .= $this->serialPort->read();
确保在循环之前初始化 data2。
我正在想办法解决这个问题
我有一个 'sequence' 要通过串行端口(在 RPI 上)执行。
我在 Laravel 运行 中有一个受监督的 PHP 命令连接到 MQTT 代理。
当我向该代理发送消息时,RPI 会拾取并处理它。现在,我有时间等待用户交互。这里的问题是,有时用户不与系统交互,PI 一直“等待”串行数据。当用户按下按钮时,我会得到我可以处理的串行数据。
我尝试使用 while (true) {}
循环读取串行数据,但它突然停止了。这是一些示例代码;
$configure = new TTYConfigure();
$configure->removeOption("9600");
$configure->setOption("115200");
$this->serialPort = new SerialPort(new SeparatorParser("\n"), $configure);
$serialDevice = config('app.device_type') === 'usb' ? '/dev/ttyACM0' : '/dev/ttyAMA0';
$this->serialPort->open($serialDevice);
// this is a special one, we add an timeout here of 15 seconds, to prevent that the machine would get stuck.
$timeoutStart = time();
$timeout = $timeoutStart + 15; // 15 seconds of timeout.
$aborted = false;
while (true) {
$data2 = $this->serialPort->read();
if (Str::contains($data2, "Whatever I want to check for")) {
// Process the data and get out of this loop via a 'break;' statement
}
// check if 15 seconds have passed, if so, then we want to stop the vend sequence.
if (time() >= $timeout) {
$this->serialPort->write("C,STOP\n"); // STOP vending
$aborted = true;
$this->alert("vending sequence stopped");
}
}
当我将日志置于真正的循环中时,我看到它在循环,但突然停止循环(我敢打赌这是 $data2 = $this->serialPort->read();
只是“停止”读取或继续读取串行端口。
我希望能够终止循环并执行 API 调用以恢复在该操作之前发生的一些更改。
这可能吗?如果有怎么办?
我使用的软件包:
- Laravel流明
- PhpMqtt
- lepiaf\SerialPort
如果您查看 lepiaf\SerialPort 的源代码,您会发现它将流设置为非阻塞模式,但是 read 方法执行无限循环,直到找到分隔符。这意味着它永远不会 return 除非收到分隔符,并且根据您的配置,一旦达到 php 最大执行时间,您的脚本将被终止。由于库非常简单,更好的选择是编辑 read 方法并添加超时参数。编辑文件“lepiaf/SerialPort/SerialPort.php”,向下滚动到读取方法(第107行)并更改如下:
public function read($maxElapsed = 'infinite')
{
$this->ensureDeviceOpen();
$chars = [];
$timeout = $maxElapsed == 'infinite' ? 1.7976931348623E+308 : (microtime(true) + $maxElapsed);
do {
$char = fread($this->fd, 1);
if ($char === '') {
if (microtime(true) > $timeout) return false;
usleep(100); //Why waste CPU?
continue;
}
$chars[] = $char;
} while ($char !== $this->getParser()->getSeparator());
return $this->getParser()->parse($chars);
}
然后在您的代码中将方法调用为:
$data2 = $this->serialPort->read(15);
if ($data2 === false) {
//Timeout occurred
} elseif (Str::contains($data2, "Whatever I want to check for")) {
//String found
} else {
//Data received but string not found
}
if (Str::contains($data2, "Whatever I want to check for"))
以上代码是你的罪魁祸首。
$data2 = $this->serialPort->read();
可能不会一次读取整个字符串,它会在进入读取缓冲区时提供数据。所以最好在内部缓冲区中收集数据并根据您的情况检查缓冲区。
$data2 .= $this->serialPort->read();
确保在循环之前初始化 data2。