えいあーるれいの技術日記

ROS2やM5 Stack、Ubuntuについて書いています

Zenoh-C用環境構築(Pub/Sub)

ここでは、Zenoh-C用環境構築について軽く紹介します。

できれば講習資料としてまとめる予定でしたが、あまり進捗が進んでいなかったので、とりあえずCの使い方のみ先に公開します。


Zenohとは

Zenoh(ゼノー)はzetta scale社が開発しているP2P・クエリ型の通信プロトコルです。

拡張性・速度の速さを謳っており、IoTなど低消費電力デバイスへの搭載・他通信プロトコルとの相互運用性などの点において他プロトコルと比べて優れているそうです。

P2P間のPub/SubからZenohルータを用いたNAT越えを実現することができるので、最近だとROS2のNAT越えに使われたりするそうです。

詳しくは以下の記事で詳しく書かれています。

qiita.com

qiita.com

(公式)

zenoh.io


C++のCMakeの書き方(Pub/Sub)

ZenohはベースがRustで書かれており、そこからRust・C/C++Pythonで叩けるAPIを提供しています。(C/C++ APIについてはビルド元が同じでヘッダーが異なる感じ)

PythonとRustであれば環境構築はわかりやすいのですが、CMakeListsを書く必要があるので結構面倒&時間がかかるのでメモを残します。(ついでに冗長な部分を省いたサンプルも残しておきます)

Zenohを採用するついでにRustに移行すれば全く問題ない話ではあるのですが。


環境構築

Ubuntu22.04を使用しました。

同じディレクトリ内にpub.c・sub.c・CMakeLists.txtを作成し、buildディレクトリ内でビルドをする想定です。

|
|-- pub.c
|-- sub.c
|-- CMakeLists.txt
|-- build
   |
   |- (ビルド時に生成されるであろうファイル)


zenoh-cの自体ビルドは次のとおり。

libzenoh-cにはヘッダー情報しかなかったようでビルドがうまく行かなかったので、直ビルドが必要です。

※ライブラリのビルドのためにRustのビルド環境が必要です:Install Rust - Rust Programming Language

git clone https://github.com/eclipse-zenoh/zenoh-c.git
cd zenoh-c/
mkdir build
cd build/
cmake ..
cmake --build . --config Release
sudo cmake --build . --target install


空ファイル作成

コピペしやすいように空ファイルを作成しておきます。

VSCodeなら開いたファイルウィンドウにコードを貼り付けるだけでOKです。

mkdir -p zenohc_example/build
touch zenohc_example/pub.c && touch zenohc_example/sub.c && touch z_example/CMakeLists.txt

## VSCodeがインストールされている場合
# code zenohc_example/pub.c  zenohc_example/sub.c zenohc_example/CMakeLists.txt

pub側

単純にカウントアップしていくプログラムです。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "zenoh.h"

int main(int argc, char **argv) {
    char *keyexpr = "namespace_a/topic_a_1";
    char *value = "Pub from C!";
    char buf[256];
    int i = 0;

    z_owned_config_t config = z_config_default();
    printf("Opening session...\n");
    z_owned_session_t s = z_open(z_move(config));

    printf("Declaring Publisher on '%s'...\n", keyexpr);
    z_owned_publisher_t pub = z_declare_publisher(z_loan(s), z_keyexpr(keyexpr), NULL);
    while (i < 100) {
        sprintf(buf, "[%4d] %s", i, value);
        printf("Putting Data ('%s': '%s')...\n", keyexpr, buf);
        z_publisher_put_options_t options = z_publisher_put_options_default();
        options.encoding = z_encoding(Z_ENCODING_PREFIX_TEXT_PLAIN, NULL);
        z_publisher_put(z_loan(pub), (const uint8_t *)buf, strlen(buf), &options);
        sleep(1);
        i++;
    };

    z_undeclare_publisher(z_move(pub));
    z_close(z_move(s));
    return 0;
}

sub側

受信したものをそのまま出力しています。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "zenoh.h"

void data_handler(const z_sample_t *sample, void *arg) {
    z_owned_str_t keystr = z_keyexpr_to_string(sample->keyexpr);
    char recieved_type[10];
    switch (sample->kind) {
        case Z_SAMPLE_KIND_PUT:
            strcpy(recieved_type, "PUT");
            break;
        case Z_SAMPLE_KIND_DELETE:
            strcpy(recieved_type, "DELETE");
            break;
    }

    printf(">> Received %s ('%s': '%.*s')\n",
        recieved_type,
        z_loan(keystr),
        (int)sample->payload.len,
        sample->payload.start);
    z_drop(z_move(keystr));
}

int main(int argc, char **argv) {
    char c = 0;
    char *expr = "namespace_a/topic_a_1";

    z_owned_config_t config = z_config_default();
    // Open a session
    z_owned_session_t s = z_open(z_move(config));

    z_owned_closure_sample_t callback = z_closure(data_handler);
    printf("Declaring Subscriber on '%s'...\n", expr);
    z_owned_subscriber_t sub = z_declare_subscriber(z_loan(s), z_keyexpr(expr), z_move(callback), NULL);

    printf("Enter 'q' to quit...\n");

    while (c != 'q') {
        c = getchar();
        if (c == -1) {
            sleep(1);
        }
    }

    z_undeclare_subscriber(z_move(sub));
    z_close(z_move(s));
    return 0;
}

CMakeLists.txt

ターゲット直指定でビルドする場合の最小構成

find_package(zenohc REQUIRED)

add_executable(pub pub.c)
target_link_libraries(pub zenohc::lib)

add_executable(sub sub.c)
target_link_libraries(sub zenohc::lib)

ファイルをGLOBでまとめて取得する場合

(ついでにcmake_minimum_requiredとprojectを追加し CMake Warning を除く)

cmake_minimum_required(VERSION 3.20)
project(zenohc_examples LANGUAGES C)

find_package(zenohc REQUIRED)

# get all build target
file(GLOB files "${CMAKE_CURRENT_SOURCE_DIR}/*.c")

foreach(file ${files})
    get_filename_component(target ${file} NAME_WE)
    add_executable(${target} ${file})
    target_link_libraries(${target} PRIVATE zenohc::lib)
endforeach()


実行

1つめのターミナル

./pub

2つめのターミナル

./sub

参考資料(GitHub

github.com

他にもいろいろなオプションがあり、データ型をカスタマイズすることや通信方式を調整することもできるそうです。

zenoh-c.readthedocs.io