OS X用GigE Visionカメラドライバ - その9 [OS X用GigE Vision]
昨日はデバイス探索のためにUDPのブロードキャストを流す、というコードを書いてみた。今日は、ではどんなデータをブロードキャストすればいいのかと、カメラはそのときどう答えるのか、ということについて。
10/29追記:コードに間違いがあった。修正。
GVCPのコマンドフレームは少なくとも最初の8バイト(通信分野では8オクテットというのが正しいらしい)はこれと同じ構造をしている。それぞれの詳細はGVCPコマンドを説明するときに一括してまとめる。 これをCの構造体として書くと
この0x00000002というコマンドがデバイス探索であるぞ、というしるしである。また先頭の0x0042はGVCPのフレームであるぞ、というキーコードで、デバイス側では先頭バイトの値がこれ以外のフレームはすべて無視される。
ちなみにlengthはこのヘッダをのぞいたバイト数で、デバイス探索コマンドの場合このあとになにもないので0である。コマンドによっては付加情報が必要なので、その付加情報のバイト数を表すことになっている。
最後のreq_idはデバイス探索コマンドの場合0以外ならなんでもいい。この意味はあとで説明する。
デバイス側はすでにIPアドレスが決まって、ホストから接続されるのを待っている状態のはずである。
デバイス側のコードはたとえば
そしてsocketにアドレスをバインドしてrecvfrom()でデータを待つ。
ブロードキャストが到達するとfrecvfrom()から返ってlenが正の数だとなにかがこのポートに来た、ということになる。ようするにたとえばNTPやDHCPなんかのUDPを経由するサーバがやってることと基本的には全く同じ(NTPやDHCPは複数の接続に対応できるように、もうすこし複雑にはなってるけど)。
あとは到着したデータの先頭バイトが0x0042であってコマンドがデバイス探索のコマンドであれば、デバイスは応答を返すことになる。
recvfrom()システムコールは送ってきた相手のアドレスがわかるようになっている(上の疑似コードのsentAddrに入ってくる。その値そのものはもともとIPヘッダに含まれている)ので、応答はこのアドレスに返せばいい。
10/29追記:コードに間違いがあった。修正。
4.2 デバイス探索のコマンドフレーム
一般のコマンドフレームはまたあとで整理するとして、このときに送り出したデバイス探索のためのコマンドフレームを図-1に示しておく。GVCPのコマンドフレームは少なくとも最初の8バイト(通信分野では8オクテットというのが正しいらしい)はこれと同じ構造をしている。それぞれの詳細はGVCPコマンドを説明するときに一括してまとめる。 これをCの構造体として書くと
#import <CoreFoundation/CoreFoundation.h> typedef struct _gigeCommandHeader { UInt8 keyCode; // 0x0042 UInt8 flag; // 0x8000 UInt16 command; // 0x00000002 UInt16 length; // 0 UInt16 req_id; } gigeCommandHeader;となる。ちなみにCで書いたときにカメラへ流すためにはそれぞれのフィールドのバイトオーダはネットワークエンディアン(すなわちビッグエンディアン)でないといけないことに注意する。
この0x00000002というコマンドがデバイス探索であるぞ、というしるしである。また先頭の0x0042はGVCPのフレームであるぞ、というキーコードで、デバイス側では先頭バイトの値がこれ以外のフレームはすべて無視される。
ちなみにlengthはこのヘッダをのぞいたバイト数で、デバイス探索コマンドの場合このあとになにもないので0である。コマンドによっては付加情報が必要なので、その付加情報のバイト数を表すことになっている。
最後のreq_idはデバイス探索コマンドの場合0以外ならなんでもいい。この意味はあとで説明する。
4.3 デバイスが反応する
このデバイス探索のフレームがデバイスに届いたとき、デバイスはどうするか、というのを疑似コードを書いて考えてみる。もちろんデバイスのOSがunixであるとは限らないので、こんなコードで書かれているかどうかはわからない。デバイス側はすでにIPアドレスが決まって、ホストから接続されるのを待っている状態のはずである。
デバイス側のコードはたとえば
int sock; struct sockaddr_in recvAddr; struct sockaddr_in sentAddr; ssize_t len; socklen_t sentLen = sizeof(sentAddr); sock = socket(AF_INET, SOCK_DGRAM, 0); recvAddr.sin_family = AF_INET; recvAddr.sin_port = htons(3956); recvAddr.sin_addr.s_addr = INADDR_ANY; bind(sock, (struct sockaddr *)&recvAddr, sizeof(recvAddr)); len = recvfrom(sock, buf, sizeof(buf), 0 (struct sockaddr *)&sentAddr, &sentLen);というようなはずである。つまりsocketを作って、コマンドのポート番号で、アドレスにはINADDR_ANYを指定して、どこからきてもいい、ということにする。
そしてsocketにアドレスをバインドしてrecvfrom()でデータを待つ。
ブロードキャストが到達するとfrecvfrom()から返ってlenが正の数だとなにかがこのポートに来た、ということになる。ようするにたとえばNTPやDHCPなんかのUDPを経由するサーバがやってることと基本的には全く同じ(NTPやDHCPは複数の接続に対応できるように、もうすこし複雑にはなってるけど)。
あとは到着したデータの先頭バイトが0x0042であってコマンドがデバイス探索のコマンドであれば、デバイスは応答を返すことになる。
recvfrom()システムコールは送ってきた相手のアドレスがわかるようになっている(上の疑似コードのsentAddrに入ってくる。その値そのものはもともとIPヘッダに含まれている)ので、応答はこのアドレスに返せばいい。
2014-10-27 21:17
nice!(0)
コメント(0)
トラックバック(0)
コメント 0