はじめに
最近は連続してZenohについて書かせてもらっています。
今回はROS2のようにメッセージを構造体で宣言してPubSubしちゃおうっていう発端です。
実装
今回はserde、そしてserde_jsonクレートを用いました。
そもそもjsonとはJavaScript Object Notationの略でJavaScriptというプログラミング言語におけるオブジェクトの書き方を参考に作られたデータの記述形式であり、
SerdeはRustにおけるデータのシリアライズ、デシリアライズを効率的かつ汎用的に行うフレームワークです。(下に参考にした資料を貼っております。)
これらを用いるため、Cargo.tomlには以下のように記述しました。
[dependencies]
serde = {version = "1.0.195", features = ["derive"]}
serde_json = "1.0"
また、他にzenohを用いるため最終的には以下のようになりました。
[dependencies]
serde = {version = "1.0.195", features = ["derive"]}
serde_json = "1.0"
zenoh = "0.7.0-rc"
async-std = "1.12.0"
メッセージの定義
パッケージ内にlib.rsを作成し、メッセージ型を定義します。
use serde::{Serialize, Deserialize};
use serde_json;
#[derive(Serialize, Deserialize)]
pub struct Message
{
pub name:String,
pub age:i32,
pub favorite_number:f32
}
シリアライズまたは、デシリアライズしたい構造体にserdeのSerializeトレート、Deserializeトレートをderive(派生)します。
また、あとから呼び出しやすいように以下のように関数を作成しました。
pub fn serialize(msg:Message)->String
{
serde_json::to_string(&msg).unwrap()
}
pub fn deserialize(msg:String)->Message
{
let result:Message = serde_json::from_str(&msg).unwrap();
result
}
Message構造体を引数にStringを返す関数と受信したStringをMessage構造体にする関数ですね。
ちなみにderiveしていない構造体をto_stringの引数に渡した場合以下のようなErrorが出ました。
the trait bound 構造体名: Serialize
is not satisfied
PubSubしてみる
まず、Publisherは以下のように実装してみました。
use zenoh_msg_test;
use zenoh::{
config::Config,
prelude::r#async::*,
Error
};
use async_std;
use std::time::Duration;
#[async_std::main]
async fn main()->Result<(), Error>
{
let session = zenoh::open(Config::default()).res().await.unwrap();
let topic = "test";
let publisher = session.declare_publisher(topic).res().await.unwrap();
let mut count = 0;
loop {
let msg = zenoh_msg_test::Message{
name:"Motii".to_string(),
age:count,
favorite_number:66.66,
};
let serialized = zenoh_msg_test::serialize(msg);
println!("publish :{}", serialized);
publisher.put(serialized).res().await.unwrap();
count += 1;
std::thread::sleep(Duration::from_millis(1000));
}
}
zenohについての部分は今回は省略させていただきます。
countを1ずつ増加させながら構造体を初期化し、それを先ほど作成した関数でシリアライズ、送信しています。
つぎにSubscriber側です。
use zenoh_msg_test;
use zenoh::{
config::Config,
prelude::r#async::*,
Error
};
use async_std;
#[async_std::main]
async fn main()->Result<(), Error>
{
let session = zenoh::open(Config::default()).res().await.unwrap();
let topic = "test";
let subscriber = session.declare_subscriber(topic).res().await.unwrap();
loop {
let sample = subscriber.recv().unwrap();
let str_value = sample.value.to_string();
let deserialized = zenoh_msg_test::deserialize(str_value);
println!("Subscribed");
println!("fav_num:{}, age:{}, name:{}", deserialized.favorite_number, deserialized.age, deserialized.name);
}
}
subscriberが受信した内容のvalueを
String型にして、先ほど作成した関数の引数としました。
実行
ひとつめのターミナルでpublisherを実行します。
cargo run --bin pub_test
実行結果は以下のようになりました。
user@user-Laptop:~/zenoh_msg_test$ cargo run --bin pub_test
Finished dev [unoptimized + debuginfo] target(s) in 0.08s
Running `target/debug/pub_test`
publish :{"name":"Motii","age":0,"favorite_number":66.66}
publish :{"name":"Motii","age":1,"favorite_number":66.66}
publish :{"name":"Motii","age":2,"favorite_number":66.66}
publish :{"name":"Motii","age":3,"favorite_number":66.66}
publish :{"name":"Motii","age":4,"favorite_number":66.66}
publish :{"name":"Motii","age":5,"favorite_number":66.66}
publish :{"name":"Motii","age":6,"favorite_number":66.66}
publish :{"name":"Motii","age":7,"favorite_number":66.66}
publish :{"name":"Motii","age":8,"favorite_number":66.66}
publish :{"name":"Motii","age":9,"favorite_number":66.66}
publish :{"name":"Motii","age":10,"favorite_number":66.66}
これではまだ情報の変換、送受信ができているかわかりませんね。
別のターミナルでSubscriberも実行しましょう
cargo run --bin sub_test
実行結果は以下のとおりです。
user@user-Laptop:~/zenoh_msg_test$ cargo run --bin sub_test
Blocking waiting for file lock on build directory
Compiling zenoh_msg_test v0.1.0 (/home/taiga/zenoh_msg_test)
Finished dev [unoptimized + debuginfo] target(s) in 3.45s
Running `target/debug/sub_test`
Subscribed
fav_num:66.66, age:7, name:Motii
Subscribed
fav_num:66.66, age:8, name:Motii
Subscribed
fav_num:66.66, age:9, name:Motii
Subscribed
fav_num:66.66, age:10, name:Motii
少し遅れたため、最初の方を受け取れていませんが、受信したメッセージをデシリアライズできていることを確認できました。
おわりに
今回はserdeを用いてZenohのメッセージ通信を実装することができました。好きな型をメッセージにできるのが便利ですね。余談ですが、serdeってどのように読むのでしょうか。「さーでぃ」「さーど」とかでしょうか。ともかくこのようなクレートをすぐに使えるのはRustで実装されたZenohの強みだと思います。また、下に今回のパッケージのリポジトリも載せているので、ご覧ください。
参考
パッケージのリポジトリ