diff --git a/.gitignore b/.gitignore index 358b045..72e676e 100755 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # 配置文件 .vscode .idea +.cache # 编译文件 *build* diff --git a/cmake/RMVLCompilerOptions.cmake b/cmake/RMVLCompilerOptions.cmake index 41d08b8..c8ae16c 100755 --- a/cmake/RMVLCompilerOptions.cmake +++ b/cmake/RMVLCompilerOptions.cmake @@ -50,6 +50,7 @@ if(NOT HAVE_CXX17) endif() set(CMAKE_CXX_STANDARD ${max_version}) +set(CMAKE_CXX_STANDARD_REQUIRED ON) # ---------------------------------------------------------------------------- # Detect target platform architecture diff --git a/cmake/templates/python/rmvl_pygen.py b/cmake/templates/python/rmvl_pygen.py index 63d4237..ef9986d 100644 --- a/cmake/templates/python/rmvl_pygen.py +++ b/cmake/templates/python/rmvl_pygen.py @@ -174,6 +174,7 @@ def split_parameters(params: str) -> Tuple[List[str], List[str], List[str]]: "float": "float", "double": "float", "bool": "bool", + "char": "str", "string": "str", "string_view": "str", # cv types @@ -257,6 +258,14 @@ def convert_pair_tuple(match: re.Match) -> str: converted = ", ".join(type_convert(x.strip()) for x in inner.split(",")) return f"tuple[{converted}]" + def convert_variant(match: re.Match) -> str: + """ + Convert `variant<...>` to `Union[...]` + """ + inner: str = match.group(1) + converted = ", ".join(type_convert(x.strip()) for x in inner.split(",")) + return f"Union[{converted}]" + def convert_unordered_map(match: re.Match) -> str: """ Convert `unordered_map<...>` to `dict[...]` @@ -342,6 +351,8 @@ def convert_simple_vec(match: re.Match) -> str: cpp_type = re.sub(r"^array<([^,]+),\s*\d+>$", convert_array, cpp_type) # pair<...>, tuple<...> -> tuple[...] cpp_type = re.sub(r"^(pair|tuple)<(.+)>$", convert_pair_tuple, cpp_type) + # variant<...> -> Union[...] + cpp_type = re.sub(r"^variant<(.+)>$", convert_variant, cpp_type) # unordered_map<...> -> dict[...] cpp_type = re.sub(r"^unordered_map<(.+)>$", convert_unordered_map, cpp_type) # optional<...> -> Union[... | None] diff --git a/doc/tools/pyrmvl_fns.cfg b/doc/tools/pyrmvl_fns.cfg index ada0420..aa17edb 100644 --- a/doc/tools/pyrmvl_fns.cfg +++ b/doc/tools/pyrmvl_fns.cfg @@ -207,11 +207,14 @@ rm::Subscriber Subscriber sub_name,addr[,port[,users]] rm::Subscriber subscribe pub_name,fields nds ##################################### types #################################### -rm::StateInfo add type None +rm::StateInfo add key,val None +rm::StateInfo add key,str None rm::StateInfo remove key None rm::StateInfo contains key [[Is_Contained_?]] rm::StateInfo clear "" None rm::StateInfo empty "" [[Is_Empty_?]] +rm::StateInfo at_numeric key val +rm::StateInfo at_string key val rm::StateInfo [[get]] key val rm::StateInfo [[set]] key,val None diff --git a/doc/tutorials/modules/tools/serial.md b/doc/tutorials/modules/tools/serial.md index c8744fd..8581fec 100644 --- a/doc/tutorials/modules/tools/serial.md +++ b/doc/tutorials/modules/tools/serial.md @@ -14,66 +14,80 @@ 相关类 rm::SerialPort -### 简介 +### 1. 简介 串口通信是一种通过串行数据传输进行通信的方式。它使用串行接口将数据以逐位的方式进行传输,常用于连接计算机与外部设备、嵌入式系统之间的数据传输。 串口通信一般涉及两个主要概念: **串口** 和 **波特率** 。 -- 串口:指的是通信中的物理接口,常见的有`RS-232`、`RS-485`、`UART`等标准。每个串口都有相应的引脚用于发送和接收数据以及控制信号。 -- 波特率:也称作数据传输速率,指的是每秒钟传输的位数。波特率决定了传输速度的快慢,通常使用常见的波特率如9600、115200等。 +- 串口:指的是通信中的物理接口,常见的有 `RS-232`、`RS-485`、`UART` 等标准。每个串口都有相应的引脚用于发送和接收数据以及控制信号。 +- 波特率:也称作数据传输速率,指的是每秒钟传输的位数。波特率决定了传输速度的快慢,通常使用常见的波特率如 9600、115200 等。 -在 [Unix](https://unix.org/) 中,Termios 是一个用于串口通信的 API ,它提供了对串口终端设备进行配置和控制的功能。 +### 2. 使用{#serialport_how_to_use} -Termios 接口允许开发人员通过编程来设置串口终端的各种参数,如波特率、数据位数、校验位、流控制等。通过使用 Termios 接口,可以实现对串口通信的灵活控制,以满足各种应用需求。 +#### 2.1 初始化{#serialport_init} -主要的 Termios 函数包括 +rm::SerialPort 的构造函数原型如下 + +```cpp +SerialPort(std::string_view device, SerialPortMode mode = {}); +``` -| 函数 | 意义 | -| :----------------------------: | :-------------------------------------------------------: | -| `tcgetattr()` | 获取当前的终端属性(设置),将属性保存到 Termios 结构体中 | -| `tcsetattr()` | 设置终端属性,通过提供 Termios 结构体中的属性值进行设置 | -| `cfsetispeed(), cfsetospeed()` | 设置串口的输入和输出波特率 | -| `tcflush()` | 刷新输入或输出缓冲区 | -| `tcdrain()` | 等待所有输出数据完成发送后再返回 | -| `tcflow()` | 控制数据流向 | +| 参数 | 含义 | +| :------: | :-----------------------------------------------------------------------: | +| `device` | 设备名,在 Windows 上一般是 `COMx`,Linux 上一般为 `ttyUSBx` 或 `ttyACMx` | +| `mode` | 串口通信模式,包含波特率 rm::BaudRate 和读取模式 rm::SerialReadMode | -### 使用{#serialport_how_to_use} +#### 2.2 数据 I/O {#serialport_io} -#### 初始化{#serialport_init} +rm::SerialPort 提供了极其方便的串口读取、写入的接口。 -rm::SerialPort 的构造函数原型如下 +###### 读取函数原型 ```cpp -explicit SerialPort(const std::string &device = {}, int baud_rate = B115200); +template +bool read(uint8_t head, uint8_t tail, AggregateT &data); // (1) 指定头尾帧的读取结构化数据 ``` -| 参数 | 含义 | -| :---------: | :----------------------------------------------------------: | -| `device` | 设备名,如果为空,则会自动搜寻名为 `ttyUSBx` 或 `ttyACMx` 的串口文件,如果不为空,则直接打开指定的设备 | -| `baud_rate` | 波特率,默认为 `B115200`,所有可用的波特率都被标准化为 `B` 开头的宏定义 | +```cpp +template +bool read(AggregateT &data); // (2) 读取结构化数据 +``` -#### 数据 I/O {#serialport_io} +```cpp +bool read(std::string &data); // (3) 读取字符串数据 +``` -rm::SerialPort 提供了极其方便的串口读取、写入的接口。 +```cpp +template +SerialPort &operator>>(AggregateOrStringT &data); // (4) 串口读取操作符重载 +``` + +###### 写入函数原型 + +```cpp +template +bool write(const AggregateT &data); // (5) 写入结构化数据 +``` + +```cpp +bool write(std::string_view data); // (6) 写入字符串数据 +``` + +```cpp +template +SerialPort &operator<<(const AggregateOrStringT &data); // (7) 串口写入操作符重载 +``` + +@param data 读取、写入的数据 +@param head 帧头 +@param tail 帧尾 -| 功能 | 函数 | 功能 | -| :--: | :-----: | :------------------------------------------------: | -| 读取 | `read` | 指定头尾帧,读取缓冲区中最新的能够组成结构体的数据 | -| 写入 | `write` | 传入待写入的结构体完成数据的写入 | +**注解** -- 读取函数原型 - ```cpp - template - inline bool read(unsigned char head_flag, unsigned char tail_flag, Tp &data) - ``` -- 写入函数原型 - ```cpp - template - inline bool write(Tp &data_struct); - ``` +
(3) 和 (5) 带有 Python 接口
-#### 链路层协议 {#serialport_protocol} +#### 2.3 链路层协议 {#serialport_protocol} **写数据(视觉端 → 电控端)** @@ -86,5 +100,5 @@ SerialPort 的通信协议一般就是指数据链路层的协议,采用封装 需要注意的是,在读取数据前需要留意 - 协议(结构体)内容 -- 内存对齐规则,关于标准的内存对齐可使用 `#pragma once` 宏或 `alignas` 关键字 +- 内存对齐规则,关于内存对齐可使用 `#pragma pack` 宏或使用 C++ 标准的 `alignas` 关键字 - 头尾帧是否一致 diff --git a/extra/combo/include/rmvl/combo/armor.h b/extra/combo/include/rmvl/combo/armor.h index 7c7c861..0ac0320 100755 --- a/extra/combo/include/rmvl/combo/armor.h +++ b/extra/combo/include/rmvl/combo/armor.h @@ -173,88 +173,28 @@ inline cv::Ptr Armor::_svm = nullptr; * * @param[in] armor_size 装甲板大小类型 */ -constexpr const char *to_string(ArmorSizeType armor_size) -{ - switch (armor_size) - { - case ArmorSizeType::SMALL: - return "small"; - case ArmorSizeType::BIG: - return "big"; - default: - return "unknown"; - } -} +const char *to_string(ArmorSizeType armor_size); /** - * @brief 字符串转为装甲板大小类型 + * @brief StateType 转为装甲板大小类型 * * @param[in] str 字符串 */ -constexpr ArmorSizeType to_armor_size_type(std::string_view str) -{ - if (str == "small") - return ArmorSizeType::SMALL; - else if (str == "big") - return ArmorSizeType::BIG; - return ArmorSizeType::UNKNOWN; -} +ArmorSizeType to_armor_size_type(const StateType &tp); /** * @brief 机器人类型转为字符串 * * @param[in] robot 机器人类型 */ -constexpr const char *to_string(RobotType robot) -{ - switch (robot) - { - case RobotType::HERO: - return "hero"; - case RobotType::ENGINEER: - return "engineer"; - case RobotType::INFANTRY_3: - return "infantry_3"; - case RobotType::INFANTRY_4: - return "infantry_4"; - case RobotType::INFANTRY_5: - return "infantry_5"; - case RobotType::OUTPOST: - return "outpost"; - case RobotType::BASE: - return "base"; - case RobotType::SENTRY: - return "sentry"; - default: - return "unknown"; - } -} +const char *to_string(RobotType robot); /** - * @brief 字符串转为机器人类型 + * @brief StateType 转为机器人类型 * * @param[in] str 字符串 */ -constexpr RobotType to_robot_type(std::string_view str) -{ - if (str == "hero") - return RobotType::HERO; - else if (str == "engineer") - return RobotType::ENGINEER; - else if (str == "infantry_3") - return RobotType::INFANTRY_3; - else if (str == "infantry_4") - return RobotType::INFANTRY_4; - else if (str == "infantry_5") - return RobotType::INFANTRY_5; - else if (str == "outpost") - return RobotType::OUTPOST; - else if (str == "base") - return RobotType::BASE; - else if (str == "sentry") - return RobotType::SENTRY; - return RobotType::UNKNOWN; -} +RobotType to_robot_type(const StateType &type); //! @} combo_armor diff --git a/extra/combo/src/armor/armor.cpp b/extra/combo/src/armor/armor.cpp index 38eec59..9f53281 100755 --- a/extra/combo/src/armor/armor.cpp +++ b/extra/combo/src/armor/armor.cpp @@ -20,6 +20,82 @@ namespace rm { +const char *to_string(ArmorSizeType armor_size) +{ + switch (armor_size) + { + case ArmorSizeType::SMALL: + return "small"; + case ArmorSizeType::BIG: + return "big"; + default: + return "unknown"; + } +} + +ArmorSizeType to_armor_size_type(const StateType &tp) +{ + if (tp.index() == 0) + return ArmorSizeType::UNKNOWN; + + const auto &str = std::get(tp); + if (str == "small") + return ArmorSizeType::SMALL; + else if (str == "big") + return ArmorSizeType::BIG; + return ArmorSizeType::UNKNOWN; +} + +const char *to_string(RobotType robot) +{ + switch (robot) + { + case RobotType::HERO: + return "hero"; + case RobotType::ENGINEER: + return "engineer"; + case RobotType::INFANTRY_3: + return "infantry_3"; + case RobotType::INFANTRY_4: + return "infantry_4"; + case RobotType::INFANTRY_5: + return "infantry_5"; + case RobotType::OUTPOST: + return "outpost"; + case RobotType::BASE: + return "base"; + case RobotType::SENTRY: + return "sentry"; + default: + return "unknown"; + } +} + +RobotType to_robot_type(const StateType &type) +{ + if (type.index() == 0) + return RobotType::UNKNOWN; + + const auto &str = std::get(type); + if (str == "hero") + return RobotType::HERO; + else if (str == "engineer") + return RobotType::ENGINEER; + else if (str == "infantry_3") + return RobotType::INFANTRY_3; + else if (str == "infantry_4") + return RobotType::INFANTRY_4; + else if (str == "infantry_5") + return RobotType::INFANTRY_5; + else if (str == "outpost") + return RobotType::OUTPOST; + else if (str == "base") + return RobotType::BASE; + else if (str == "sentry") + return RobotType::SENTRY; + return RobotType::UNKNOWN; +} + std::shared_ptr Armor::make_combo(LightBlob::ptr p_left, LightBlob::ptr p_right, const ImuData &imu_data, double tick, ArmorSizeType armor_size_type) { diff --git a/extra/decider/src/rune_decider/rune_decider.cpp b/extra/decider/src/rune_decider/rune_decider.cpp index de8d7f1..851bd58 100755 --- a/extra/decider/src/rune_decider/rune_decider.cpp +++ b/extra/decider/src/rune_decider/rune_decider.cpp @@ -63,7 +63,7 @@ DecideInfo RuneDecider::decide(const std::vector &groups, const Stat for (auto &p_tracker : groups.front()->data()) if (p_tracker->state().at("rune") == flag.at("rune")) true_trackers.emplace_back(p_tracker); - bool is_active = flag.at("rune") == "active"; + bool is_active = flag.at_string("rune") == "active"; if (last_target != nullptr) for (auto &p_tracker : true_trackers) diff --git a/extra/detector/src/armor_detector/find.cpp b/extra/detector/src/armor_detector/find.cpp index 2b2053e..6fd26c4 100755 --- a/extra/detector/src/armor_detector/find.cpp +++ b/extra/detector/src/armor_detector/find.cpp @@ -157,7 +157,7 @@ void ArmorDetector::eraseErrorArmors(std::vector &armors) void ArmorDetector::eraseFakeArmors(std::vector &armors) { armors.erase(remove_if(armors.begin(), armors.end(), [&](Armor::ptr &it) { - return it->state().at("robot") == "unknown"; + return it->state().at_string("robot") == "unknown"; }), armors.end()); } diff --git a/extra/detector/src/gyro_detector/find.cpp b/extra/detector/src/gyro_detector/find.cpp index ca3c029..4a98696 100755 --- a/extra/detector/src/gyro_detector/find.cpp +++ b/extra/detector/src/gyro_detector/find.cpp @@ -156,7 +156,7 @@ void GyroDetector::eraseErrorArmors(std::vector &armors) void GyroDetector::eraseFakeArmors(std::vector &armors) { armors.erase(std::remove_if(armors.begin(), armors.end(), [](Armor::const_ptr it) { - return it->state().at("robot") == "unknown"; + return it->state().at_string("robot") == "unknown"; }), armors.end()); } diff --git a/extra/detector/src/gyro_detector/match.cpp b/extra/detector/src/gyro_detector/match.cpp index 567d5a4..19a6190 100755 --- a/extra/detector/src/gyro_detector/match.cpp +++ b/extra/detector/src/gyro_detector/match.cpp @@ -298,7 +298,7 @@ void GyroDetector::eraseFakeTracker(std::vector &trackers) { // 删除 trackers.erase(remove_if(trackers.begin(), trackers.end(), [](tracker::const_ptr p_tracker) { - return p_tracker->state().at("robot") == "unknown"; + return p_tracker->state().at_string("robot") == "unknown"; }), trackers.end()); } diff --git a/extra/feature/src/tag/tag.cpp b/extra/feature/src/tag/tag.cpp index 4005639..054c3e1 100644 --- a/extra/feature/src/tag/tag.cpp +++ b/extra/feature/src/tag/tag.cpp @@ -24,7 +24,7 @@ Tag::Tag(const std::vector &corners, char type) if (corners_size != 4) RMVL_Error_(RMVL_StsBadArg, "the size of the argument \"corners\" should be 4, but now it is %zu.", corners_size); _corners = std::vector(corners.begin(), corners.end()); - _state["tag"] = type; + _state["tag"] = std::string(type, 1); cv::Point2f center; for (const auto &corner : corners) center += corner; diff --git a/extra/types/include/rmvl/types.hpp b/extra/types/include/rmvl/types.hpp index 3eb2737..521fc7c 100644 --- a/extra/types/include/rmvl/types.hpp +++ b/extra/types/include/rmvl/types.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include "rmvl/core/rmvldef.hpp" @@ -25,6 +26,8 @@ namespace rm //! @addtogroup types //! @{ +using StateType = std::variant; //!< 状态类型 + //! 状态类型系统 class RMVL_EXPORTS_W StateInfo { @@ -32,23 +35,33 @@ class RMVL_EXPORTS_W StateInfo RMVL_W StateInfo() = default; /** - * @brief 添加状态类型,示例代码如下: + * @brief 添加数值状态 * @code {.cpp} * StateInfo state; - * state.add("tag1: 0"); // 添加 tag1 状态类型,值为 '0' - * state.add("tag2: a, tag3: +"); // 添加 tag2 状态类型,值为 'a',tag3 状态类型,值为 '+' - * state.add("tag4"); // 添加 tag4 状态类型 - * state.add("tag5: ABC, xx1"); // 添加 tag5 状态类型,值为 'ABC',xx1 状态类型,值为空 + * state.add("tag1", 1.2); // 添加 tag1 状态类型,值为 1.2 * @endcode * - * @param[in] type 状态类型 + * @param[in] key 状态类型名称 + * @param[in] val 数值状态值 */ - RMVL_W void add(std::string_view type); + RMVL_W void add(std::string_view key, double val); + + /** + * @brief 添加字符串状态 + * @code {.cpp} + * StateInfo state; + * state.add("tag1", "hello"); // 添加 tag1 状态类型,值为 "hello" + * @endcode + * + * @param[in] key 状态类型名称 + * @param[in] str 字符串状态值 + */ + RMVL_W void add(std::string_view key, std::string_view str); /** * @brief 移除状态类型 * - * @param[in] key 状态类型 + * @param[in] key 状态类型名称 * @return 是否移除成功 */ RMVL_W bool remove(std::string_view key); @@ -56,7 +69,7 @@ class RMVL_EXPORTS_W StateInfo /** * @brief 是否包含状态类型 * - * @param[in] key 状态类型 + * @param[in] key 状态类型名称 * @return 是否包含 */ RMVL_W bool contains(std::string_view key) const noexcept; @@ -68,33 +81,49 @@ class RMVL_EXPORTS_W StateInfo RMVL_W bool empty() const noexcept; /** - * @brief 获取状态类型 + * @brief 获取状态 + * + * @param[in] key 状态类型名称 + * @return 状态 + */ + const StateType &at(std::string_view key) const; + + /** + * @brief 获取数值状态,若状态类型不是数值类型,则抛出 `std::bad_variant_access` 异常 + * + * @param[in] key 状态类型名称 + * @return 状态 + */ + RMVL_W double at_numeric(std::string_view key) const; + + /** + * @brief 获取字符串状态,若状态类型不是字符串类型,则抛出 `std::bad_variant_access` 异常 * - * @param[in] key 状态类型 + * @param[in] key 状态类型名称 * @return 状态 */ - const std::string &at(std::string_view key) const; + RMVL_W const std::string &at_string(std::string_view key) const; /** - * @brief 设置状态类型 + * @brief 设置状态 * - * @param[in] key 状态类型 + * @param[in] key 状态类型名称 * @return 状态 */ - std::string &at(std::string_view key); + StateType &at(std::string_view key); /** - * @brief 访问状态类型 + * @brief 访问状态 * - * @param[in] key 状态类型 + * @param[in] key 状态类型名称 * @return 状态 */ - std::string &operator[](std::string_view key) noexcept; + StateType &operator[](std::string_view key) noexcept; RMVL_W_SUBST("At") private: - std::unordered_map _states; //!< 状态类型 + std::unordered_map _states; //!< 状态散列表 }; //! @} types diff --git a/extra/types/misc/python.json b/extra/types/misc/python.json index b78c287..ebcfbd7 100644 --- a/extra/types/misc/python.json +++ b/extra/types/misc/python.json @@ -1,12 +1,14 @@ { "At": { "bind": [ - ".def(\"__getitem__\", [](StateInfo &s, std::string_view key) -> std::string & { return s.at(key); })", - ".def(\"__setitem__\", [](StateInfo &s, std::string_view key, const std::string &val) { s[key] = val; })" + ".def(\"__getitem__\", [](StateInfo &s, std::string_view key) -> rm::StateType & { return s.at(key); })", + ".def(\"__setitem__\", [](StateInfo &s, std::string_view key, const std::string &val) { s[key] = val; })", + ".def(\"__setitem__\", [](StateInfo &s, std::string_view key, double val) { s[key] = val; })" ], "pyi": [ - "def __getitem__(self, key: str) -> str: ...", - "def __setitem__(self, key: str, val: str) -> None: ..." + "def __getitem__(self, key: str) -> float | str: ...", + "def __setitem__(self, key: str, val: str) -> None: ...", + "def __setitem__(self, key: str, val: float) -> None: ..." ] } } \ No newline at end of file diff --git a/extra/types/perf/perf_types.cpp b/extra/types/perf/perf_types.cpp index 7dfaa15..bdd07c8 100644 --- a/extra/types/perf/perf_types.cpp +++ b/extra/types/perf/perf_types.cpp @@ -64,7 +64,7 @@ static void types_enum(benchmark::State &state) } } -static void types_state_info(benchmark::State &state) +static void str_types_state_info(benchmark::State &state) { for (auto _ : state) { @@ -84,7 +84,28 @@ static void types_state_info(benchmark::State &state) } } -BENCHMARK(types_enum)->Name("enum types (init x3, modify x3)")->Iterations(10000); -BENCHMARK(types_state_info)->Name("string types (init x3, modify x3)")->Iterations(10000); +static void num_types_state_info(benchmark::State &state) +{ + for (auto _ : state) + { + auto state = StateInfo(); + state["test1"] = 1.1; + benchmark::DoNotOptimize(state); + state["test2"] = 2.1; + benchmark::DoNotOptimize(state); + state["test3"] = 3.1; + benchmark::DoNotOptimize(state); + state["test1"] = 1.2; + benchmark::DoNotOptimize(state); + state["test2"] = 2.2; + benchmark::DoNotOptimize(state); + state["test3"] = 3.2; + benchmark::DoNotOptimize(state); + } +} + +BENCHMARK(types_enum)->Name("enum types (init x3, modify x3)")->Iterations(50000); +BENCHMARK(str_types_state_info)->Name("string states (init x3, modify x3)")->Iterations(50000); +BENCHMARK(num_types_state_info)->Name("numeric states (init x3, modify x3)")->Iterations(50000); } // namespace rm::rm_test diff --git a/extra/types/src/types.cpp b/extra/types/src/types.cpp index 534521f..e1841c6 100644 --- a/extra/types/src/types.cpp +++ b/extra/types/src/types.cpp @@ -10,28 +10,13 @@ */ #include "rmvl/types.hpp" -#include "rmvl/core/str.hpp" namespace rm { -void StateInfo::add(std::string_view type) -{ - auto types = str::split(type, ","); - for (const auto &val : types) - { - auto type = str::strip(val); - auto pos = type.find(':'); - if (pos == std::string::npos) - _states.insert_or_assign(std::string(type), ""); - else - { - auto key = str::strip(type.substr(0, pos)); - auto value = str::strip(type.substr(pos + 1)); - _states.insert_or_assign(std::string(key), std::string(value)); - } - } -} +void StateInfo::add(std::string_view type, double val) { _states[std::string(type)] = val; } + +void StateInfo::add(std::string_view type, std::string_view str) { _states[std::string(type)] = std::string(str); } bool StateInfo::remove(std::string_view key) { return _states.erase(std::string(key)) > 0; } @@ -41,10 +26,14 @@ void StateInfo::clear() noexcept { _states.clear(); } bool StateInfo::empty() const noexcept { return _states.empty(); } -const std::string &StateInfo::at(std::string_view key) const { return _states.at(std::string(key)); } +const StateType &StateInfo::at(std::string_view key) const { return _states.at(std::string(key)); } + +StateType &StateInfo::at(std::string_view key) { return _states.at(std::string(key)); } + +double StateInfo::at_numeric(std::string_view key) const { return std::get(_states.at(std::string(key))); } -std::string &StateInfo::at(std::string_view key) { return _states.at(std::string(key)); } +const std::string &StateInfo::at_string(std::string_view key) const { return std::get(_states.at(std::string(key))); } -std::string &StateInfo::operator[](std::string_view key) noexcept { return _states[std::string(key)]; } +StateType &StateInfo::operator[](std::string_view key) noexcept { return _states[std::string(key)]; } } // namespace rm diff --git a/extra/types/test/test_types.cpp b/extra/types/test/test_types.cpp index 66d6532..ad12af9 100644 --- a/extra/types/test/test_types.cpp +++ b/extra/types/test/test_types.cpp @@ -19,9 +19,14 @@ namespace rm::rm_test TEST(RMStatus_test, add_type) { StateInfo state; - state.add("robot: hero, armor_size: big"); - EXPECT_EQ(state.at("robot"), "hero"); - EXPECT_EQ(state.at("armor_size"), "big"); + state.add("robot", "hero"); + state.add("armor_size", "big"); + EXPECT_EQ(state.at_string("robot"), "hero"); + EXPECT_EQ(state.at_string("armor_size"), "big"); + state.add("value", 1.3); + EXPECT_EQ(state.at_numeric("value"), 1.3); + state["armor_size"] = 2.5; + EXPECT_EQ(state.at_numeric("armor_size"), 2.5); } } // namespace rm::rm_test diff --git a/modules/core/include/rmvl/core/io.hpp b/modules/core/include/rmvl/core/io.hpp index 5d94f6b..0b8d28f 100644 --- a/modules/core/include/rmvl/core/io.hpp +++ b/modules/core/include/rmvl/core/io.hpp @@ -197,11 +197,11 @@ class RMVL_EXPORTS_W SerialPort * @return 是否读取成功 */ template >> - inline bool read(unsigned char head_flag, unsigned char tail_flag, Tp &data) + bool read(uint8_t head_flag, uint8_t tail_flag, Tp &data) { bool retval{}; constexpr int LENGTH = 512, SIZE = sizeof(Tp); - unsigned char buffer[LENGTH]{}; + uint8_t 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) @@ -221,7 +221,7 @@ class RMVL_EXPORTS_W SerialPort * @return 是否读取成功 */ template >> - inline bool read(Tp &data) + bool read(Tp &data) { bool retval{}; constexpr int MAX_LENGTH = 512, MAX_READ_DST = sizeof(Tp); @@ -245,7 +245,7 @@ class RMVL_EXPORTS_W SerialPort bool read(std::string &data); template || std::is_same_v>> - inline SerialPort &operator>>(Tp &data) { return (this->read(data), *this); } + SerialPort &operator>>(Tp &data) { return (this->read(data), *this); } /** * @brief 数据写入串口 @@ -256,7 +256,7 @@ class RMVL_EXPORTS_W SerialPort * @return 是否写入成功 */ template >> - inline bool write(const Tp &data) { return (sizeof(data) == fdwrite(&data, sizeof(data))); } + bool write(const Tp &data) { return (sizeof(data) == fdwrite(&data, sizeof(data))); } /** * @brief 写入字符串到串口 @@ -264,10 +264,10 @@ class RMVL_EXPORTS_W SerialPort * @param[in] data 待写入的字符串 * @return 是否写入成功 */ - inline bool write(std::string_view data) { return fdwrite(data.data(), data.length()) > 0; } + bool write(std::string_view data) { return fdwrite(data.data(), data.length()) > 0; } template || std::is_same_v>> - inline SerialPort &operator<<(const Tp &data) { return (this->write(data), *this); } + SerialPort &operator<<(const Tp &data) { return (this->write(data), *this); } //! 串口是否打开 bool isOpened() const;