節點撰寫教學
===
初始化
---
1. 引入必要的抬頭檔,若依據README的指示安裝了core套件,這些抬頭檔都會在系統指定路徑裡。
```cpp=
#include "NodeHandler.hpp"
#include <boost/thread.hpp>
#include "utils.hpp"
```
2. 將IP、Port等可能需要指定的參數作為執行輸入,以利變更。
```cpp=
int main(int argc, char* argv[])
{
std::string master_ip = "127.0.0.1"; // NodeCore 執行的裝置ip。
std::string local_ip = "127.0.0.1"; // 當前檔案執行的裝置ip。
uint32_t port = 9999; // NodeCore 執行的port。
double freq = 1000; // 本節點想執行的頻率。
// 讀取使用者輸入作為參數
core::ArgParse aps(argc, argv);
aps.get("MasterIP", master_ip);
aps.get("MasterPort", port);
aps.get("LocalIP", local_ip);
aps.get("Frequency", freq);
```
3. 初始化NodeHandler,這個物件用於綁定與其他節點的通訊,訂閱、發布都是基於這個物件。
```cpp=
int main(int argc, char* argv[])
{
// ...
core::NodeHandler nh(master_ip, port, local_ip);
```
至此就準備好一個基礎的節點了,之後可以使用它來與其他的節點通訊,需注意要給對的IP和port才能正常運作,例如主機端(Master)為192.168.2.127:8982,節點(Node)為192.168.2.34,那麼參數配置應該如下。
```cpp=
int main(int argc, char* argv[])
{
// ...
std::string master_ip = "192.168.2.127"; // NodeCore 執行的裝置ip。
std::string local_ip = "192.168.2.34"; // 當前檔案執行的裝置ip。
uint32_t port = 8982; // NodeCore 執行的port。
```
你可以選擇直接改在檔案裡,或者執行時給定這些參數。
訂閱訊息
---
初始化節點後,可以宣告訂閱者(Subscriber)物件,並定義返回函數。
```cpp=
int main(int argc, char* argv[])
{
// ...
core::Subscriber sub = nh.subscriber("topic name");
```
該物件初始化需要指定收聽的話題名稱,通訊就是基於給定相同話題名稱進行的。
```cpp=
#include "robot_msg.hpp"
robot_msg::Gait tmsg;
inline void test_cb(std::shared_ptr<robot_msg::Gait> msg)
{
tmsg = *msg;
}
```
返回函數需要在一開始宣告,一般來說會需要知道你想要收聽的訊息的種類,並引入相對應的抬頭檔,關於如何自己定義訊息可以參考[自訂義訊息](https://hackmd.io/@peichunhuang/ByORmidln)。
```cpp=
int main(int argc, char* argv[])
{
// ...
sub.spinOnce(&test_cb);
```
在定義了訂閱者與返回函數後,使用此函數就可以更新一次訊息,更新的訊息是該話題裡目前拿到的最新的訊息。
發布訊息
---
發布訊息與訂閱訊息類似,宣告一個發布者後將訊息發至特定的話題。
``` cpp=
int main(int argc, char* argv[])
{
// ...
core::Publisher pub = nh.publisher("topic name");
robot_msg::Gait pub_msg;
pub.publish(pub_msg);
```
編譯與執行
---
以上的範例完整檔案如下,這是一個自發自收的範例節點專案:
:::spoiler test
##### node_test.cpp
``` cpp=
#include "NodeHandler.hpp"
#include <boost/thread.hpp>
#include "std_msg.hpp"
#include "geometry_msg.hpp"
#include "robot_msg.hpp"
#include "utils.hpp"
robot_msg::Gait tmsg;
inline void test_cb(std::shared_ptr<robot_msg::Gait> msg)
{
tmsg = *msg;
}
int main(int argc, char* argv[])
{
std::string master_ip = "127.0.0.1";
std::string local_ip = "127.0.0.1";
uint32_t port = 9999;
double freq = 1000;
std::vector<std::string> topics {"default"};
core::ArgParse aps(argc, argv);
aps.get("MasterIP", master_ip);
aps.get("MasterPort", port);
aps.get("LocalIP", local_ip);
aps.get("Frequency", freq);
aps.get("Topics", topics);
boost::asio::io_context timer_io(1);
robot_msg::Gait pub_msg;
pub_msg.header.seq = 0;
pub_msg.header.timestamp = 0;
core::NodeHandler nh(master_ip, port, local_ip);
core::Subscriber test_sub = nh.subscriber(topics[0]);
core::Publisher test_pub = nh.publisher(topics[0]);
boost::thread node_service(boost::bind(&boost::asio::io_service::run, &core::node_ios));
uint sleep_us = 1000000 / freq;
while(1)
{
boost::asio::deadline_timer timer(timer_io, boost::posix_time::microseconds(sleep_us));
pub_msg.header.seq += 1;
test_pub.publish(pub_msg);
test_sub.spinOnce(&test_cb);
// tmsg.show();
timer.wait();
}
return 0;
}
```
##### CMakeLists.txt
```txt=
cmake_minimum_required(VERSION 3.10)
project(test C CXX ASM)
set(CMAKE_C_COMPILER "/usr/bin/gcc")
set(CMAKE_CXX_COMPILER "/usr/bin/g++")
set(CMAKE_BUILD_TYPE Release)
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
set (CMAKE_EXE_LINKER_FLAGS)
set (CMAKE_CXX_STANDARD 17)
set(CMAKE_C_FLAGS "-std=c99")
set(CMAKE_CXX_FLAGS "-std=c99 -Wno-error=deprecated-declarations -Wno-deprecated-declarations ")
set(BOOST_COMPONENTS ALL)
set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/devel)
find_package(Boost REQUIRED COMPONENTS serialization thread program_options)
link_directories(/usr/local/lib)
include_directories(${Boost_INCLUDE_DIRS})
# 一般而言這以上都會寫在最外層的CMakeLists.txt,不過這是一個測試用的專案,所以你需要以上這些。
add_executable(node_test node_test.cpp)
target_link_libraries(node_test NodeHandler ${Boost_LIBRARIES} Utility)
```
##### build
:::
</br>
編譯與執行流程如下:
$ cd test/build
$ cmake ..
$ make
$ NodeCore & ./node_test
輸出應該會如下:
[1] 39918
default 127.0.0.1 62727
這代表你的NodeCore在進程39918執行,而目前**default**這個話題有一對使用者正在互相收發。
因為以上的方式會讓NodeCore在背景執行,要結束需要手動關閉,可以依據剛剛輸出的進程來結束程式。
$ ^C
$ kill 39918