由于使用的是独立的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
网友评论