计算输出或"Calcout"记录类似于Calc记录,其增加了能够输出的特性(一个"output link"和一个"output event"),根据计算结果条件地执行它们。这种特性允许在一个EPICS数据库内实现了条件分支(例如:只在Record_B有一个0值时才运行Record_A)。Calcout记录也类似于Wait记录,但使用EPICS标准INLINK和OUTLINK字段而不是在Wait记录中使用的DBF_STRING字段。对于新的数据库,推荐使用Calcout记录替代Wait记录。
在下面描述记录特定的字段,按功能分组。
Calcout记录有用于指定在什么情况下运行这个记录的标准字段。这些字段在Scan Fields中列出。
Calcout记录的读取参数由12个输入链接INPA, INPB,..., INPL组成。这些字段可以是数据库链接、通道访问链接或者常数。如果它们是链接,它们必须指定另一个记录的字段。如果它们是常数,用它们被配置的值初始化它们,并且可以通过dbPuts被更改。
这些字段不能是硬件地址。此外,Calcout记录包含INAV,INBV, ..., INLV字段,它们指明了链接字段的状态,例如,指定的PV是否被找到以及是否建立了指向它的链接。
表达式
像Calc记录一样,Calcout记录有一个CALC字段,开发者能够在其中输入一个内嵌表达式,在这个记录例程运行这个记录时,其将计算这个表达式。产生的值被放入VAL字段。这个值可以接着被OOPT字段使用来确定是否写到输出链接或者提交一个输出事件。它也可以时被写到输出链接的值。CALC表达式实际被转换为操作码并且以逆向Polish标记形式存储在RPCL字段。它是实际用于计算VAL的表达式。在运行时计算逆向Polish表达式比一个内嵌表达式效率更高。CALC可以在运行时被更改,并且一个special记录例程将调用一个函数去转换它为逆向Polish标记。
可以使用的内嵌表达式非常类似于C表达式语法,但在操作含义以及优先级中有写增加以及细微差别。字符串可以包含一系列由分号字符';'分隔的表达式,其中任何一个表达式可以实际提供计算结果;但包含的所有别的表达式必须把它们的结果赋给一个变量。以下描述的所有字母元素都是大小写无关的,因此在变量和函数名中根据需要可以使用和混合大小写字母。除了在组成单个表达式元素的字符之间,在一个表达式中任何地方可以使用空格。
这个计算记录支持的表达式范围被分为文字,常数,操作数,代数操作符,三角操作符,关系操作符,逻辑操作符,赋值操作符,括号和逗号,以及问号或"?:"操作符。
表达式可以使用从INPx链接获取的值作为操作数,虽然常数也可以用作操作数。从输入链接获取的这些值被存储在A-L字段。要在表达式中使用的值是由这个字段字母的简单引用。例如,从INPA获取的值被存储在字段A,而从INP链接获取的值被存储在字段B。在表达式中包含的名称将操作它们各自的值,诸如A+B。
关键字VAL返回表达式的结果字段的当前内容,即:VAL字段对应CALC表达式,而OVAL字段对应OCAL表达式。(可以通过CA put写这些字段,所以它会不是从上次计算这个表达式的结果)。
支持开和闭括号。支持嵌套的括号。
当用于分隔一个二元函数的参数时,支持逗号。
分号用于分隔表达式。虽然只允许一个传统的计算表达式,单允许多个赋值表达式。
支持C语言的问号运算符。格式是:condition? True result : False result;
A + B + 10
结果是A + B + 10
(A + B) < (C + D)
(A + B) < (C + D) ? E : F + L +10
在base 3.14.9前,忽略:和条件的第二个部分是合法的,就像这样:
(A + B) < (C + D) ? E
从3.14.9以后,这个表达式应该被写成(A + B) < (C + D) ? E : VAL
A & B
使得以下发生:
sin(a) ; a:=a+D2R
使得Calc记录1度间隔输出一条正弦曲线的连续值。
这些参数指定和控制Calcout记录的输出功能。它们确定何时写输出,向何处写输出,以及输出将是什么。OUT链接指向这个结果将被写到哪个过程变量。
OOPT字段确定使得向输出链接进行写的条件。其菜单有6个选项:
DOPT字段确定了当执行输出时什么数据被写到输出链接。这个字段时一个有两个选项的菜单字段:
如果指定了Use CALC,当这个记录写它的输出时,它将写在CALC字段中表达式的结果,即是,它将这VAL字段的值。如果指定Use OCAL,这个记录将写在OCAL字段中表达式的结果,在OVAL字段中包含这个结果。OCAL字段完全类似CALC字段,并且有相同功能,它可以包含一个在运行时计算的表达式的字符串表示。因而,如果需要,这个记录可以使用CALC表达式的结果来确定数据是否应该被写并且可以使用OCAL表达式的结果作为要写的数据。
如果OEVT字段指定一个非0整数并且满足在OOPT中的条件,这个记录将提交一个对应的事件。如果ODLY字段是非0,在执行OUT链接或者提交这个输出事件前,这个记录暂停指定的秒数。在这段等待期间,这个记录是"活动的",并且在等待结束前将不再被运行。在延时期间,字段DLYA等于1。延时入口系统的分辨率系统相关。
IVOA字段指定了如果Calcout记录进入一个INVALID警报状态时对OUT链接采取什么动作。这些操作是Continue normally, Don't drive outputs和Set output to IVOV。如果IVOA字段是Set output to IVOV,如果记录警报严重性是INVALID,输入到IVOV字段的数据被写到了OUT链接。
这些参数用于显示有意义数据到操作者。一些用于在运行时显示这个记录的状态。
EGU字段包含一个由用户提供的最长16个字符的字符串,其描述正在操作的值。在调用例程get_units()时,获取这个字符串。EGU字符串是唯一用于操作者并且不是必须被使用。
HOPR和LOPR字段仅指向VAL,HIHI,HIGH,LOW和LOLO字段的限制。PREC控制VAL字段的精度。
INAV-INLV字段各自指定在INPA-INPL字段中指定的指向PVs链接的状态。这些字段可能有四个可能的值:
OUTV字段指明OUT链接的状态。它有与INAV-INLV字段相同可能的值。
CLCV和OLCV字段各自指明在CALC和OCAL字段中表达式的有效性。如果表达式无效,这个字段被设置为1。在ODLY中指定的延时期间,DLYA字段被设为1。
有关记录名(NAME)和描述(DESC)字段更多信息见Fields Common to All Record Types。
calcout记录的可能警报条件是SCAN,READ,计算以及限制警报。SCAN和READ警报被记录支持程序调用。当CALC表达式是一个无效表达式时,对这个表达式产生一条错误消息,计算警报被记录process例程调用。
以下警报参数,由用户配置,定义VAL字段的限制警报以及对应那些情况的严重性。
HYST字段为每个限制定义一个警报死区。
记录警报和标准字段的完整解释见Alarm Specification。Alarm Fields列出了所有记录类型都有的警报相关联的其它字段。
这些参数用于确定何时发送这个值字段的monitors。当这个值字段超过了上次受监视字段合适死区时,发送这些monitors。ADEL用于存档monitors,而MDEL字段用于所有其它类型monitors。如果这些字段有一个0值,每次这个值变化时,触发monitors;如果它们有一个-1值,每次扫描这个记录时,触发monitors。
这些字段不是使用配置工具配置的,并且在运行时都不可修改。它们用于运行这个记录。
LALM字段用于实现警报限制的回滞因子。
LA-LL字段用于决定何时触发相应字段的monitors。例如,如果LA不等于A的值时,触发A的monitor。MLST和ALST字段以对应VAL字段相同方式被使用。
1) init_record
对于每个常数输入链接,如果输入链接是CONSTANT,用这个常数值初始化相应的值字段或者如果这个链接是PV_LINK,创建一个通道访问链接。
一个例程postfix被调用转换在CALC和OCAL中的内嵌表达式为反向Polish标记。结果各自被存储在RPCL和ORPC。
2) process
见下部分。
3) special
如果CALC或OCAL被更改,调用这个例程。special调用postfix。
4) get_units
获取EGU。
5) get_precision
获取精度。
6) get_graphic_double
为一个字段设置上显示和下显示限制。如果字段是VAL,HIHI,HIGH,LOW或LOLO,这些限制被设置HOPR和LOPR,否则如果这个字段有定义的上和下限制,使用它们,否则将使用对应这个字段类型的上和下最值。
7) get_control_double
为一个字段设置上控制和下控制限制。如果字段是VAL,HIHI,HIGH,LOW或LOLO,这些限制被设置HOPR和LOPR,否则如果这个字段有定义的上和下限制,使用它们,否则将使用对应这个字段类型的上和下最值。
8) get_alarm_double
设置以下值:
- upper_alarm_limit = HIHI
- upper_warning_limit = HIGH
- lower warning_limit = LOW
- lower_alarm_limit = LOLO
process()例程实现以下算法:
1、获取所有参数。
2、调用例程calcPerform(),它从在CALC中给出表达式的前缀版本计算VAL。如果calcPerform()返回成功,UDF被设置成False。
3、检查警报。这个例程检查新的VAL是否引起警报状态和严重性变化。如果这样,NSEV,NSTA和LALM被设置。它也遵守警报回滞因子(HYST)。因而,在警报状态和严重性变化前,这个值必须至少变化HYST。
4、确定是否满足输出执行选项(OOPT)。如果满足,要么(如果ODLY=0)执行输出链接(和输出事件)或者在指定间隔后调度一个回调。见以下execOutput()例程的解释。
5、检查是否应该调用monitors。
6、如果没有指定输出延时,如果需要,扫描forward链接,设置PACT为FALSE,并且返回。
1) 如果DOPT字段指定使用OCAL,为在OCAL中表达式的后缀版本调用例程calcPerform()。否则,使用VAL。
2) 如果警报严重性是INVALID,按照字段IVOA指定的选项。
3) 警报严重性不是INVALID或者IVOA制定了"Continue Normally",把OVAL的值放入OUT链接并且提交OEVT(如果非零)中的事件。
4) 如果实现了一个输出延时,运行forward链接。
1) 建立一个以下数据库:
- record(longin, "$(USER):Int1")
- {
- field(SCAN, "Passive")
- field(INP, "10")
- field(DTYP, "Soft Channel")
- field(FLNK, "$(USER):Calcout")
- }
-
-
- record(longin, "$(USER):Int2")
- {
- field(SCAN, "Passive")
- field(INP, "5")
- field(DTYP, "Soft Channel")
- field(FLNK, "$(USER):Calcout")
- }
-
- record(calcout, "$(USER):Calcout")
- {
- field(SCAN, "Passive")
- field(INPA, "$(USER):Int1")
- field(INPB, "$(USER):Int2")
- field(CALC, "A + B")
- field(OCAL, "A - B")
- field(OEVT, "1")
- field(OUT, "$(USER):Float")
- field(OOPT, "On Change")
- field(DOPT, "Use CALC")
- }
-
- record(ai, "$(USER):Float")
- {
- }
-
-
-
- record(calc, "$(USER):Count")
- {
- field(SCAN, "Event")
- field(EVNT, "1")
- field(INPA, "$(USER):Count.VAL")
- field(CALC, "A + 1")
- }
2) 将以上数据库实例化,获取以下的记录:
- epics> dbl
- blctrl:Calcout
- blctrl:Int1
- blctrl:Int2
- blctrl:Count
- blctrl:Float
3) 零开一个终端,用通道访问命令进行测试:
- [root@bjAli ~]# caget blctrl:Count
- blctrl:Count 0
- [root@bjAli ~]# caput blctrl:Int2 30
- Old : blctrl:Int2 5
- New : blctrl:Int2 30
- [root@bjAli ~]# caget blctrl:Count
- blctrl:Count 1
- [root@bjAli ~]# caget blctrl:Float # 使用了A+B
- blctrl:Float 40
- [root@bjAli ~]# caput blctrl:Calcout.DOPT "Use OCAL" # 从使用CALC中表达式切换到使用OCAL中表达式
- Old : blctrl:Calcout.DOPT Use CALC
- New : blctrl:Calcout.DOPT Use OCAL
- [root@bjAli ~]# caget blctrl:Float
- blctrl:Float 40
- [root@bjAli ~]# caput blctrl:Int1 38 # 使用了A-B
- Old : blctrl:Int1 10
- New : blctrl:Int1 38
- [root@bjAli ~]# caget blctrl:Float
- blctrl:Float 8
- [root@bjAli ~]# caget blctrl:Count
- blctrl:Count 2