美文网首页
串口通信boost::asio::serail_port

串口通信boost::asio::serail_port

作者: 诸事圆成 | 来源:发表于2022-10-27 22:40 被阅读0次

    由于使用的是独立的asio,没有用其他boost的模块,想实现一个超时读写不能用boost::dead_time,只好自己研究写写了,可能有问题,还带测试。

    #ifndef  ROBOTIQ_ASIO_SERIALPORT_H
    #define ROBOTIQ_ASIO_SERIALPORT_H
    
    #include "asio.hpp"
    #include "asio/serial_port.hpp"
    
    #include <thread>
    #include <chrono>
    #include <future>
    #include <memory>
    #include <iostream>
    #include <vector>
    #include <condition_variable>
    
    #include <QtDebug>
    #include <QByteArray>
    #include <QThread>
    
    #include "checksum.h"
    
    #define INPUT_SOF 0x03
    #define OUTPUT_SOF 0x01
    
    class RobotiqAsioSerialPort
    {   
        enum{RECV_BUFFER_SIZE = 128};                                       //接受缓冲区大小
        enum {READ_STATUS_SINGLE_PACKAGE_LEN = 11};   //读状态单包回复最大长度
        enum {ACTION_SIGNLE_PACKAGE_LEN = 8};                  //atcion单包回复最大长度
    
    public:  
        enum class eResultCode
        {
            eSuccess,
            eTimeOut,
            eError,
        };
    
        enum class eCommandType
        {
            eInput,   /*read status */
            eOutput, /*action */
        };
    
        RobotiqAsioSerialPort(): io_(), port_(io_), backgroud_th_() {
            recv_status_buffer_ = new char[RECV_BUFFER_SIZE];
        }
    
        RobotiqAsioSerialPort(const std::string& devname, unsigned int baud_rate,
                              asio::serial_port_base::parity opt_parity=asio::serial_port_base::parity(asio::serial_port_base::parity::none),
                              asio::serial_port_base::character_size opt_csize=asio::serial_port_base::character_size(8),
                              asio::serial_port_base::flow_control opt_flow=asio::serial_port_base::flow_control(asio::serial_port_base::flow_control::none),
                              asio::serial_port_base::stop_bits opt_stop=asio::serial_port_base::stop_bits(asio::serial_port_base::stop_bits::one))
            : io_(), port_(io_), backgroud_th_()
        {
            recv_status_buffer_ = new char[RECV_BUFFER_SIZE];
            open(devname,baud_rate,opt_parity,opt_csize,opt_flow,opt_stop);
        }
    
        ~RobotiqAsioSerialPort(){
            io_.stop();
    
            if (backgroud_th_.joinable())
                backgroud_th_.join();
        }
    
        bool open( const std::string& devname, unsigned int baud_rate,
                   asio::serial_port_base::parity opt_parity=asio::serial_port_base::parity(asio::serial_port_base::parity::none),
                   asio::serial_port_base::character_size opt_csize=asio::serial_port_base::character_size(8),
                   asio::serial_port_base::flow_control opt_flow=asio::serial_port_base::flow_control(asio::serial_port_base::flow_control::none),
                   asio::serial_port_base::stop_bits opt_stop=asio::serial_port_base::stop_bits(asio::serial_port_base::stop_bits::one)
                )
        {
            if (isOpen())
                close();
    
            try {
                port_.open(devname);
                port_.set_option(asio::serial_port_base::baud_rate(baud_rate));
                port_.set_option(opt_parity);
                port_.set_option(opt_csize);
                port_.set_option(opt_flow);
                port_.set_option(opt_stop);
    
                io_.post([this](){
                    doRead();
                });
    
                std::thread t([this](){
                    io_.run();
                });
    
                backgroud_th_.swap(t);
    
            } catch (std::system_error& ev) {
                qDebug() << "open error: " << ev.what();
                return false;
            }
    
            qDebug() << "open serail port success";
            return true;
        }
    
        bool isOpen() const
        {
            return port_.is_open();
        }
    
        bool close()
        {
            if (!isOpen())
                return false;
    
            io_.post([this](){
                doClose();
            });
            backgroud_th_.join();
            io_.reset();
    
            return true;
        }
    
        void doClose()
        {
            port_.cancel();
            port_.close();
        }
    
        size_t write(const char *data, size_t size)
        {
            return asio::write(port_, asio::buffer(data, size));
        }
    
        size_t write(const std::vector<char>& data)
        {
            return asio::write(port_, asio::buffer(&data[0], data.size()));
        }
    
    
        void doRead()
        {
            memset(tmp_read_buffer_, '\0',  sizeof(tmp_read_buffer_));
            port_.async_read_some(asio::buffer(tmp_read_buffer_), [this](const asio::error_code &ec, const size_t bytes_transferred){
                if (ec) {
                    qDebug() << "read error" << QString::fromStdString(ec.message());
                } else {
                    //  qDebug() << "current receive data: "<< QByteArray(tmp_read_buffer_, 11);
                    pushToBuffer(tmp_read_buffer_, bytes_transferred);
                    doRead();
                }
            });
        }
    
        eResultCode requestGripper(int id, std::vector<char> request, char* response, size_t len, eCommandType type = eCommandType::eInput,  int timeout = 1000)
        {
            std::lock_guard<std::mutex> lock(order_mtx_);
    
            const auto dead_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout);
            //同步写
            write(request);
    
            //在超时时间内等待回复
            while (std::chrono::steady_clock::now() <  dead_time)
            {
                std::lock_guard<std::mutex> lk(buffer_mtx_);
                if (type == eCommandType::eOutput)
                {
                    if (pickOutputResponse(id, response, len))
                    {
                        return eResultCode::eSuccess;
                    }
                }
                else
                {
                    if(pickInputResponse(id, response, len))
                    {
                        return eResultCode::eSuccess;
                    }
                }
            }
            return eResultCode::eTimeOut;
        }
    
    private:
        int pushToBuffer(char* buf, size_t len)
        {
            std::lock_guard<std::mutex> lock(buffer_mtx_);
            if (status_index >=  RECV_BUFFER_SIZE){
                status_index = 0;
                memset(recv_status_buffer_, '\0',   RECV_BUFFER_SIZE);
            }
    
            memcpy(recv_status_buffer_ + status_index, buf, len);
            status_index += len -1;
            return status_index;
        }
    
        bool pickInputResponse(int id, char *response, size_t len)
        {
            if (status_index < READ_STATUS_SINGLE_PACKAGE_LEN - 1)
            {
                return false;
            }
    
            for (int idx = 0; idx < status_index - 1; idx++)
            {
                //找数据头
                if (recv_status_buffer_[idx] == uint8_t(id) && recv_status_buffer_[idx+1] == uint8_t(INPUT_SOF))
                {
                    //长度检测-- 正确的长度是11
                    if (status_index - idx >= READ_STATUS_SINGLE_PACKAGE_LEN - 1)
                    {
                        //crc检测
                        uint8_t crc_low = recv_status_buffer_[idx + READ_STATUS_SINGLE_PACKAGE_LEN - 1];
                        uint8_t crc_high = recv_status_buffer_[idx + READ_STATUS_SINGLE_PACKAGE_LEN - 2];
                        uint8_t data[READ_STATUS_SINGLE_PACKAGE_LEN -2 ] = {0x0}; //报头+数据段
                        memcpy(data, recv_status_buffer_ + idx, READ_STATUS_SINGLE_PACKAGE_LEN - 2);
                        uint16_t crc = Checksum::crc16ForModbus(data, READ_STATUS_SINGLE_PACKAGE_LEN - 2);
                        if (uint8_t(crc) == crc_high && uint8_t(crc >>8) == crc_low)
                        {
                            //获取正确的数据包
                            memcpy(response, recv_status_buffer_ + idx, len);
                            memset(recv_status_buffer_,  0x0, READ_STATUS_SINGLE_PACKAGE_LEN);
                            status_index = 0;
                            return true;
                        }
                    }
                }
            }
    
            //找不到正确的报文则继续读串口
            return false;
        }
    
        bool pickOutputResponse(int id, char *response, size_t len)
        {
            if (status_index < ACTION_SIGNLE_PACKAGE_LEN - 1)
            {
                return false;
            }
    
            for (int idx = 0; idx < status_index - 1; idx++)
            {
                //找数据头
                if (recv_status_buffer_[idx] == uint8_t(id) && recv_status_buffer_[idx+1] == uint8_t(OUTPUT_SOF))
                {
                    //长度检测-- 正确的长度是8
                    if (status_index - idx >= ACTION_SIGNLE_PACKAGE_LEN - 1)
                    {
                        //crc检测
                        uint8_t crc_low = recv_status_buffer_[idx + ACTION_SIGNLE_PACKAGE_LEN - 1];
                        uint8_t crc_high = recv_status_buffer_[idx + ACTION_SIGNLE_PACKAGE_LEN - 2];
                        uint8_t data[ACTION_SIGNLE_PACKAGE_LEN -2 ] = {0x0}; //报头+数据段
                        memcpy(data, recv_status_buffer_ + idx, ACTION_SIGNLE_PACKAGE_LEN - 2);
                        uint16_t crc = Checksum::crc16ForModbus(data, ACTION_SIGNLE_PACKAGE_LEN - 2);
                        if (uint8_t(crc) == crc_high && uint8_t(crc >>8) == crc_low)
                        {
                            //获取正确的数据包
                            memcpy(response, recv_status_buffer_ + idx, len);
                            memset(recv_status_buffer_,  0x0, RECV_BUFFER_SIZE);
                            status_index = 0;
                            return true;
                        }
                    }
                }
            }
    
            //找不到正确的报文则继续读串口
            return false;
        }
    
    private:
        char tmp_read_buffer_[64];
        char *recv_status_buffer_ = nullptr;
        int status_index = 0;   //当前写到缓冲区的位置
    
        std::mutex buffer_mtx_;  //读写缓冲区锁
        std::mutex order_mtx_;
    
        asio::io_service io_;
        asio::serial_port port_;
        std::thread backgroud_th_;
    };
    
    #endif // ROBOTIQ_ASIO_SERIALPORT_H
    
    

    相关文章

      网友评论

          本文标题:串口通信boost::asio::serail_port

          本文链接:https://www.haomeiwen.com/subject/letqtdtx.html