PiNub その3 [Raspberry Pi]
Raspberry Piの汎用GPIO制御サーバソフトウェアPiNubの設計の続き。ローカルネットワーク上に複数のPiNubサーバとクライアントが存在することになる。今日はクライアントが必要とするPiNubを探す方法について....
そのためにネットワーク上を探索することにする。つまりGigEカメラのマネをして、UDPブロードキャストを使うことにする。PiNubは起動するとブロードキャストを待ち受ける。Clientからブロードキャストがあると、情報を送り返す、ということにする。
具体的には
また、上ではブロードキャストを受け取ってからTCPポートを開くとしたけど、ブロードキャストを受けるUDPポートと、コマンドを受けるTCPポートは起動と同時に開き、さらに接続されるまで両方のポートは開いたままでもかまわないな。TCPポートがバインドされて、最初のコマンドを受け取ると、UDPポートの方は閉じるようにする。
それを知らせるために、最初に回路記述ファイルをすべてClientに転送することにする。従って最初のコマンドは回路記述ファイル転送コマンドとなる。
本来、ファイル全体を転送する必要性はまったくないが、回路記述ファイルを読み込んで必要なオブジェクトをインスタンス化する場合、Raspberry Pi側とClient側とで全く同じ構造を作ることができる。
そうするとこで、Client上ではローカルなオブジェクトにコマンドを発行すれば、ネットワークを経由してRaspberry Pi側の対応するオブジェクトにコマンドが転送されるように書くことができる。機能を追加する場合などこれが楽である。
具体的なイメージとしてはこんな つまり、Raspberry PiのPiNubはEE-PROMに書かれている回路記述を読み込んで、GPIOに接続された基板上の回路をモデル化したObject treeを作る。Controllerオブジェクトは回路を操作するオブジェクトである。Clientが必要とするPiNubを見つけると、EE-PROMの中身をそのまま受け取ってClientでもObect treeを作る。ClientとPiNubで全く同じコードを使うのでObject treeも(ポインタサイズなどは別にして)全く同じものが作られるはずである。
Clientのユーザインターフェイス(UI)からControllerにコマンドを渡すと、Raspberry Pi側の対応するControllerにそのまま転送されて、Driverを駆動する。Driverからレスポンスがある場合はその逆の経路を通ってUIに渡される、ということにする。
通信を司るTransportオブジェクトは自分がClientかRaspberry Piのどちらで動いているか知っていて、Controllerはそれを知らずにコマンドの受け取りやdriverへの発行はTransportに対して行う、ということにすれば美しいけど、コマンドの形式を揃えないといけなくなるのでめんどくさい。それにそこまでするほど大きなソフトウェアにはならないはずである。
もう少し簡単な解としては、Controllerは2種類の形式のコマンドを受けることにする。ひとつは関数経由で、もう1つはコマンドフレームを渡す形式。関数経由で呼ばれた場合はClient側で動作しているとみなして、コマンドフレームを作ってTransportに渡す。コマンドフレームを渡された場合はRaspberry Pi側で動作しているとみなして、Driverを駆動する。このほうが簡単で、コマンドフレームの形式はControllerごとに勝手に決めていいので楽である。
Driverからのレスポンスも同じ伝で振り分ければいい。こんな感じでいいだろう。
2.4 PiNub探索
工場の現場で使うことを考えているので、同じローカルネットワーク上に複数のクライアントとRaspberry Piが存在するということを前提にする。さらにGPIO接続の回路基板とRaspberry Piの組み合わせは交換可能だとしたので、Clientは接続先をホスト名やIPアドレスでは特定できない、ということになる。つまり、Clientは接続したいPiNubがどれなのか、どこにあるか、をあらかじめ知らない、ということを前提にしなければならない。そのためにネットワーク上を探索することにする。つまりGigEカメラのマネをして、UDPブロードキャストを使うことにする。PiNubは起動するとブロードキャストを待ち受ける。Clientからブロードキャストがあると、情報を送り返す、ということにする。
具体的には
- 決まったUDPポートでブロードキャストを受ける(PiNubの実行はブロックする)
- Clientはブロードキャストメッセージを送る。メッセージには
- 識別文字列
- バージョン
- クライアントの情報(ホスト名など)
- ブロードキャストを受け取ったPiNubは
- 識別文字列
- PiNub側のバージョン
- ネットワークホスト名
- 回路基板名称、説明
- コマンドを受け付けるTCPポート番号
- 指定したTCPポート番号で待ち受ける
- クライアントは接続したいPiNubが見つかれば、そのネットワークホストのTCPポート番号を呼び出す
- 接続を受けたPiNubは回路基板の詳細情報を返し、コマンドを待つ
- ブローキャスト受信を終了する
- 接続を受けなかったPiNubは、タイムアウトを待つ
- タイムアウトした場合、TCPポートを閉じてさらに他のClientからのブロードキャストを待ち受ける
また、上ではブロードキャストを受け取ってからTCPポートを開くとしたけど、ブロードキャストを受けるUDPポートと、コマンドを受けるTCPポートは起動と同時に開き、さらに接続されるまで両方のポートは開いたままでもかまわないな。TCPポートがバインドされて、最初のコマンドを受け取ると、UDPポートの方は閉じるようにする。
2.5 接続確立後
TCPポートで接続が確立すると、PiNubはコマンドの待ち受けに移行するが、Clientは基板の名前がわかっただけで、どのコマンドが何の機能を実現しているか、例えばA/D変換コマンドが電圧か使えるかわからないとしておく。それを知らせるために、最初に回路記述ファイルをすべてClientに転送することにする。従って最初のコマンドは回路記述ファイル転送コマンドとなる。
本来、ファイル全体を転送する必要性はまったくないが、回路記述ファイルを読み込んで必要なオブジェクトをインスタンス化する場合、Raspberry Pi側とClient側とで全く同じ構造を作ることができる。
そうするとこで、Client上ではローカルなオブジェクトにコマンドを発行すれば、ネットワークを経由してRaspberry Pi側の対応するオブジェクトにコマンドが転送されるように書くことができる。機能を追加する場合などこれが楽である。
具体的なイメージとしてはこんな つまり、Raspberry PiのPiNubはEE-PROMに書かれている回路記述を読み込んで、GPIOに接続された基板上の回路をモデル化したObject treeを作る。Controllerオブジェクトは回路を操作するオブジェクトである。Clientが必要とするPiNubを見つけると、EE-PROMの中身をそのまま受け取ってClientでもObect treeを作る。ClientとPiNubで全く同じコードを使うのでObject treeも(ポインタサイズなどは別にして)全く同じものが作られるはずである。
Clientのユーザインターフェイス(UI)からControllerにコマンドを渡すと、Raspberry Pi側の対応するControllerにそのまま転送されて、Driverを駆動する。Driverからレスポンスがある場合はその逆の経路を通ってUIに渡される、ということにする。
通信を司るTransportオブジェクトは自分がClientかRaspberry Piのどちらで動いているか知っていて、Controllerはそれを知らずにコマンドの受け取りやdriverへの発行はTransportに対して行う、ということにすれば美しいけど、コマンドの形式を揃えないといけなくなるのでめんどくさい。それにそこまでするほど大きなソフトウェアにはならないはずである。
もう少し簡単な解としては、Controllerは2種類の形式のコマンドを受けることにする。ひとつは関数経由で、もう1つはコマンドフレームを渡す形式。関数経由で呼ばれた場合はClient側で動作しているとみなして、コマンドフレームを作ってTransportに渡す。コマンドフレームを渡された場合はRaspberry Pi側で動作しているとみなして、Driverを駆動する。このほうが簡単で、コマンドフレームの形式はControllerごとに勝手に決めていいので楽である。
Driverからのレスポンスも同じ伝で振り分ければいい。こんな感じでいいだろう。
2019-11-10 21:18
nice!(0)
コメント(0)
コメント 0