OS X用GigE Visionカメラドライバ - その37 [OS X用GigE Vision]
Photonics West用のデモ装置の運び出しまであと2週間しかないのに、新しく買ったカメラが動かない。焦るんだけど、GEN<i>CAMのXMLファイルを読み込んだあとのカメラの設定ができない。今頃そんなところでまごまごしている場合ではないんだけど、どうしようもない。Windowsならメーカ謹製のドライバがちゃんとあって、表示するには問題はないけど、僕はWindowsには全く何も書けないので、そのあとの処理がなにもできない。このブログで繰り返し語られるマイナなOSを選択してしまったプログラマの悲劇。
えらいこっちゃ....
ということで今日は前回の続き、GEN<i>CAMに現れるノードが持つエレメント(ノードのプロパティ)の実装について。
単に値を保持しているだけのエレメントとその値を他のノードから得るエレメントがある。それぞれ存在するのは当たり前でわかるんだけど、意味によって扱い方が違っている。
例えばバイト幅を保持するためにはLengthとpLengthのふたつがあって、一方は定数を保持して、もう一方は参照するノードの名前を保持している。そしてどちらかを選択するようになっている。つまりひとつのノードに両方があることはない。
ところがレジスタのアドレスを保持するには定数用のAddressとノード参照用のpAddressとさらに間接アドレスのためのpIndexと、なんらかの計算を行ってアドレスを決めるためのIntSwissKnifeの4つがある。さらにこれがひとつのノードに同時に存在してもいいことになっていて、複数あるときは符号なし整数として全部を足した値がアドレスと解釈される。
アドレスの値が必要なノードは、どれとどれがXMLファイルの中に存在しているかを知っている必要がある。
こんなものはAddressというエレメントがひとつあって、Addressエレメントのなかで値を決定する方式を複数もたせた方が簡単になる。なぜならノードはAddressエレメントがただひとつ存在することを確認したら、アドレスの値はそのAddressエレメントに問い合わせるというただひとつのことができればいいだけである。Lengthも同じようにすればノードはその中身が定数なのか他のノードを参照しているのかを知る必要はなく、Addressとまったく同じ処理をすればいいだけである。ノードには他にもいろいろやることがあるので、なるべくシンプルにした方が実装上はいい。
ところがGEN<i>CAMの規格上はそうなっていない。すべてノードが判断してバイト幅を知るならこう、アドレスを知るならこう、と別々の処理することになる。こういう直交性の悪さは実装を複雑にする原因となるので、避けるのが普通だけど、そうなっていないのは作った奴の頭が悪いか、他にしがらみがあるのかのどちらかである。
またイライラしてきたので文句になってしまったけど、文句を言っててもしょうがないので、まず、エレメントとその使われ方を整理することにする。そうしないとクラス設計がそもそも始められない。
おおまかには
実装のために全部列挙する。 次のリストの2レベル目はスキーマファイルにある型で、3レベル目はエレメントの名前である。
pのついた参照タイプのエレメントはスキーマ上はすべてCName_tになっているけど、pのないエレメントは型がいろいろになっている。従ってpつきのほうはセマンティクスによって型を分類しないといけない。またpつきだけあってpなしがないこともあって、さっきの型と直交しているとは言えないので、すべて独立にあつかう必要がある。
pありなし両方あるエレメントは
前にも書いたけど、設計としてはもう一つ下のレイヤの仕事にしたほうが簡単になる(例えばpInvalidatorがノードの中にはただひとつだけ必ずあることにして、invalidateする条件の数の管理はpInvalidatorエレメントが一括する)。そうすればノードオブジェクトはinvalidateする条件をただひとつあるエレメントに問い合わせればいいだけになるし、ほかのエレメントも同じにすれば、すべてのエレメントに同じ形式で問い合わせることができる。うまく設計すればノードの持つエレメントは集合(NSSet)として保持することができるはずであるが、残念ながらそうなっていない。
しかし、Addressエレメントがふたつあってもしょうがないし、pAddressとpIndexが同時に存在する場合に、もしpAddressの参照先とpIndexのベースアドレスの参照先が同じだったらどうするだ?というような疑問もある。
ひとつはEnumerationで、このエレメントはさらにEnumEntryというノードを含んでいる。
もうひとつはpIndexで、これはpAddressと同じようにアドレスの値を参照して、さらにOffsetあるいはpOffsetというXMLのアトリビュートを持つことができる。エレメントのなかでこれだけが名前以外のアトリビュートを持っている(こういう例外を作るなよなあ)。
さらに、エレメントとしてIntSwissKnifeを持つノードがいくつかある。IntSwissKnifeがエレメントとして使われる場合は名前を持たないので、ノードとしての役割はなく、単に汎用計算機として使っているだけである。
最後にStructRegである。これはStructEntryというノードを複数持つ。これは実はGroupと同じで、それぞれのStructEntryは同じレジスタを参照するMaskedRegノードに展開されることを期待している。しかしはっきり言ってXMLファイルの解析を複雑にするだけで、このノードが定義されていることによるカメラのエンドユーザへの利益(例えばそれでなければ表現できない機能があるとか)はまったくない。普通の規格なら積極的に排除すべきものである。
LinuxでのOpen Source実装であるaravisも、ノードを基底クラスのサブクラスとして実装していて、サブクラスはクラス定義だけのヘッダファイルがずらっと並んでいる。
でもaravisも結局サブクラスで個別に実装する部分ばっかりになって、プロトコルを適用するだけの場合とあまり違わなくなってしまう。こうなってしまうんだよなあ。めんどくさいなあ。
えらいこっちゃ....
ということで今日は前回の続き、GEN<i>CAMに現れるノードが持つエレメント(ノードのプロパティ)の実装について。
10 ノードの持っているエレメントの整理
実装に入ろうと思ったんだけど、すっきり始められない。なんでかというと、GenApiのノード、つまりカメラの機能を表す単位だけど、それらはエレメント、つまり内部状態を保持するプロパティをたくさん持っている。ノードごとに違ったエレメントを持っているのは当たり前だと思うんだけど、そのノードのなかでの扱い方にいろいろな種類がある。単に値を保持しているだけのエレメントとその値を他のノードから得るエレメントがある。それぞれ存在するのは当たり前でわかるんだけど、意味によって扱い方が違っている。
例えばバイト幅を保持するためにはLengthとpLengthのふたつがあって、一方は定数を保持して、もう一方は参照するノードの名前を保持している。そしてどちらかを選択するようになっている。つまりひとつのノードに両方があることはない。
ところがレジスタのアドレスを保持するには定数用のAddressとノード参照用のpAddressとさらに間接アドレスのためのpIndexと、なんらかの計算を行ってアドレスを決めるためのIntSwissKnifeの4つがある。さらにこれがひとつのノードに同時に存在してもいいことになっていて、複数あるときは符号なし整数として全部を足した値がアドレスと解釈される。
アドレスの値が必要なノードは、どれとどれがXMLファイルの中に存在しているかを知っている必要がある。
こんなものはAddressというエレメントがひとつあって、Addressエレメントのなかで値を決定する方式を複数もたせた方が簡単になる。なぜならノードはAddressエレメントがただひとつ存在することを確認したら、アドレスの値はそのAddressエレメントに問い合わせるというただひとつのことができればいいだけである。Lengthも同じようにすればノードはその中身が定数なのか他のノードを参照しているのかを知る必要はなく、Addressとまったく同じ処理をすればいいだけである。ノードには他にもいろいろやることがあるので、なるべくシンプルにした方が実装上はいい。
ところがGEN<i>CAMの規格上はそうなっていない。すべてノードが判断してバイト幅を知るならこう、アドレスを知るならこう、と別々の処理することになる。こういう直交性の悪さは実装を複雑にする原因となるので、避けるのが普通だけど、そうなっていないのは作った奴の頭が悪いか、他にしがらみがあるのかのどちらかである。
またイライラしてきたので文句になってしまったけど、文句を言っててもしょうがないので、まず、エレメントとその使われ方を整理することにする。そうしないとクラス設計がそもそも始められない。
10.1 エレメントの分類
エレメントにはいろいろな分類が必要になる。それぞれを区別しないと実装できない。10.1.1 エレメントの型
ノードにInterfaceという型があるように、エレメントにも型がある。細かくはスキーマファイルの「???_t」とあるのがそれで、合計27個ある。わずらわしい。おおまかには
- 文字列
- 整数
- 浮動小数点数
- 列挙型(含BOOL型)
- 式
実装のために全部列挙する。 次のリストの2レベル目はスキーマファイルにある型で、3レベル目はエレメントの名前である。
- 文字列
- CName_t(名前などの汎用の文字列)
- pで始まるエレメント全部
- ModelName
- VendorName
- Name
- nonEmptyString_t(説明など)
- ToolTip
- Description
- DisplayName
- Unit
- ValidValueSet
- GUID_t
- ProductGuid
- VersionGuid
- DocURL_t
- UpperCaseString_t
- SwissKnifeConversion_t
- VariableName_t
- CName_t(名前などの汎用の文字列)
- 整数
- HexOrDecimal_t
- Address
- Value
- Index
- ValueDefault
- Min
- Max
- CommandValue
- OnValue
- OffValue
- DisplayPrecision
- Offset
- PositiveHexOrDecimal_t
- Length
- Inc
- nonNegativeHexOrDecimal_t
- SchemaMajorVersion
- SchemaMinorVersion
- SchemaSubMinorVersion
- MajorVersion
- MinorVersion
- SubMinorVersion
- PollingTime
- Bit
- LSB
- MSB
- Hex_t
- EventID
- ChunkID
- IntegerLength_t
- xs:float(不動小数点は汎用タイプ)
- Value
- ValueDefault
- Min
- Max
- Inc
- HexOrDecimal_t
- 列挙型
- MergePriority_t(-1、0、1)
- MergePriority
- Sign_t(Signed、Unsigned)
- Sign
- NameSpace_t(Custom、Standard)
- NameSpace
- StandardNameSpace_t(None、IIDC、DEV、CL、USB)
- StandardNameSpace
- IntRepresentation_t(Linear、Logarithmic、Boolean、PureNumber、HexNumber、IPV4Address、MACAddress)
- Representation
- FloatRepresentation_t(Linear、Logarithmic、PureNumber)
- Representation
- Access_t(RO、WO、RW)
- AccessMode
- ImposedAccessMode
- Endianess_t(LittleEndian、BigEndian)
- Endianess
- Visibility_t(beginner、Expert、Guru、Invisible)
- Visibility
- YesNo_t(Yes、No)
- IsDeprecated
- ExposeStatic
- Streamable
- IsLinear
- SwapEndianess
- CacheChunkData
- Slope_t(Increasing、Decreasing、Varying、Automatic)
- Slope
- DisplayNotation_t(Automatic、Fixed、Scientific)
- DisplayNotation
- MergePriority_t(-1、0、1)
- 式 スキーマ上はただのCName_t
10.1.2 定数と参照
また、型とは違う属性のひとつとして、定数を保持するエレメントなのか参照先を保持しているのかの違いがある。 これはAddressとpAddressの違いのように、エレメントの名前の頭に「p」がつくかそうでないかで区別できる。値の取り出し方が違うのでこれも区別しないといけない。pのついた参照タイプのエレメントはスキーマ上はすべてCName_tになっているけど、pのないエレメントは型がいろいろになっている。従ってpつきのほうはセマンティクスによって型を分類しないといけない。またpつきだけあってpなしがないこともあって、さっきの型と直交しているとは言えないので、すべて独立にあつかう必要がある。
pありなし両方あるエレメントは
- Address、pAddress
- Length、pLength
- Value、pValue
- Min、pMin
- Max、pMax
- Inc、pInc
- Offset、pOffset
- CommandValue、pCommandValue
- ChunkID、pChunkID
- pError
- pIndex
- pOffset
- pPort
- pIsImplemented
- pIsValiable
- pIsLocked
- pBlockPolling
- pInvalidator
- pAlias
- pCastAlias
- pFeature
- pValuCopy
- pValurIndexed
- pValuedefault
- pSelected
10.1.3 uniqueness
また、エレメントの別の属性として一つのノードの中にいくつあるか、でも区別する必要がある。 日本語でなんて言えばいいのかよく分からないけど、ひとつのノードの中に出現する回数による分類である。すなわち- 必ず1個なければいけない(Lengthなど)
- 0個あるいは1個(DisplayName、Cachableなど)
- 1個以上いくつでも(StructEntryなど)
- 0個以上いくつでも(pInvalidatorなど)
- 他のエレメントを含めてひとつ以上(AddressとpAddressなど)
前にも書いたけど、設計としてはもう一つ下のレイヤの仕事にしたほうが簡単になる(例えばpInvalidatorがノードの中にはただひとつだけ必ずあることにして、invalidateする条件の数の管理はpInvalidatorエレメントが一括する)。そうすればノードオブジェクトはinvalidateする条件をただひとつあるエレメントに問い合わせればいいだけになるし、ほかのエレメントも同じにすれば、すべてのエレメントに同じ形式で問い合わせることができる。うまく設計すればノードの持つエレメントは集合(NSSet)として保持することができるはずであるが、残念ながらそうなっていない。
10.1.4 排他性、共存性
例えばLengthとpLengthはどちらか片方がなければいけない。これらはお互いに排他的である。一方、Address、pAddress、IntSwissKnife、pIndexはスキーマ上は最低どれかひとつあって、同時に複数存在してもいいということになっている。同時にあった場合は結果の合計をとることになっている。これらは共存的である。しかし、Addressエレメントがふたつあってもしょうがないし、pAddressとpIndexが同時に存在する場合に、もしpAddressの参照先とpIndexのベースアドレスの参照先が同じだったらどうするだ?というような疑問もある。
10.1.5 内部構造の有無
スキーマ上はxs:simpleTypeとxs:complexTypeという分類があるがそのことではない。エレメントに関するxs:simpleTypeとxs:complexTypeの分類のことである。内部構造というのは、エレメントが複数の要素からできているということで、これには4通りある。ひとつはEnumerationで、このエレメントはさらにEnumEntryというノードを含んでいる。
もうひとつはpIndexで、これはpAddressと同じようにアドレスの値を参照して、さらにOffsetあるいはpOffsetというXMLのアトリビュートを持つことができる。エレメントのなかでこれだけが名前以外のアトリビュートを持っている(こういう例外を作るなよなあ)。
さらに、エレメントとしてIntSwissKnifeを持つノードがいくつかある。IntSwissKnifeがエレメントとして使われる場合は名前を持たないので、ノードとしての役割はなく、単に汎用計算機として使っているだけである。
最後にStructRegである。これはStructEntryというノードを複数持つ。これは実はGroupと同じで、それぞれのStructEntryは同じレジスタを参照するMaskedRegノードに展開されることを期待している。しかしはっきり言ってXMLファイルの解析を複雑にするだけで、このノードが定義されていることによるカメラのエンドユーザへの利益(例えばそれでなければ表現できない機能があるとか)はまったくない。普通の規格なら積極的に排除すべきものである。
10.1.6 内容
さらに、セマンティクスによる分類が必要となっている。スキーマでは正確に表現できないので、名前あるいは動作から解釈する必要がある。例えば整数の型を持つものだけについて列挙してみる。- アドレスを表す。Address、pAddress、pIndex、IntSwissKnife
- バイト長さを表す。Length、pLength
- (エレメントではなく)ノードの値を表す。Value、pValue、ValueIndexed、pValueIndexed
10.2 分類のまとめ
とまあ、こういう具合で非常に煩わしい。これらを考慮できる実装でなければならない。しかもセマンティクスによって処理が違う場合があるのでなるべく共通の実装法をとろう、つまり汎用の基底クラスからサブクラスを派生する形でやろうと思っても、結局サブクラスだらけになって共通の実装がほとんどない、ということになってしまう。LinuxでのOpen Source実装であるaravisも、ノードを基底クラスのサブクラスとして実装していて、サブクラスはクラス定義だけのヘッダファイルがずらっと並んでいる。
でもaravisも結局サブクラスで個別に実装する部分ばっかりになって、プロトコルを適用するだけの場合とあまり違わなくなってしまう。こうなってしまうんだよなあ。めんどくさいなあ。
2016-01-11 21:38
nice!(0)
コメント(0)
トラックバック(0)
コメント 0