EPICS scalcout - String Calculation Output记录

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")
}

猜你喜欢

转载自blog.csdn.net/yuyuyuliang00/article/details/130608827