EPICS学习: 数据库定义


一、概述

这一章描述了数据库定义。将会描述以下定义:

  • 菜单
  • 记录类型
  • 设备
  • 驱动
  • 注册
  • 变量
  • 功能
  • 断点表
  • 记录类型

记录实例从根本上与其他定义不同。包含记录实例的文件不应包含任何其他定义,反之亦然。因此要遵循以下规则:

  • 数据库定义文件:包含除记录实例外的任何类型定义的文件。
  • 记录实例文件:只包含记录实例定义的文件。

本章还描述了对这些定义进行操作的实用程序。
定义的任何组合都可以出现在单个文件中,也可以出现在通过include语句相互关联的一组文件中。

二、数据库语法总结

下面总结了数据库定义的语法:

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

recordtype(record_type) {}

recordtype(record_type) {
	include "filename"
	field(field_name, field_type) {
		asl(asl_level)
		initial("init_value")
		promptgroup("group_name")
		prompt("prompt_value")
		special(special_value)
		pp(pp_value)
		interest(interest_level)
		base(base_type)
		size(size_value)
		extra("extra_info")
		menu(name)
		prop(yesno)
	}
	%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)

三、数据库定义的一般规则

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)  or  ${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表示一个十六进制数,它可以有任意数量的十六进制数字,但在生成的字符中只表示最后2个。

3.6 注释

注释符号为“#”。每当注释符号出现在带引号的字符串之外时,它以及通过行尾的所有后续字符都将被忽略。

3.7 定义在引用之前

一般来说,只有在定义了项目之后才能引用它们。例如在device定义引用的recordtype被定义或至少被声明之前,device定义不能出现。另一个例子是:在定义了记录实例的关联记录类型之前,无法显示该记录实例。

此规则的一个显著例外是,在记录类型定义中,菜单字段可能引用未直接包含在记录的.dbd文件中的菜单。

3.8 多重定义

如果菜单、设备、驱动程序或断点表定义了多次,则只使用第一个实例。后续定义可与第一个定义进行比较,如果不同,则报告错误(dbdExpand.pl这样执行,IOC目前没有)。记录类型定义只能加载一次;即使后面的定义与第一个定义相同,重复也会导致错误。但是,在定义设备对该类型的支持的.dbd文件中,可以使用记录类型声明来代替记录类型定义。

记录实例定义(通常)是累积的,因此可以加载同一记录的多个实例,并且每次遇到字段值时,它都会替换以前的值。

3.9 文件扩展名

按照惯例:

  • 如果记录实例文件还包含可视化布局信息,则文件扩展名为“.db”或“.vdb”
  • 数据库定义文件的扩展名为“.dbd”

四、数据库定义语句

4.1 path addpath – 路径定义

4.1.1 格式

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

路径字符串遵循操作系统的标准约定,即在Unix上目录名用冒号“:”,在Windows上目录名用分号“;”。

path语句指定加载数据库和数据库定义文件时使用的当前搜索路径。addpath语句将目录追加到当前路径。路径用于定位初始数据库文件和包含的文件。非空路径字符串开头、中间或结尾处的空路径组件表示搜索当前目录。例如:

	nnn::mmm # Current directory is between nnn and mmm
	:nnn # Current directory is first
	nnn: # Current directory is last

加载数据库文件(dbExpand、dbLoadDatabase等)的实用程序允许用户指定初始路径。path和addpath命令可用于更改或扩展初始路径。
初始历经确定如下:

  1. 如果命令提供了path,则使用它。否则:
  2. 如果定义了环境变量EPICS_DB_INCLUDE_PATH,则使用它。否则:
  3. 路径为“.”,即当前目录。

如果要搜索的文件名包含/或\字符,则根本不使用搜索路径。使用指定文件名的第一个实例。

4.2 include – inlcude语句

4.2.1 格式

	include "filename"

include语句可以出现在摘要中显示的任何位置。它使用上面描述的搜索路径来定位命名文件。

4.3 menu – 菜单定义

4.3.1 格式

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

4.3.2 定义

 - name
	菜单的名字。这是标识菜单的唯一名称。如果指定了重复定义,则只使用第一个定义。
 - choice_name
 	在enum中使用的名字由dbdToMenuH.pl或dbdToRecordtypeH.pl生成。这必须是合法的C/C++标识符。
 - choice_string
	与此特定选择关联的文本字符串。

4.3.3 例程

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

4.4 recordtype – 记录类型定义

4.4.1 格式

recordtype(record_type) {}

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

不提供字段描述的记录类型语句是一个声明,类似于C中的函数声明(原型)或前向定义。它允许在不需要完整记录类型定义的情况下使用给定的记录类型名称。

4.4.2 字段描述符的规则

 - asl
	设置字段的访问安全级别。
 - initial
	提供字段的初始(默认)值。
 - promptgroup
	数据库配置工具的字段所属的组。
 - prompt
	数据库配置工具的提示字符串。如果未定义promptgroup,则为可选。
 - special
	如果指定,则需要在运行时对此字段进行特殊处理。
 - pp
	通道访问写入此字段时是否应处理被动记录。
 - interest
	该字段的兴趣水平。
 - base
	对于整数字段,是将字段值转换为字符串时使用的基数。
 - size
	必须为DBF_STRING字段指定。
 - extra
	必须为DBF_NOACCESS字段指定。
 - menu
	必须为DBF_MENU字段指定。它是关联菜单的名称。
 - prop
	必须为“是”或“否”(默认)。指示字段保存通道访问元数据。

4.4.3 定义

- record type
	记录类型的唯一名称。不允许重复定义,并且将拒绝该定义。
	
- field name
	字段名称,必须是有效的C和C++标识符。生成include文件时,字段名将转换为小写,用作记录结构成员名。如果字段名称的小写版本是C或C++关键字,
	则将使用结构成员名称的原始名称。以前版本的EPICS要求字段名最多为四个大写字符,但这些限制不再适用。
	
- field_type
	必须是下列的值之一:
	DBF_STRING
	DBF_CHAR, DBF_UCHAR
	DBF_SHORT, DBF_USHORT
	DBF_LONG, DBF_ULONG
	DBF_FLOAT, DBF_DOUBLE
	DBF_ENUM, DBF_MENU, DBF_DEVICE
	DBF_INLINK, DBF_OUTLINK, DBF_FWDLINK
	DBF_NOACCESS
	
- as_level
	必须是下列的值之一
	ASL0
	ASL1
	操作中经常更改的字段被分配为ASL0。其他字段分配为ASL1。例如,模拟输出记录的VAL字段分配为ASL0,所有其他字段分配为ASL1。这是因为在正常
	操作期间只应修改VAL字段。
	
- init_value
	数据类型的合法值。
	
- prompt-value
	数据库配置工具的提示值。
	
- group_name
	数据库配置工具(DCTs)用来将相关字段分组在一起的字符串。
	
	只应为可以在记录实例文件中合理配置的字段设置promptgroup。
	
	组名集合不再固定。在早期版本的Base中,以GUI开头的预定义选项集是唯一允许的组名。现在,在数据库定义文件中找到的组名字符串被收集并存储在
	一个全局列表中。为组名称指定的字符串必须与要分组的字段完全匹配。
	
	为了支持组的排序和处理,Base中使用的名称有以下约定:
		(1) 名称以两位数字开头,后跟空格短划线空格序列。
		(2) 名字是按数字升序排列的。
		(3) 组名称(或可能只是短划线后面的部分)可以由工具显示为组的标题。
		(4) 在许多相同的情况下(例如21个相似的输入),字段分布在多个组中。只有字段以5或10的倍数分组显示。具有多个实例的组以+1递增。这样就
		可以进行更复杂的处理,例如显示第一组打开,其他组折叠。
		
	记录类型可以定义自己的组名。但是,为了提高一致性,记录应该尽可能使用来自Base的以下名称。此集合还示范了不同记录类型中使用的组名可以共
	享同一个编号。
		Common	所有或多个记录类型共用的常规字段
		Scan	扫描机制、优先级和相关属性
		Action	特定于记录类型的行为和处理操作
		Link	链接和相关属性
		Input	输入链接和属性
		Output	输出链接和属性
		Convert	原始值和工程值之间的转换
		Alarm	报警相关属性、严重性和阈值
		Display	客户端相关配置、字符串、死区
		Simulate	模拟模式相关属性
			注:旧版本的Base包含一个头文件guigroup.h,它定义了一组固定的组名及其匹配的索引号。该头文件已被删除。静态数据库访问库现在提供了在
			组索引键和关联的组名字符串之间进行转换的函数。

- special value
	必须是以下值之一:
	(1)SPC_MOD:修改时通知记录支持。每当数据库访问例程修改字段时,都将调用记录支持special例程。
	(2)SPC_NOMOD:不允许外部修改。此值禁用对字段的外部写入,因此只能由记录或设备支持模块设置。
	(3)SPC_DBADDR:当记录或设备支持之外的代码连接到字段时,如果应该调用记录支持的cvt_dbaddr例程来调整字段描述,请使用此选项。
	(4)SPC_SCAN:扫描相关领域。
	(5)SPC_ALARMACK:报警确认字段。
	(6)SPC_AS:访问安全字段。
	以下值已弃用,请改用SPC_MOD替代:
	(1)大于103的整数值。
	(2)SPC_RESET:正在修改重置字段。
	(3)SPC_LINCONV:正在修改线性转换字段。
	(4)SPC_CALC:正在修改计算字段。

- pp_value
	通道访问写入此字段时是否应处理被动记录?允许的值为:
	(1)FALSE
	(2)TRUE
		
- interset_level
	dbpr命令的兴趣级别。
	
- base
	对于整数类型字段,为默认基。合法值是:
	(1) DECIMAL(Default)
	(2)HEX
	
- size_value
	DBF_STRING字段的字符数。
	
- extra_info
	对于DBF_NOACCESS字段,这是该字段的C语言定义。定义必须以小写的字段名结尾。

- %C_declaration
	记录正文中的百分号%引入了一行代码,该代码将包含在生成的C头文件中。

4.4.4 例程

以下是事件记录类型的定义:

	recordtype(event) {
		include "dbCommon.dbd"
		field(VAL,DBF_STRING) {
			prompt("Event Name To Post")
			promptgroup("40 - Input")
			special(SPC_MOD)
			asl(ASL0)
			size(40)
		}
		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")
			interest(1)
			menu(menuYesNo)
		}
		field(SIMS,DBF_MENU) {
			prompt("Sim mode Alarm Svrty")
			promptgroup("90 - Simulate")
			interest(2)
			menu(menuAlarmSevr)
		}
	}

4.5 device – 设备支持证明

4.5.1 格式

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

4.5.2 定义

- record_type
	记录类型。记录类型和选项字符串的组合必须是唯一的。如果同一组合出现多次,则只使用第一个定义。
- 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值可以用于不同的记录类型,但对于每个特定的记录类型必须是唯一的。

4.5.3 例程

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

4.6 driver – 驱动声明

4.6.1 格式

	driver(drvet_name)

4.6.2 定义

如果进行了重复定义,则只使用第一个。

4.6.3 例程

	driver(drvVxi)
	driver(drvXy210)

4.7 register – 注册声明

4.7.1 格式

	registrar(function_name)

4.7.2 定义

- function_name
	不接受参数、返回void并在源文件中用epicexportregistrator声明标记的C函数的名称,例如。
		static void myRegistrar(void);
		epicsExportRegistrar(myRegistrar);

这可以用来注册子例程记录使用的函数,或者可以从iocsh调用的函数。第2.2节“IOC应用示例”中描述的示例应用程序给出了如何为子例程记录注册函数的示例。

4.7.3 例程

	registrar(myRegistrar)

4.8 variable – 变量声明

4.8.1 格式

	variable(variable_name[, type])

4.8.2 定义

- variable_name
	在源文件中用epicsExportAddress声明标记的C变量的名称。

- type
	C变量的类型。如果不存在,则假定为int。目前只支持intdouble变量。

这将注册设备或驱动程序支持的 diagnostic/configuration变量或子例程记录子例程。这个变量可以用iocsh var命令读取和设置

4.8.3 例程

在应用程序的C代码文件:

	#include <epicsExport.h>
	static double myParameter;
	epicsExportAddress(double, myParameter);

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

	variable(myParameter, double)

4.9 function – 函数声明

4.9.1 格式

	function(function_name)

4.9.2 定义

- function_name
	从源文件中导出的带有epicsRegisterFunction声明的C函数的名称

这将注册一个函数,以便可以在函数注册表中找到该函数,以供sub或aSub等按名称引用函数的记录类型使用。

4.9.3 例程

在应用程序的C代码文件:

	#include <registryFunction.h>
	#include <epicsExport.h>
	static long myFunction(void *argp) {
		/* my code ... */
	}
	epicsRegisterFunction(myFunction);

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

	function(myFunction)

4.10 breaktable-- 断点表

4.10.1 格式

	breaktable(name) {
		raw_value eng_value
		...
	}

4.10.2 定义

- name
	断点表的名称,必须是字母数字。如果指定了重复项,则使用第一个。
	
- raw_value
	原始值,即与间隔开始相关联的实际ADC值。
	
- eng_value
	与间隔开始相关的工程值。

4.10.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
	}

4.11 record-- 记录实例

4.11.1 格式

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

4.11.2 定义

- record_type
	记录类型,或“*”(见下面记录名称下的讨论)。
	
- record_name
	记录名称。它只能由以下字符组成:
		a-z A-Z 0-9 _ - + : [ ] < > ;
	注:如果使用宏替换,则必须引用名称。
	
	只要记录类型相同,通常允许对记录进行重复定义。每个字段的最后一个值是所使用的值。如果正在使用重复的定义,并且记录已经加载,则后续定义可
	以使用“*”来代替记录实例中的记录类型。
	
	可以使用iocsh var命令将变量dbrecordsononly设置为任何非零值,从而使将重复记录定义加载到IOC是非法的。

- alias_name
	记录的备用名称,遵循与记录名称相同的规则。
	
- field_name
	一个字段名
	
- field_value
	命名字段的值,适用于其特定的字段类型。当在双引号内给出时,字段值字符串可能包含转义字符,这些字符将在加载数据库时进行适当的转换。有关支
	持的转义字符列表,请参阅第6.3.5节。各种字段类型的允许值如下:
		(1)DBF_STRING
			任何ASCII字符串。如果它超过字段长度,它将被截断。
		(2)DBF_CHAR, DBF_UCHAR, DBF_SHORT, DBF_USHORT, DBF_LONG, DBF_ULONG
			表示有效整数的字符串。应用标准的C约定,即前导0表示值以八进制表示,前导0x表示值以十六进制表示。
		(3)DBF_FLOAT, DBF_DOUBLE
			字符串必须表示有效的浮点数。也允许使用无穷大或NaN。
		(4)DBF_MENU
			字符串必须是关联菜单的有效选项之一。
		(5)DBF_DEVICE
			字符串必须是有效的设备选择字符串之一。
		(6)DBF_INLINK, DBF_OUTLINK, DBF_FWDLINK
			注:a. 如果字段名为INP或OUT,则此字段与DTYP关联,并且允许的值由当前DTYP选项字符串选择的设备支持的链接类型确定。其他
			       DBF_INLINK和DBF_OUTLINK字段必须是CONSTAN或PV_LINKs。
			    b. 一个设备支持,指定了一个链接类型的常量可以是一个常量或一个PV_链接。
			字段的允许值取决于设备支持的链接类型,如下所示:
			a. CONSTANT
				一个数值文本,对要读入的字段类型有效。
			b. PV_LINK
				如下形式的值:
					record.field process maximize
				record是这个或另一个IOC中存在的记录的名称。
				.field、process和maximize部分都是可选的。
				.field的默认值为.VAL。
				process可以具有以下值之一:
					NPP - 无进程被动(默认)
					PP  - 过程被动
					CA  - 强制链接为通道访问链接
					CP  - 监视器上的CA和进程
					CPP - 如果记录是被动的,则CA和监视器上的进程
					注:CP和CPP仅对DBF_INLINK字段有效。
						DBF_FWDLINK字段可以使用PP或CA。如果DBF_FWDLINK是通道访问链接,它必须引用目标记录的PROC字段。
				maximize可以有下列值之一:
					NMS - No Maximize Severity (Default)
					MS  - Maximize Severity
					MSS - Maximize Severity and Status
					MSI - Maximize Severity if Invalid
			c. VEM_IO
				#Ccard Ssignal @parm
				card   - 相关硬件模块的卡号
				signal - 卡上的信号
				parm   - 最多31个字符的任意字符串。此字段是可选的,并且特定于设备。
			d. CAMAC_IO
				#Bbranch Ccrate Nstation Asubaddress Ffunction @parm
				branch, crate, station, subaddress和function对于camac用户应该是显式的。subaddress和function是可选的(如果不显式就是
				0)。parm也是可选的,是特定于设备的(最多25个字符)。
			e. AB_IO
				#Llink Aadapter Ccard Ssignal @parm
				link    - 扫描器,即vme扫描器编号
				adapter - 适配器
				card    - 相关硬件模块的卡号
				signal  - 卡上的信号
				parm    - 可选的特定于设备的字符串(最多27个字符)
			f. GPIB_IO
				#Llink Aaddr @parm
				link - gpib链路,即接口
				addr - GPIB地址
				parm - 特定于设备的字符串(最多31个字符)
			g. BITBUS_IO
				#Llink Nnode Pport Ssignal @parm
				link - 链路,即vme位总线接口
				node - bitbus节点
				port - 节点端口号
				signal - 端口号上的信号
				parm - 特定于设备的字符串(最多31个字符)
		    h. INST_IO @parm
				parm – 与设备相关的字符串
		    i. INST_IO @parm
				#Llink Bbbaddr Ggpibaddr @parm
				link - 链路,即bitbus接口
				bbadddr  - bitbus地址
				gpibaddr - gpib地址
				parm - 可选的特定于设备的字符串(最多31个字符)
			j. RF_IO
				#Rcryo Mmicro Ddataset Eelement
			k. VXI_IO
				#Vframe Cslot Ssignal @parm (Dynamic addressing)
				or
				#Vla Signal @parm (Static Addressing)
				frame - VXI帧数量
				slot  - VXI帧内的槽
				la    - 逻辑地址
				signa - 信号数量
				parm  - 特定于设备的字符串(最多25个字符)

- info_name 
	与此记录相关的项的名称。有关信息项的更多信息,请参见下文第6.5节。
-info_value
	任何ASCII字符串。使用此信息项的IOC应用程序可能会对字符串的内容附加限制。

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

五、记录信息项

信息项提供了一种将命名字符串值附加到与记录定义同时加载的单个记录实例的方法。它们可以被附加到任何记录,而不必修改记录类型,并且可以由运行在IOC上的程序检索(它们在频道访问中根本看不到)。附加到单个记录的每个项目都必须有一个唯一的名称,通过该名称可以寻址,并且数据库访问提供了一些例程,允许扫描、搜索、检索和设置记录的信息项。在运行时,void*指针也可以与每个项目关联,尽管只有字符串值可以在加载数据库时从记录定义初始化。

六、记录属性

每个记录类型可以有任意数量的记录属性。每个属性都是一个psuedo字段,可以通过数据库和通道访问来访问。每个属性都有一个类似于字段名的名称,但对记录类型的所有实例返回相同的值。为每种记录类型自动生成两个属性:RTYP和VERS。RTYP的值是记录类型名称。RTYP的值是记录类型名称。VERS的默认值是“none specified”,它可以通过记录支持进行更改。记录支持可以调用以下例程来创建新属性或更改现有属性:

	long dbPutAttribute(char *rtype, char *name, char *value);

参数有以下:

	rtype - 记录类型的名称。
	name  - 属性名,即psuedo字段名。
	value - 指定给属性的值。

七、断点表 – 讨论

菜单menuConvert用于ai和ao记录的字段LINR。这些记录允许通过以下方式之一将原始数据转换为工程单位:

  1. No Conversion(无转换)
  2. Slope Conversion(坡度转换)
  3. Linear Conversion(线性转换)
  4. Breakpoint table(断点表)

其他记录类型也可以使用此功能。第一个选项指定不转换;第二个和第三个都是线性转换,区别在于对于坡度转换,用户直接指定转换坡度和偏移值,而对于线性转换,则根据所需工程单位范围的设备支持和设备支持对硬件转换范围的了解来计算。其余选项假定为断点表的名称。如果选择了断点表,记录支持模块将调用cvtRawToEngBpt或cvtEngToRawBpt。你可以查看ai和ao记录支持模块以了解详细信息。

如果用户希望添加其他断点表,则应执行以下操作:

  • 复制来自EPICS base/src/ioc/bpt的menuConvert.dbd文件
  • 将新断点表的定义添加到末尾
  • 将新断点表的定义添加到末尾

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

如果选择了断点表,则在调用iocInit之前,必须将相应的断点文件加载到IOC中。

通常,需要直接创建断点表。然而,有时需要从表示等间距工程单位的原始值表中创建断点表。然而,有时需要从表示等间距工程单位的原始值表中创建断点表。提供了一个工具makeBpt来将这些数据转换为断点表。

从等间距工程值对应的原始值数据表生成断点表的格式为:

	!comment line
	<header line>
	<data table>

标题行包含以下信息:

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

一个示例定义是:

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

断点表可以通过执行

	makeBpt bptXXX.data

输入文件必须具有数据扩展名。输出文件名与输入文件名相同,扩展名为.dbd。
创建断点表的另一种方法是在生成文件Makefile中包含以下定义:

BPTS += bptXXX.dbd

注:这需要命名约定,即所有数据表的格式都是bpt.data和断点表bpt.dbd。

八、 菜单和记录类型包括文件生成

8.1 介绍

给定一个包含菜单定义的文件,程序dbdToMenuH.pl生成一个C/C++头文件,供需要这些菜单的代码使用。给定一个包含菜单定义和记录类型定义任意组合的文件,程序dbdToRecordtypeH.pl生成一个C/C++头文件,供需要这些菜单和记录类型的代码使用。

EPICS Base使用以下约定来管理菜单和记录类型定义。鼓励生成本地记录类型的用户遵循这些规则:

  • 由数据库common(例如menuScan)中的字段使用的或全局使用的(例如menuYesNo)的每个菜单都应在其自己的文件中定义。文件名与菜单名相同,扩展名为.dbd。生成的include文件的名称是菜单名,扩展名为.h。因此menuScan在文件中定义menuScan.dbd生成的include文件名为menuScan.h
  • 每种记录类型都在自己的文件中定义。该文件还应该包含仅由该记录类型使用的菜单定义。特定于特定记录类型的菜单应该使用该记录类型名称作为菜单名称的前缀。文件的名称与记录类型相同,后面跟着record .dbd。生成的包含文件的名称与.dbd文件相同,但扩展名为.h。因此,记录类型ao定义在文件aoRecord中。生成的包含文件名为aoRecord.h。由于aoRecord有一个名为aoOIF的私有菜单,因此dbd文件和生成的包含文件将具有该菜单的定义。因此,对于每种记录类型,都有两个源文件(xxxRecord;dbd和xxxrecord . d)和一个生成的文件(xxxRecord.h)。

注意,开发人员通常不会执行dbdToMenuH.pl或者dbdToRecordtypeH.pl手动编程。如果使用了正确的命名约定,则只需向适当的Makefile添加定义。有关详细信息,请参阅关于EPICS构建设施的章节。

8.2 dbdToMenuH.pl

此工具的执行如下:

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

它读取输入文件菜单.dbd并生成包含输入文件中菜单的枚举类型定义的C/C++头文件。

可以提供多个-I选项来指定在查找包含的文件时必须搜索的目录。如果没有使用-o menu.h选项或作为最终命令行参数指定输出文件名,则将根据输入文件名构造输出文件名,将.dbd替换为.h。

-D 选项使程序将输出文件的Makefile依赖关系信息输出到标准输出,而不是实际执行上述函数。例如菜单优先.dbd,其中包含处理优先级的定义包含:

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

生成的包含文件menuPriority.h包含::

	/* menuPriority.h generated from menuPriority.dbd */
	#ifndef INC_menuPriority_H
	#define INC_menuPriority_H
	typedef enum {
		menuPriorityLOW /* LOW */,
		menuPriorityMEDIUM /* MEDIUM */,
		menuPriorityHIGH /* HIGH */,
		menuPriority_NUM_CHOICES
	} menuPriority;
	#endif /* INC_menuPriority_H */

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

8.3 dbdToRecordtypeH.pl

本工具的执行方式如下:

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

它读取输入文件xRecord.dhd公司并生成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")
		}
		... many more field definitions
	}

生成的包含文件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 */

模拟输出记录支持模块和所有相关设备支持模块都应包含此文件。其他代码不应该使用它。
让我们讨论一下文件的各个部分:

  • 从菜单定义生成的enum应用于为与该菜单关联的字段提供值。
  • 记录支持和设备支持使用定义记录的typedef结构访问模拟输出记录中的字段。
  • 下一个enum为记录中的每个字段定义一个索引号。这对于向DBADDR结构传递指针的记录支持例程很有用。它们可以有如下代码:
	switch (dbGetFieldIndex(pdbAddr)) {
	case aoRecordVAL :
	...
	break;
	case aoRecordXXX:
	...
	break;
	default:
	...
	}

生成的例程aoRecordSizeOffset在记录类型注册到IOC时执行。例程是用记录类型代码编译的,并且被标记为static,因此它在该文件之外不可见。只有在如下定义GEN_SIZE_OFFSET宏之后,关联记录支持源代码才能包含生成的头文件:

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

此约定确保例程只定义一次。epicsExportRegistrator语句确保记录注册代码可以找到并调用例程。

九、 dbdExpand.pl

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

这个程序从所有输入文件读取并组合数据库定义,然后写入一个包含输入文件中所有信息的输出文件。输出内容与输入的不同之处在于,注释行被删除,所有定义的宏和包含文件都将展开。与以前的dbExpand程序不同,此程序不理解数据库实例,并且不能与.db或.vdb文件一起使用。

可以提供多个-I选项来指定在查找包含的文件时必须搜索的目录。宏替换允许使用多个-S选项,也可以在单个选项中指定多个宏。如果没有使用-o指定输出文件名输出数据库选项,则输出将转到stdout。
-D选项使程序将输出文件的Makefile依赖关系信息输出到标准输出,而不是实际执行上述函数。

十、dbLoadDatabase

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

此IOC命令加载一个数据库文件,该文件可能包含本章中描述的任何数据库定义。dbdfile字符串可能包含格式为${MOTOR}的环境变量宏,该宏将在文件打开之前展开。path和substitutions参数可以为null或为空,并且通常被省略。注意,dbLoadDatabase只应用于加载数据库定义(.dbd)文件,尽管目前也可以使用它来加载记录实例(.db)文件。
在读取文件的每一行时,将执行“替换”中指定的替换。替换规定如下:

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

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

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

如果使用的话,数据库文件中的任何变量 ( a ) 、 (a)、 (a)(b)、$©都将在解析过程中替换相应的值。

十一、 dbLoadRecords

	dbLoadRecords(char* dbfile, char* substitutions)

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

11.1 举例

例如,让test.db文件包含:

	record(ai, "$(pre)testrec1")
	record(ai, "$(pre)testrec2")
	record(stringout, "$(pre)testrec3") {
		field(VAL, "$(STR)")
		field(SCAN, "$(SCAN)")
	}

然后发出命令:

	dbLoadRecords("test.db", "pre=TEST,STR=test,SCAN=Passive")

与加载结果相同:

	record(ai, "TESTtestrec1")
	record(ai, "TESTtestrec2")
	record(stringout, "TESTtestrec3") {
		field(VAL, "test")
		field(SCAN, "Passive")
	}

十二、dbLoadTemplate

	dbLoadTemplate(char *subfile, char *substitutions)

为数据库提供一个文件替换命令$,这些文件可能包含一个用于加载宏的指令。此命令在加载请求的数据库实例时执行这些替换。subfile参数提供要使用的模板替换文件的名称。可选的substitutions参数可能包含其他全局宏值,这些值可以被替换文件中给定的值覆盖。

subfile参数提供要使用的模板替换文件的名称。可选的substitutions参数可能包含其他全局宏值,这些值可以被替换文件中给定的值覆盖。
MSI程序可以用来在构建时扩展模板,而不是在运行时使用这个命令;两者都理解相同的替换文件语法。

12.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}的任何环境变量宏,则任何记录实例文件名都必须出现在引号内,这些宏将在打开命名文件之前展开。

12.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, ... }
	}
or:
	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, ... }
	}

第一行(文件名称.模板)指定记录实例输入文件。文件名可以用双引号括起来;如果名称包含不在以下集合中的任何字符,或者如果它包含格式为${VAR_name}的环境变量宏,则必须展开这些宏才能生成文件名:

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

{}中包含的每一组定义都是输入文件的变量替换。输入文件将每个集合应用到其中,以生成一个包含所有已完成替换的复合文件。版本1应该很明显。在版本2中,变量列在模式{}行中,该行必须位于大括号替换行之前。大括号替换行包含与模式{}行匹配的集合。

12.3 例程

下面显示了两个简单的模板文件示例。这些示例指定了要执行的相同替换:
对于第一组,This =sub1 and that=sub2;对于第二组,This =sub3 and that=sub4。

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

假设 test.template 包含:

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

使用dbLoadTemplate与任何一个输入是相同的定义记录:

	record(ai,"sub1record") {
		field(DESC,"this = sub1")
	}
	record(ai,"sub2record") {
		field(DESC,"this = sub2")
	}
	record(ai,"sub3record") {
		field(DESC,"this = sub3")
	}
	record(ai,"sub4record") {
		field(DESC,"this = sub4")
	}
  • 14
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值