SSブログ

OS X用GigE Visionカメラドライバ - その31 [OS X用GigE Vision]

GEN<i>CAMの規格をざっくり読んで、それにいちゃもんをつけてたんだけど、前回だけで終わらなかった。それだけ気に入らないところがいっぱいある、ということなんだけど、そう感じるのは僕だけかなあ。かなりボッコボコに書いてるので、他所から文句言ってもらいやすいようにとりあえず今日でいちゃもんはまとめてしまう。中の人に見てもらいたいなあ。Baslerの人に見てもらうためには英語で書かないとダメか....

6.2.5  符号なし整数の不在

GEN<i>CAMでは符号付きと符号なし整数の区別がなく、全部符号付き整数として扱われる。しかし、GenApiでは具体的なカメラの内部メモリのアドレスや汎用デジタル入出力の値と、ゲインやシャッタなどの物理量に対応する値を整数で表したものとが混在している。メモリアドレスやデジタル入出力では符号は無意味だし、逆に物理量は(負の値が使われないとしても)符号付きでないとおかしいものが多い。それらをごっちゃにしてる。

IInterfaceは64ビット幅符号付き整数となっていて、アドレスもそれで表される。64ビットアドレスも許されるような書き方になっているけど、実は最大の符号なし整数は263−1と書いてある。また、規格書には値のアクセスのための疑似コードが載っているが、そこではIIntegerインターフェイスの値のアクセスはすべて64ビット幅の符号付き整数でするようになっている。

一方でIIntegerインターフェイスを持つノードにはSignエレメントやLengthエレメントがある。
  <Sign>Unsigned</Sign>
  <Length>8</Length>
という設定もスキーマ上は許されるが、GenApiはアクセスできない可能性があるということになる。小さなカメラでこれはほとんどあり得ない、ということだろうけど、規格としてはすっきりしないし、今後もしカメラデバイス側のアドレス空間が64ビットとなっているのが一般的になったときに互換性を保ったまま拡張することができなくなる。逆に小さなデバイスではアドレス内のメモリ配置をスパース(隙間だらけ)にして32ビットではなく64ビットアドレスにしたほうがハードウェアが簡単になる、と言うことがありえる。なんでこんな中途半端なことをしたのか。

素直に符号付きと符号なしを区別するか、ビット幅を決めうちにするかすべきだったと僕は思う。標準実装がどうなっているのかわからないけど、厳密にはGenApi内部ではIInterfaceのビット幅(Length)によって正の最大数に対するキャストのチェックを入れないといけなくなる。

整数を統一した結果、実装に混乱が起こるというダサい設計になっている。符号の有無とビット幅が直交するので単一継承と相性が悪いと思ったのか、とも思ったけど、それなら多重継承を許せばいいだけで、C++ではそのまま実装できるし、言語仕様上多重継承のサポートがないJavaやObjective-Cでも別に問題が発生する訳ではない。理解できない。

6.2.6  Portリクエストは通信を起動する

Portノードは実際のデバイスのプロキシとなっていて、GigE Visionカメラの場合、Portへのリクエストはネットワークアクセスを発生させる。いくらUDPが軽いとはいえ、コマンドを流してACKを受け取るまでミリ秒かかる。またUDPではパケットがロストすることもあるので、ACKが返ってこないでタイムアウトで完了するという場合もある。

ところがPortがGenTLにアクセスする手段はRead()とWrite()だけで、通信の間ブロックするような書き方になっている(GEN<i>CAMのデバイスアクセス(トランスポート)実装であるGenTLには単純なRead、Write関数しか定義されていないように見える)。

Camera Linkだったらホストとカメラの通信は信頼性が高いしレイテンシもほとんどないので、これでも全然問題ない。IEEE1394/IIDCでも通信が失敗することはホットスワップに関係するタイミングでしか発生しないので、このやりかたで大きな問題は発生しない。しかしGigE VisionではUDPを経由していてミリ秒を超えるレイテンシがあるし、失敗する可能性もある。

つまり、(キャッシュではなく)実際の通信を発生させるようなリクエストもそうでないのと同じ扱いになっていて、実装は結構悩ましい。

このようなドライバの一般的な実装では、コマンドの送受信と画像データの受け取りは別のスレッドに乗せるように書くので、Portリクエストが完了するまでの間、画像データの受け取りができない、なんてことはない。しかしGEN<i>CAMではGenApiはユーザインターフェイスに直接対応するようになっていて、そして特にOS Xではユーザインターフェイスはメインスレッドで動作させることになっている(CocoaアプリではユーザインターフェイスはRunLoopというひとつの大きなforループの中で全て処理されないといけない)ので、Portリクエストが発行されればユーザインターフェイスがストールするということになる。

ほかのOSではどうなのかよく知らないけど、ユーザインターフェイスとGenApiの作業を同じスレッドで行うと同じ問題が発生する。

Portリクエストを非同期に実装するのがGigE Visionにとっては望ましいということになるが、具体的な実装では
  • キャッシュされた値が返るノードと、実際にトランスポートレイヤへのアクセスが発生するノードとのタイミングの問題などの確認
  • トランスポートレイヤアクセスを別スレッドにした時のデッドロック排除
  • ノードの値が更新される時に副作用的に影響を受けるノードの把握
などに注意する必要がある。

6.2.7  その他の実装上の問題 - レイヤ構造

GEN<i>CAMのサブ規格であるGenApiモジュールは、カメラの持っている機能を知ってユーザに提示し、ユーザからの操作を受けてプロキシオブジェクトであるpPortからカメラのレジスタの読み込みを発行する。これだけ考えるとGenApiはユーザインターフェイスの下のハードウェアを抽象化するレイヤとして考えることができそうである。

しかし前回書いたようなGenApiの守備範囲の問題がある。

GigE Visionではアドレスが固定されたブートストラップレジスタを読み書きしないとデータ転送を始めることができない。例えばブートストラップレジスタにCCPというレジスタがあって、これに最初に書き込みをしたホストにそれ以降の書き込み特権が与えられる。そしてパケットサイズやホスト側のポート番号などをカメラに知らせることが可能になる。こういう作業を抽象化してopen:やconnect:などとしたときにGenApiは何の役にも立たない。

となると構造としては図-3のようにしないといけない。
0315fig03.png
しかし、これではレイヤ構造とは言えない。ユーザインターフェイスからみたときに作業の種類によって指示するオブジェクトが異なる、ということになる。このそもそもの原因はGenApiがユーザインターフェイスに直接つながるメソッドを持っているくせにデバイスを十分に抽象化していないということにある。また、GenApiは具体的なユーザインターフェイスの要素、例えばチェックボックスやドロップダウンメニューなんかに直接対応する機能を持っていて、カメラの機能を表す、という観点から見るとユーザインターフェイスに近すぎる。

なぜそうなっているかというと何度も書いたように、GEN<i>CAMではGigE Visionで決められている部分はGenApiとは別にGenTLというモジュールが対応していて、GenTLはGigE VisionだけでなくIIDCやUSBなどのGenApi以外の部分を抽象化しているからである。

GenTLはC++インターフェイスでAPIが決められていて、Windows用にはライブラリがしっかりと供給されている(したがってWindowsではGenApiの標準実装とGenTLライブラリがあれば、あとはユーザインターフェイスを書くだけでカメラアプリが完成する)。で、もちろんOS X用などは存在しない。

また、デバイスとの接点を表すpPortはGenApiでは中身はからっぽで何もしない。pPortを出口にして実際のカメラレジスタにアクセスするコードを書かないといけない。これもGEN<i>CAMではGenTLの仕事だ、ということになっている。

僕の場合、GenTLのようなGEN<i>CAMでいうトランスポートレイヤを統一したいわけではなく、GigE VisionカメラをOS Xで使うためにはGenApiモジュールを実装するしかないからで、しかたなくGEN<i>CAM規格に準拠した実装をしようとしているにすぎない。したがってトランスポートレイヤを独立に抽象化できるよりも、カメラ全体を抽象化できたほうがずっといい。

どうするかというと、図-4のようにする必要がある。
0315fig04.png
つまり、ユーザインターフェイスから見たときにもうひとつ薄皮一枚をかぶせて、GenApiの出口であるpPortはブートストラップレジスタアクセスと共通にしないといけない。これによってカメラを抽象化して、例えばこれではじめて複数台のカメラをつないだときの対応が簡単になる。

しかし、こういう書き方をするとGenApiは中途半端な切り口になっていてこの部分の扱いは結構難しい。悩ましい。

6.2.8  その他の実装上の問題 - MaskedIntReg

あともうひとつ、小さな問題ではあるけど、ノードタイプのひとつにMaskedIntRegというのがある。これは小さなビット幅を整数にパックしたデータをアクセスするためのもので、ビット位置を指定して整数として取り出す。

例えば、僕が動かそうとしているカメラのXMLファイルの一部に
  <MaskedIntReg Name="StrobeMode_Enable" NameSpace="Custom">
    <pAddress>StrobeModeRegisterAddress</pAddress>
    <AccessMode>RW</AccessMode>
    <Bit>31</Bit>
  </MaskedIntReg>
  <MaskedIntReg Name="StrobeMode_Polarity" NameSpace="Custom">
    <pAddress>StrobeModeRegisterAddress</pAddress>
    <AccessMode>RW</AccessMode>
    <Bit>30</Bit>
  </MaskedIntReg>
  <MaskedIntReg Name="StrobeMode_Operation" NameSpace="Custom">
    <pAddress>StrobeModeRegisterAddress</pAddress>
    <AccessMode>RW</AccessMode>
    <Bit>29</Bit>
  </MaskedIntReg>
というようなものがある。分かりやすさのために一部省略してある。これは31ビット目をStrobeMode_Enableに、30ビット目をStrobeMode_Polarityに、29ビット目をStrobeMode_Operationに割り当てるということを表している。

これ自身はごく普通の考え方で別段不思議なところはないように見えるけど、実装上は悩ましい。というのはGenApiとGenTLの間のAPIにはビットアクセスの機能はなく、アクセスの最小単位はバイトである。つまり、この例のStrobeModeをEnableにしようとすると、そのビットだけを操作するように書き込むことはできないので、StrobeMode_PolarityとStrobeMode_Operationを同時に書き込まないとそちらのデータを破壊することになる。

また、ビットはいっぱい余ってるので他のノードがStrobeModeRegisterAddressのビットを使っているかもしれない。もちろんStrobeMode_Enableをオンにするために、それらのデータを破壊してはならないのは当然である。StrobeMode_Enableを書き込む時はそのデータも考慮する必要がある。

ところが、GenApiはノードごとに独立した読み書きを許していて、ノード間の依存性は < pValue > などの「p」サフィックスのついたエレメントで表すことになっているだけである。これは一貫性がなく非常にダサい設計である。

普通こういうのに対応するためには
  1. 同じレジスタを参照しているノードに強制的に現状の値を書き込みするよう指示する
  2. レジスタの値をキャッシュし、書き込みビットのみ変更して、そのデータを書き込む
  3. 一旦レジスタを読みだして、書き込みビットのみ変更して、そのデータを書き込む
  4. GenTLにビット操作命令を加える
などが考えられる。GigE Visionではそもそもビット操作がサポートされていないので4番目は不可能である。また、3番目もレイテンシの大きなGigE Visionではやりたくない動作である。

1番目2番目3番目はMaskedIntReg以外のノードには必要のないことであって、MaskedIntRegノードタイプだけを特別扱いしないと実装できない。

この問題の解決は技術的にはそれほど難しいことではないけど、これは本来GenTLのAPIを前提にすれば、一つのレジスタにアクセスするノードはひとつにして、そのノードに複数の機能を割り当てるような構造にする方がずっと簡単であり、しかも一貫性を損なわない。GenApiとGenTLのメンバ間は仲が悪かったとしか思えない。

しかしこうやってみてみると、GigE Visionの規格はわりとカチッとしていて、余分なところや抜けがない。IIDCという先達があったからということもあるんだろう、雰囲気はIIDCに似ている。一方でGEN<i>CAMは曖昧なところや決められていないところが結構あってルーズな感じがする。一方で膨張主義と言うか守備範囲はなるべく広く取ろうと言う意思のようなものが感じられる。比喩的に言えばGigE Vision規格は大柄ではないけど筋肉質の健康体質で、GEN<i>CAM規格は巨体自慢のカウチポテトな肥満体質という感じが僕にはする。

まあしかし決めてしまったものはどうしようもないし、僕が一人でガタガタ言っても誰も聞くわけはない。これに従うことにするしかないのでそうする。
nice!(0)  コメント(2)  トラックバック(0) 

nice! 0

コメント 2

Music and Lyrics by Huey

Hi,

>これもGEN<i>CAMではGenTLの仕事だ、ということになっている。
First of all, GenTL is not mandatory for some transport layer standard such as HS-Link.

>「p」サフィックスのついたエレメントで表すことになっているだけである。これは一貫性がなく非常にダサい設計である。
Yes, you could say that and also you can contribute its own architecture to the committee whenever you want :-) for free.

>GenApiとGenTLのメンバ間は仲が悪かったとしか思えない。
It's not true, it's a historical reason as you can image. For example, Version 1.0 of GenApi was released September 2006. The fisrt verison of GenTL was released November 2009.

>1.同じレジスタを参照しているノードに強制的に現状の値を書き込みするよう指示
GenICam says Node's link can establish one of the Dependency Types such as Invalidator.

>比喩的に言えばGigE Vision規格は大柄ではないけど筋肉質の健康体質で
>GEN<i>CAM規格は巨体自慢のカウチポテトな肥満体質
You could say that but slightly some view differ from their policy since GenICam arms to the generic interface on top of every transport layers. On the contrary , GEV arms to the single transport layer as just a protocol.

>まあしかし決めてしまったものはどうしようもないし、僕が一人でガタガタ言っても誰も聞くわけはない。
Machine Vision cameras are typically used to the standard however a few camera vendors do not have a standard driver due to some reason like you. So you won't need to use GenICam at all as if you disagree to those of specification :-). A device description file is just text, so to speak :-)

Thank you for valuable information.
Huey


by Music and Lyrics by Huey (2015-03-24 14:23) 

decafish

日本語のないコメントは受け付けられないようです。知りませんでした。

Thank you for your comments.
And I'm happy to get them with a serious manner against such a exaggerrated opinion.

> First of all, GenTL is not mandatory for some transport layer standard such as HS-Link.

Yes I know but I mean that I should implement a module equivalent to GenTL as long as my driver can follow Gen<i>Cam XML file.


> Yes, you could say that and also you can contribute its own architecture to the committee whenever you want :-) for free.

I want to hear your idea about it to improve my code if you can.


> It's not true, it's a historical reason as you can image. For example, Version 1.0 of GenApi was released September 2006. The fisrt verison of GenTL was released November 2009.

I'm sorry, it was only a joke.


> GenICam says Node's link can establish one of the Dependency Types such as Invalidator.

I can not understand you mean.
For example, I now have a camera with its description XML file that includes some MaskedIntReg nodes without any invalidators and I cannot solve the bit-desctruction problem without additional code to check when they write.
I beg your idea about it.


> You could say that but slightly some view differ from their policy since GenICam arms to the generic interface on top of every transport layers. On the contrary , GEV arms to the single transport layer as just a protocol.

I can understand that their covering areas are different. It's only my feeling. Please ignore it from our discussion.


> Machine Vision cameras are typically used to the standard however a few camera vendors do not have a standard driver due to some reason like you. So you won't need to use GenICam at all as if you disagree to those of specification :-). A device description file is just text, so to speak :-)

Of course I know. Actually I now can read images from cameras by my own code with read/write codes for GigE bootstrap register maps and hard-coded implementations for read-out XML file but it's very ugly. Thus, I should implement along the Gen<i>Cam standard including GenTL when I want to have general-purose driver for GigE cameras which I already have and/or want to use. Obviously there is the easiest way for me that camera vedors deside to spport OS X.


I'm sorry to write many words.
Your comments are very valuable and very interesting for me.
I want to hear you more and discuss with you from now on if you can.

by decafish (2015-03-24 18:08) 

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。