# Presentation: 502568

## Record Support

- Marty Kraimer
- APS

- Record Support
- Implements a Record Type
- Plugs into database common environment
- Implementation steps
- Prepare a Database Definition (.dbd) file
- Create a C source file which implements semantics
- Start with existing support!!!
- Documentation
- App Dev Guide - Chapters 3 and 8
- makeBaseApp example - xxxRecord
- base/src/rec

## Database Definition File - dbd

- Syntax
- recordtype(<recordtype>) {
- 	include “dbCommon.dbd”
- 	field(<fieldname>,<fieldtype>) {
- 		<fieldattributes>
- 	}
- 	field(<fieldname>,<fieldtype>) {
- 		<fieldattributes>
- 	}
- 	...
- }
- Example
- recordtype(example) {
- 	include “dbCommon.dbd”
- 	field(PREC,DBF_DOUBLE) {
- 		prompt(“Display Precision”)
- 		promptGroup(GUI_DISPLAY)
- 	}
- 	field(VAL,DBF_DOUBLE)
- }

## dbd - cont.

- <recordtype>Should be alphanumeric starting with letter
- <fieldname>
- Should be alphanumeric starting with letter
- For now length is limited to four characters
- In dbd file this should be upper case.It is converted to lower case in include file.
- <fieldtype>
- 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

## dbd - cont

- <fieldattributes>
- asl : access security level
- ASL1 - Fields seldom modified - default
- ASL0 - Fields meant to be modified
- initial : initial value
- prompt : string for Database Configuration
- promptgroup : Should DCT allow field to be configured? Many groups but no DCT distinguishes between groups.
- special: Special handling on put
- Several values
- SPC_MOD, SPC_DBADDR, SPC SPC_NOMOD
- pp : process passive record on put?
- interest : interest level used by dbpr
- base : DECIMAL (default) or HEX
- size : For DBF_STRING
- extra : For DBF_NOACCESS
- menu : For DBF_MENU.

## String Field

- DBF_STRING fields
- Fixed length null terminated strings
- Channel access support max of 40 characters
- Arrays of character strings are possible
- Example
- field(VAL,DBF_STRING) {
- 	size(40)
- 	prompt(“value”)
- 	promptgroup(GUI_DISPLAY)
- }

## Numeric Fields

- DBF_CHAR, ... , DBF_DOUBLE
- Standard C datatypes
- Note that DBF_CHAR, DBF_UCHAR problem
- All attributes except size, extra, menu meaningful
- Arrays
- dbd file must define special(SPC_DBADDR)
- Record support must implement cvt_dbaddr, get_array_info, put_array_info
- No support by Database Configuration Tools.

## Enumerated Fields

- DBF_ENUM
- Record Support provides choices and index
- See biRecord, mbbiRecord, etc
- Actual field type is unsigned short
- Record support MUST implement
- get_enum_str
- put_enum_str
- get_enum_strs

## Menu Fields

- DBF_MENU
- menu(<menuname>) {
- 	choice(<choicename>,”<choicevalue>“)
- 	...
- }
- ...
- recordtype(<recordtype>) {
- 		field(<fieldname>,DBF_MENU) {
- 			menu(<menuname>)	}
- Example
- menu(menuYesNo) {
- 	choice(menuYesNo,”NO”)
-   choice(menuYesNo,”YES”)
- }
- ...
- 	field(PINI,DBF_MENU) {
- 		...
- 		menu(menuYesNo)
- 	}

## Device Field

- DBF_DEVICE
- Database Common Field - DTYP
- Related to device definitions
- Has associated INP or OUT field
- Device Support must be provided
- Example
- device(ai,CONSTANT,devAiSoft,”Soft Channel”)
- device(ai,CONSTANT,devAiSoftRaw,”Raw Soft Channel”)

## link Fields

- DBF_INLINK, DBF_OUTLINK
- Not INP or OUT
- Constant link - Just initialization
- PV_LINK becomes DB_LINK or CA_LINK
- INP or OUT and Device Support
- device definitions determine choices
- Associated with choice is bus type
- Gory details in link.h
- DBF_FWDLINK
- references another record which is processed if it is passive.
- FLNK
- In dbCommon
- record support calls recGblFwdLink
- Other DBF_FWDLINKs can be defined
- Call dbScanFwdLink NOT recGblFwdLink

## Generated Include Files

- dbToMenuH
- Code should use generated files.
- menuYesNo.h
- typedef enum {
- 	menuYesNoNO,
- 	menuYesNoYES
- } menuYesNo;
- dbToRecordTypeH
- Code MUST use generated files
- Include code to compute field sizes, offsets
- typedef struct aiRecord {
- 	char	name[29];
- 	...
- }
- #define aiRecordNAME 0
- ...
- #ifdef GEN_SIZE_OFFSET
- int aiRecordSizeOffset(dbRecordType *p)
- { ... }
- Only record support and associated device support is allowed to use the include files.

## Record Support Routines

- Provide Execution Semantics
- Defined via RSET - Record Support Entry Table
- /* Create RSET - Record Support Entry Table
- #define report NULL
- #define initialize NULL
- static long init_record();
- static long process();
- ...
- struct rset <recordtype>RSET = {
- 	RSETNUMBER,
- 	report,
- 	initialize,
- 	init_record,
- 	process,
- 	...
- };

## Record Support Routines - Summary

- Report, Initialization, Processing
- report
- initialize
- init_record
- process
- Support for Channel/ Database Access
- special
- cvt_dbaddr
- get_array_info
- put_array_info
- get_units
- get_precision
- get_enum_str
- put_enum_str
- get_enum_strs
- get_graphic_double
- get_control_double
- get_alarm_double

## Report, Initialization, Processing

- report(void *precord)
- Nothing calls this.
- Normally not implemented
- initialize(void)
- Called once before first call to init_record
- Not normally needed
- init_record(void *precord,	int pass)
- Normally implemented
- Called twice
- pass 0 - Can do anything except access other records. For example storage for arrays should be allocated during pass 0.
- pass 1 - Finish initialization and also call associated device support initialization
- process
- VERY IMPORTANT
- Implements record semantics

## init_record example

- /* assume val is double *val */
- typedef struct xxxdset {
- 	long number,
- 	DEVSUPFUN report;
- 	DEVSUPFUN init;
- 	DEVSUPFUN init_record;
- 	DEVSUPFUN get_ioint_info;
- 	DEVSUPFUN read;
- } xxxdset;
- static long init_record(void *prec, int pass)
- {
- 	xxxarrayRecord *paf = (xxxarrayRecord *)prec;
-    xxxdest *pdset = (xxxdset *)paf->dset;
- 	if(pass==0) {
- 		if(paf->nelm<0) paf->nelm = 1;
- 		paf->val = (double *)calloc(
- 			paf->nelm,sizeof(double));
- 		return(0);
- 	}
- 	/* Must have dset defined */
- 	if(!pdset !! (pdset->number< 5) || !pdest->read ) {
- 		epicsPrintf(“%s no or invalid dset\n”,
- 			paf->name);
- 		return(0);
- 	}
- 	(*pdset->init_record)(paf);
- 	return(0);
- }

## process overview

- process is called if
- Decision was made to process the record
- Record not active (PACT FALSE)
- Record is enabled (DISV !=DISA)
- process with device support must
- set record active (PACT TRUE)
- Perform I/O
- Check for record specific alarms
- Raise database monitors
- Request processing of forward links
- Set PACT FALSE and return

## process overview continued

- For asynchronous devices.
- Asynchronous start
- Initiate I/O.
- Determine a method for again calling process when operation completes
- Return leaving PACT TRUE.
- Asynchronous completion
- Call process as follows:dbScanLock(precord);*(prset->process)(precord);dbScanUnlock(precord);
- process completes record processing
- Complete I/O
- Check for record specific alarms
- Raise database monitors
- Request processing of forward links
- Set PACT FALSE
- return

## process example

- static long process(void *prec, int pass)
- {
- 	xxxarrayRecord *paf = (xxxarrayRecord *)prec;
-    xxxdest *pdset = (xxxdset *)paf->dset;
- 	unsigned char pact = paf->pact;
- 	unsigned short monitor_mask;
- 	/* Must have dset defined */
- 	if(!pdset !! (pdset->number< 5) || !pdest->read ) {
- 		/*set pact true so process will not be called*/
- 		paf->pact = TRUE;
- 		return(0);
- 	}
- 	(*pdset->read)(paf);
- 	/*return if beginning of asyn processing */
- 	if(!pact && paf->pact) return(0);
-    paf->pact = TRUE;
- 	alarm(paf);
- 	/*sample monitor code */
- 	monitor_mask = recGblResetAlarms(paf);
- 	monitor_mask |= (DBE_LOG|DBE_VALUE)
- 	db_post_events(paf,paf->val,monitor_mask);
- 	recGblFwdLink(paf);
- 	paf->pact = FALSE
- 	return(0);
- }

## alarm and monitors

- alarms
- recGblSetSevr(void *prec, <alarm_status>, <alarm_severity>)
- Status and severity values defined in alarm.h
- Must prevent alarm storms on numeric values
- value fluctuating around alarm limit
- See Application Developer’s guide for algorithm
- process must call recGblResetAlarms after all alarms have been raised and before calling recGblFwdLink. Also, if possible, before raising monitors.
- monitors
- db_post_event(void *prec,	void *pfield, monitor_mask)
- Call for all fields that change as a result of record processing
- Support value change hysteresis when possible.
- Look at aiRecord for an example

## Routines with a DBADDR argument

- Called by database/channel access
- Call dbGetFieldIndex to find out which field.
- int fieldIndex;
- fieldIndex = dbGetFieldIndex(pdbaddr);
- if(fieldIndex == aiRecordVAL)...
- DBADDR
- 	typedef struct dbAddr{
- 		dbCommon *precord;
- 		void *pfield;
- 		void *pfldDes;
- 		void *asPvt;
- 		long no_elements;
- 		short field_type;
- 		short field_size;
- 		short special;
- 		short dbr_field_type;
- 	}DBADDR;

## Record Support Routines - Cont.

- special(DBADDR *pdbaddr,int after)
- Called if special defined in dbd file
- Called before and after field is modified.
- dbGetFieldIndex(pdbaddr)
- pdbaddr->special
-  The special type as defined in .dbd file.
- Must include file special.h
- cvt_dbaddr(DBADDR *pdbaddr)
- Called by database library when DB/CA connects to field.
- Called if special(SPC_DBADDR) specified
- Following fields of DBADDR can be changed
- no_elements
- field_type
- field_size
- special
- dbr_type

## Record Support Routines - Cont.

- Array Routines
- Called if cvt_dbaddr sets no_elements > 1
- get_array_info(DBADDR *pdbaddr,long *no_elements, long *offset)
- Called when database access gets an array
- Must set no_elements
- Can set offset if field is circular buffer
- put_array_info(	DBADDR *pdbaddr,long nNew)Called after database access has written array

## Record Support Routines - Cont.

- Units/Precision
- get_units(DBADDR *pdbaddr, char *punits)CA has MAX_UNITS_SIZE 8
- get_precision(DBADDR *pdbaddr,	long *precision)Problems with only precision.
- Enumerated Values
- get_enum_str(DBADDR *pdbaddr,char *p)
- put_enum_str(DBADDR *pdbaddr,char *p)
- get_enum_strs(DBADDR *pdbaddr,	dbr_enumStrs *p)CA has limit of 16 choices, 26 characters in each choice
- Graphic/ Control/ Alarm Limits
- get_graphic_double(DBADDR *pdbaddr,struct dbr_grDouble *p)
- get_control_double(DBADDR *pdbaddr,struct dbr_ctrlDouble *p)
- get_alarm_double(DBADDR *pdbaddr,struct dbr_alDouble *p)

## Global Record Support Routines

- All start with recGbl
- Intended for record/device support but also called by other code
- Reporting ErrorsDeprecated - Use epicsPrintf instead
- Alarm Processing
- recGblSetSevr(void *precord, short newstat, short newsevr)
- recGblResetAlarms(void *precord);
- Limits
- Defaults for fields record support doesn’t know how to handle
- Channel Access client often not happy
- Graphics, Control, Alarm limits
- recGblInitConstantLinkFor use on link fields with a constant value. Only works on numeric values!!

## Global Record Support Routines continued

- recGblGetTimeStamprecord support should call this each time record completes processing.
- recGblFwdLinkMust be called with pact true and all other processing complete

## Database Access Routines for Record Support

- When in context of process use dbGetLink, dbPutLink
- Link information
- dbGetPdbAddrFromLinkgets address of DBADDR.
- dbGetRsetgets address of Record Support Entry table
- dbIsLinkConnectedIs link connected?