EPICS应用程序开发6 -- 数据库定义

1 概要

本文描述数据库定义。描述以下定义:

  • 菜单
  • 记录类型
  • 设备
  • 驱动
  • 注册器
  • 变量
  • 函数
  • 断点表

记录实例根本上不同于其它定义。一个包含记录实例的文件不应该包含任何其它定义,反之亦然。因而以下遵守规则:

数据库定义文件

一个除记录实例外包含任何定义类型的文件。

数据库实例文件

一个只包含记录实例定义的文件。

本文也描述操作这些定义的工具程序。定义的任意组合可以出现在单个文件中或者一个通过包含文件而相互关联的文件集合中。

2、数据库语法的概要

以下概括了数据库定义的语法:

path "path"
addpath "path"
include "filename"
#comment
menu(name) {
    include "filename"
    choice(choice_name, "choice_value")
    ...
}

recordtype(record_type) {
    include "filename"
    field(field_name, field_type) {
        asl(asl_level)
        initial("init_value")
        promptgroup(gui_group)
        prompt("prompt_value")
        special(special_value)
        pp(pp_value)
        interest(interest_level)
        base(base_type)
        size(size_value)
        extra("extra_info")
        menu(name)
    }
    %C_declaration
    ...
}

device(record_type, link_type, dset_name, "choice_string")

driver(drvet_name)

registrar(function_name)

variable(variable_name)

breaktable(name) {
    raw_value eng_value
    ...
}

以下定义了一个记录实例:

record(record_type, record_name) {
include "filename"
field(field_name, "value")
    alias(alias_name)
    info(info_name, "value")
    ...
}
alias(record_name,alias_name)

扫描二维码关注公众号,回复: 14534204 查看本文章

3 数据库定义的一般规则

3.1 关键字

以下是关键字,即:除非它们被包围在引号中,否则它们不可以用作值:

path addpath include menu choice recordtype field
device driver registrar function variable
breaktable record grecord info alias

3.2 无引号的字符串

在概要部分,一些值以带引号显示而一些值以不带引号显示。实际规则是由以下字符组成的任何字符串不必一定带引号,除非它包含其中包含一个以上的关键字。

a-z A-Z 0-9 _ + - : . [ ] < > ;

这些对于过程变量名都是合法字符,但由于"."在一个PV名称中从字段名分隔记录,在记录名中步允许"."。因而在很多情况中,在数据库文件中不需要引号围绕记录或字段名。但包含一个宏的任何字符串需要带引号。

3.3 带引号的字符串

一个带引号的字符串可以包含除引号字符"外的任何ascii字符。可以通过使用\作为转义,给出引号字符自身。例如"\""是一个薄荷单个字符"的带引号字符。

3.4 宏替换

在带引号字符串中允许宏替换。宏实例采取这个格式:

$(name)或${name}

对于分隔符使用小括号或花括号之间没有差别,但对于一个指定的宏实例二者必须匹配。宏名可以可以产生自另外的宏,例如:

$(name_$(sel))

一个宏实例也可以提供一个默认值,当没有定义指定名称的宏时,将使用这个默认值。如果需要,可以根据其它宏定义这个默认值,但不能包含任何非转义的逗号字符。用于指定一个默认值的语法如下:

$(name=default)

最后,宏实例也可以包含其它宏的定义,它们可以(临时地)重写那些宏地任何已有值,但作用域仅限这个宏实例地展开过程中。这些定义由逗号分隔地name=value序列组成,例如:

$(abcd=$(a)$(b)$(c)$(d),a=A,b=B,c=C,d=D)

3.5 转义序列

数据库例程仅转换在数据库字段值内部的标准C转义序列。受支持的标准C转义序列是:

\a \b \f \n \r \t \v \\ \? \' \" \ooo \xhh

\ooo代表一个有1,2或3个数字的八进制数值。\xhh代表一个有1或2个数字的十六进制数值。

3.6 注释

注释符号是'#'。当注释符号出现时,它以及到本行末尾的所有字符都被忽略。

3.7 在引用前定义

在定义项后才能引用这个项。例如,除非已经定义了一个菜单定义,否则recordtype menu字段不能引用这个菜单。另一个示例是在一个记录实例的相关联记录类型被定义前,这个记录类型的实例不能出现。

3.8 多个定义

如果一个菜单,记录类型,设备,驱动或者断点表被定义了多次,则仅使用第一个实例。但记录实例定义通常是叠加的,所以相同记录的多个实例可以被装置并且每次遇到一个字段值时,它替代先前的值。

3.9 文件名展开

依据规则:

  • 记录实例文件有扩展名".db",如果记录实例文件也包含可视布局信息,它有扩展名".vdb"。
  • 数据库定义文件有扩展名".dbd"。

4 path addpath 路径定义

4.1 格式

path "dir:dir...:dir"
addpath "dir:dir...:dir"

路径字符串按照操作系统的标准规则,即:目录名在Unix上是由一个分号":"隔开,在Windows上用一个分号";"隔开。

path命令指定在装载数据库和数据库定义文件时使用的当前搜索路径。文件addpath追加目录名称到当前路径。这个路径被用于定位初始的数据库文件和被包含文件。在一个非空路径字符串开头、中间或末尾的空dir表示当前路径。例如:

nnn::mmm    # 当前路径在nnn和mmm之间
:nnn        # 当前路径最先
nnn:        # 当前路径最后

装载数据库文件的工具(dbExpand, dbLoadDatabase等)允许用户指定一个初始路径。path和addpath命令可以用于更快或者扩展这个初始路径。

按照以下确定这个初始路径:

如果指定了一个初始路径,使用它。Else:

如果环境变量EPICS_DB_INCLUDE_PATH被定义,使用它。Else:

默认路径是".",即:当前路径。

除非文件名包含一个/或\,否则使用这个路径。使用包含指定文件名的第一个目录。

5 inluce -- 包含文件

5.1 格式

include "filename"

一个include语句可以出现在概要中展示的任何位置。它使用如以上指定的路径。

6 menu -- 菜单定义

6.1 格式

menu(name) {
    choice(choice_name, "choice_string")
    ...
}

6.2 定义

name:菜单名称。这是标识这个菜单的唯一名称。如果指定了重复定义,只使用第一个定义。

choice_name:由dbToMenuH或dbToRecordtypeH产生的在enum中使用的名称。这必须是一个合法的C/C++标识符。

choice_string:与这个特定选项相关联的文本字符串。

6.3 示例

menu(menuYesNo) {
    choice(menuYesNoNO, "NO")
    choice(menuYesNoYES, "YES")
}

7 recordtype -- 记录类型声明

7.1 格式

recordtype(record_type) {
    field(field_name, field_type) {
        asl(as_level)
        initial("init_value")
        promptgroup(gui_group)
        prompt("prompt_value")
        special(special_value)
        pp(pp_value)
        interest(interest_level)
        base(base_type)
        size(size_value)
        extra("extra_info")
        menu("name")
    }
    %C_declaration
    ...
}

7.2  字段定义规则

asl:为字段设置访问安全。

initial:为字段提供一个初始(默认)值。

promptgroup:这个字段属于哪个组,用于数据库配置工具。

prompt:一个用于数据库配置工具的提示字符串。如果未定义promptgroup,可选的。

special:如果指定,在运行时此自读需要特殊(special)运行。

pp:当通道访问写入这个字段时,是否应该运行一个被动记录。

interest:用于这个字段的兴趣水平。

base:对于整数字段,当转换这个字段值为一个字符串时,使用的数字基数。

size:必须为DBF_STRING字段指定这个。

extra:必须为DBF_NOACCESS字段指定这个。

menu:必须为DBF_MENU字段指定这个。它是相关联菜单的名称。

7.3 定义

record_type:这个记录类型的唯一名称。如果重复指定了,只使用第一个定义。

field_name:这个字段名,其必须是一个有效的C标识符。当

field_type:这必须是以下值之一:

  • DBF_STRING
  • DBF_CHARDBF_UCHAR
  • DBF_SHORTDBF_USHORT
  • DBF_LONGDBF_ULONG
  • DBF_FLOATDBF_DOUBLE
  • DBF_ENUMDBF_MENUDBF_DEVICE
  • DBF_INLINKDBF_OUTLINKDBF_FWDLINK
  • DBF_NOACCESS

as_level:这必须是以下值之一:

  • ASL0
  • ASL1 (默认值)

操作者通常更改的这个字段被赋给ASL0。其它字段被分配ASL1。例如,一个模拟输出记录的VAL字段被分配ASL0,而所有其它字段被分配ASL1。这是因为在一般操作过程中仅VAL字段应该被修改。

init_value:对应数据类型的合法值。

prompt_value:用于数据库配置工具的一个提示值。

gui_group:这必须是以下值之一:

  • GUI_COMMON
  • GUI_ALARMS
  • GUI_BITS1
  • GUI_BITS2
  • GUI_CALC
  • GUI_CLOCK
  • GUI_COMPRESS
  • GUI_CONVERT
  • GUI_DISPLAY
  • GUI_HIST
  • GUI_INPUTS
  • GUI_LINKS
  • GUI_MBB
  • GUI_MOTOR
  • GUI_OUTPUT
  • GUI_PID
  • GUI_PULSE
  • GUI_SELECT
  • GUI_SEQ1
  • GUI_SEQ2
  • GUI_SEQ3
  • GUI_SUB
  • GUI_TIMER
  • GUI_WAVE
  • GUI_SCAN

此信息是由数据库配置工具使用。只为了能够被数据库配置工具赋值的字段定义这个值。文件guigrooup.h包含所有可能的定义。这使得数据库配置工具按功能分组字段,而不是按名字排列它们。这种特性很少被使用,因而很多记录类型没有分配合适值给某些字段。

special_value:必须是以下之一:

必须是以下值之一:

  • SPC_MOD:当被修改时,通知记录支持。当这个字段被修改时,记录支持special例程将被调用。
  • SPC_NOMOD:不允许外部修改。这个值禁用外部对这个字段的写,因而它只能被记录或设备支持模块设置。
  • SPC_DBADDR:如果记录支持cvt_dbaddr例程应该被dbNameToAddr调用,使用这个值,即:当外部的记录/设备支持连接到这个字段。
  • SPC_SCAN:扫描相关的字段。
  • SPC_ALARMMACK:警告确认字段。
  • SPC_AS:访问安全字段。
  • 以下值过时了,使用SPC_MOD替代。
  • 一个大于103的整数值。
  • SPC_RESET:一个重置字段正在被修改。
  • SPEC_LINCONV:一个线性转换字段正在被修改。
  • SPC_CALC:一个calc字段正在被修改。

pp_value:当通道访问写到这个字段时,应该运行一个被动记录吗?被允许的值为:

  • NO (默认)
  • YES

interest_level:一个用于dbpr命令的值。

base:对于整数字段,默认的基数。合法值是:

  • DECIMAL (默认)
  • HEX

size_value:用于DBF_STRING字段的字符数目。

extra_info:对于DBF_NOACCESS字段,这是用于这个字段的C语言定义。此定义必须以小写的文件名结束。

%C_declaration:在记录体内的一个百分号%引入一行代码,将在产生的C头文件中包含此代码。

7.4 示例

以下顶event记录类型的定义:

recordtype(event) {
        include "dbCommon.dbd"


        field(VAL,DBF_STRING) {
                prompt("Event Name To Post")
                promptgroup("40 - Input")
                special(SPC_MOD)
                asl(ASL0)
                size(40)
        }
        %#include "dbScan.h"
        field(EPVT, DBF_NOACCESS) {
                prompt("Event private")
                special(SPC_NOMOD)
                interest(4)
                extra("EVENTPVT epvt")
        }


        field(INP,DBF_INLINK) {
                prompt("Input Specification")
                promptgroup("40 - Input")
                interest(1)
        }


        field(SIOL,DBF_INLINK) {
                prompt("Sim Input Specifctn")
                promptgroup("90 - Simulate")
                interest(1)
        }
        field(SVAL,DBF_STRING) {
                prompt("Simulation Value")
                size(40)
        }
        field(SIML,DBF_INLINK) {
                prompt("Sim Mode Location")
                promptgroup("90 - Simulate")
                interest(1)
        }
        field(SIMM,DBF_MENU) {
                prompt("Simulation Mode")
                special(SPC_MOD)
                interest(1)
                menu(menuYesNo)
        }
        field(SIMS,DBF_MENU) {
                prompt("Sim mode Alarm Svrty")
                promptgroup("90 - Simulate")
                interest(2)
                menu(menuAlarmSevr)
        }
        field(OLDSIMM,DBF_MENU) {
                prompt("Prev. Simulation Mode")
                special(SPC_NOMOD)
                interest(4)
                menu(menuSimm)
        }
        field(SSCN,DBF_MENU) {
                prompt("Sim. Mode Scan")
                promptgroup("90 - Simulate")
                interest(1)
                menu(menuScan)
                initial("65535")
        }
        field(SDLY,DBF_DOUBLE) {
                prompt("Sim. Mode Async Delay")
                promptgroup("90 - Simulate")
                interest(2)
                initial("-1.0")
        }
        %#include "callback.h"
        field(SIMPVT,DBF_NOACCESS) {
                prompt("Sim. Mode Private")
                special(SPC_NOMOD)
                interest(4)
                extra("epicsCallback            *simpvt")
        }


}

8 device - 设备支持声明

8.1 格式

 device(record_type, link_type, dset_name, "choice_string")

8.2 定义

record_type:record_type和choice_string的组合必须唯一。如果相同组合出现多次,仅使用第一个定义。

link_type:链接类型。这必须是以下之一:

  • CONSTANT
  • PV_LINK
  • VME_IO
  • CAMAC_IO
  • AB_IO
  • GPIB_IO
  • BITBUS_IO
  • INST_IO
  • BBGPIB_IO
  • RF_IO
  • VXI_IO

dset_name:对应此设备支持的设备支持入口表的名称

choice_string:用于设备支持的DTYP选项字符串。一个choice_string值可以对不同记录类型重复使用,但对于每种特定记录类型必须唯一。

8.3 示例

 device(ai,CONSTANT,devAiSoft,"Soft Channel")
 device(ai,VME_IO,devAiXy566Se,"XYCOM-566 SE Scanned")

9 驱动--驱动声明

9.1 格式

driver(drvet_name)

9.2 定义

drvet_name:如果定义被重复了,仅使用第一次的。

9.3 示例

driver(drvVxi)
driver(drvXy210)

10 registrar -- 注册器声明

10.1 格式

 registrar(function_name)

10.2 定义

function_name:一个不接受参数的C函数的名称,返回void,并且在其源文件中已经用一个epicsExportRegistrar声明被标记,例如:

static void myRegistrar(void);
epicsExportRegistrar(myRegistrar);

这可以用于注册由subroutine记录使用或者可以从iocsh被调用的函数。

registrar(myRegistrar)

10.3 示例

registrar(myRegistrar)

11 varialbe -- 变量声明

11.1 格式

  variable(variable_name[, type])

11.2 定义

variable_name:一个C变量的名称,其已经在其源文件中用一个epicsExportAddress声明被标记。

type:C变量的类型。如果未出现,认为是int。当前只支持int和double。

这位设备或驱动支持或者一个subroutine记录的子程序注册一个诊断/配置变量。

在一个应用程序C源代码文件中:

#include <epicsExport.h>

static double myParameter;
epicsExportAddress(double, myParameter);

在一个应用程序数据库定义文件中:

variable(myParameter, double)

12 function--函数声明

12.1 格式

function(function_name)

12.2 定义

function_name:C函数的名称,已经从其源文件中用一个epicsRegisterFunction函数被导出。这注册一个函数,使得可以在函数注册表中找打它,由诸如通过名称指向这个函数的sub或aSub记录类型使用。

12.3 示例

在一个应用程序C源代码文件中:

#include <epicsExport.h>
#include <registryFunction.h>

static long myFunction(void *argp) {
        /* my code ... */
}
epicsRegisterFunction(myFunction);

在应用程序数据库定义文件中:

 function(myFunction)

13 breaktable -- 断点表

13.1 格式

breaktable(name) {
    raw_value eng_value
    ...
}

13.2 定义

name:这个断点表的名称,其必须是字母数值。如果被重复指定,使用第一个断点表。

raw_value:原始值,即:与间隔起始相关联的实际ADC值。

eng_value:与间隔开始相关联的工程单位。

13.3 示例

breaktable(typeJdegC) {
    0.000000 0.000000
    365.023224 67.000000
    1000.046448 178.000000
    3007.255859 524.000000
    3543.383789 613.000000
    4042.988281 692.000000
    4101.488281 701.000000
}

14、record -- 记录实例

14.1 格式

record(record_type, record_name) {
    alias(alias_name)
    field(field_name, "field_value")
    info(info_name, "info_value")
    ...
}
alias(record_name, alias_name)

record_type:记录类型。 

record_name:记录名称。这必须由以下字符组成:

 a-z A-Z 0-9 _ - + : [ ] < > ;

注意:如果使用宏替换,名称必须加引号。

只要记录类型相同,通常允许为一个记录重复定义。对应每个字段最后传给的值是使用的值。 

可以使用iocsh var命令设置变量dbRecordsOnceOnly为任何非0值,使得装载重复记录定义到IOC成为非法。

alias_name:这个记录的一个替代名称,按照与记录名称相同的规则。

field_name:一个字段名。

field_value:指定名称字段的值,取决于特定字段类型。在双引号中,字段值字符串可以包含转义的C89字符,诸如\", \t, \n, \064和\x7e,并且在装载这个数据库时这些将被恰当地转义。

1)  DBF_STING:一个ASCII串。如果它超过了这个字段长度,它将被截短。

2)  DBF_CHAR, DBF_UCHAR, DBF_SHORT, DBF_USHORT, DBF_LONG, DBF_ULONG:一个代表一个有效整数地字符串。使用标准地C规则,即:一个前导0表示用八进制给出值,而一个前导0x表示用十六进制给出值。

3) DBF_FLOAT, DBF_DOUBLE:这个字符串必须代表一个有效浮点数值。

4)DBF_MENU:字符串必须是相关联菜单地有效选项之一。

5) DBF_DEVICE:字符串必须是有效设备选项字符串之一。

6) DBF_INLINK, DBF_OUTLINK, DBF_FWDLINK

注意:

如果字段名称是INP或OUT,则这个字段与DTYP相关联,并且可用值是由当前DTYP选项字符串选取的设备支持地链接类型确定。其它的DBF_INLINK和DBF_OUTLINK字段必须是CONSTANT或PV_LINKs。

一个指定CONSTANT链接类型的设备支持可以被传给一个常数或者一个PV_LINK。

这个字段的可用值取决于设备支持的链接类型,如下:

a) CONSTANT:一个数值文字,对应这个字段类型有效,它要被读取到。

b) PV_LINK:一个格式如下的值:record.field process maximize

record是是存在在这个或者另一个IOC中一个记录的名称。

.field, process和maximize部分都是可选的。

对应.field的默认值是.VAL。

process可以是以下值之一:

a) NPP:不过程被动(默认)

b) PP:过程被动。

c) CA:强制链接成为一个通道访问链接。

d) CP:CA并且在monitor时运行。

d) CPP:CA并且如果记录是被动的,在monitor时运行。

CP和CPP只对DBF_INLINK字段有效。

DBF_FWDLINK字段可以使用PP或CA。如果一个DBF_FWDLINK是一个通道访问链接,它必须指向目标记录的PROC字段。

maximize可以是以下值之一:

  • NMS:不最大化严重性(默认)
  • MS:最大化严重性。
  • MSS:最大化严重性和状态。
  • MSI:如果无效,最大化严重性。

3) VME_IO

4) CAMAC_IO

5) AB_IO

6) GPIB_IO

7) BITBUS_IO

8) INST_IO @parm

9) BBGPIB_IO

10) RF_IO

11) VXI_IO

info_name:与这个记录相关联的信息项的名称。

info_value:任意ASCII串。使用此信息项的IOC应用程序可能在此字符串的内容上放置更多限制。

14.3 示例

record(ai,STS_AbAiMaS0) {
    field(SCAN,".1 second")
    field(DTYP,"AB-1771IFE-4to20MA")
    field(INP,"#L0 A2 C0 S0 F0 @")
    field(PREC,"4")
    field(LINR,"LINEAR")
    field(EGUF,"20")
    field(EGUL,"4")
    field(EGU,"MilliAmps")
    field(HOPR,"20")
    field(LOPR,"4")
}
record(ao,STS_AbAoMaC1S0) {
    field(DTYP,"AB-1771OFE")
    field(OUT,"#L0 A2 C1 S0 F0 @")
    field(LINR,"LINEAR")
    field(EGUF,"20")
    field(EGUL,"4")
    field(EGU,"MilliAmp")
    field(DRVH,"20")
    field(DRVL,"4")
    field(HOPR,"20")
    field(LOPR,"4")
    info(autosaveFields,"VAL")
}
record(bi,STS_AbDiA0C0S0) {
    field(SCAN,"I/O Intr")
    field(DTYP,"AB-Binary Input")
    field(INP,"#L0 A0 C0 S0 F0 @")
    field(ZNAM,"Off")
    field(ONAM,"On")
}

15 记录信息项

信息项提供一种连接指定名称字符串值到各自记录实例的方式,这些记录实例与记录定义相同时间被装载。它们可以被连接到任何记录,而不必修改这个记录类型,并且可以被在IOC上运行的程序获取(通过通道访问,它们完全不可见)。连接到单个记录的每项必须有一个唯一名称,通过其寻址它,并且数据库访问提供例程去允许一个记录的info项被扫描,搜索,获取和设置。在运行时,一个void *指针也可以与每项相关联,但仅字符串值在数据库被装载时才能从记录定义被初始化。

16 记录属性

每种记录类型可以任意数目的记录属性。每种属性是一个伪字段,其可以通过数据库和通道访问被访问。每种属性有一个名称,其作用就像一个字段名,但对应这个记录类型的所有实例返回相同的值。每种记录类型都自动产生两种属性:RTYP和VERS。RTYP的值是记录类型名。VERS的默认值是"none specified",可以由记录支持更改它。记录支持可以调用以下例程来创建新的属性或更改已有属性:

 long dbPutAttribute(char *recordTypename, char *name, char*value)

参数是:

1) recordTypename:记录类型的名称。

2) name:属性名,即:伪字段名。

3) value:分配给这个属性的值。

17 断点表--讨论

菜单menuConvert用于ai和ao记录的字段LINR。这些记录允许原始数据通过以下之一被转换成工程单位或者从工程单位转换成原始数据:

  1. No Conversion
  2. Slope Conversion
  3. Linear Conversion
  4. Breakpoint table

另一种记录类型也可以使用这种特性。第一种选项指定不转换;第二种选项和第三种选项都是线性转换,对于Slope转换的差别为用户直接指定转换斜率和偏移值,而对于Linear转换,这些是由设备支持从请求的工程单位范围和设备支持知道的硬件转换范围计算来的。余下的选项被认为是断点表的名称。如果选择了一个断点表,记录支持模块调用cvtRawToEngBpt或cvtEngToRawBpt。详细地你看一下ai和ao记录支持模块。

如果一个用户想要添加其它断点表,则应该做以下事情:

1) 从EPICS base/src/bpt复制menuConvert.dbd文件

2) 添加新断点表地定义到末尾。

3) 确认修改地menuConvert.dbd被装载到了IOC替换EPICS版本。

只在一个记录实例实际选择它时,才需要装载一个断点表。也应该提到Allen Bradley IXEW设备支持错误使用了LINR字段。如果你使用这个模块,你不更改EPICS在menuConvert.dbd中提供的任何定义是非常重要的。只要在末尾添加你的定义。

如果选择了一个断点表,则在调用iocInit前,必须装载对应的断点表到这个IOC。

通常,需要直接创建这个断点表。但有时,需要从一个代表等距工程的原始值表创建一个断点表。一个代表示例是在OMEGA Engineering, INC Temperature Measurement Handbook中的热偶表。提供了一个makeBpt工具转换这些的数据为一个断点表。

用于从对应等距值的raw值数据表产生一个断点表的格式是:

!comment line
<header line>
<data table>

header  line包含余下信息:

  • Name:一个字母数字ascii串,指定断点表名称
  • Low Value Eng:对应第一个断点表条目的工程单位值
  • Low Value Raw:对应第一个断点表的原始值
  • High Value Eng:工程单位:所需最大值
  • High Value Raw:对应High Value的Raw值
  • Error:允许误差(工程单位)
  • First Table:对应第一个数据表条目的工程单位
  • Last Table:对应最后一个数据表条目的工程单位
  • Delta Table:每个数据表条目在工程单位中的变化

一个示例定义是:

  "TypeKdegF" 32 0 1832 4095 1.0 -454 2500 1
   <data table>

通过执行以下产生断点表:

makeBpt bptXXX.data

输入文件必须有data扩展名。输出文件名是输入文件名相同,扩展名被替换成.dbd。创建断点表的另一种方式是在Makefile中包含以下定义:

BPTS += bptXXX.dbd

注意:这需要命名规则:所有数据表为格式bpt<name>.data并且断点表格式bpt<name>.dbd。

18 菜单和记录类型Include File产生

18.1 介绍

给出一个包含菜单的文件,dbToMenuH产生一个include文件,它可以被使用相关联菜单的任何代码使用。给出一个包含菜单定义和记录类型定义任意组合的文件,dbToRecordtypeH产生一个include文件,其可以被使用这些菜单和记录类型的任何代码使用。

EPICS base使用以下规则管理菜单和记录类型定义。鼓励生成本地记录类型的用户照做。

1) 每个菜单,其要么用于在数据库common中的字段(例如:menuScan)要么是全局使用(例如:menuYesNo),在一个单独文件中被定义。这个文件的名称与菜单名相同,扩展名dbd。产生的incluce文件的名称是带有一个.h后缀的菜单名。在一个menuScan.dbd文件中定义了menuScan,而产生的include文件被命名为menuScan.h。

2) 每种记录类型定义被定义在一个单独文件中。此外,这个文件包含了仅被此记录类型使用的任何菜单定义。文件名与记录类型名相同,后跟Record.dbd。产生的include文件名称相同,扩展名.h。在aoRecord.dbd文件中定义了aoRecord,而产生的include文件被命名为aoRecord.h。由于aoRecord有一个名为aoOIF的私有菜单,这个dbd文件和产生的include文件有一个对应这个菜单的定义。因而,对于每种记录类型,有两个元文件(xxxRecord.dbd和xxxRecord.h)和一个产生的文件(xxxRecord.h)。

在继续前,应该提到开发者通常不需要执行dbToMenuH.pl或dbToRecord.pl自身。如果使用了合适的命名规则,仅需要添加定义到合适的Makefile。

18.2 dbToMenuH.opl

按如下执行这个工具:

dbdToMenuH.pl [-D] [-I dir] [-o menu.h] menu.dbd [menu.h]

它读取输入文件menu.dbd并且产生一个C/C++头文件,这个头文件包含对应在输入文件中发现菜单的枚举类型定义。

可以提供多个-I选项去指定目录,当查找被包含文件时必须搜索这个目录。如果没有用-o menu.h选项或者以末尾命令行参数指定输出文件名,则将用.h替换.dbd从输入文件名构建输出文件名。

-D选项使得这个程序输出对应这个输出文件的Makefile依赖信息到标准输出,替代实际执行以上描述的函数。

[blctrl@main-machine exer8]$ dbdToMenuH.pl -I ./  -D menuPriority.dbd menuPriority.h
menuPriority.h: .//menuPriority.dbd

.//menuPriority.dbd:

例如menuPriority.dbd,其包含处理优先级的定义,包括:

menu(menuPriority) {
    choice(menuPriorityLOW,"LOW")
    choice(menuPriorityMEDIUM,"MEDIUM")
    choice(menuPriorityHIGH,"HIGH")
}

产生的include文件包含:

[blctrl@main-machine exer8]$ dbdToMenuH.pl -I ./  menuPriority.dbd menuPriority.h
[blctrl@main-machine exer8]$ ls
menuPriority.dbd  menuPriority.h
[blctrl@main-machine exer8]$ cat menuPriority.h
/* menuPriority.h generated from menuPriority.dbd */

#ifndef INC_menuPriority_H
#define INC_menuPriority_H

#ifndef menuPriority_NUM_CHOICES
typedef enum {
    menuPriorityLOW                 /* LOW */,
    menuPriorityMEDIUM              /* MEDIUM */,
    menuPriorityHIGH                /* HIGH */
} menuPriority;
#define menuPriority_NUM_CHOICES 3
#endif

需要优先级菜单值的任何代码应该包含这个文件并且使用这些定义。

18.3 dbdToRecordtypeH.pl

按如下执行这个工具:

bdTorecordtypeH.pl [-D] [-I dir] [-o xRecord.h] xRecord.dbd [xRecord.h]

它读取输入文件xRecord.dbd并且产生一个C/C++头文件,其定义指定记录类型的内存中结构体并且为编译器提供其它相关联信息。如果输入文件包含任意菜单定义,它们也将在输出文件中被转成枚举类型。

提供多个-I选项去指定路径,在搜索被包含文件时,必须搜索这些路径。如果没有用-o xRecord.h选项或者以一个末尾命令行参数指定输出文件名,把.dbd替换.h从输入文件名构建输出文件名。

-D选项使得这个程序输出对应输出文件的Makefile依赖信息到标准输出,而不是实际执行以上函数。

例如,aoRecord.dbd,其包含模拟输出记录的定义,包含:

menu(aoOIF) {
    choice(aoOIF_Full,"Full")
    choice(aoOIF_Incremental,"Incremental")
}

recordtype(ao) {
    include "dbCommon.dbd"
    field(VAL,DBF_DOUBLE) {
        prompt("Desired Output")
        promptgroup("50 - Output")
        asl(ASL0)
        pp(TRUE)
    }
    field(OVAL,DBF_DOUBLE) {
        prompt("Output Value")
    }
    field(OUT,DBF_OUTLINK) {
        prompt("Output Specification")
        promptgroup("50 - Output")
        interest(1)
    }
...
   field(OMOD,DBF_UCHAR) {
        prompt("Was OVAL modified?")
        special(SPC_NOMOD)
    }
}

产生的include文件aoRecord.h包含: 

[blctrl@main-machine exer9]$ /usr/local/EPICS/base/bin/linux-x86_64/dbdToRecordtypeH.pl  -I ./ aoRecord.dbd aoRecord.h
[blctrl@main-machine exer9]$ ls
aoRecord.dbd  aoRecord.h  dbCommon.dbd
[blctrl@main-machine exer9]$ cat aoRecord.h
/⋆ aoRecord.h generated from aoRecord.dbd ⋆/ 
 
#ifndef INC_aoRecord_H 
#define INC_aoRecord_H 
 
#include "epicsTypes.h" 
#include "link.h" 
#include "epicsMutex.h" 
#include "ellLib.h" 
#include "epicsTime.h" 
 
typedef enum { 
    aoOIF_Full                      /⋆ Full ⋆/, 
    aoOIF_Incremental               /⋆ Incremental ⋆/, 
    aoOIF_NUM_CHOICES 
} aoOIF; 
 
typedef struct aoRecord { 
    char                name[61];   /⋆ Record Name ⋆/ 
    ... define remaining fields from database common 
    epicsFloat64        val;        /⋆ Desired Output ⋆/ 
    epicsFloat64        oval;       /⋆ Output Value ⋆/ 
    ... define remaining record specific fields 
} aoRecord; 
 
typedef enum { 
    aoRecordNAME = 0, 
    aoRecordDESC = 1, 
    ... indices for remaining fields in database common 
    aoRecordVAL = 43, 
    aoRecordOVAL = 44, 
    ... indices for remaining record specific fields 
} aoFieldIndex; 
 
#ifdef GEN_SIZE_OFFSET 
 
#ifdef __cplusplus 
extern "C" { 
#endif 
#include <epicsExport.h> 
static int aoRecordSizeOffset(dbRecordType ⋆prt) 
{ 
    aoRecord ⋆prec = 0; 
    prt->papFldDes[aoRecordNAME]->size = sizeof(prec->name); 
    ... code to compute size for remaining fields 
    prt->papFldDes[aoRecordNAME]->offset = (char ⋆)&prec->name - (char ⋆)prec; 
    ... code to compute offset for remaining fields 
    prt->rec_size = sizeof(⋆prec); 
    return 0; 
} 
epicsExportRegistrar(aoRecordSizeOffset); 
 
#ifdef __cplusplus 
} 
#endif 
#endif /⋆ GEN_SIZE_OFFSET ⋆/ 
 
#endif /⋆ INC_aoRecord_H ⋆/

模拟输出记录支持模块和所有相关联的设备支持模块应该包含这个文件。别的代码不应该使用它。

让我们讨论这个文件的各个部分:

1) 从菜单定义产生的enum应该用于为与那个菜单相关联的字段提供值。

2) 定义这个记录的typedef struct是由记录支持和设备支持使用去访问模拟输出记录中的字段。

3) 接下来的enum为在这个记录中每个字段定义一个索引编号。这对被被传递一个指向一个DBADDR结构体的指针的记录支持例程有用。它们可以有像以下的代码:

switch (dbGetFieldIndex(pdbAddr)) { 
    case aoRecordVAL : 
        ... 
        break; 
    case aoRecordXXX: 
        ... 
        break; 
    default: 
        ... 
}

当记录类型向一个IOC注册时,执行产生的例程aoRecordSizeOffset。这个例程和记录类型代码一起被编译,并且被标记为static,因此它在此文件外是不可见的。相关联的记录支持源代码必须只在定义了GEN_SIZE_OFFSET宏后包含产生的头文件,像这样:

#define GEN_SIZE_OFFSET 
#include "aoRecord.h" 
#undef GEN_SIZE_OFFSET

这个规则确保此例程只被定义一次。epicsExportRegistrar声明确保记录注册代码可以找到并且调用这个例程。

19 dbdExpand.pl

dbdExpand.pl [-D] [-I dir] [-S mac=sub] [-o out.dbd] in.dbd ...

此程序从所有输入文件读取并且组合数据库定义,接着写出包含来自输入文件的所有信息到单个输出文件。输出内容不同于输入,注释行被移除,并且所有定义的宏和include文件被展开。不同于先前的dbExpand程序,这个程序不理解数据库实例并且使用.db或.vdb文件。

可以提供多个-I选项去指定目录,当搜索被包含文件时必须搜索这些目录。多个-S选项被允许用于宏替换,或者在单个操作中可以指定多个宏。如果没有用-o out.dbd选项指定输出文件名,则输出将传送到stdout。

-D选项使得这个程序为输出文件输出Makefile依赖信息到标准输出,替代实际执行以上描述的函数。

20 dbLoadDatabase

dbLoadDatabase(char ⋆dbdfile, char ⋆path, char ⋆substitutions)

这个IOC命令装载一个数据库文件,其可以包含在本文中描述的任何数据库定义。dbdfile字符串包含格式${MOTOR}的环境变量宏,在打开此文件前,将展开这个宏。path和substitutions参数可以是null或者空,并且通常被忽略。注意:虽然使用dbLoadDatabase装载记录实例(.db)文件当前也是可能的,但dbLoadDatabase只应该用于装载数据库定义(.dbd)文件。

随着读取这个文件的每一行,执行在substitutions中指定的替换。按照以下指定替换:

"var1=sub1,var2=sub3,..."

用语法$(var)或${var}在这个文件中使用变量。如果使用这个替换字符串

"a=1,b=2,c=\"this is a test\""

在数据库文件中$(a), $(b), $(c)任意变量在解析过程中会有替换的合适值。

21 dbLoadRecords

dbLoadRecords(char⋆ dbfile, char⋆ substitutions)

这个IOC命令装载一个包含记录实例、记录别名和/或断点表的文件。dbfile字符串可以包含格式$(MOTOR)的环境变量宏,在打开这个文件前,将展开它。substitutions参数可以是null或空,并且经常被忽略。注意:虽然使用dbLoadRecords装载数据库定义(.dbd)也当前是可能的,但dbLoadRecords应该只用于装载记录实例(.db)文件。

22.1 示例

在此举一个完整的示例:

1) 在用户家目录下创建如下目录:exer/exer10,并且进入这个目录。

2) 用base的makeBaseApp.pl工具,产生一个程序目录以及ioc启动目录:

[blctrl@main-machine exer10]$ /usr/local/EPICS/base/bin/linux-x86_64/makeBaseApp.pl -t ioc softTest
[blctrl@main-machine exer10]$ /usr/local/EPICS/base/bin/linux-x86_64/makeBaseApp.pl -i -t ioc softTest
Using target architecture linux-x86_64 (only one available)
The following applications are available:
    softTest
What application should the IOC(s) boot?
The default uses the IOC's name, even if not listed above.
Application name?
[blctrl@main-machine exer10]$ ls
configure  iocBoot  Makefile  softTestApp

3)在softTestApp/Db目录下建立一个test.db文件,并且在这个文件中添加以下内容:

record(ai, "$(user):ai1")
record(ai, "$(user):ai2")
record(stringout, "$(user):so"){
        field(VAL, "$(STR)")
}

4) 编辑softTestApp/Db/Makefile文件,在#DB += xxx.db行下,添加以下一行:

DB += test.db

5) 返回这个程序的顶层目录,即:exer10,执行make,进行程序编译

6) 编译 iocBoot/iocsoftTest/st.cmd文件,并且在#dbLoadRecords("db/xxx.db","user=blctrl")下添加以下一行:

dbLoadRecords("db/test.db","user=blctrl,STR=HelloWorld")

7) 进入 iocBoot/iocsoftTest/目录,用以下命令启动这个程序:

[blctrl@main-machine iocsoftTest]$ ../../bin/linux-x86_64/softTest st.cmd

8) 进行记录验证:

epics> dbl
blctrl:ai1
blctrl:ai2
blctrl:so
epics>
epics> dbpr blctrl:so
ASG :               DESC:               DISA: 0             DISP: 0
DISV: 1             NAME: blctrl:so     SEVR: NO_ALARM      STAT: UDF
TPRO: 0             VAL : HelloWorld

23 dbLoadTemplate

dbLoadTemplate(char ⋆subfile, char ⋆substitutions)

这个IOC命令读取一个模板替换文件,它提供用于装载数据库实例的指令并且为它们包含的$(xxx)宏传递值。这个命令在装载请求的数据库实例时执行那些替换。

subfile参数传递要被使用的模板替换的名称。可选的substituions参数可以包含其它全局的宏值,它可以被在这个substition文件内给出的值替换。

MSI程序可以用于在构建时时展开模板,替代在运行时使用这个命令;二者都理解相同的替换文件语法。

23.1 模板文件语法

用以下扩展的Backus-Naur Form语法描述模板替换文件语法:

substitution-file ::= ( global-defs | template-subs )+

global-defs ::= 'global' '{' variable-defs? '}'

template-subs ::= template-filename '{' subs? '}'
template-filename ::= 'file' file-name
subs ::= pattern-subs | variable-subs

pattern-subs ::= 'pattern' '{' pattern-names? '}' pattern-defs?
pattern-names ::= ( variable-name ','? )+
pattern-defs ::= ( global-defs | ( '{' pattern-values? '}' ) )+
pattern-values ::= ( value ','? )+

variable-subs ::= ( global-defs | ( '{' variable-defs? '}' ) )+
variable-defs ::= ( variable-def ','? )+
variable-def ::= variable-name '=' value

variable-name ::= variable-name-start variable-name-char⋆
file-name ::= file-name-char+ | double-quoted-str | single-quoted-str
value ::= value-char+ | double-quoted-str | single-quoted-str

double-quoted-str ::= '"' (double-quoted-char | escaped-char)⋆ '"'
single-quoted-str ::= "'" (single-quoted-char | escaped-char)⋆ "'"
double-quoted-char ::= [̂"\]
single-quoted-char ::= [̂'\]
escaped-char ::= '\' .

value-char ::= [a-zA-Z0-9_+:;./\<>[] | '-' | ']'
variable-name-start ::= [a-zA-Z_]
variable-name-char ::= [a-zA-Z0-9_]
file-name-char ::= [a-zA-Z0-9_+:;./\] | '-'

注意:对于最后三种定义,当前实现可以接受比这里列出的字符更广范围的字符,但未来的发行版可能限制字符为以上给出的那些字符。

如果名称包含格式$(ENV_VAR_NAME)的任何环境编号宏,任何记录实例文件名必须出现在引号内,在打开指定名称文件前,将展开它。

23.2 模板文件格式

以上给出的语法支持两种不同模板格式。格式是:

file name.template {
    { var1=sub1_for_set1, var2=sub2_for_set1, var3=sub3_for_set1, ... }
    { var1=sub1_for_set2, var2=sub2_for_set2, var3=sub3_for_set2, ... }
    { var1=sub1_for_set3, var2=sub2_for_set3, var3=sub3_for_set3, ... }
}

或

file name.template {
pattern { var1, var2, var3, ... }
    { sub1_for_set1, sub2_for_set1, sub3_for_set1, ... }
    { sub1_for_set2, sub2_for_set2, sub3_for_set2, ... }
    { sub1_for_set3, sub2_for_set3, sub3_for_set3, ... }
}

第一行(file name.template)指定记录实例输入文件。文件名可以出现在双引号中,如果这个名称包含不在以下集合中的任何字符,或者如果它包含必须被展开来产生文件名的格式${VAR_NAME}的环境变量宏,双引号是必须的:

a-z A-Z 0-9 _ + - . / \ : ; [ ] < >

被包围在{}中的每个定义集合是用于输入文件的变量替换。输入文件使得每个集合应用于它去用在其内所有完整的替换产生一个符合文件。版本1应该明显。在版本2中,在pattern行中列出的变量,它们必须在花括号替换行前。花括号替换行包含了匹配pattern{}行的集合。

23.3 示例

以下展示两个简单的模板文件示例。这些示例指定要执行的替代,第一个示例为第一个集合执行this=sub1和that=sub2,为第二个集合执行this=sub3和that=sub4。第二个示例为第一个集合执行this=sub5和that=sub6,为第二个集合执行this=sub7和that=sub8。

使用以上的示例,在第三步中,在softTestApp/Db目录下建立一个test.template文件,并且在这个文件中添加以下内容:

record(ai, "${this}:ai"){
        field(DESC, "this = ${this}")
}

record(ai, "${that}:ai"){
        field(DESC, "this = ${that}")
}

在相同目录中的Makefile文件中添加如下一行:

DB += test.template

返回到顶层目录,执行make rebuild进行程序的重新构建。

进入到 iocBoot/iocsoftTest/目录下,创建一个名为test.substitutions的文件,并且添加以下内容:

file ../../db/test.template{
        { this=sub1, that=sub2 }
        { this=sub3, that=sub4}
}

file ../../db/test.template{
        pattern {this, that}
        {sub5, sub6}
        {sub7, sub8}
}

编辑相同路径下的st.cmd文件,并且在iocInit命令前,添加一个用模板加载记录实例的命令:

...
cd "${TOP}/iocBoot/${IOC}"
dbLoadTemplate("test.substitutions")
iocInit
...

用以下命令启动这个IOC程序,并且验证加载到这个IOC的记录实例:

[blctrl@telecom iocsoftTest]$ ../../bin/linux-x86_64/softTest st.cmd
[blctrl@telecom iocsoftTest]$ ../../bin/linux-x86_64/softTest st.cmd
#!../../bin/linux-x86_64/softTest
< envPaths
epicsEnvSet("IOC","iocsoftTest")
epicsEnvSet("TOP","/home/blctrl/exer/exer9")
epicsEnvSet("EPICS_BASE","/usr/local/EPICS/base")
cd "/home/blctrl/exer/exer9"
## Register all support components
dbLoadDatabase "dbd/softTest.dbd"
softTest_registerRecordDeviceDriver pdbbase
## Load record instances

cd "/home/blctrl/exer/exer9/iocBoot/iocsoftTest"
dbLoadTemplate("test.substitutions")
iocInit
Starting iocInit
############################################################################
## EPICS R7.0.3.1
## EPICS Base built Aug 28 2022
############################################################################
iocRun: All initialization complete
## Start any sequence programs
#seq sncxxx,"user=blctrl"
epics> dbl
sub1:ai
sub2:ai
sub3:ai
sub4:ai
sub5:ai
sub6:ai
sub7:ai
sub8:ai
epics>

猜你喜欢

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