这个记录类型通常用于发送一个模拟输出值到一个输出设备,如果需要,把它从工程单位转成一个整数值。这个记录支持警报和驱动限制,变化率限制,输出值积分,从工程单位线性和断点表转换,以及图形和控制限制。
记录专用菜单
菜单aoOIF
使用这个菜单的OIF字段控制记录是否用作一个增量(Incremental)或(Full)。
参数字段
在以下描述这个记录特定的字段。
这些字段控制了这个记录在其运行时如何确定被被输出的值:
在记录运行过程中按顺序执行以下步骤:
获取值,积分
OMSL菜单字段用于在运行过程中是否应该使用DOL链接以及OIF字段:
- 如果OSML是superviosry, 不使用DOL和OIF字段。从VAL字段获取新的输出值,可能已经从别处设置了它。
- OMSL是closed_loop,读取DOL链接字段获取一个值;如果OIF是Incremental,并且成功读取DOL链接,这个记录的先前输出值PVAL被加到它。
驱动限制
只要DRVH>DRVL时,输出值现在被限制到了DRVL到DRVH的范围内。结果被复制到了VAL和PVAL字段。
变化率
如果OROC字段不是0,VAL字段现在被调整这样,它与在OVAL中给定的先前输出值的差别不多于OROC。OROC因而确定了在每次运行这个记录时输出值能够发生的最大变化。结果被复制到了OVAL字段,它被用作对接下来单位转换阶段的输入。
单位转换
对于不使用Soft Channel设备支持例程的模拟输出记录,对OVAL字段执行指定的转换(如果存在)并且在其被在AOFF和ASLO字段中的值调整后,在RVAL字段中产生的值被发送到在输出链接中包含的地址。
转换相关的字段和转换过程
除了使用Soft Channel设备支持的模拟输出记录外,LINR字段确定了是否执行一个转换以及使用哪种算法转换OVAL到RVAL。
LINR字段可以为线性转换指定LINEAR或SLOPE,NO CONVERSION对应完全不转换,诸如typeKdegC的断点表名称对应断点表。
应该为LINEAR转换设置EGUF和EGUL字段,为SLOPE转换设置ESLO和EOFF。注意:对于使用Soft Channel设备支持的记录,所有这些字段都没有作用。
EGUF,EGUL:当为使用LINEAR转换的记录配置数据库时,用户必须设置这些字段。它们用于计算ESLO和EOFF。有关如何计算这些字段的更多信息,见转换说明。
ESLO,EOFF:当LINR指定LINEAR时,从EGUF和EGUL计算其由设备支持计算。当LINR字段指定SLOPE时,这些值必须由用户提供。
AOFF,ASLO:这些字段字段是用于raw输出值的调整参数。在从工程单位转换后,它们应用于raw输出值。
ROFF:这个字段用于补偿由转换过程产生的raw值,某些硬件类型需要它。
转换过程如下:
1) 如果LINR==LINEAR或LINR==SLOPE,则X=(VAL-EOFF)/ESLO,或者如果LINR=NO_CONVERSION,则X=VAL,否则通过断点表获取X。
2) X=(X-AOFF)/ASLO
3) RAL=round(X)-ROFF
要看Raw Soft Channel设备支持例程如何使用这些字段,更多信息见以下"Device Support For Soft Records"。
输出格式
模拟输出记录发送其所需输出到在OUT字段中的地址。对于写它们的值到设备的模拟输出记录,OUT字段必须指定I/O板卡的地址。此外,DTYP字段必须包含设备支持模块的名称。小心:根据使用的I/O总线,地址格式不同。有关硬件地址的格式信息见"Address Specification"。
操作显示参数
这些参数用于向操作者显示有意义的数据。它们以文本或图形显示模拟输出的值和其它参数。EGU是一个最长16个字符的字符串,它描述了这个模拟输出测量的单位。通过get_units列出获取它。HOPR和LOPR字段为VAL,OVAL,PVAL,HIHI,HIGH,LOW和LOLO字段设置上下显示限制。get_graphic_double和get_control_double记录支持例程获取这些数据。如果这些值被定义了,它们必须在DRVL<=LOPR<=HOPR<=DRVH。
PREC字段去顶了用哪个浮点精度显示VAL,OVAL和PVAL。当get_precision记录支持例程被调用时,使用它。
有关记录名(NAME)和描述(DESC)字段的更多信息见Fields Common to All Record Types。
用于警报的参数
模拟输出记录的可能警报情况是SCAN,READ,INVALID和限制警报。SCAN,READ和INVALID警报被记录或设备支持例程调用。
用户在HIHI,LOLO,HIGH和LOW字段中配置限制警报,它们必须是浮点值。对应这些字段中每个字段,有一个相应的严重性字段,其可以是NO_ALARM, MINOR或MAJOR。
有关IVOA和IVOV字段的更多信息见Invalid Output Action Fields。
Alarm Fields列出与所有记录类型共有的警报相关联的其它字段。
用于监视的字段
这些参数用于为VAL字段指定死区。当这个值字段超过了上次受监视的字段指定死区时,发送这些monitors。如果这些字段有一个0值,这个值每次变化时,将触发一个monitor;如果它们有一个-1值,当每次运行这个记录时,触发monitors。ADEL是用于存档monitors的死区,而MDEL是用于所有其它类型的monitors。monitors的完整解释见Monitor Specification。
运行时参数
这些参数由运行时代码使用用于运行这个模拟输出记录。它们是不可配置的。它们代表这个记录的当前状态。记录支持例程为了高效运行使用它们中的一些。
ORAW字段用于确定当为VAL触发monitors时,是否应该为RVAL触发monitors。RBV字段是从硬件自身或从相关联设备驱动程序获取的回读值。为这个字段赋一个值是设备支持例程的责任。
ORBV用于确定在为VAL中变化触发monitors的同时是否为RBV触发monitors。
INIT字段用于初始化LBRK以及用于平滑。
PBRK字段包含一个指向当前断点表(如果有)的指针,而LBRK包含一个指向上次被使用的断点表的指针。
OMOD字段指示OVAL似乎否不同于VAL。如果从记录上次运行以来,VAL或OVAL发生变化了,或者在当前运行过程中VAL已经被OROC调整了,它将不同。
仿真模式参数
以下字段用于在仿真模式中操作这个记录。
如果SIMM(通过SIML获取)是YES,记录被置入SIMS严重性并且通过SIOL不经转换写这个值。SSCN设置在仿真模式中要使用的一个不同SCAN机制。SDLY设置用于异步仿真运行的延时(以秒为单位)。
有关仿真模式和其字段的更多信息见Output Simulation Fields。
记录支持
记录支持例程
以下是应用程序开发者感兴趣的记录支持例程。其它例程是get_units, get_precision, get_graphic_double和get_control_double例程。
1) init_record
long init_record(aoRecord *prec, int pass);
如果SIML是一个常数,这个例程初始化SIMM,或者如果SIML是PV_LINK,创建一个通道访问链接。如果SIOL是PV_LINK,创建一个通道访问链接。
这个例程接着检查设备支持是否可用。如果DOL是一个常数,则用其值初始化VAL,并且UDF被设置为FALSE。
这个例程接着检查是否定义了设备支持写例程。如果设备支持或设备支持写例程中有一个不存在,发出一条错误消息并且终止正在运行的process。
为了兼容老的不知道EOFF的设备支持,如果EOFF和ESLO都有它们的默认值,EOFF设置为EGUL。
如果设备支持包含init_record(),调用它。
INIT设为TRUE。者使得PBRK,LBRK以及平滑被重新初始化。如果请求"反向"线性转换,则使用以下算法从RVAL计算VAL:
VAL = ((RVAL+ROFF) * ASLO + AOFF) * ESLO + EOFF
并且设置UDF为FALSE。
对于断点表转换,对cvtEngToRawBpt进行调用并且接着UDF被设置为FALSEA。PVAL被设置为VAL。
2) process
long process(aoRecord *prec);
3) special
long special(DBADDR *paddr, int after);
对于模拟输出记录的唯一speical运行是SPC_LINCONV,当字段LINR,EGUF,EGUL或ROFF中有任何一个字段被更改时,调用special。如果设备支持例程special_linconv存在,调用它。
INIT设置为TRUE。者使得PBRK,LBRK和平滑被再次初始化。
4) get_alarm_double
long get_alarm_double(DBADDR *, struct dbr_alDouble *);
设置以下值:
upper_alarm_limit = HIHI
upper_warning_limit = HIGH
lower_warning_limit = LOW
lower_alarm_limit = LOLO
记录运行实现以下算法:
记录运行
记录运行实现以下算法:
1) 检查合适的设备支持是否存在。如果它不存在,发出一条错误消息并且用PACT设为TRUE终止运行的process。这确保了将不再为这个记录调研processes。因而不会发生错误风暴。
2) 检查PACT:如果PACT是FALSE,调用fetch_values和convert,它们执行以下操作:
a) fetch_values:
- 如果DOL是DB_LINK以及OMSL是CLOSED_LOOP,则从DOL获取值。
- 如果OIF是INCREMENTAL,则设置value=value+VAL否则value=VAL
b) convert:
- 如果定义了Drive,强制value在这个限制之内
- 设置VAL等于value
- 设置UDF为False
- 如果OVAL没有被定义,设置它为value
- 如果OROC被定义了,斌且不为0,使得|value - OVAL|<=OROC
- 设置OVAL等于value
- 从OVAL计算RVAL,使用线性或断点表转换。对于线性转换,算法是RVAL=(OVAL-EOFF)/ESLO。
- 对于断点表转换,对cvtEngToRawBpt进行一次调用。
- 在那之后,对于所有转换类型,使用公式RVAL=(RVAL-AOFF)/ASLO-ROFF计算。
3)检查警报:这个例程检查新的VAL是否使得警报状态和严重性发生变化。如果这样,设置NSEV,NSTA。它也考虑警报回滞因子(HYST)。因而在警报状态和严重性被降低前,这个值必须至少变化了HYST。
4)检查严重性并且写这个新值。有关无效警报如何影响输出记录的细节见Invalid Alarm Output Action。
5) 如果PACT已经变为了TRUE,设备支持写输出例程已经开始,但还没有完成写这个新值。在这种情况中,运行的process例程仅返回,保留PACT为TRUE。
6) 检查是否应该调用monitors。
- 如果警报状态或严重性发生变化,调用警报monitors
- 如果ADEL和MDEL条件满足,调用存档和值变化monitors
- 当调用了其它monitors,检查RVAL和RBV的monitors。
- 重置NSEV和NSTA为0
7) 如果需要扫描forward链接,设置PACT和INIT为FALSE,并且返回。
设备支持
设备支持相关的字段
每个模拟输出记录必须有一个相关联的设备支持例程集合。设备支持例程的首要任务是在调用write_ao时输出一个新值。设备支持例程主要使用以下字段:
- PACT:运行有效,用于指示异步完成。
- DPVT:设备私有,为设备支持使用保留
- OUT:输出链接,提供寻址信息
- EGUF:工程单位满量程
- EGUL:工程单位最低量程
- ESLO:工程单位斜率
- EOFF:工程单位偏移量
- OVAL:输出值,以工程单位
- RVAL:在转换后的Raw输出
设备支持例程
设备支持由以下例程组成:
1) report
long report(int level);
这个可选例程被IOC命令dbior调用并且被传递用户请求的报告等级。它应该打印一个有关设备状态的报告到stdout。level参数可以用于用更高等级输出更详细信息,或者用不同等级选择不同信息类型。等级0应该打印一小段概要。
2) init
long init(int after);
这个可选的例程在IOC初始化时被调用两次。第一次用整数参数after设为0在进行任何init_record()调用前发生。第二次调用用after设为1在进行了所有init_record()调用后发生。
3) init_record
long init_record(aoRecord *prec);
记录初始化代码为其DTYP字段被设置为使用这种设备支持的每个ao记录实例调用这个可选的例程。它通常用于检查OUT地址有期望的类型并且指向一个有效设备;分配任何记录专用缓存空间和其它内存;以及连接write_ao()例程正常工作所需的任何通信通道。
如果记录类型的单位转换特性被使用,init_record()应该从EGUL和EGUF字段值为ESLO和EOFF字段计算合适的值。只在记录的LINR字段设为LINEAR时,才执行计算,但首先检查那个条件不是必须的。相同计算发生在special_linconv()例程中,因此实现通常可以只调用那个例程执行任务。
如果最后的输出值可以从硬件回读,这个例程应该获取那个值并且放它到记录的RVAL或VAL字段。如果RVAL字段已经被设置,返回值应该是0,如果VAL字段已经被设置或者不能获取上个输出值,返回值为2。
3) get_ioint_info
long get_ioint_info(int cmd, aoRecord *prec, IOSCANPVT *piosl);
当这个记录的SCAN字段被更改为I/O Intr或者从值I/O Intr更改时,调用这个可选例程查找该记录应该被添加到或者被从哪个I/O中断扫描列表删除。如果没有提供这个例程,设置SCAN字段为值I/O Intr是完全不可能的。
当记录被添加到扫描列表时,cmd参数是0,并且当它被从这个列表删除时,cmd是1。例程必须确定记录应该被连接到哪个中断源,在返回前它用其在*piosl位置指向的扫描列表表明。通过返回一个非0值给其调用者,它能够完全阻止SCAN字段被更改。
在大多数请求下,通过为每个单独的中断源调用一次void scanIoInit(IOSCANPVT * piosl),设备支持将创建它为自己返回的I/O中断扫描列表。API分配内存并且初始化这个列表,接着在*piosl位置中回传一个指向新列表的指针。当设备支持接收到中断已经发生的通知时,它通过调用scanIoRequest(IOSCANPVT iosl)通告IOC,调用的函数将安排在合适线程中运行合适的记录。从在一个嵌入架构商中断服务例程调用scanIoReqest()是安全的。
4) write_ao
long write_ao(aoRecord *prec);
当记录有一个新输出值要发送给设备时,调用这个必需的例程。它负责使用在记录的OVAL字段中找到的工程单位或者如果使用了这个记录类型的单位转换从记录的RVAL字段中raw值执行写操作。一个0返回值表明成功,任何其它值表明一个错误发生。
如果设备要花费几个毫秒来接受这个新值,这个例程一定不能阻塞(忙等待)。在那种情况下,这个例程必须使用异步完成来告诉这个记录写操作何时最终结束。通过在其返回前设置这个记录的PACT字段为TRUE,它表示这是一个异步操作,安排这个写操作一旦结束后这个记录的process()例程要被调用。当那样发生时,将用PACT仍设置为TRUE再次调用write_ao()例程;它应该接着设置其为FALSE来表明写操作已经结束,并且返回。
5) special_linconv
long special_linconv(aoRecord *prec, int after);
如果记录类型的单位转换被使用RVAL字段而非OVAL或VAL的设备支持的write_ao()例程使用,应该提供可选的例程。当任何字段LINR,EGUL或EGUF中任何字段被修改并且LINR有值LINEAR,它被记录代码调用。这个例程必须根据EGUL和EGUF的新值,计算并且合适地设置字段EOFF和ESLO。
这些计算可以用write_ao()例程在RVAL字段中能够接受的最小和最大raw值表述。当VAL是EGUF,RVAL字段将被设置成RVAL_max,而当VAL是EGUL时,RVAL字段将变为RVAL_min。要使用的公式是:
EOFF = (RVAL_max * EGUL − RVAL_min * EGUF) / (RVAL_max − RVAL_min)
ESLO = (EGUF − EGUL) / (RVAL_max − RVAL_min)
注意:在调用这个例程前,记录支持设置EOFF为EGUL,者将是一种非常普遍的情况(RVAL_min是0)。
对软记录的设备支持
为与实际硬件不相关的输出记录两个软设备支持模块Soft Channel和Raw Soft Channel。OUT链接类型是CONSTANT, DB_LINK或CA_LINK。
Soft Channel:这个模块写OVAL的当前值。如果OUT链接是PV_LINK,则init_record()调用dbCaAddLnlink。init_record()总是返回一个2值,其表示将不尝试转换。write_ao调用recGblPutLinkValue()写VAL的当前值。
Raw Soft Channel:这个模块像前面一个,除了它写RVAL的当前值。
示例:
数据库实例文件:
1) 这个数据库实例文件有8个记录实例组成
2) $(USER):SetVoltage记录类型是ai,用于模拟获取用户通道通道访问进行输入,并且驱动$(USER):VolEvent运行
3) $(USER):VoEvent记录类型是event,其作用是其运行时,驱动$(USER):SetVoltage_RBV1和$(USER):SetVoltage_RBV2两个记录运行。
4) $(USER):Ao1和$(USER):Ao2两个记录类型是ao,用于模拟将对其的输入进行转化,并输出到OUT链接指向的记录,并且驱动其运行。
5) $(USER):Raw1和$(USER):Raw2两个记录类型是longin,用于模拟设备,接受来自ao记录的输出,并且分别驱动$(USER):SetVoltage_RBV1和$(USER):SetVoltage_RBV2运行。
6) $(USER):SetVoltage_RBV1和$(USER):SetVoltage_RBV2记录类型是ai,用于模拟从设备读取值,并且进行转化,并且与$(USER):SetVoltage记录设定值进行比较。
record(ai, "$(USER):SetVoltage")
{
field(SCAN, "Passive")
field(DTYP, "Soft Channel")
field(INP, "0.0")
field(FLNK, "$(USER):VolEvent")
}
record(event, "$(USER):VolEvent")
{
field(SCAN, "Passive")
field(DTYP, "Soft Channel")
field(INP, "1")
}
record(ai, "$(USER):SetVoltage_RBV1")
{
field(SCAN, "Passive")
field(DTYP, "Raw Soft Channel")
field(INP, "$(USER):Raw1")
field(AOFF,"0")
field(ROFF,"0")
field(LINR, "NO CONVERSION")
field(ASLO, "0.01")
}
record(ao, "$(USER):Ao1")
{
field(SCAN, "Event")
field(EVNT, "1")
field(OMSL, "closed_loop")
field(DOL, "$(USER):SetVoltage.VAL")
field(OIF, "Full")
field(DTYP, "Raw Soft Channel")
field(DRVH, "10")
field(DRVL, "0")
field(LINR, "NO CONVERSION")
field(AOFF, "0")
field(ASLO, "0.01")
field(ROFF, "0")
field(OUT, "$(USER):Raw1.VAL CA PP")
}
record(longin,"$(USER):Raw1")
{
field(SCAN, "Passive")
field(INP, "0")
field(DTYP, "Soft Channel")
field(FLNK, "$(USER):SetVoltage_RBV1.PROC")
}
record(ai, "$(USER):SetVoltage_RBV2")
{
field(SCAN, "Passive")
field(DTYP, "Raw Soft Channel")
field(INP, "$(USER):Raw2")
field(AOFF,"0")
field(ROFF,"0")
field(LINR, "NO CONVERSION")
field(ASLO, "0.01")
}
record(ao, "$(USER):Ao2")
{
field(SCAN, "Event")
field(EVNT, "1")
field(OMSL, "closed_loop")
field(DOL, "$(USER):SetVoltage.VAL")
field(OIF, "Full")
field(DTYP, "Raw Soft Channel")
field(DRVH, "10")
field(DRVL, "0")
field(LINR, "SLOPE")
field(AOFF, "0")
field(ASLO, "1")
field(ROFF, "0")
field(ESLO, "0.01")
field(EOFF, "0")
field(OUT, "$(USER):Raw2.VAL CA PP")
}
record(longin,"$(USER):Raw2")
{
field(SCAN, "Passive")
field(INP, "0")
field(DTYP, "Soft Channel")
field(FLNK, "$(USER):SetVoltage_RBV2.PROC")
}
将以上数据库实例文件加载到IOC种,查看其记录实例:
epics> dbl
blctrl:VolEvent
blctrl:Ao1
blctrl:Ao2
blctrl:Raw1
blctrl:Raw2
blctrl:SetVoltage
blctrl:SetVoltage_RBV1
blctrl:SetVoltage_RBV2
用通道访问命令在另一个窗口测试,这些记录的运行情况:
[root@bjAli ~]# caput blctrl:SetVoltage 0.2
Old : blctrl:SetVoltage 0
New : blctrl:SetVoltage 0.2
[root@bjAli ~]# caget blctrl:SetVoltage blctrl:Raw1 blctrl:Raw2 blctrl:SetVoltage_RBV1 blctrl:SetVoltage_RBV2
blctrl:SetVoltage 0.2
blctrl:Raw1 20
blctrl:Raw2 20
blctrl:SetVoltage_RBV1 0.2
blctrl:SetVoltage_RBV2 0.2
[root@bjAli ~]# caput blctrl:SetVoltage 5.21
Old : blctrl:SetVoltage 0.2
New : blctrl:SetVoltage 5.21
[root@bjAli ~]# caget blctrl:SetVoltage blctrl:Raw1 blctrl:Raw2 blctrl:SetVoltage_RBV1 blctrl:SetVoltage_RBV2
blctrl:SetVoltage 5.21
blctrl:Raw1 521
blctrl:Raw2 521
blctrl:SetVoltage_RBV1 5.21
blctrl:SetVoltage_RBV2 5.21
[root@bjAli ~]# caput blctrl:SetVoltage 10
Old : blctrl:SetVoltage 5.21
New : blctrl:SetVoltage 10
[root@bjAli ~]# caget blctrl:SetVoltage blctrl:Raw1 blctrl:Raw2 blctrl:SetVoltage_RBV1 blctrl:SetVoltage_RBV2
blctrl:SetVoltage 10
blctrl:Raw1 1000
blctrl:Raw2 1000
blctrl:SetVoltage_RBV1 10
blctrl:SetVoltage_RBV2 10
[root@bjAli ~]# caput blctrl:SetVoltage 15
Old : blctrl:SetVoltage 10
New : blctrl:SetVoltage 15
[root@bjAli ~]# caget blctrl:SetVoltage blctrl:Raw1 blctrl:Raw2 blctrl:SetVoltage_RBV1 blctrl:SetVoltage_RBV2
blctrl:SetVoltage 15
blctrl:Raw1 1000
blctrl:Raw2 1000
blctrl:SetVoltage_RBV1 10
blctrl:SetVoltage_RBV2 10