【备忘】关于php dio 读取串口数据时间长的问题分析

文章背景

最近要做一个电子秤数据读取功能,php有扩展php_dio.dll可以实现。

在网上参考相关代码之后遇到了各种问题,一一排除后最终可以读取电子称的数据,但是总是不顺利,最后调试代码发现dio_read方法读取数据时消耗了6-8秒,因为我的电子称连接的是收银台,这个时间是不能接受的,网上也没查到dio_read读取时间长的文章,而且相关文章少之又少,最后只能自己一步一步分析代码,断点调试,最后发现定位倒dio_read函数时间太长。

定位问题

使用日志逐行调试,最终定位在dio_read函数上。

分析问题

网上查阅各种文章均无收获,最后查阅dio_read官方文档,粗略阅读,未发现解决办法,无奈之下只得沉下心来在代码中查看dio_read函数的具体代码,代码包含了两个参数,其中第2个参数是$len,表示读取字节长度,默认1k,猜测改变该参数可能会发生变化。

发现问题

修改参数$len为512之后,查看调试日志,并无明显变化,dio_read还是在6秒,随后修改为128,发现了变化,日志中$shuju变量数据变少了,随后再次修改为32,变化非常明显,从dio_read函数读出的重量数据是被拼接了2次的数据,至此我猜测,默认$len参数是1kb,那么dio_read要把数据读取并凑够1024b才返回,出现6秒的时间则是重复读取并拼接的原因。随后再把数字改为16b,则日志显示数据简洁,从电子称读取的数据很干净,并无多次拼接现象。

处理问题

最大化提升效率,则把$len参数值修改为16,不能再小了,会截断数据出现乱码。

优化代码

<?php
/*
 * description:
 * author:wh
 * email:
 * createTime:{2023/5/4} {17:27} 
 */


//header('Content-Type: text/event-stream'); // 以事件流的形式告知浏览器进行显示
//header('Cache-Control: no-cache'); // 告知浏览器不进行缓存
//header('X-Accel-Buffering: no'); // 关闭加速缓冲

// 设置脚本运行时间,为0代表无限时
set_time_limit(0);
try {

    //调用
    cron();

}catch (\Exception $e){
    inlog('error: 系统异常.'.$e->getTrace(),$e->getTraceAsString());
}


//window系统开机执行此文件
//循环读取到数据之后实时上传至云端
//云端界面读取数据显示
function cron(){

    inlog(date('Y-m-d H:i:s'),'a');
    //读取串口数据前,确保串口不被占用,如果出现权限拒绝,多半是被其它应用占用了
    //处理方案:
    //找到占用com串口的应用,找到应用服务商,关闭串口占用服务,运行代码读取串口数据


    $shuju = null;
    // 定义com口为com3(可以修改),波特率为115200(可修改)
    exec('mode COM2: baud=9600 data=8 stop=1 parity=n xon=on');

    // 打开串口
    $ck = dio_open('COM2:', O_RDWR);
    inlog(date('Y-m-d H:i:s'),'b');
    // 如果打开串口失败,停止脚本,并输出“打开串口COM3失败”;
    if(!$ck){
        inlog('打开串口COM2失败');
        return '打开串口COM2失败';
    }

    //正常来说这里可以顺利读取数据,但也可能出现一直读取不到数据并且一直循环,
    //而外部还是每N秒访问一次该文件,这样就会逐渐增加系统资源消耗,直到系统卡死
    $shuju = null;
    while(true)
    {
       
        inlog(date('Y-m-d H:i:s'),'c');

        //读取串口并将读取到的数据赋值给变量‘$shuju’;
        $shuju = dio_read($ck,256);

        inlog(date('Y-m-d H:i:s'),'d');
        //向串口发送数据(本人猜测这里只是为了让php_read凑够$len参数的字节数,因为不凑够则会一直执行)
        //if($shuju != null){
        //    //如果接收到了数据,就向串口写回去
        //    dio_write ($ck, $shuju);
        //}


        inlog('上报重量开始',$shuju);


        $url = 'http://xbcstore.playone.cn/index/checkstand/cominfo';
        curl_post($url,[
            'com_data'=>isset($shuju)?$shuju:'no_data'
        ]);

        //ob_flush();
        //flush();
        sleep(1);
        //ob_end_flush();
    }




    //反向字符串
    //echo strrev($shuju);

    //关闭串口
    //dio_close($ck);

    //return '重量数据实时读取中,请勿关闭本界面!!!';
}

function curl_post( $url, $postdata) {

    $timeout = 5;
    $connect_timeout = 4;
    $set_time_limit = 10;
    //if($timeout + $connect_timeout < $set_time_limit) throw new \Exception('脚本超时值必须大于等于连接超时与请求处理超时之和');
    //set_time_limit($set_time_limit);
    $header = array(
        'Accept: application/json',
    );

    //初始化
    $curl = curl_init();
    //设置抓取的url
    curl_setopt($curl, CURLOPT_URL, $url);
    //设置头文件的信息作为数据流输出
    curl_setopt($curl, CURLOPT_HEADER, 0);
    //设置获取的信息以文件流的形式返回,而不是直接输出。
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    // 超时设置
    curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
    //发起连接前等待的时间,如果设置为0,则无限等待。
    curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $connect_timeout);

    // 超时设置,以毫秒为单位
    // curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);

    // 设置请求头
    curl_setopt($curl, CURLOPT_HTTPHEADER, $header);

    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE );
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE );

    //设置post方式提交
    curl_setopt($curl, CURLOPT_POST, 1);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $postdata);
    //执行命令
    $data = curl_exec($curl);

    //上报结果
    inlog(json_encode($data,JSON_UNESCAPED_UNICODE));
    // 显示错误信息
    if (curl_error($curl)) {

        //inlog('code:'.curl_errno($curl).'. msg:'.curl_error($curl));
        //返回错误码
        //return ['code'=>curl_errno($curl), 'msg'=>curl_error($curl)];
    } else {
        //关闭句柄
        curl_close($curl);
        // 返回的内容
        //inlog('code:200'.'. msg:ok');

    }
}

function inlog($txt,$data=''){

    $date = date('Y-m-d_H');
    $filepath = "./log/";
    if(!file_exists($filepath)){
        mkdir($filepath,0777,true);
    }
    $file_name = "log_{$date}.txt";


    file_put_contents($filepath.$file_name, "\n".date('Ymd H:i:s').' '.$txt.' | '.$data, FILE_APPEND);

}

总结

dio_read读取时间长短取决于$len参数,同时每个电子称的数据量不一样,需要读取一次打印在日志里,根据数据量多少来定$len的大小,我的数据量在16b的样子最合适,而不是函数默认的1kb。

此代码复制可用,但最终效果需要自己测试并修改。

作为一个急躁的业余码农,很难潜心研究一样东西,不知道什么时候才能不这样。

END

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SDL大华

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值