Merge pull request #17 from westonrobot/devel

async_port: updated latest async_port impl from wrp_sdk
This commit is contained in:
Du Ruixiang
2022-02-09 10:49:05 +08:00
committed by GitHub
9 changed files with 93 additions and 108 deletions

View File

@@ -76,7 +76,7 @@ int main(int argc, char **argv) {
while (true) { while (true) {
// motion control // motion control
std::cout << "Setting motion command: 0.0, 0,0, 0.8" << std::endl; std::cout << "Setting motion command: 0.0, 0,0, 0.8" << std::endl;
scout->SetMotionCommand(0.0, 0.0, 0.8); scout->SetMotionCommand(0.0, 0.3, 0.0);
// get robot state // get robot state
auto state = scout->GetRobotState(); auto state = scout->GetRobotState();

View File

@@ -59,7 +59,7 @@ int main(int argc, char **argv) {
while (true) { while (true) {
// motion control // motion control
std::cout << "Motor: 1.0, 0" << std::endl; std::cout << "Motor: 1.0, 0" << std::endl;
tracer->SetMotionCommand(1.0, 0.0); tracer->SetMotionCommand(0.0, 0.0);
// get robot state // get robot state
auto state = tracer->GetRobotState(); auto state = tracer->GetRobotState();

View File

@@ -16,34 +16,53 @@
#include <linux/can.h> #include <linux/can.h>
#include <atomic>
#include <memory> #include <memory>
#include <thread>
#include <functional>
#include "asio.hpp"
#include "asio/posix/basic_stream_descriptor.hpp" #include "asio/posix/basic_stream_descriptor.hpp"
#include "ugv_sdk/details/async_port/async_port_base.hpp"
namespace westonrobot { namespace westonrobot {
class AsyncCAN : public AsyncPortBase, class AsyncCAN : public std::enable_shared_from_this<AsyncCAN> {
public std::enable_shared_from_this<AsyncCAN> {
public: public:
using ReceiveCallback = std::function<void(can_frame *rx_frame)>; using ReceiveCallback = std::function<void(can_frame *rx_frame)>;
public: public:
AsyncCAN(std::string can_port = "can0"); AsyncCAN(std::string can_port = "can0");
~AsyncCAN();
void StopService() override; // do not allow copy
AsyncCAN(const AsyncCAN &) = delete;
AsyncCAN &operator=(const AsyncCAN &) = delete;
// Public API
bool Open();
void Close();
bool IsOpened() const;
void SetReceiveCallback(ReceiveCallback cb) { rcv_cb_ = cb; } void SetReceiveCallback(ReceiveCallback cb) { rcv_cb_ = cb; }
void SendFrame(const struct can_frame &frame); void SendFrame(const struct can_frame &frame);
private: private:
std::string port_;
std::atomic<bool> port_opened_{false};
#if ASIO_VERSION < 101200L
asio::io_service io_context_;
#else
asio::io_context io_context_;
#endif
std::thread io_thread_;
int can_fd_; int can_fd_;
asio::posix::basic_stream_descriptor<> socketcan_stream_; asio::posix::basic_stream_descriptor<> socketcan_stream_;
struct can_frame rcv_frame_; struct can_frame rcv_frame_;
ReceiveCallback rcv_cb_ = nullptr; ReceiveCallback rcv_cb_ = nullptr;
bool SetupPort();
void DefaultReceiveCallback(can_frame *rx_frame); void DefaultReceiveCallback(can_frame *rx_frame);
void ReadFromPort(struct can_frame &rec_frame, void ReadFromPort(struct can_frame &rec_frame,
asio::posix::basic_stream_descriptor<> &stream); asio::posix::basic_stream_descriptor<> &stream);

View File

@@ -1,61 +0,0 @@
/*
* async_port_base.hpp
*
* Created on: Jun 22, 2021 16:19
* Description:
*
* Copyright (c) 2021 Weston Robot Pte. Ltd.
*/
#ifndef ASYNC_PORT_BASE_HPP
#define ASYNC_PORT_BASE_HPP
#include <thread>
#include <mutex>
#include <functional>
#include <iostream>
#include "asio.hpp"
namespace westonrobot {
class AsyncPortBase {
public:
AsyncPortBase(std::string port) : port_(port) {}
virtual ~AsyncPortBase(){};
// do not allow copy
AsyncPortBase() = delete;
AsyncPortBase(const AsyncPortBase& other) = delete;
virtual bool IsOpened() const { return port_opened_; }
bool StartListening() {
if (SetupPort()) {
io_thread_ = std::thread([this]() { io_context_.run(); });
return true;
}
std::cerr
<< "[ERROR] Failed to setup port, please check if specified port exits "
"or if you have proper permissions to access it"
<< std::endl;
return false;
};
virtual void StopService() {}
protected:
std::string port_;
bool port_opened_ = false;
#if ASIO_VERSION < 101200L
asio::io_service io_context_;
#else
asio::io_context io_context_;
#endif
std::thread io_thread_;
virtual bool SetupPort() = 0;
};
} // namespace westonrobot
#endif /* ASYNC_PORT_BASE_HPP */

View File

@@ -14,48 +14,63 @@
#include <memory> #include <memory>
#include <array> #include <array>
#include <mutex> #include <mutex>
#include <thread>
#include <functional>
#include "asio.hpp"
#include "ugv_sdk/details/async_port/async_port_base.hpp"
#include "ugv_sdk/details/async_port/ring_buffer.hpp" #include "ugv_sdk/details/async_port/ring_buffer.hpp"
namespace westonrobot { namespace westonrobot {
class AsyncSerial : public AsyncPortBase, class AsyncSerial : public std::enable_shared_from_this<AsyncSerial> {
public std::enable_shared_from_this<AsyncSerial> {
public: public:
using ReceiveCallback = using ReceiveCallback =
std::function<void(uint8_t *data, const size_t bufsize, size_t len)>; std::function<void(uint8_t *data, const size_t bufsize, size_t len)>;
public: public:
AsyncSerial(std::string port_name, uint32_t baud_rate = 115200); AsyncSerial(std::string port_name, uint32_t baud_rate = 115200);
~AsyncSerial();
void StopService() override; // do not allow copy
AsyncSerial(const AsyncSerial &) = delete;
AsyncSerial &operator=(const AsyncSerial &) = delete;
bool IsOpened() const override { return serial_port_.is_open(); } // Public API
void SetBaudRate(unsigned baudrate);
void SetHardwareFlowControl(bool enabled);
bool Open();
void Close();
bool IsOpened() const;
void SetReceiveCallback(ReceiveCallback cb) { rcv_cb_ = cb; } void SetReceiveCallback(ReceiveCallback cb) { rcv_cb_ = cb; }
void SendBytes(const uint8_t *bytes, size_t length); void SendBytes(const uint8_t *bytes, size_t length);
void SetBaudRate(unsigned baudrate);
void SetHardwareFlowControl(bool enabled) { hwflow_ = enabled; }
private: private:
std::string port_;
bool port_opened_ = false;
#if ASIO_VERSION < 101200L
asio::io_service io_context_;
#else
asio::io_context io_context_;
#endif
std::thread io_thread_;
// serial port
asio::serial_port serial_port_; asio::serial_port serial_port_;
uint32_t baud_rate_ = 115200; uint32_t baud_rate_ = 115200;
bool hwflow_ = false; bool hwflow_ = false;
ReceiveCallback rcv_cb_ = nullptr; ReceiveCallback rcv_cb_ = nullptr;
// tx/rx buffering // tx/rx buffering
static constexpr uint32_t rxtx_buffer_size = 1024 * 8; static constexpr uint32_t rxtx_buffer_size = 1024 * 8;
// rx buffer
std::array<uint8_t, rxtx_buffer_size> rx_buf_; std::array<uint8_t, rxtx_buffer_size> rx_buf_;
// tx buffer
uint8_t tx_buf_[rxtx_buffer_size]; uint8_t tx_buf_[rxtx_buffer_size];
RingBuffer<uint8_t, rxtx_buffer_size> tx_rbuf_; RingBuffer<uint8_t, rxtx_buffer_size> tx_rbuf_;
std::recursive_mutex tx_mutex_; std::recursive_mutex tx_mutex_;
bool tx_in_progress_ = false; bool tx_in_progress_ = false;
bool SetupPort();
void DefaultReceiveCallback(uint8_t *data, const size_t bufsize, size_t len); void DefaultReceiveCallback(uint8_t *data, const size_t bufsize, size_t len);
void ReadFromPort(); void ReadFromPort();
void WriteToPort(bool check_if_busy); void WriteToPort(bool check_if_busy);

View File

@@ -15,6 +15,7 @@
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#include <atomic> #include <atomic>
#include <iostream>
#include "ugv_sdk/details/async_port/async_can.hpp" #include "ugv_sdk/details/async_port/async_can.hpp"
#include "ugv_sdk/details/interface/robot_common_interface.hpp" #include "ugv_sdk/details/interface/robot_common_interface.hpp"
@@ -60,7 +61,7 @@ class AgilexBase : public RobotCommonInterface {
// must be called at a frequency >= 50Hz // must be called at a frequency >= 50Hz
void SendMotionCommand(double linear_vel, double angular_vel, void SendMotionCommand(double linear_vel, double angular_vel,
double lateral_vel, double steering_angle) { double lateral_vel, double steering_angle) {
if (can_connected_) { if (can_->IsOpened()) {
// motion control message // motion control message
AgxMessage msg; AgxMessage msg;
if (parser_.GetParserProtocolVersion() == ProtocolVersion::AGX_V1) { if (parser_.GetParserProtocolVersion() == ProtocolVersion::AGX_V1) {
@@ -94,7 +95,7 @@ class AgilexBase : public RobotCommonInterface {
// one-shot light command // one-shot light command
void SendLightCommand(AgxLightMode front_mode, uint8_t front_custom_value, void SendLightCommand(AgxLightMode front_mode, uint8_t front_custom_value,
AgxLightMode rear_mode, uint8_t rear_custom_value) { AgxLightMode rear_mode, uint8_t rear_custom_value) {
if (can_connected_) { if (can_->IsOpened()) {
AgxMessage msg; AgxMessage msg;
msg.type = AgxMsgLightCommand; msg.type = AgxMsgLightCommand;
msg.body.light_command_msg.enable_cmd_ctrl = true; msg.body.light_command_msg.enable_cmd_ctrl = true;
@@ -110,7 +111,7 @@ class AgilexBase : public RobotCommonInterface {
} }
void DisableLightControl() { void DisableLightControl() {
if (can_connected_) { if (can_->IsOpened()) {
AgxMessage msg; AgxMessage msg;
msg.type = AgxMsgLightCommand; msg.type = AgxMsgLightCommand;
@@ -124,7 +125,7 @@ class AgilexBase : public RobotCommonInterface {
// motion mode change // motion mode change
void SetMotionMode(uint8_t mode) { void SetMotionMode(uint8_t mode) {
if (can_connected_) { if (can_->IsOpened()) {
AgxMessage msg; AgxMessage msg;
msg.type = AgxMsgSetMotionModeCommand; msg.type = AgxMsgSetMotionModeCommand;
msg.body.motion_mode_msg.motion_mode = mode; msg.body.motion_mode_msg.motion_mode = mode;
@@ -169,7 +170,6 @@ class AgilexBase : public RobotCommonInterface {
/* feedback group 3: common sensor */ /* feedback group 3: common sensor */
// communication interface // communication interface
bool can_connected_ = false;
std::shared_ptr<AsyncCAN> can_; std::shared_ptr<AsyncCAN> can_;
// connect to roboot from CAN or serial // connect to roboot from CAN or serial
@@ -177,12 +177,11 @@ class AgilexBase : public RobotCommonInterface {
bool ConnectPort(std::string dev_name, CANFrameRxCallback cb) { bool ConnectPort(std::string dev_name, CANFrameRxCallback cb) {
can_ = std::make_shared<AsyncCAN>(dev_name); can_ = std::make_shared<AsyncCAN>(dev_name);
can_->SetReceiveCallback(cb); can_->SetReceiveCallback(cb);
can_connected_ = can_->StartListening(); return can_->Open();
return can_connected_;
} }
void DisconnectPort() { void DisconnectPort() {
if (can_connected_) can_->StopService(); if (can_->IsOpened()) can_->Close();
} }
void SetBrakeMode(AgxBrakeMode mode) { void SetBrakeMode(AgxBrakeMode mode) {

View File

@@ -20,9 +20,11 @@
namespace westonrobot { namespace westonrobot {
AsyncCAN::AsyncCAN(std::string can_port) AsyncCAN::AsyncCAN(std::string can_port)
: AsyncPortBase(can_port), socketcan_stream_(io_context_) {} : port_(can_port), socketcan_stream_(io_context_) {}
bool AsyncCAN::SetupPort() { AsyncCAN::~AsyncCAN() { Close(); }
bool AsyncCAN::Open() {
try { try {
const size_t iface_name_size = strlen(port_.c_str()) + 1; const size_t iface_name_size = strlen(port_.c_str()) + 1;
if (iface_name_size > IFNAMSIZ) return false; if (iface_name_size > IFNAMSIZ) return false;
@@ -36,7 +38,7 @@ bool AsyncCAN::SetupPort() {
const int ioctl_result = ioctl(can_fd_, SIOCGIFINDEX, &ifr); const int ioctl_result = ioctl(can_fd_, SIOCGIFINDEX, &ifr);
if (ioctl_result < 0) { if (ioctl_result < 0) {
StopService(); Close();
return false; return false;
} }
@@ -48,13 +50,14 @@ bool AsyncCAN::SetupPort() {
const int bind_result = const int bind_result =
bind(can_fd_, (struct sockaddr *)&addr, sizeof(addr)); bind(can_fd_, (struct sockaddr *)&addr, sizeof(addr));
if (bind_result < 0) { if (bind_result < 0) {
StopService(); Close();
return false; return false;
} }
port_opened_ = true; port_opened_ = true;
std::cout << "Start listening to port: " << port_ << std::endl; std::cout << "Start listening to port: " << port_ << std::endl;
} catch (std::system_error &e) { } catch (std::system_error &e) {
port_opened_ = false;
std::cout << e.what() << std::endl; std::cout << e.what() << std::endl;
return false; return false;
} }
@@ -72,11 +75,13 @@ bool AsyncCAN::SetupPort() {
std::ref(socketcan_stream_))); std::ref(socketcan_stream_)));
#endif #endif
// start io thread
io_thread_ = std::thread([this]() { io_context_.run(); });
return true; return true;
} }
void AsyncCAN::StopService() { void AsyncCAN::Close() {
// stop io thread
io_context_.stop(); io_context_.stop();
if (io_thread_.joinable()) io_thread_.join(); if (io_thread_.joinable()) io_thread_.join();
io_context_.reset(); io_context_.reset();
@@ -88,6 +93,8 @@ void AsyncCAN::StopService() {
port_opened_ = false; port_opened_ = false;
} }
bool AsyncCAN::IsOpened() const { return port_opened_; }
void AsyncCAN::DefaultReceiveCallback(can_frame *rx_frame) { void AsyncCAN::DefaultReceiveCallback(can_frame *rx_frame) {
std::cout << std::hex << rx_frame->can_id << " "; std::cout << std::hex << rx_frame->can_id << " ";
for (int i = 0; i < rx_frame->can_dlc; i++) for (int i = 0; i < rx_frame->can_dlc; i++)
@@ -102,7 +109,7 @@ void AsyncCAN::ReadFromPort(struct can_frame &rec_frame,
asio::buffer(&rec_frame, sizeof(rec_frame)), asio::buffer(&rec_frame, sizeof(rec_frame)),
[sthis](asio::error_code error, size_t bytes_transferred) { [sthis](asio::error_code error, size_t bytes_transferred) {
if (error) { if (error) {
sthis->StopService(); sthis->Close();
return; return;
} }

View File

@@ -19,15 +19,17 @@
namespace westonrobot { namespace westonrobot {
AsyncSerial::AsyncSerial(std::string port_name, uint32_t baud_rate) AsyncSerial::AsyncSerial(std::string port_name, uint32_t baud_rate)
: AsyncPortBase(port_name), : port_(port_name), baud_rate_(baud_rate), serial_port_(io_context_) {}
baud_rate_(baud_rate),
serial_port_(io_context_) {} AsyncSerial::~AsyncSerial() { Close(); }
void AsyncSerial::SetBaudRate(unsigned baudrate) { void AsyncSerial::SetBaudRate(unsigned baudrate) {
serial_port_.set_option(asio::serial_port_base::baud_rate(baudrate)); serial_port_.set_option(asio::serial_port_base::baud_rate(baudrate));
} }
bool AsyncSerial::SetupPort() { void AsyncSerial::SetHardwareFlowControl(bool enabled) { hwflow_ = enabled; }
bool AsyncSerial::Open() {
using SPB = asio::serial_port_base; using SPB = asio::serial_port_base;
try { try {
@@ -67,11 +69,13 @@ bool AsyncSerial::SetupPort() {
asio::post(io_context_, std::bind(&AsyncSerial::ReadFromPort, this)); asio::post(io_context_, std::bind(&AsyncSerial::ReadFromPort, this));
#endif #endif
// start io thread
io_thread_ = std::thread([this]() { io_context_.run(); });
return true; return true;
} }
void AsyncSerial::StopService() { void AsyncSerial::Close() {
// stop io thread
io_context_.stop(); io_context_.stop();
if (io_thread_.joinable()) io_thread_.join(); if (io_thread_.joinable()) io_thread_.join();
io_context_.reset(); io_context_.reset();
@@ -84,6 +88,8 @@ void AsyncSerial::StopService() {
port_opened_ = false; port_opened_ = false;
} }
bool AsyncSerial::IsOpened() const { return serial_port_.is_open(); }
void AsyncSerial::DefaultReceiveCallback(uint8_t *data, const size_t bufsize, void AsyncSerial::DefaultReceiveCallback(uint8_t *data, const size_t bufsize,
size_t len) {} size_t len) {}
@@ -93,7 +99,7 @@ void AsyncSerial::ReadFromPort() {
asio::buffer(rx_buf_), asio::buffer(rx_buf_),
[sthis](asio::error_code error, size_t bytes_transferred) { [sthis](asio::error_code error, size_t bytes_transferred) {
if (error) { if (error) {
sthis->StopService(); sthis->Close();
return; return;
} }
@@ -122,7 +128,7 @@ void AsyncSerial::WriteToPort(bool check_if_busy) {
asio::buffer(tx_buf_, len), asio::buffer(tx_buf_, len),
[sthis](asio::error_code error, size_t bytes_transferred) { [sthis](asio::error_code error, size_t bytes_transferred) {
if (error) { if (error) {
sthis->StopService(); sthis->Close();
return; return;
} }
std::lock_guard<std::recursive_mutex> lock(sthis->tx_mutex_); std::lock_guard<std::recursive_mutex> lock(sthis->tx_mutex_);

View File

@@ -15,7 +15,7 @@ bool ProtocolDectctor::Connect(std::string can_name) {
can_ = std::make_shared<AsyncCAN>(can_name); can_ = std::make_shared<AsyncCAN>(can_name);
can_->SetReceiveCallback( can_->SetReceiveCallback(
std::bind(&ProtocolDectctor::ParseCANFrame, this, std::placeholders::_1)); std::bind(&ProtocolDectctor::ParseCANFrame, this, std::placeholders::_1));
return can_->StartListening(); return can_->Open();
} }
ProtocolVersion ProtocolDectctor::DetectProtocolVersion(uint32_t timeout_sec) { ProtocolVersion ProtocolDectctor::DetectProtocolVersion(uint32_t timeout_sec) {