EPICS aSub Record介绍

aSub记录是'sub'(subroutine)记录的高级变体,它有大量增加的特性:

1)它提供了20个不同的输入和输出字段,它们可以保存数组或标量值。这些的类型和数组容量是用户可配置的,并且它们都有相关联的输入或输出链接。

2)在这个记录运行时要被调用的C或C++子程序的名称在IOC运行时能够被动态的修改。能够使用输入链接从另一个记录获取这个名称,或者直接被写入SNAM字段。

3)用户可以选项是否为这个输出字段提交monitor事件。

4) VAL字段被设置成从用户子程序返回值,它被当成一个状态值并且控制是否使用这个输出链接。如果状态值非0,记录也可以产生一个所选严重性的警报。

1、记录专用菜单

1)菜单aSubLFLF

LFLG菜单字段控制在记录运行时是否读取SUBL链接来更新要被调用的子程序的名称。

2)菜单aSubEFLG

EFLG菜单字段指明是否为VALA..VALU输出值字段提交monitor事件。 

2、参数字段

1)subroutine字段

VAL字段被设置成用户子程序的返回值。这个值被当成一个错误状态值(0值时表示成功)。输出链接OUTA .. OUTU将仅在这个字程序返回一个0状态时被用作转发相关联的输出值字段。如果返回的状态小于0,这个记录将进入由BRSV字段指定严重性的SOFT_ALARM状态。

INAM字段可用作命名一个子程序,在IOC初始化时将被调用一次。

LFLG告诉记录这个记录是否读取或忽略SUBL链接。如果这个值是READ,则在运行时从SUBL读取要被调用的子程序的名称。如果这个值是IGNORE,子程序的名称是当前保存于SNAM中。

从SUBL链接读取一个字符串获取记录运行时要被运行的子程序的名称。

SNAM保存在记录运行时要被运行的子程序的名称。如果LFLG设为READ,在这个字段中的值被SUBL链接重写。

SADR字段仅能从C代码访问;它指向要被调用的子程序。

CADR字段被用户子程序设定来指向另一个函数,它将就在设置SARD字段为其它某个程序前立即被调用。这使得只要的用户子程序在其首次被调用时定位资源并且在不再使用它们时再次释放它们。

2)操作显示参数

PREC字段指定了用于显示值字段A...U和VALA...VALU的值的十进制位数。除了在不适用它时。

3) 输出Event标记

这个字段告诉这个记录何时提交输出字段VALA...VALU的变化事件。如果这个值为NEVER,不再提交事件。如果这个值是ALWAYS,每次记录运行时提交事件。如果这个值为ON CHANGE,在一个数组的任何元素更改值时,提交事件。这个标记控制值, 日志(存档)和警报变化事件。

4)输入链接字段

记录运行时从这些链接获取A...U的值

 5) 输入值字段

这些字段保存从输入链接INPA,...,INPU获取的标量或数组值。

6) 输入值数据类型

输入值字段的字段类型。通过指向menuFtype定义的链接能够找到这些选项。

7) 输入值数组的容量

这些字段指定这个输入值字段可以保存多少数组元素。

 8) 输入值数组的尺寸

这些字段指定了输入值字段当前包含多少数组元素。

 9) 输出链接字段

在记录运行时,只要这个程序返回0,VALA,.. VALU字段值通过这些输出链接被发送。

10) 输出值字段

这些字段保存保存由这个子程序产生的标量或数组数据,在记录运行时将通过OUTA,..., OUTU链接发送它们。

11) 老的值字段

输出字段的先前值。这些字段用于确定在EFLG设为ON CHANGE时何时提交事件。

12) 输出值数据类型

输出值字段的字段类型。跟随指向menuFtype定义的链接,能够找到这些选项。

13) 输出值数组容量

这些字段指定了输出值字段可以保存多少数组元素。

14) 输出值数组尺寸

这些字段指定了输出值字段当前包含了多少数组元素。

15) 老的值数组尺寸

这些字段指定了老的值字段当前包含了多少数组元素。

2、记录支持程序

1) init_record

  long (*init_record)(struct dbCommon *precord, int pass)

在iocInit时,这个程序被调用两次。在首次调用时,它做以下事情:

1)calloc足够空间来保存由FTA-FTU和NOA-NOU字段的设置定义的输入标量和/或数组。初始化NE*为相关联的NO*字段值的值。
2)calloc足够空间来保存由FTVA-FTVU和NOVA-NOVU的设置定义的输出标量和/或数组的数目。对于输出字段,calloc空间来保存一个字段先前的值。当决定是否提交事件时,这是必需的。

第二次调用中,它做以下事情:

1) 如果SUBL是一个常数链接,初始化SUBL。

2)初始化每个常数输入链接。

3)如果设置了字段INAM,查找这个程序的地址并且调用它。

4)如果LFLG字段设置为IGNORE,并且定义了SNAM,查找process程序的地址。

2) process

long (*process)(struct dbCommon *precord)

这个程序实现了以下算法:

1)如果PACT是FALSE,执行正常运行。

2)如果PACT是TRUE,执行异步结束运行。

正常运行:

  • 设置PACT为TRUE。
  • 如果字段LFLG设为READ,从SUBL链接读取子程序名称。如果这个名称不是NULL,并且与先前的子程序名称不同,查找子程序地址。设置老的子程序名称ONAM,为当前名称SNAM。
  • 从输入链接获取这些值。
  • 设置PACT为FALSE。
  • 如果所有输入链接获取成功,调用由SNAM指定的程序。
  • 设置VAL为从SNAM指定的程序的返回值。
  • 如果SNAM程序设置PACT为TRUE,则返回。在这种情况中,我们假设这个程序已经安排了之后某个时间为异步完成调用这个process。
  • 设置PACT为TRUE。
  • 如果VAL是0,使用输出链接写输出值。
  • 获取运行的时间并且将其放入时间戳字段。
  • 如果VAL变化了,为这个字段提交值变化和日志事件。如果EFLG设置为ALWARYS,为每个输出字段提交值变化和日志事件。如果EFLG设置为ON CHANGE,为发生变化的每个输出字段提交值变化和日志事件。在数组的情况,如果数组中任何单个元素变化了,将提交一个事件。如果EFLG被设置为NEVER,不为输出字段提交值变化或日志事件。
  • 运行位于转发链接结尾的记录(如果存在)。
  • 设置PACT为FALSAE。

异步结束的运行:

  • (再次)调用由SNAM指定的程序。
  • 设置VAL为从由SNAM指定程序的返回值。
  • 设置PACT为TRUE。
  • 如果VAL是0,使用输出链接写输出值。
  • 获取运行时间并且放置其到时间戳字段。
  • 如果VAL变化了,为这个字段提交值变化和日志事件。如果EFLG设置为ALWARYS,为每个输出字段提交值变化和日志事件。如果EFLG设置为ON CHANGE,为每个发生变化的输出字段提交值变化和日志事件。在数组的情况中,如果数组中任何单个元素发生变化,将提交一个事件。如果EFLG设置为NEVER,不为输出字段提交值变化或日志事件。
  • 运行转发链接末尾的记录(如果存在)。
  • 设置PACT为FALSE。

3、aSub记录的使用

aSub记录有输入值字段(A-U)和输出值字段(VALA-VALU),它们是完全独立的。输入值字段有相关联的输入链接(INPA-INPU),而输出值字段有相关联的输出链接(OUTA-OUTU)。输入和输出都有类型字段(FTA-FTU, FTVA-FTVU,它们默认为"DOUBLE"),以及元素数目字段(NOA-NOU, NOVA-NOVU,它们都默认为'1')。如果这个子程序返回一个0值(OK)状态值,输出链接OUTA-OUTU才被处理。

1)示例数据库片段

要使用A字段从某个其它记录读取一个数组,则,我需要一个数据库片段,其看起来像这样:

 record(aSub,"my_asub_record") {
        field(SNAM,"my_asub_routine")
        ...
        field(FTA, "LONG")
        field(NOA, "100")
        field(INPA, "myWaveform_1 NPP NMS")
        ...
    }

2)示例子程序片段

使用A字段的相关联的子程序代码看起来像如下:

   static long my_asub_routine(aSubRecord *prec) {
        long i, *a;
        double sum=0;
        ...
        a = (long *)prec->a;
        for (i=0; i<prec->noa; i++) {
            sum += a[i];
        }
        ...
        return 0; /* process output links */
    }

注意:即使A-U, VALA-VALU仅包含单个元素,这个子程序也必须总是按数组处理这些值字段(A-U, VALA-VALU)。

3)需要导出代码

除了你自己的代码,你必须导出并且注册你的子程序,因而这个记录可以定位它们。最简单的方式如下:

  #include <registryFunction.h>
    #include <epicsExport.h>
    
    static long my_asub_routine(aSubRecord *prec) {
        ...
    }
    epicsRegisterFunction(my_asub_routine);

4) 需要数据库定义代码

被ioc装载的.dbd文件必须包含以下一行,此行告诉连接器要在IOC程序中包含你的对象文件:

 function(my_asub_routine)

5) 设备支持,写入硬件

aSub记录不调用任何任何支持程序。如果你想要写入硬件,你可以使用你的输出字段和链接去写入某个其它的能够写入硬件的记录。

6)在记录运行中动态地更改被调用的用户程序

aSub记录允许在记录运行时动态的更改被调用的程序。做这件事有两种方式:

1)LFLG字段可以设为READ,因而从SUBL链接读取这个程序的名称。因而,在aSub记录运行前,提供给这个链接的任何东西可以更改这个程序的名称。在这种情况中,在从这个链接获取的程序名称改变时,记录在符号表中查找这个符号名。

2)LFLG字段可以设置为IGNORE。在这种情况中,在记录运行时被调用的程序是在SNAM字段中指定的那个程序。在这些情况下,可以通过通道访问写入那个字段来更改SNAN字段。在开发中,在尝试若干版本的这个程序时,重启IOC并且重载这个数据库不是必要的。用vxWorkd ld命令可以装载一个新程序,并且使用通道访问或dbpf命令放置这个程序名称到这个记录的SNAM字段。当SNAM字段被修改时,这个记录将在符号表中查找这个符号名。因为vxWorkd符号查找返回已经被装载代码的最新版本,所以甚能够至使用相同程序名。

猜你喜欢

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