2 GenApi模块 – 配置相机
2.1. 简介
GenApi模块解决如何去配置相机的问题。主要的思路是,让相机生产厂商为他们的相机提供机器可以识别的产品说明。这些相机描述文件(camera description files)包含所有需要的信息,用以自动地把相机的属性(features)和其寄存器(registers)相对应。
相机的Gain属性是一个典型的例子,假设用户想令Gain=42,利用GenICam,通用的软件可以读相机的描述文件并发现,要把Gain属性设成42意味着向地址为0x0815的寄存器写入值0x2A。其他要做的工作可能是检查相机是否提供Gain属性,并检查要写入的值是否在Gain的允许范围内。
请注意,给相机添加新的属性仅仅意味着扩展相机的描述文件,就可以对所有符合GenICam标准的程序立即生效。
图2 Layers for accessing a camera
2.3. 节点、接口和抽象特征
相机描述文件中的每个节点只描述一个项目。基于项目的自然性,节点有一个特定的类型(node type)和一个特定的接口(interface)。下列接口目前可用3(每个接口有一个控件用于映射到GUI):
l IInteger – 映射到一个带有value、min、max和increment的slider
l IFloat – 映射到一个带有value、min、max和一个物理单位的slider
l IString – 映射到一个显示字符串的编辑框
l IEnumeration – 映射到一个下拉框
l ICommand – 映射到一个命令按钮
l IBoolean – 映射到一个复选框(check box)
l IRegister – 映射到一个显示16进制字符串的编辑框
l ICategory – 映射到一个可以反映相机属性结构的树控件
l IPort – 映射到一个相机端口,通常不用图形显示
第2.9章给出了接口特性的更多细节。可用的节点类型在第2.8章中说明。可能有多种节点类型实现同样的接口类型。例如,IInteger接口,被下列(不是全部)节点类型实现:
l IntReg – 根据字节边界,从寄存器取出一个整数
l MaskedIntReg – 从寄存器的一段取出一个整数,例如,从第8位到第12位
l Integer – 从不同的节点得到value、min、max和increment属性,合并在一起
每个节点类型从不同的源,用不同的方法取出一个整数值。对于需要输入一个整数值的连接,所有这些节点的输出值都可以用作类型安全的输入。
当用户读或写一个节点的值,节点会触发节点图内一系列的读写操作。为了说明这一点,图4显示了Gain属性的一个更复杂的例子。Gain属性可以抽象成一个IInteger接口,通过这个接口,用户可以设置Value并且可以读(或其它操作)Min和Max值。图4的例子假定相机有3个寄存器,一个是Gain的Value,另两个是Min和Max。利用IntReg节点可以从每个寄存器取出相应的值。名字为Gain的Integer节点收集并合并这些数据,再通过IInteger接口把结果传递出来。
图4 Example of the control flow when getting and setting features
如果用户读取Gain节点的值,调用会被分派到GainValue节点,而GainValue节点会通过IPort接口向Device节点查询正确的寄存器。
如果用户试图设置Gain节点的值,实现程序可能首先会从GainMin和GainMax节点读出Min和Max值以检查范围。如果输入值在允许的范围内,Gain节点会通过GainValue节点和Device节点把值写入相机。注意,根据相应IntReg节点的Cacheable属性,实现程序可能会把Min和Max值放入缓存。
2.5. 访问模式
每个节点都有一个下表定义的访问模式:
Readable |
Writable |
Implemented |
Access Mode |
* |
* |
0 |
NI – 未实现 |
0 |
0 |
1 |
NA – 不可用 |
0 |
1 |
1 |
WO – 只写 |
1 |
0 |
1 |
RO – 只读 |
1 |
1 |
1 |
RW – 读写 |
1 = yes, 0 = no, * = don’t care
属性可能已经被相机实现,但暂时不可用。如果可用,则根据定义,属性被实现并且可以读和/或写。
有些节点由某些元素来控制访问权限,例如寄存器节点(参见2.8.3)。另外,GenICam还提供3种在运行时改变访问权限的机制。
l 根据另一个节点的值,属性可能暂时被锁定(locked)。属性在锁定的状态下不可写。根据上面的表格,写标志位暂时强制为0。
l 根据另一个节点的值,属性可能暂时不可用(not available)。根据上面的表格,写标志位和读标志位暂时强制为0。
l 根据另一个节点的值,属性可能没有实现(not implemented)。根据上面的表格,实现标志位始终强制为0。
“可用”和“已实现”是不同的,因为GUI可能想用不同的方法来处理这两种情况。未实现的属性不会显示给用户;而暂时不可用的项目只是灰色的并且值会被替换,例如替换成“—”;暂时被锁定的属性是灰色的,但是属性的值仍然会显示。
以硬件Trigger为例来说明让一个属性暂时不可用(temporarily not available)的情况,硬件Trigger的值可以为On或Off。如果Trigger是On,另外一个属性TriggerPolarity变得可用,TriggerPolarity属性指示硬件信号是ActiveHigh还是ActiveLow。如果Trigger是Off,则TriggerPolarity属性是无意义的,应该被置为灰色。
图5显示了这个信息在相机描述文件中如何处理,Trigger和TriggerPolarity属性用Enumeration类型的节点来实现,这种类型把一组枚举型的入口映射到整数值。例如,Trigger属性的入口是On=1和Off=0。利用IntReg类型的节点把整数值映射到寄存器。
图5 Controlling whether a feature is accessible
TriggerPolarity节点有个叫pIsAvailable的连接,这个连接需要指向一个提供IInteger接口的节点。如果这个被指向的节点的值为0,那么TriggerPolarity节点暂时不可访问。
在这个例子中,pIsAvailable可以直接指向TriggerReg,因为Trigger=On被映射为1,Trigger=Off被映射为0。如果不是这样的话,一个类型为IntSwissKnife的节点会很有用,它能根据数学公式计算其他整数节点的值,并得到一个整数的结果。在XML文件中,节点看起来像这个样子:
- <IntSwissKnife Name="TriggerEnabled">
- <ToolTip>Determines if the Trigger feature is switched on</ToolTip>
- <pVariable Name="TRIGGER">TriggerReg</pVariable>
- <Formula>TRIGGER==1</Formula>
- </IntSwissKnife>
<Formula>入口的数学公式被计算,得到节点的计算结果。在计算之前,变量的符号名称被对应节点的整数值替换。在本例中,只有一个<pVariable>入口指向TriggerReg节点,符号名称是TRIGGER。这个名称在公式“TRIGGER==1”中也有。
如果GUI升级了,就会去查看TriggerPolarity节点是否可用。而TriggerPolarity节点会去检查IntSwissKnife,而IntSwissKnife又会根据TriggerReg节点的值去计算结果。
兼容DCAM的1394相机的BytesPerPacket属性是“暂时锁定属性”( temporarily locked)的一个典型例子。用户可以改变相机的这个参数,仅当PC适配器的DMA未被设定为采集图像的时候5。设定DMA的意思是,传输层查询相机的BytesPerPacket参数,并把这个参数设置到DMA。这个工作完成后,直到传输层释放DMA之后才能改变BytesPerPacket。在这之前,相机的这个参数必须被锁定。
注意,相机本身没有办法知道DMA是否设置。因此,相机描述文件中“普通的”节点不能被用来控制BytesPerPacket的锁定状态。
图6 Locking a feature
GenApi内的解决方案是提供一个浮动的Boolean型节点TLParamsLocked(参见图6)。BytesPerPacket通过pIsLocked连接到这个节点。传输层(TL)需要通过更新TLParasLocked节点的值来反映其DMA状态。在设定DMA之前,它会通过令TLParamsLocked为true的方式来锁定相机参数(例如BytesPerPacket),在采集图像完成之后,它会把TLParamsLocked设置回false。改变TLParamsLocked节点会更新所有关联节点的锁定状态,例如BytesPerPacket节点。
注意,为保证这种方式正常工作,TLParamsLocked必须是标准的节点名,并且传输层必须有访问相机的GenApi接口的权限。另外,相机描述文件的设计者必须注意哪个参数要被传输层锁定。这个信息包含在传输层标准之中,例如DCAM规范,这个规范规定,在采集数据的时候每桢的包数和每个包的大小必须固定。
“属性没有被实现”( not implemented)的一个典型的例子是,同一个家族的某些相机有Gamma属性而有些则没有。如果相机有一个查询位(inquiry bit)来标识是否实现了Gamma属性,就可以为这个家族的所有相机维护一个相机描述文件。
图7显示了GenICam如何处理这种情况。Gamma属性节点有一个叫pIsImplemented的连接指向GammaInq节点,GammaInq节点映射相机的查询位。通常把多个查询位合并到一个寄存器里。为了取得这些位,要使用MaskedIntReg节点类型。这个类型有点像IntReg节点,但是,你可以像查询整数一样取查询想要的一个或一组连续的位。
图7 Checking whether a feature is implemented