[Zenoh]serdeを使ってメッセージ通信する記事

# はじめに
最近は連続して[Zenoh](https://zenoh.io/)について書かせてもらっています。
今回はROS2のようにメッセージを構造体で宣言してPubSubしちゃおうっていう発端です。

# 実装
今回はserde、そしてserde_jsonクレートを用いました。

https://docs.rs/serde/latest/serde/

https://docs.rs/crate/serde_json/0.8.6

そもそも**json**とはJavaScript Object Notationの略でJavaScriptというプログラミング言語におけるオブジェクトの書き方を参考に作られたデータの記述形式であり、
**Serde**はRustにおけるデータのシリアライズ、デシリアライズを効率的かつ汎用的に行うフレームワークです。(下に参考にした資料を貼っております。)

これらを用いるため、Cargo.tomlには以下のように記述しました。
```toml:Cargo.toml
[dependencies]
serde = {version = "1.0.195", features = ["derive"]}
serde_json = "1.0"
```
また、他にzenohを用いるため最終的には以下のようになりました。
```toml:Cargo.toml
[dependencies]
serde = {version = "1.0.195", features = ["derive"]}
serde_json = "1.0"

zenoh = "0.7.0-rc"

async-std = "1.12.0"
```

## メッセージの定義
パッケージ内にlib.rsを作成し、メッセージ型を定義します。
```rs:src/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(派生)します。

また、あとから呼び出しやすいように以下のように関数を作成しました。
```rs:src/lib.rs
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は以下のように実装してみました。
```rs:src/bin/pub_test.rs
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を１ずつ増加させながら構造体を初期化し、それを先ほど作成した関数でシリアライズ、送信しています。

つぎにSubscriber側です。
```rs:src/bin/sub_test.rs
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の強みだと思います。また、下に今回のパッケージのリポジトリも載せているので、ご覧ください。


# 参考

https://www.json.org/json-ja.html

https://serde.rs/

パッケージのリポジトリ

https://github.com/motii8128/zenoh_msg_test