これは #NervesJP Advent Calendar 2023 の23日目です。昨日は @myasu さんの ElixirでGUI・Scenic [新・入門編] でした。
はじめに
去年あたりから Zenoh という言葉を聞くようになりました。どうやら素敵な Pub/Sub のプラットフォームのようです。IoT でのプログラミングに Pub/Sub は欠かせないので大変気になります。
それでもって本家には Elixir 用の API がなかったのです。が、 @Shintaro_Hosoai さんが Zenohex という Elixir 用の API を手掛けてくれてますので、それを使ってみることにしましょう。なお、おそらく Zenohex
の発音は「ゼノヘックス」ではなく「ゼノ〜・イーエックス」だと思います。
なお Zenoh がどう素晴らしそうなのかは、やはり@Shintaro_Hosoai さんがお書きになってる Pub/Sub通信ライブラリZenohのススメ を御覧ください。
いごかしてみる
まずは zenohex の環境を作ります。いつものお作法通りです。
% mix new zenoh
% cd zenoh
mix.exs
に1行追加します。
defp deps do
[
+ {:zenohex, "~> 0.1.3"}
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
]
end
そして依存関係を解決します。
% mix deps.get
これで準備ができました。
Pub/Sub 通信させてみる
2つのpublisherと2つのsubscriberを同じ key(トピック)で通信してみます1。以下では4つのターミナルを使います。全部同一のマシン上でいごかします。トピックは afo
です。
- terminal 1: Pub側
- terminal 2: Pub側
- terminal 3: Sub側
- terminal 4: Sub側
全てのターミナルで iex を起動する
% iex -S mix
Erlang/OTP 26 [erts-14.2.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit]
Interactive Elixir (1.16.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>
%iex -S mix
Erlang/OTP 26 [erts-14.2.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit]
Interactive Elixir (1.16.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>
%iex -S mix
Erlang/OTP 26 [erts-14.2.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit]
Interactive Elixir (1.16.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>
% iex -S mix
Erlang/OTP 26 [erts-14.2.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit]
Interactive Elixir (1.16.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>
Publisher の準備をする
さきに Pub 側の準備をします。
まず Zenohex.open/0
関数で session を作ります。
iex(1)> session = Zenohex.open
#Reference<0.1263928079.4256563207.212580>
つぎに Publisher の宣言を Session.declare_publisher/2
で行います。
- 第1引数: さきほどの session の #ref
- 第2引数: トピック (key)
今回はトピック (key) を "afo"
にします。
iex(2)> {:ok, publisher} = Session.declare_publisher(session, "afo")
{:ok, #Reference<0.1263928079.4256563201.214873>}
もうひとつの Publisher 用のターミナル2でも同じことをします。
iex(1)> session = Zenohex.open
#Reference<0.1869669285.1303773189.148236>
iex(2)> {:ok, publisher} = Session.declare_publisher(session, "afo")
{:ok, #Reference<0.1869669285.1303773187.149528>}
Subscriber の準備をする
つぎに Sub 側の準備をします。
まず Zenohex.open/0
関数で session を作ります。
iex(1)> session = Zenohex.open
#Reference<0.1665375593.2109341703.230733>
つぎに Subscriber の宣言を Session.declare_subscriber/3
で行います。
- 第1引数: さきほどの session の #ref
- 第2引数: トピック (key)
- 第3引数: コールバック関数
先程の publisher の場合より引数が増えてることに注意です。
iex(2)> Session.declare_subscriber(session, "afo", fn m -> IO.inspect(m) end)
:ok
この宣言で、該当するトピック(key)で pub されたら、コールバック関数が呼ばれます。そのときのメッセージ(value)はコールバック関数の引数に渡されます。
今回は貰った値を IO.inspect/2
でそのままターミナルに出力します。
もうひとつの Subscriber 用のターミナル4でも同様のことをします。
iex(1)> session = Zenohex.open
#Reference<0.34013382.2646212610.123859>
iex(2)> Session.declare_subscriber(session, "afo", fn m -> IO.inspect(m) end)
:ok
通信してみる
では Pub/Sub 通信をしてみましょう。まず、ターミナル1で発信します。
iex(3)> Publisher.put(publisher, "foo")
:ok
"foo"
"foo"
Subscriber のあるターミナル3とターミナル4とが受け取って出力してます。
こんどはターミナル2から発信します。
iex(3)> Publisher.put(publisher, "bar")
:ok
"bar"
"bar"
こちらもそのまま出力されました。
まとめ
Zenoh の Elixir API である Zenohex を用いて Pub/Sub をしてみました。簡単に Pub/Sub できました。
今回は NAT越えやネットワーク間通信をするための Zenoh ルータを用いていません。また Zenoh 独自の機能である pull や get も使っていません。今後、試していきたいと思います。
さて、明日の #NervesJP Advent Calendar 2023 の記事は @mitsuhiro さんの「Giocci on Nerves たぶん。」です。お楽しみに!
参考文献
-
通常の Pub/Sub で、トピック・メッセージと呼ぶ通信の単位を、Zenoh ではちょっとDBっぽく
key
とvalue
と読んでます。 ↩