###### tags: `Robotics`
# ROS2 msg src action / 2019-10-01
## ROS2 Misc
- Message: `.msg` 檔通常會放到 `/msg` 目錄
- Services: `.srv` 檔通常會放到 `/srv` 目錄
- Actions: .action
- [ ] Execution model
- [ ] ROS2 launch
...
### **`ROS_DOMAIN_ID`**
在同一網路內,為了不要和其他使用者的節點產生衝突,可以設定這個環境變數,以區隔其他在網路上節點。
```bash
$ export ROS_DOMAIN_ID=1
```
`ROS_DOMAIN_ID` 的資料型態為無號數,範圍為 0~65535,當使用者沒設定這個環境變數會預設為 0,設定不同 `ROS_DOMAIN_ID` 的 shell 無法看到對方的節點、topic 等。
以下為對應 rwm impl. 中的實作
[rmw_fastrtps_shared_cpp/src/rmw_node.cpp](https://github.com/ros2/rmw_fastrtps/blob/f8aec9bf904edc2e5a4739071df83f69d538e814/rmw_fastrtps_shared_cpp/src/rmw_node.cpp#L235)
```cpp
participantAttrs.rtps.builtin.domainId = static_cast<uint32_t>(domain_id);
```
### Message
.msg 檔寫法幾乎與 ROS1 相同,使用相同資料型態的命名,並相較於 ROS1 多了新的功能。`rosidl` 這個 package 會將 .msg 檔轉換成 hpp 標頭檔和 DDS IDL 檔。
.msg 格式為:
```
<type> <name> <optional_default_value>
```
Example:
sensor_msgs/LaserScan.msg
```
std_msgs/Header header
float32 angle_min
float32 angle_max
float32 angle_increment
float32 time_increment
float32 scan_time
float32 range_min
float32 range_max
float32[] ranges
float32[] intensities
```
ROS2 新增的功能:
1. Upper boundaries: 指定陣列大小、上界,字串長度上界
```cpp
int32[5] five_integers_array
int32[<=5] up_to_five_integers_array
string<=10 up_to_ten_characters_string
```
2. Default values
```cpp
uint8 x 42
int16 y -2000
string full_name "John Doe"
int32[] samples [-200, -100, 0, 100, 200]
```
Message field type 與不同語言的映射可參閱 [About ROS Interfaces](https://index.ros.org/doc/ros2/Concepts/About-ROS-Interfaces/)
| Type name | C++ | Python | DDS type |
| -------- | --- | ------ | ------- |
| bool | bool | builtins.bool | boolean |
| float64 | double | builtins.float* | double |
| uint64 | uint64_t | builtins.int* | unsigned long long |
|...||||
| static array | std::array<T, N> | builtins.list* | T[N] |
| string | std::string | builtins.str* | string
| __unbounded dynamic array__ | std::vector | builtins.list | sequence |
| bounded dynamic array | custom_class<T, N> | builtins.list* | sequence<T, N> |
#### Write a custom message
:::warning
幾項限制:
1. Custom message name (檔名) 必須以大寫開頭
2. Field names 只能有小寫、數字、和底線
:::
<details><summary>修改 package.xml 和 CMakeLists.txt</summary>
<p>
directory tree:
```
ros_course_demo/
├── msg
│ └── Custom.msg
├── CMakeLists.txt
├── package.xml
```
在 package.xml 加入以下幾行:
```xml
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
```
CMakeLists.txt:
```
rosidl_generate_interfaces(${PROJECT_NAME}
${msg_files})
set(msg_files
"msg/Custom.msg")
ament_export_dependencies(rosidl_default_runtime)
```
:::info
目前在同一個 package 內編譯時無法找到標頭檔,之後再查閱 CMakeList.txt 問題。
References: https://index.ros.org/doc/ros2/Tutorials/Rosidl-Tutorial/
:::
</p>
</details>
<br>
使用自己定義的 message
```cpp
#include "ros_course_demo/msg/custom.hpp"
auto node2 = std::make_shared<rclcpp::Node>("custom node publisher");
auto pub_ = node2->create_publisher<ros_course_demo::msg::Custom>("custom_topic");
```
### Service
Request–response communication
語法和 msg 一樣,虛線上方為 request,下方為 response
Example of a srv file:
```
float64 A
float64 B
---
float64 Sum
```
在 client 端使用以下函式發送 request
```cpp http://docs.ros2.org/dashing/api/rclcpp/classrclcpp_1_1Client.html#a7567297f43b72f96e8ec57fa7ff2f4e1
SharedFuture rclcpp::Client<ServiceT>::async_send_request(SharedRequest request)
```
在 server 端註冊 callback 有兩個參數:
```cpp
// callback prototype
void handle_service(
const std::shared_ptr<rmw_request_id_t> request_header,
const std::shared_ptr<AddTwoInts::Request> request,
const std::shared_ptr<AddTwoInts::Response> response)
```
:::info
範例待補
:::
### Actions
Long running Request–response communication with feedback.
Actions 是設計給可能會在 server 上長時間 (幾秒甚至數分鐘) 執行的操作,除了基本的 request-response,還可以讓 server 決定是否要接受 request,回傳目前狀態 (feedback),或是 client 向 server 請求取消 request。
以下為 .action 檔的範例 [Fibonacci.action](https://github.com/ros2/example_interfaces/blob/master/action/Fibonacci.action),.action 檔分為三個部份,上面兩個部份跟 service 一樣,分別是 request (在 action 中稱為 goal) 和 response,最後一部分則是 feedback,是在回傳 result 之前 server 向 client 傳送的資料。
Example of a .action file
``` Fibonacci.action
# Goal
int32 order
---
# Result
int32[] sequence
---
# Feedback
int32[] sequence
```
State machine in Action:

白色區塊為 active state,藉由回傳 `rclcpp_action` 定義的常數轉移狀態,以下為 `rclcpp::action` 定義的常數。

三個在 server 上重要 callback (必須為 non-blocking):
- handle_goal: 決定是否要接受 Request
- handle_cancel: 決定是否要取消目標 (goal)
- handle_accepted: give the user a handle to the goal. (因為必須是 non-blocking,比較常用的作法是用 thread 執行工作)
三個 active states:
- ACCEPTED: 目標已被接受正在等待執行
- EXECUTING: 目標正在執行
- CANCELING: 取消的要求已被接受,正在處理 clean up 等動作
client 端,`async_send_goal` 和 `async_get_result` 回傳的資料型態為 `std::shared_future`,處理非同步操作,多個執行序可以呼叫 `get()` 等待結果。
```cpp=
auto action_client = rclcpp_action::create_client<Fibonacci>(g_node, "fibonacci");
auto goal_msg = Fibonacci::Goal();
goal_msg.order = 10;
auto send_goal_options = rclcpp_action::Client<Fibonacci>::SendGoalOptions();
send_goal_options.feedback_callback = feedback_callback;
auto goal_handle_future = action_client->async_send_goal(goal_msg, send_goal_options);
...
auto result_future = action_client->async_get_result(goal_handle);
```
server 端,註冊所有 callback
```cpp
auto action_server = rclcpp_action::create_server<Fibonacci>(
node,
"fibonacci",
handle_goal,
handle_cancel,
handle_accepted);
```
:::info
範例待補
:::
### Execution model
The Executor coordinates the execution of all callbacks issued by these nodes by checking for available work (**timers**, **services**, **messages**, **subscriptions**, etc.) from the DDS queue and dispatching it to one or more threads.[^1]

[^1]: [Analysis of rclcpp standard executor](https://micro-ros.github.io/docs/concepts/client_library/real-time_executor/#analysis-of-rclcpp-standard-executor)
- SingleThreadedExecutor
- MultiThreadedExecutor
#### **`spin()`** API Overview
| Function | Description |
| -------- | -------- |
| [`spin()`](http://docs.ros2.org/dashing/api/rclcpp/namespacerclcpp.html#a21e13577f5bcc5992de1d7dd08d8652b) | 在一個節點上 spin,為 **blocking** call 直到收到 `SIGINT` 訊號 |
| `spin_once()` | 只執行一個可用的工作 |
| [`spin_some()`](http://docs.ros2.org/dashing/api/rclcpp/namespacerclcpp.html#ad48c7a9cc4fa34989a0849d708d8f7de) | 執行所有可用的工作,但不會執行在呼叫 spin_some() 時進來的新工作[^2] |
| [`spin_until_future_complete()` ](http://docs.ros2.org/dashing/api/rclcpp/namespacerclcpp.html#aa700b1a030143a2eef597d1aae519600) | 在 future (實作為 `std::shared_future`) 的結果為有效之前,持續 spin,也可以指定 timeout 時間[^3] |
[^2]: https://answers.ros.org/question/296480/functional-difference-between-ros2-spin_some-spin_once-and-spin_until_future/
[^3]: http://docs.ros2.org/dashing/api/rclcpp/namespacerclcpp_1_1executors.html#ab2c1d20615d8e5023393295a23212237
#### call of rclcpp::spin()
[`rclcpp::spin()`](https://github.com/ros2/rclcpp/blob/dashing/rclcpp/src/rclcpp/executors.cpp#L40) -> [`rclcpp::executors::SingleThreadedExecutor::spin` ](https://github.com/ros2/rclcpp/blob/dashing/rclcpp/src/rclcpp/executors/single_threaded_executor.cpp#L27)-> [`Executor::execute_any_executable`](https://github.com/ros2/rclcpp/blob/dashing/rclcpp/src/rclcpp/executor.cpp#L279) -> ...
-> [`rcl_wait()`](https://github.com/ros2/rcl/blob/dashing/rcl/src/rcl/wait.c#L515)
-> [`rmw_wait()`](https://github.com/ros2/rmw_fastrtps/blob/dashing/rmw_fastrtps_cpp/src/rmw_wait.cpp#L23) declared in [ros2/rmw/](https://github.com/ros2/rmw/blob/dashing/rmw/include/rmw/rmw.h#L810) -> [`rmw_fastrtps_shared_cpp::__rmw_wait()`](https://github.com/ros2/rmw_fastrtps/blob/dashing/rmw_fastrtps_shared_cpp/src/rmw_wait.cpp#L92)