Skip to content

Commit

Permalink
✨ 串口库支持直接的字符串 IO,并增加 Python 支持
Browse files Browse the repository at this point in the history
  • Loading branch information
zhaoxi-scut authored and TooPretty0108 committed Nov 24, 2024
1 parent 446a55f commit 37f905c
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 25 deletions.
3 changes: 3 additions & 0 deletions doc/tools/pyrmvl_fns.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ rm::Timer reset "" None
rm::Timer now "" [[Current_Time]]
rm::ImuData write output_file,datas None
rm::ImuData read input_file,datas None
rm::SerialPort SerialPort device[,mode] [[con]]
rm::SerialPort read "" res,data
rm::SerialPort write data res
rm::PipeServer PipeServer name [[con]]
rm::PipeServer read "" res,data
rm::PipeServer write data res
Expand Down
48 changes: 34 additions & 14 deletions modules/core/include/rmvl/core/io.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,29 +144,31 @@ void readCorners(std::istream &in, std::vector<std::vector<std::array<float, 2>>
enum class BaudRate : uint8_t
{
BR_1200, //!< 波特率 1200
BR_2400, //!< 波特率 2400
BR_4800, //!< 波特率 4800
BR_9600, //!< 波特率 9600
BR_19200, //!< 波特率 19200
BR_38400, //!< 波特率 38400
BR_57600, //!< 波特率 57600
BR_115200, //!< 波特率 115200
};

//! 串口数据读取模式
enum class SPReadMode : uint8_t
enum class SerialReadMode : uint8_t
{
BLOCK, //!< 阻塞模式,即读取数据时会一直等待直到有数据到来
NONBLOCK //!< 非阻塞模式,即读取数据时不会等待,如果没有数据到来则立即返回 `-1`
};

//! 串口通信模式
struct RMVL_EXPORTS SerialPortMode
struct RMVL_EXPORTS_W_AG SerialPortMode
{
BaudRate baud_rate{BaudRate::BR_115200}; //!< 波特率
SPReadMode read_mode{SPReadMode::BLOCK}; //!< 读取模式
RMVL_W_RW BaudRate baud_rate{BaudRate::BR_115200}; //!< 波特率
RMVL_W_RW SerialReadMode read_mode{SerialReadMode::BLOCK}; //!< 读取模式
};

//! 串行接口通信库
class RMVL_EXPORTS SerialPort
class RMVL_EXPORTS_W SerialPort
{
RMVL_IMPL;

Expand All @@ -177,7 +179,7 @@ class RMVL_EXPORTS SerialPort
* @param[in] device 设备名
* @param[in] mode 串口通信模式
*/
SerialPort(std::string_view device, SerialPortMode mode = {});
RMVL_W SerialPort(std::string_view device, SerialPortMode mode = {});

SerialPort(const SerialPort &) = delete;
SerialPort(SerialPort &&) = default;
Expand All @@ -199,7 +201,7 @@ class RMVL_EXPORTS SerialPort
{
bool retval{};
constexpr int LENGTH = 512, SIZE = sizeof(Tp);
unsigned char buffer[LENGTH] = {0};
unsigned char buffer[LENGTH]{};
auto len_result = fdread(buffer, LENGTH);
for (long int i = 0; (i + SIZE + 1) < len_result; i++)
if (buffer[i] == head_flag && buffer[i + SIZE + 1] == tail_flag)
Expand All @@ -222,19 +224,27 @@ class RMVL_EXPORTS SerialPort
inline bool read(Tp &data)
{
bool retval{};
constexpr int LENGTH = 512, SIZE = sizeof(Tp);
unsigned char buffer[LENGTH] = {0};
auto len_result = fdread(buffer, LENGTH);
if (len_result >= SIZE)
constexpr int MAX_LENGTH = 512, MAX_READ_DST = sizeof(Tp);
char buffer[MAX_LENGTH]{};
auto len_result = fdread(buffer, MAX_LENGTH);
if (len_result > 0 && len_result <= MAX_READ_DST)
{
auto p = std::memcpy(&data, buffer, SIZE);
auto p = std::memcpy(&data, buffer, len_result);
if (p == &data)
retval = true;
}
return retval;
}

template <typename Tp, typename Enable = std::enable_if_t<std::is_aggregate_v<Tp>>>
/**
* @brief 不带头尾标志的数据读取,从串口读取字符串
*
* @param[out] data 读取的字符串
* @return 是否读取成功
*/
bool read(std::string &data);

template <typename Tp, typename Enable = std::enable_if_t<std::is_aggregate_v<Tp> || std::is_same_v<Tp, std::string>>>
inline SerialPort &operator>>(Tp &data) { return (this->read(data), *this); }

/**
Expand All @@ -248,12 +258,22 @@ class RMVL_EXPORTS SerialPort
template <typename Tp, typename Enable = std::enable_if_t<std::is_aggregate_v<Tp>>>
inline bool write(const Tp &data) { return (sizeof(data) == fdwrite(&data, sizeof(data))); }

template <typename Tp, typename Enable = std::enable_if_t<std::is_aggregate_v<Tp>>>
/**
* @brief 写入字符串到串口
*
* @param[in] data 待写入的字符串
* @return 是否写入成功
*/
inline bool write(std::string_view data) { return fdwrite(data.data(), data.length()) > 0; }

template <typename Tp, typename Enable = std::enable_if_t<std::is_aggregate_v<Tp> || std::is_same_v<Tp, std::string_view>>>
inline SerialPort &operator<<(const Tp &data) { return (this->write(data), *this); }

//! 串口是否打开
bool isOpened() const;

RMVL_W_SUBST("Serial")

private:
//! 写入数据(基于文件描述符)
long int fdwrite(const void *data, size_t len);
Expand Down
4 changes: 2 additions & 2 deletions modules/core/include/rmvl/core/version.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
#include "rmvldef.hpp"

#define RMVL_VERSION_MAJOR 2
#define RMVL_VERSION_MINOR 1
#define RMVL_VERSION_MINOR 2
#define RMVL_VERSION_PATCH 0
#define RMVL_VERSION_STATUS ""
#define RMVL_VERSION_STATUS "-dev"

#define RMVLAUX_STR_EXP(__A) #__A
#define RMVLAUX_STR(__A) RMVLAUX_STR_EXP(__A)
Expand Down
12 changes: 12 additions & 0 deletions modules/core/misc/python.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"Serial": {
"bind": [
".def(\"read\", [](SerialPort &s) { std::string data; return std::make_tuple(s.read(data), data); })",
".def(\"write\", [](SerialPort &s, std::string_view data) { return s.write(data); })"
],
"pyi": [
"def read(self) -> Tuple[bool, str]: ...",
"def write(self, data: str) -> bool: ..."
]
}
}
45 changes: 36 additions & 9 deletions modules/core/src/serial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,27 +31,39 @@ static unsigned int getBaudRate(BaudRate baud_rate)
switch (baud_rate)
{
case BaudRate::BR_1200:
return 1200;
return CBR_1200;
case BaudRate::BR_2400:
return CBR_2400;
case BaudRate::BR_4800:
return 4800;
return CBR_4800;
case BaudRate::BR_9600:
return 9600;
return CBR_9600;
case BaudRate::BR_19200:
return CBR_19200;
case BaudRate::BR_38400:
return CBR_38400;
case BaudRate::BR_57600:
return 57600;
return CBR_57600;
case BaudRate::BR_115200:
return 115200;
return CBR_115200;
default:
return 115200;
return CBR_115200;
}
#else
switch (baud_rate)
{
case BaudRate::BR_1200:
return B1200;
case BaudRate::BR_2400:
return B2400;
case BaudRate::BR_4800:
return B4800;
case BaudRate::BR_9600:
return B9600;
case BaudRate::BR_19200:
return B19200;
case BaudRate::BR_38400:
return B38400;
case BaudRate::BR_57600:
return B57600;
case BaudRate::BR_115200:
Expand All @@ -64,6 +76,21 @@ static unsigned int getBaudRate(BaudRate baud_rate)

SerialPort::SerialPort(std::string_view device, SerialPortMode mode) : _impl(new Impl(device, mode)) {}
bool SerialPort::isOpened() const { return _impl->isOpened(); }

bool SerialPort::read(std::string &data)
{
bool retval{};
constexpr int MAX_LENGTH{256};
char buffer[MAX_LENGTH]{};
auto len_result = fdread(buffer, MAX_LENGTH);
if (len_result > 0)
{
data.assign(buffer, len_result);
retval = true;
}
return retval;
}

long int SerialPort::fdwrite(const void *data, size_t length) { return _impl->fdwrite(data, length); }
long int SerialPort::fdread(void *data, size_t len) { return _impl->fdread(data, len); }

Expand All @@ -87,7 +114,7 @@ void SerialPort::Impl::open()
}

COMMTIMEOUTS timeouts{};
if (_mode.read_mode == SPReadMode::BLOCK)
if (_mode.read_mode == SerialReadMode::BLOCK)
{
timeouts.ReadIntervalTimeout = 0;
timeouts.ReadTotalTimeoutConstant = 0;
Expand Down Expand Up @@ -179,7 +206,7 @@ void SerialPort::Impl::open()
_is_open = false;
return;
}
if (_mode.read_mode == SPReadMode::BLOCK)
if (_mode.read_mode == SerialReadMode::BLOCK)
{
// 清除 O_NDELAY 标志,设置为阻塞模式
int flags = fcntl(_fd, F_GETFL, 0);
Expand All @@ -200,7 +227,7 @@ void SerialPort::Impl::open()
option.c_cflag |= CS8; // 8 位数据长度
option.c_cflag &= ~CSTOPB; // 1 位停止位
option.c_cc[VTIME] = 0;
option.c_cc[VMIN] = _mode.read_mode == SPReadMode::BLOCK ? 1 : 0;
option.c_cc[VMIN] = _mode.read_mode == SerialReadMode::BLOCK ? 1 : 0;
cfsetspeed(&option, getBaudRate(_mode.baud_rate)); // 设置输入波特率

// 设置新属性,TCSANOW:所有改变立即生效
Expand Down

0 comments on commit 37f905c

Please sign in to comment.