1、介绍
字符串计算输出或"scalcout"记录派生自EPICS base中calcout记录,并且通过在数值表达式外还支持字符串表达式,扩展它。这个记录有12个字符串字段(AA..LL)和12个double字段(A..L),它们被用作表达式的输入变量。它调用一个扩展版本的EPICS计算引擎,它接收字符串参数,支持很多种类的字符串函数,并且产生一个字符串结果和一个数值结果。
如果包含任何字符串操作符或操作数,calc引擎产生一个字符串结果,并且使用atof()转换它为double;否则,它使用来自EPICS base的cvtDoubleToString()产生一个数值结果并且转换它为字符串。
当写入一个字符串PV(任何DBF_STRING, DBF_ENUM, DBF_MENU, DBF_DEVICE, DBF_INLINK, DBF_OUTLINK, DBF_FWDLINK),记录(实际上,设备支持)写起字符串值(SVAL或OSV)。当写入任何其它类型PV时,这个记录写起数值(VAL或OVAL)。
要成功地写入一个DBF_MENU或DBF_ENUM(例如,bo或mbbo记录的VAL字段),这个记录的字符串值必须时这个PV的可能字符串之一,或者,它必须是为这个PV指定字符串数值[0..N]的一个整数。例如,例如,当写入一个其ZNAM是"No"和其ONAM是"Yes"的bo记录时,这个字符串值必须是以下"No","Yes",“0”和"1"。要确保数值被转换成整数,设置精度(PREC字段)为0。
2、扫描参数
scalcout记录有用于指定将在什么情况下运行这个记录的标准字段。这些字段和其用法见EPICS记录参考手册。
3、读取参数
scalcout记录的读取参数由24个输入链接组成:12个对应数值字段(INPA->A, INPB->B, ..., INPL->L);以及12个对应字符串字段(INAA->AA, INBB->BB, ... , INLL->LL)。这些字段可以是数据库链接,通道访问链接,或者常数。如果它们是链接,它们必须指定另一个记录的字段。如果它们是常数,将用设置它们的只初始化它们,并且通过dbPuts可以被修改。这些字段不能是硬件地址。此外,scalcout记录包含字段INAV,INBV,...,INLV,这些表明了指向数值字段的链接的状态,并且字段IAVV,IBBV,...,ILLV,这些指明了指向字符串的链接的状态。这些字段表明是否找到了指定的PV以及指向它的链接是否建立了。这些字段的详细解释见第6部分,操作显示参数。
对于大部分PV类型,scalcoout记录的字符串输入链接以字符串获取数据,并且依靠EPICS转换一个PV的本地值为字符串。但当scalcout记录的字符串输入链接指明其数据类型是一个DBF_CHAR或DBF_UCHAR的数组的PV时,这个记录用本地格式从数组最多获取39个数组元素,使用epicsStrSnPrintEscaped()转换这个结果,删掉任何后面的"\000"字符串部分,并且截短这个结果适配一个40个字符字符串。
有关如何指定数据库链接的信息见EPICS记录参考手册。
类似Calc记录,scalcout记录有一个CALC字段,你向此字段输入一个用于记录运行时进行计算的表达式。产生的数值将被放入VAL字段,而产生的字符串值将被放入SVAL字段。VAL可被OOPT字段使用(见第5部分,输出参数)来是否写入输出链接或者提交一个输出事件。VAL和SVAL也可以被写入输出链接。(如果你选择写一个输出值,取决于这个输出链接另一端字段的数据类型,这个记录将在VAL和SVAL之间选择。
CALC表达式实际上被转化为opcodes并且被以后缀标记形式存储在RPCL字段。它是实际用于计算VAL的表达式。在运行时计算后缀表达式效率高于一个内嵌表达式。当在运行时修改了CALC,记录支持例程special()将调用一个函数检查它并且转化它为后缀标记。
记录也有在第5部分输出参数中描述的第二个计算相关字段的集合。
字符串计算记录支持的表达式可以包含操作数,代数操作操作符和函数,三角函数,关系操作符,逻辑操作符,字符串操作符和函数,括号和逗号以及条件'?:'操作符。表达式可以有任何这些操作符以及来自这些输入字段的任何值(它们是操作数)组成。
用括号对操作数分组不但指定了要进行操作的顺序,而且也区分了其名称是拼写的操作符和操作数,诸如逻辑操作符'AND'。表达式'AANDB',其表示‘A AND B’,将被解释器错误地解释,由于'AA'作为一个字符串变量,它将解释‘AA’。
4.1 操作数
表达式可以使用从输入链接,和诸如"abc", "1.5"和"23"字面常量获取地值作为操作数。(在表达式中数值常量必须是浮点数值或者十进制整数。像"0x03"的数值将不被表达式解释器理解)。从输入链接获取的值被存储在A-L,和AA-LL字段。在表达式中使用的值被其字段名称引用。例如,从INPA链接获取的值被存储在字段A中,从INPB获取的值被存储在B。字段名称可以被包含在表达式中,此表达式将对它们各自的值进行操作,诸如在"A+B"。
如果一个有效输入链接于这个字段相关联,则它不可以被修改。
有一些特殊操作数,它们与输入字段不相关联,而是由这个记录定义(更确切地,由这个记录使用来计算表达式的calc引擎定义)。除RNDM外所有都是常数。
4.2 代数函数/操作符
4.3 三角函数
4.4 关系操作符
4.5 逻辑操作符
4.6 位操作符
4.7 特殊字符
在表达式中允许(有时需要)括号,和嵌套括号。
需要逗号分隔一个二元函数的参数。
允许两种类型的引号("和')来表明一个字符串表达式的开始和结束。表达式解析器用完全相同的方式处理这些引号字符,因此你可以使用任何一种对。在一个字符串中使用一种引号允许你包含另外一种。注意:在EPICS数据库中定义的字符串calc表达式必须使用'字符来定界嵌入的字符串,因为数据库工具将使用""来定界整个字符串。
EPICS例程dbTranslateEscape()和epicsStrSnEscpate()理解的转义序列被允许在由scalcout记录处理的字符串中,并且sCalc引擎提供了编码和解码转义字符串的功能。这是当前转义列表:
\a \b \f \n \r \t \v \\ \? \' \" \000 \xhh
注意:'\000'代表一个八进制数值,并且可以包含一个,两个或者三个八进制数字。\xhh是一个2数字十六进制数值。有关这个话题的更多内容见应用程序开发手册。
4.8 条件表达式
支持C语言的?操作符。格式如下:
<expression> ? <expression-true result> : <expression-false result>
4.9 字符串功能/操作符
如果它们的参数/操作数都是字符串,大部分2参数字符串函数和二元字符串操作符是执行字符串操作的"超载的"数值函数"和操作符。
op | 描述 | 示例 |
MIN | 最小词汇(两个或多个参数的函数) | MAX('a', 'b', 'c')->'a' |
MAX | 最大词汇(两个或多个参数的函数) | MAX('a', 'b', 'c')->'c' |
INT | 找到字符串中第一个数值;返回最接近的整数(单参数函数) 取整 |
INT('1.9')->1 INT('abc1.9')->1 |
MINT | 找到字符串中第一个数值;返回最接近的整数(单参数函数) 四舍五入 |
INT('1.9')->2 INT('abc1.9')->2 |
+ | 连接(二元) | 'a'+'b' ->'ab' |
- | 删除首次出现的子串(二元) | 'abca'-'a'->'bca' |
-| | 删除首次出现的子串(二元) | 'abca'-'a'->'bca' |
|- | 删除末尾出现的子串(二元) | 'abca'-'a'->'abc' |
>= | 词汇大于或等于(二元) | 'a'>='b' -> 0 |
> | 词汇大于(二元) | 'a'>'b' -> 0 |
<= | 词汇小于或等于(二元) | 'a<='b' -> 1 |
< | 词汇大于(二元) | 'a<'b' -> 1 |
!= | 词汇不等于(二元) | 'a'!='b' -> 1 |
== | 词汇等于(二元) | 'a<='b' -> 0 |
>> | 右移字符(用空格填充)。(二元) | 'abc'>>2 -> ' abc' |
<< | 左移字符。(二元) | 'abc'<<2 -> 'c' |
DBL | 转化参数为double(单参数函数) | DBL('1')->1.0 DBL('abc1.23')->1.23 |
STR | 转化参数为字符串(单参数函数) | STR(1)->'1.000000' |
BYTE | 转化字符串参数的第一个字符为double(单参数函数) | BYTE('abc') -> 97.0 |
PRINTF | 返回从格式字符串和字符串或double参数构建的字符串(双参数函数)。(注意:'$P'是PRINTF的同义词) 允许格式指示符:%cdeEfgGiosuxX 在指示符中不允许:* |
PRINTF("%.2f",1.23) -> '1.23' |
SSCANF | 返回根据格式字符串被从字符串参数解析字符串或double。 (注意:'$S'是SSCANF的同义词)。 允许的格式指示符:%*[cdeEfgGhilosuxX 在指示符中不允许:$npw |
SSCANF('V=1.25', "%*2c%1f") -> 1.25 |
READ | 读取二进制数据。此函数格式类似SSCANF,并且功能基本类似,但它操作二进制数据。第一个参数是从其复制数据的字符串。如果这个字符串包含任何不可打印的字符,它们将已经被传输它们到此处的软件转化成转义序列。READ()使用EPICS函数dbTranslateEscape()转化这个字符串为一个原始二进制数组,从此数组复制字节到一个临时的数值变量,其类型取决于格式字符串,转化这个结果为double,并且返回这个值。 (注意:'$R'是READ的同义词)。 格式字符串类似于SSCANF函数的格式字符串,但只支持格式指示符的以下子集: 1)%i %d:复制四个字节到一个有符号长整数 2)%hi %hd:复制两个字节到一个有符号长整数 3)%o %u %x %X:复制四个字节到一个无符号长整数 4)%e %E %f %g %G:复制四个字节到一个float 5)%le %lE %lf %lg %lG:复制八个字节到一个double 6) %c:复制一个字节到一个有符号字符 7) %*:阻止对下个转换指示符赋值。仅可使用一个'%*'。这是在一个二进制输入字符串中跳过指定字节数的唯一可靠方法。对于一个非转义字符,如'aa[3,5]'也起作用。但如果字符串aa包含转义序列时,在转义字符串中的字符偏移将取决于字符的值,因为可打印字符不被转义。 允许的格式指示符:%*cdeEfgGhilouxX 不可用的指示符:$npw[s |
READ('\x01\x02', "%hu") -> 258 READ('\x01\x02', "%*c%hu") -> 2 解释:十六进制'\x01\x02'转成二进制为: 000100000010转成10进制为258 |
WRITE | 写二进制数据。这个函数形式上类似PRINTF,并且功能类似,但它操作二进制数数据。第一个参数是格式字符串;第二个参数是要被写的数据。在数据已经被写入到一个缓存后,这个缓存被epicsStrSnEscaped()处理并且返回。 (注意:'$W'是WRITE的同义词) 格式字符串类似于PRINTF函数的格式字符串,但仅支持以下子集的格式化指示符。 1)%i %d:转换一个有符号长整数并且写四个字节。 2)%hi %hd:转换一个有符号短整数并且写两个字节。 3)%o %u %x %X:转换一个无符号长整数并且写四个字节。 4)%ho %hu %hx %hX:转换一个无符号短整数并且写两个字节。 5)%e %E %f %g %G:转换一个float并且复制4个字节 6)%le %lE %lf %lg %lG:转换一个double并且复制8个字节 7)%c:转换一个有符号字符并且复制一个字节 可用的格式指示符:%cdeEfgGhilouxX 不可用的指示符:$npw[s |
WRITE('%hd', 1250) ->'\004\342' 解释: 1250的二进制: 010011100010 用八进制表示为: \004\342 |
TR_ESC | 转换转义序列为它们代表的字符串(单参数函数)。(注意:'$T'是TR_ESC的同义词) TR_ESC()对其参数使用dbTranslateEscape()。 这个函数大致与ESC相反。一个被转义的null字符将结束这个结果字符串。 |
TR_ESC("a\x62c") ->'abc' |
ESC | 转换转义字符为等价的转义序列。(单参数函数)(注意:'$E'是ESC的同义词)。ESC()对其参数使用epicsStrEscaped()。在输入字符串中找到的第一个null字符结束它。如果没有null字符,将认为这个字符串40个字符长。 | ESC("a<return>c") ->'a\rc' |
CRC16 | 计算modbus/RTU 16位CRC并且以一个转义字符串返回它。 |
CRC16('\x01\x03') -> '\x01\x03\x40\x21' |
MODBUS | 计算modbus/RTU 16位CRC并且以一个转义字符串添加它到这个参数末尾 | CRC16('\01\03') -> '\x40\x21' |
LRC | 计算Modbus/ASCII 8位LRC并且以以一个转义字符串返回它。 | LRC('\01\03') -> '\xfc' |
AMODBUS | 计算Modbus/ASCII 8位LRC并且以一个转义字符串添加它到这个参数。注意:这没有为一个实际设备做了所需的一些事情。这个字符串打算被转成在末尾添加了LRC以及在前面添加了':'的二进制。然后整个字符串被转成了ASCII字符。 | AMODBUS('\0x01\x03') -> '\x01\x03\xfc' |
XOR8 | 计算8位XOR校验和并且以一个转义字符返回它。 | XOR8('\x01\03') -> 'x02' |
ADD_XOR8 | 计算8位XOR校验和并且以一个转义字符串添加它到这个参数后。 | ADD_XOR8('\x01\x03')-> '\x01\x03\x02' |
LEN | 返回字符串参数的长度。如果arg不是一个字符串,它将被转成字符串。 | LEN('abc')->3 |
[ | 子串 | 'abcdef'[1,3]->'bcd' 'abcdef'['ab', 'ef']->'cd' 'abcdef'[0,-1]->'abcdef' |
{ | 子串替换 | 'abcdef'{'cd', 'XX'}-> 'abXXef' |
" | 字符串指示符 | "abc" |
' | 字符串指示符 | 'abc' |
用黑体提供的函数和操作符是试验的。在此软件未来发行版中可能更改名称或实现。 |
4.10 数组操作符
op | 描述 | 示例 |
@ | 数值数组元素。把数值字段A-L视为一个数组,其元素被编号为0-11,并且返回其数值跟着的元素。 | @A |
@@ | 字符串数组元素。把字符串字段AA-LL视为一个字符串数组,其元素被编号为0-11,并且返回其数值跟着的元素。 | @@A |
4.11 各式各样的操作符
op | 描述 | 示例 |
:= | 把右侧的值存储到左侧指定的位置。 | A:=1.2 @7:='abc' AA:='abc' @@4='abc' |
UNTIL | 在其值为True前,执行表达式(二元) 迭代次数受限于ioc-shell变量sCalcLoopMax, 其默认是1000 |
until(1) until(a:=a+1;b:=b-1;b<1) |
4.12 示例
算术
A+B+10
结果是A+B+10
关系
(A+B)<(C+D)
如果(A+B)<(C+D),结果是1
如果(A+B)>=(C+D),结果是0
问号
(A+B)<(C+D)?E:F+L+10
如果(A+B)<(C+D),结果是E
如果(A+B)>=(C+D),结果是F+L+10
(A+B)<(C+D)?E
如果(A+B)<(C+D),结果是E
如果(A+B)>=(C+D),结果不变
逻辑
A&B 引起以下发生:
1) 转换A为整数
2)转换B为整数
3)执行A和B的位与
4)转换结果为浮点数
字符串
A + "abc"
A + "abc1.2"
A + AA(此处AA="abc1.2")
结果是A。字符串"abc"和"abc1.2"将隐式地被转换成数值0。
A + DBL("abc1.2")
结果是A+1.2。(通过DBL,INT或NINT)隐式地转成成一个数值比隐式转换更强势。
"abc"+"def"
结果是"abcdef"
PRINTF("abc%1.2f", A)(此处A=1.2345)
结果是"abc1.23"。
注意:不同于C语言函数printf(<format>,<arg1>, <arg2>,...)
这个函数在格式化字符串后仅接受一个参数。
SSCANF(AA, "%*3c%lf")(此处AA="abc1.2")
结果是"1.2000000"。(格式部分"%*3c"命令sscanf忽略3个字符)
注意:不同于C语言函数sscanf(<source>, <format>,<dest1>, <dest2>)
这个函数不能存储到一个变量,而是只存储到这个值的栈。
BYTE("ABC")
结果是65, "A"十进制的ASCII值。
"abcdef"[2,4]
结果是"cde"。(字符串的第一个字符编号为"0")。
"abcdef"[-2,1]
结果是"ef"。(字符串的末尾字符编号为"-1")。
"abcdef"["ab","ef"]
结果是"cd"。(如果第一个参数是一个字符串,它返回第一次成功匹配之后第一个字符的索引,或者如果没有找到匹配,这个字符串中第一个字符。如果第二个参数是一个字符串,它返回一次成功匹配之前末尾字符的索引,或者如果没有找到匹配,这个字符串中末尾字符)。
A==2 ? "yes":"no"
如果A==2,结果是"yes";否则,结果是"no"。
"abcdef"{“bcd”, "dcb"}
结果是"adcbef",将字符串中"bcd"替换为"bcd"。
"abcdef"{“zzz”, "bcd"}
结果是"abcdef"。(如果没有匹配,则没有替换)。
"abcdef"[1,-2][1,-2]
结果是"cd"。第一次分片后"bcde"[1,2],第二次分片后"cd"。
参数数组
@0:数值变量A的值。("@0"是只是A的另一个名称)
@@0:字符串变量AA的值。("@00"是只是AA的另一个名称)
@(A+B):其编号由(A+B)之和指定的数值变量的值。
存储
“Store”操作符只是不产生一个值的字符串计算操作符。因而表达式a:=0是一个不完整因而非法的计算表达式,因为它没有留给我们写入这个记录的VAL字段的东西。
1) A:=A-1;7
计算表达式A-1,存储其结果在输入变量A,并且设置VAL字段为7。
2) @0:=A-1;7
与以上1)相同,因为@0只是A的另一个名称。
3) D:=0;@D=A-1;7
与以上1)相同,因为D==0。
4) AA:="abc";7
用字符串'abc'重写字符串输入变量AA,并且设置VAL字段为7。
5) AA:="abc";b:=0;7
由';'分隔的多个存储表达式,在顶层是合法的,但是作为整体的表达式必须产生一个值。这个表达式产生值7。
6) A+(AA:="abc";b:=0;7)
在括号中的多个存储表达式也是合法的,并且括号中子表达式再次必须产生一个值。在此示例中的括号表达式产生值7,它被加到了A。
loop("UNTIL")
UNTIL函数重复计算其表达式直到表达式返回一个非0值,或者已经达到了可用的迭代数目sCalcLoopMax。当循环结束时或被取消时,这个表达式值被返回。
1) UNTIL(1)
表达式1被计算,终止循环,并且被返回。这个什么也不做的表达式等于(1)。
2) B:=10;UNTIL(B:=B-1;B<1)
这个基本什么也不做的表达式初始化B为10,在其值为0前重复地减少它,并且返回这个值1(True)。
3) B:=9; AA:='';UNTIL(AA:=AA+CC[B,B];B:=B-1;B<0)
如果我们从CC='abcdefghij'开始,这个表达式反向循环CC,在末尾添加字符产生AA="jihgfedcba"。
4)B:=0;AA:='';UNTIL(AA:=AA+(CC[B,B]==','?'':CC[B,B]);B:=B+1;B>LEN(CC))
这个表达式复制CC到AA,留下逗号不复制。
5) AA:='';B:=1;UNTIL(A:=0;C:=UNTIL(AA:=AA+(@@B)[A,A];A:=A+1;A>1);B:=B+1;B>12)
这是嵌套循环从每个字符串输入字段BB-LL赋值前两个字符,并且将它们添加到AA地末尾。注意内层'UNTIL'函数产生一个值,它必须(通过存储它到C)被被丢弃,因为它是外层UNTIL函数地表达式的组成部分。那个表达式必须产生单个值,这个UNTIL使用这个值来决定何时结束。
5、输出参数
这些参数指定和控制scaleout记录的输出功能。它们确定何时写输出,写它到哪里,以及输出将是什么。OUT链接指定结果将被写入的过程变量。OOPT字段决定引起输出链接要被写入的条件。它是一个有6个选项的菜单:
Every Time | 记录每次运行时,写输出 |
On Change | 每次VAL值变化时,写输出。即:每次表达式结果变化时 |
When Zero | 当记录运行时,如果VAL是0,写输出 |
When Non-Zero | 当记录运行时,如果VAL不是0,写输出 |
Transistion to Zero | 当记录运行时,仅在VAL是0并且上个值非0时,写输出 |
Transition to Non-Zero | 当记录运行时,仅在VAL是非0并且上个值0时,写输出 |
Never | 从不写输出 |
DOPT字段确定在执行输出时什么数据被写入输出链接。这个字段是一个有两个选项的菜单字段:Use CALC或Use OCAL。如果指定了Use CALC,当记录写其输出时,它将写在CALC字段中表达式的结果,即是,它将写VAL[SVAL]字段的值到一个double[string]目的地。如果指定了Use OCAL,这个记录将写OCAL字段中表达式的结果,其包含在OVAL字段(string结果在OSV)字段。OCAL字段完全类似CALC字段,并且有相同的功能:它可以包含在运行时一个被计算表达式的字符串表示。因而,如果需要,记录可以使用CALC表达式的结果来确定如果是否应该被写,并且使用OCAL表达式的结果作为要写的数据。
OVET字段指定了一个非0整数以及在OOPT字段被满足的条件,记录将提交一个相应的事件。如果ODLY字段非0,在执行OUT链接或者提交这个输出事件前,记录将暂停指定的秒数。在这段等待时间,记录是"活动的"并且在等待结束前将不再被运行。字段DLYA在等待时段中等于1。延时项的分辨率是1/60一秒(它使用vxWorks tickLib)。
IVOA字段指定了如果scalcout记录进入一个INVALID警报状态时对OUT链接采取什么操作。选项是Continue normally, Don't drive output, 以及Set Output to IVOV。如果IVOA字段设为Set output to IVOV,输入到IVOV字段的数据在记录警报严重性是INVALID时被写入OUT链接。
scalcout记录现在使用设备支持写入OUT链接。用.dbd格式选择随此记录提供的软设备
field(DTYP,"Soft Channel")
此设备支持使用记录的WAIT字段来确定在引起这个记录执行其转发链接前是否等待由OUTW链接启动的运行结束。执行通过其等待结束的机制需要OUT链接有属性CA,即,这个链接文本看起来像
xxx:record.field CA NMS
当前,这个记录没有尝试确保兼容地配置WAIT和OUT。如果WAIT==“Wait”,但链接看起来例如像
xxx:record.field PP NMS
记录在执行其转发链接前将不等待结束。
6、操作显示参数
这些参数是用于向操作者显示有意义参数。有些也用于在运行时显示记录地状态。
EGU字段包含一个最长16个字符的字符串,它由用户 提供,其秒数正在被操作的值。在调用例程get_units时,获取这个字符串。EGU字符串唯一用作操作者并且不是必须被使用。
HOPR和LOPR字段仅指向VAL,HIHI,HIGH,LOW和LOLO字段的限制。PREC字段控制VAL字段的精度。
INAV-INLV和IAAV-ILLV字段各自表明了在INPA-INPL和INAA-INLL字段中指定的PVs的链接的状态。这些字段有三种可能的值:
Ext PV NC | 在这个IOC上没有找到这个PV,而且一个通道访问链接还未被建立 |
Ext PV OK | 在这个IOC上没有找到这个PV,而且一个通道访问链接已经被建立 |
Local PV | 在这个IOC上找到了这个PV |
Constant | 对应的链接字段是一个常数 |
OUTV字段表明OUT链接的状态。它有与INAV-INLV字段相同可能的值。
CLCV和字段OLCV字段各自表明CALC和OCAL字段中表达式的有效性。如果表达式无效,这个字段设为1。
DLYA字段在ODLY字段中指定延时间隔期间被设为1。
有关记录名称(NAME)和描述(DESC)字段的更多信息,见EPICS记录参考手册。
7、警报参数
scalcout记录的可能警报条件是SCAN,READ,Calculation和限制警报。SCAN和READ警报是被记录支持例程调用。当CALC表达式是一个无效表达式时,Calculation警报被记录运行例程调用,对这个表达式产生一条错误消息。
由用户配置的以下警报参数为VAL字段和对应那些条件的严重性定义了警报。
HYST字段为每个限制定义了警报死区。警报和这些字段的完整解释见EPICS记录参考手册。
8、监视参数
这些参数用于确定何时为值字段发送monitors。当值字段超过了上次受监视字段合适的死区时,这些monitors被发送,ADEL用于存档监视器而MDEL字段用于所有其它类型的监视器。如果这些字段有一个0值,值每次变化时,monitors被触发;如果它们有一个-1值,记录每次被扫描时,监视器被触发。
9、运行时参数
使用配置工具不能配置这些字段,并且在运行时,它们也是不可修改的。它们用于运行这个记录。
LALM字段用于为警报限制实现回滞因子。
LA-LL字段用于决定何时触发对应字段的monitors。例如,如果LA不等于A,触发A的monitors。MLST和ALST字段以相同方式用于VAL字段。
10、记录支持例程
1) init_record
对于每个常数输入链接,如果输入链接时CONSTANT,用这个常数值初始化对应的值字段或者如果输入链接是一个PV_LINK,创建一个通道访问链接。
一个例程postfix被调用转换CALC和OCAL中的内嵌表达式为反向波兰表示。结果各自被存储在RPCL和ORPC。
2) proces
见第11部分。
3) special
如果更高了CALC或OCAL,则调用这个。special调用sCalcPostfix。
4) get_value
填充结构体valueDes的值,使得它们指向VAL。
5) get_units
获取EGU。
6) get_precision
获取PREC。
7) get_graphic_double
设置一个字段的上显示和下显示限制。如果字段是VAL,HIHI,HIGH,LOW或LOLO,这些限制被设置成HOPR和LOPR,或者如果字段有已定义的上和下限制,将使用它们,否则将使用对用这个字段的上和下最值。
8) get_control_double
设置一个字段的上控制和下控制限制。如果字段是VAL,HIHI,HIGH,LOW或LOLO,这些限制被设置成HOPR和LOPR,或者如果字段有已定义的上和下限制,将使用它们,否则将使用对用这个字段的上和下最值。
9) get_alarm_double
设置以下值:
upper_alarm_limit = HIHI
upper_warning_limit = HIGH
lower_warning_limit = LOW
lower_alarm_limit = LOLO
11、记录运行
11.1 process()
process()例程实现以下算法:
1) 获取所有参数
2) 调用例程sCalcPerform(),它从CALC中指定表达式的postfix版本计算VAL。如果sCalcPerform()返回成功,UDF设置为FALSE。
3)检测警报。这个例程检测新的VAL是否引起警报状态和严重性变化。如果这样,NSEV,NSTA,NSTA和LALM被设置。它也遵守警报回滞因子(HYST)。因而,在警报状态和严重性变化前,值必须至少变化HYST。
4)确定输出执行选项OOPT是否被满足。如果它被满足了,(如果ODLY=0),立即执行输出链接(并且输出),或者调用一个回调在指定间隔后执行。解释见execOutput()例程。
5) 检测监视器是否应该被调用。
- 如果警报状态或严重性变化了,调用警报监视器。
- 如果ADEL和MDEL条件被满足了,调用存档和值变化监视器。
- 当其它监视器被调用时,检测A-L和AA-LL的监视器。
- NSEV和NSTA被重置为0
6) 如果没有指定输出延时,如果必要,扫描转发链接,设置PACT为FALSE,并且返回。
11.2 execOutput()
1、如果DOPT字段指定了使用OCAL,为OCAL表达式的postfix版本调用例程sCalcPerform。否则,使用VAL。
2、如果警报严重性是INVALID,按照字段IVOA指定的选项。
3、如果警报严重性不是INVALID,或IVOA指定"Continue Normally",调用设备支持次哦OVAL的值到有OUT链接指定的设备或PV,并且(如果非0),提交OEVT中的事件。
4、如果一个输出延时被实现了,运行转发链接。
12、示例数据库
这是一个示例数据库片段,展示了如果在一个calc表达式内包含一个常数文字串。运行时,你可以输入一个如下表达式:
AA+printf(‘ %.3f’, A)或AA+printf(" %.3f", A)
但在数据库中,你没有那种自由,因为整个表达式是一个EPICS字段的值,其必须被包围在一个双引号中:
record(scalcout, "$(P)SRS810:$(N):SetPhas") {
field(DESC, "Create Phase Set Str")
field(CALC, "AA+printf(' %.3f',A)")
field(OUT, "$(P)SRS810:$(N):WritePhas.AOUT PP MS")
field(PREC, "2")
field(AA, "PHAS")
}