8.10.Enumeration, EnumEntry
Enumeration 节点把一个名称(name)映射到一个索引值(index value),并实现 Ienumeration 接口。Enumeration 节点拥有一系列 EnumEntries ,每一个都表现为可能的 {name, index} 对。Enumeration 节点从 Node 节点继承元素和属性。另外,它要么有一个表现当前索引值的 ,要么有一个连接到 IInteger 节点的 <pValue> 元素。
下面的例子是用 Enumeration 来描述相机的 ColorCode 。如果 ColorCodeReg 被设定为 1 ,则相机就是 Mono16 。
<Enumeration Name="ColorCode"> <EnumEntry Name="Mono8"> <Value>0</Value> </EnumEntry> <EnumEntry Name="Mono16"> <Value>1</Value> </EnumEntry> <EnumEntry Name="YUV422"> <Value>3</Value> </EnumEntry> <pValue>ColorCodeReg</pValue> </Enumeration> <IntReg Name="ColorCodeReg"> <Address>0x1234</Address> <Length>1</Length> <AccessMode>RW</AccessMode> <pPort>Device</pPort> <Sign>Unsigned</Sign> <Endianess>BigEndian</Endianess> </IntReg>
经常发生的情况是,枚举值列表中的某些值暂时不可用,因而不应该显示给用户。为用 GenICam 来描述这种情况,你可以用 EnumEntry 子节点中的 <pIsImplemented> 和 <pIsAvailable> 元素,就像你可以用其它任何节点一样。
通常,实现程序会预处理相机描述文件,并会为每个 EnumEntry 创建一个独立的节点,节点的 Name 是 “EnumerationName_EnumEntryName” 。在 Enumeration 节点中放入一个 <pEnumEntry> 元素以代替 EnumEntry 本身。在新创建的 EnumEntry 节点内,原来的 EnumEntry 名称被复制到 <Symbolic> 元素。枚举入口点所代表的索引值被复制到 EnumEntry 的 <Value> 元素。
Enumeration 节点也可以有一个 <pSelected> 元素。参见 8.4。
8.11.StringReg
字符串是一个(可能是以 NULL 结尾的)ASCII 字符串,存放在相机地址空间的某处,字符串通过一个 IString 接口来操作。 下面的例子显示了用一个 StringReg 节点来取得相机型号名的方法。我们假定 ModelName 最多可以有 128个 字节,包括结尾的空字符。
<StringReg Name="ModelName"> <Address>0x1234</Address> <Length>128</Length> <AccessMode>RO</AccessMode> <pPort>Device</pPort> </StringReg>
你可以通过 IString 来取得并设置一个字符串。
8.12.SwissKnife, IntSwissKnife, Converter, and IntConverter
为在 GenICam 中进行数学运算,我们引入了两个节点,SwissKnife(瑞士军刀) 节点用来处理浮点数,IntSwissKnife 节点用来处理整数。两个节点有相同的语法。
下面的例子显示了得到两个数字计算结果的方法。XTimesY 节点引出一个 IInteger 接口,通过这个接口可以读出 504 (=12*42):
<IntSwissKnife Name="XTimesY"> <pVariable Name="X">XValue</pVariable> <pVariable Name="Y">YValue</pVariable> <Formula>X*Y</Formula> </IntSwissKnife> <Integer Name="XValue"> <Value>42</Value> </Integer> <Integer Name="YValue"> <Value>12</Value> </Integer>
<Formula> 元素包含一个数学公式,公式指向由 <pVariable> 元素定义的变量,<pVariable> 元素指向一个 IInteger 节点,并拥有一个定义了公式中变量名的 Name 属性。变量名必须是大写的。
参考实现中使用的瑞士军刀功能相当强大。不过,为简化那些想自己写实现的人的工作,标准仅允许有限的一组数学操作。标准支持下面的操作:
符号 | 名称 |
( ) | 括号 |
+ - * / | 加减乘除 |
% | 取模 |
** | 乘方 |
& | ^ ~ | 按位与 / 或 / 异或 / 非 |
<> = > < <= >= | 逻辑关系 不等于 / 等于 / 大于 / 小于 / 小于等于 / 大于等于 |
&& || | 逻辑与 / 或 |
<< >> | 按位左移,按位右移 |
条件表达式:
<条件> ? <真操作.> : <假操作>
<condition> ? <true expr.> : <false expr.>
函数:
SGN, NEG,
仅对 SwissKnife 提供的函数,不对 IntSwissKnife 提供:
ATAN, COS, SIN, TAN, ABS, EXP, LN, LG, SQRT,
TRUNC, FLOOR, CEIL, ROUND( x, precision ),
ASIN, ACOS, SGN, NEG, E, PI
当把公式嵌入 XML 文件的时候,又引发了新的问题:不能直接使用 <,> 和 & 字符,因为它们是 XML 语法的一部分。关于这个问题有两个可能的解决方案。
第一,你可以像下面这样替换这些字符:
结果,公式 (x>0) && (x<10) 变成
<formula>(x > 0) && (x < 10)</formula>
- 第二,你可以把整个公式声明成非 XML 文本,方法是把它们用
<![CDATA[
和]]>
括起来。这样的话公式就变成了:
<formula><![CDATA[ (x>0) && (x<10) ]]>/formula>
与只读的 SwissKnife 不同,Converter 可以双向工作。它实现一个 Ifloat 接口,这一点有点像 SwissKnife ,不过它还另有一个 <pValue> 元素,这个元素可以指向一个 IInteger 或 Ifloat 接口。它有两个公式:<FormulaFrom> 描述从 int 生成 float 的方法; <FormulaTo> 描述从 float 生成 int 的方法。<Slope> 入口表示这个公式是否是单调 Increasing(增加的) 或 Decreasing(减少) ,或者是 Varying(变化的)(这种情况下,使用整个数字范围),或者 slope 是 Automatic 方式决定的。
下面的例子显示了一个 Converter 计算绝对快门值(一个浮点数)的方法,做法是把一个原始快门值(一个整数)和一个时间基数(另一个整数)相乘。
<Converter Name="ShutterAbs"> <pVariable Name="TIMEBASE">TimeBase</pVariable> <FormulaTo> FROM / TIMEBASE </FormulaTo> <FormulaFrom> TO * TIMEBASE </FormulaFrom> <pValue>ShutterRaw</pValue> <Slope>Increasing</Slope> </Converter> <Integer Name="ShutterRaw"> <Value>2</Value> </Integer> <Integer Name="TimeBase"> <Value>10</Value> </Integer>
IntConverter 很像 Converter,只不过它实现一个 IInteger 接口。
8.13.ConfRom, TextDesc, and IntKey
1394 相机用的 DCAM 标准实现一个树形数据结构的配置 ROM ,它由 IEEE 1212 标准定义。它在相机上下文中的主要作用是,提供型号名称,制造商名称,所支持的标准版本接口,以及 DCAM 标准寄存器的基地址。由于 IEEE 1212 兼容配置 ROM 的特殊排列,引入了一个特殊的 ConfROM 节点,以提供对所有这些信息的访问。
在下面的例子中,我们通过单元 ID 查找一个描述 DCAM 兼容相机的单元目录,单元 ID 由 <Unit> 元素给出。在这个单元中,加入了三个入口,作为子节点。<IntKey> CommandRegBase 元素会转换成一个带 IInteger 接口的节点,用于读 DCAM 寄存器的基地址。<TextDesc> VendorName 和 ModelName 元素转换成带 IString 接口的节点,用于读相机的制造商和型号名称(脚注:注意,不要求配置 ROM 中的字符串是以 NULL 结尾的,参见 IEEE 1212 ),元素中的 16 进制数值是各自的 key 值,和入口一起储存在单元目录中。
<Category Name="Root"> <pFeature>CommandRegBase</pFeature> <pFeature>VendorName</pFeature> <pFeature>ModelName</pFeature> </Category> <ConfRom Name="ConfRom"> <Unit>0x00A02D</Unit> <Address>0x400</Address> <pAddress>InitialNodeSpace</pAddress> <Length>0x400</Length> <pPort>Device</pPort> <IntKey Name="CommandRegBase">0x40</IntKey> <TextDesc Name="VendorName">0x81</TextDesc> <TextDesc Name="ModelName">0x82</TextDesc> </ConfRom> <Integer Name="InitialNodeSpace"> <Value>0xFFFFF0000000</Value> </Integer>
注意,ConfROM 节点有 <Address>,<pAddress>,<IntSwissKnife>,<Length> 和 <pPort> 元素,它们的含义和别的 Registers 一样(参见 2.8.3)。
典型的实现会为 <IntKey> 和 <TextDesc> 元素各自创建节点,区分的方法是通过各自入口的 Name 属性,一个指向 ConfROM 节点的 <p1212Parser> 元素,以及一个带有相应 key 值的 <Key> 元素。
8.14.DcamLock and SmartFeature
目前,大多数标准寄存器的构造是固定的,需要提供机制和方法来访问那些没有在标准中定义的自定义属性。GenICam 目前支持两种机制。
DcamLock 节点可以得到根据 DCAM 高级属性机制提供的智能属性的地址,它从 Register 节点继承元素和属性。在下面的例子中,我们对一个高级 DCAM 属性解锁,属性的 <FeatureID> 元素是 0x0030533B73C3 ,其中 0x003053 是制造商的 ID,0x3B73C3 是这个制造商所定义的属性 ID 。<Timeout> 元素的值是 0,意味着这个属性不会自动解锁。
<AdvFeatureLock Name="BaslerAdvFeatureLock"> <FeatureID>0x0030533B73C3</FeatureID> <Timeout>0</Timeout> <Address>0xfffff2f00000</Address> <Length>8</Length> <AccessMode>RW</AccessMode> <pPort>Device</pPort> </AdvFeatureLock>
如果一个智能属性在 <FeatureID> 元素中给定了一个 GUID,则 SmartFeature 节点可以得到这个属性的地址。它也从 Register 节点继承元素和属性。下面的例子中,我们取得一个智能属性的地址,属性的 GUID 是 {5590D58E-1B84-11D8-8447-00105A5BAE55} :
<SmartFeature Name="TimeStampAdr"> <FeatureID>5590D58E-1B84-11D8-8447-00105A5BAE55</FeatureID> <Address>0xfffff2f00010</Address> <pPort>Device</pPort> </SmartFeature>
8.15.Port
Port 对象仅仅是个代理,它把读写请求转送给传输层。不过要注意,这个代理有 Node 的所有特征,例如,它可以是 “未实现” ,这样就把传输层驱动暂时没有打开的信息告诉了所有从属节点,结果所有的从属属性也自动变成了 “未实现” 。另一个例子是用户设定加载器的实现。如果把一个用户设定从闪存加载到相机,则节点图内所有的属性都要无效。简单地令 Port 节点无效就可以实现这一点,用一个连接到 ReadUserSet 属性节点的 <pInvalidator> 就可以自动实现 Port 节点的无效。
如果传输层有最大数据长度的限制,或者需要特别的对齐方式,例如按 quadlet 对齐,传输层的实现必须模拟 Iport 接口,把超出最大长度的请求分成多条请求,给不符合对齐要求的请求补上附加数据。为支持某些处理 quadlet 的接口,引入了 <SwapEndianess> 元素:如果它是 true,那么在通过 Iport 接口向 GenICam 提供数据之前,每个 quadlet 的字节序都要转换。
Port 从 Node 节点继承元素和属性。另外,它有一个用来标识缓冲区内大块数据的元素。这个大块数据可能被映射到一个虚拟端口,这个虚拟端口不提供对真实设备的访问,但是提供对内存中大块数据的访问。
<Port Name="Device" NameSpace="Standard"> <ChunkID>4711</ChunkID> </Port>
8.16.Group element
元素可以让一个大的相机描述文件更具可读性。如下所示,这个元素可以把节点封装成很多块:
<Category Name="Root"> <pFeature>Analog</pFeature> <pFeature>Trigger</pFeature> </Category> <Group Comment="Analog section"> <Category Name="Analog"> <pFeature>Shutter</pFeature> <pFeature>Gain</pFeature> <pFeature>Offset</pFeature> </Category> <IntReg Name="Shutter"> <!-- more elements --> </IntReg> <IntReg Name="Gain"> <!-- more elements --> </IntReg> <IntReg Name="Offset"> <!-- more elements --> </IntReg> </Group> <Group Comment="Trigger section"> <!-- more elements --> </Group>
XML 编辑器应该可以隐藏一个组的内容,像下面的截屏图一样:
<Group> 节点有一个 Comment 属性,当组被折叠的时候,编辑器会显示这个属性。组可以在任何深度展开。组对相机的功能没有任何影响,当解析相机描述文件的时候,会忽略它们。