EPICS for the Bewildered



Getting EPICS to Do Stuff

I can see you're a glutton for punishment.

At this point it's time to feed EPICS a database that it can worry about. If you already have an application tree, I highly suggest placing it under the $EPICS directory on your host machine, typing "make," and then adding the appropriate "ld" and "dbLoadDatabase" commands to your st.cmd file. You can then get back to drinking some coffee and getting some real work done.

But it appears that you and bliss don't get along too well together. So let's get into the details....

Defining the Database

Somehow we have to tell EPICS what it is we wish to keep track of. If you already have a running EPICS server, you should peek at the existing files for examples.

• The Database Definition File (.dbd)

Much like a "struct" entry in c, we must first define our record (structure) and the fields (components) we wish to maintain within it. A structure in c looks like this:
struct rec_type {
	type field_name1;
	type field_name2;
	...
};
A .dbd file will look like this:
recordtype(rec_type) {
	include "filename.dbd"
	field(field_name1,type) {
		rule1(value1.1)
		rule2(vaule1.2)
		...
	}
	field(field_name2,type) {
		rule1(value2.1)
		rule2(vaule2.2)
		...
	}
	...
}
The Application Developer's Guide, chapter 3, details this fairly well, however, a quick overview looks like this:

rec_typeThe name of the record
field_name1, field_name2, ... The name of the field
type The type of field. As of EPICS 3.13, the allowed types are:
  • 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
rule1, rule2, ... Standard rules for this field. The following comes directly from the Application Developer's Guide:
    asl(access_security_level)
    initial("init_value")
    promptgroup(gui_gruoup)
    prompt("prompt_string")
    special(special_value)
    pp(passive_process_boolean)
    interest(interest_level)
    base(integer_base)
    size(size_of_string)
    extra("extra_info")
    menu("menu_name")
    
filename.dbd The name of a file holding further field definitions

It is rarely necessary to define a new record. Before doing so, you may want to check the existing record types defined under $EPICS_BASE/dbd. These should be fully described in the Record Reference Manual.

• The Record Instance File (.db)

It is in this file that we do the equivalent of
rec_type record_name;
record_name.field_name1 = value1;
record_name.field_name2 = value2;
...
A simple example would be:
record(rec_type,record_name) {
	include "file.db"
	field(field_name1,value1)
	field(field_name2,value2)
	...
}
where "file.db" would be any other file which defines further fields in this particular record.

• Our First Database

For our first database, we will simply make a calculation record. This record will allow us to add 2 and 2. A weighty EPICS problem.

The database definition file already exists: $EPICS_BASE/dbd/calcRecord.dbd, so we won't have to define the fields of the record, but we will have to know what those fields are so that we can manipulate the record correctly.

If we name our record instance "goombah," the following fields are of interest:

goombah.A
goombah.B
goombah.C
goombah.D
goombah.E
goombah.F
goombah.G
goombah.H
goombah.I
goombah.J
goombah.K
goombah.L
The twelve inputs to the calculation
goombah.CALCThe infix string for the calculation.
goombah.VALThe result of the calculation.
goombah.PRECThe number of decimal places to hold.

We will make our record instance file extremely simple by allowing all the fields to start at their defaults. Here's what I have:

record(calc,"goombah") {
	field(PREC,"3")
}
Pretty simple, eh? Now for the fun part: we have to do a few things before EPICS can handle this.

= EPICS 3.13

After we load iocCore, vxWorks knows how to load in database definitions and record instances, but it doesn't yet know how to manipulate the record (how to initialize it, process it, etc.). So the very next thing we need to do is load in those procedures. When you compiled EPICS for the VME crate, those processes associated with the standard records were compiled as well. All you should need to do is add the line

ld < calcRecord.o
after loading in iocCore. Following this, we will need to read in the database definition file. This requires the following lines:
cd "/opt/epics/base/dbd"
dbLoadDatabase("menuScan.dbd")
dbLoadDatabase("menuYesNo.dbd")
dbLoadDatabase("menuAlarmStat.dbd")
dbLoadDatabase("menuAlarmSevr.dbd")
dbLoadDatabase("menuPriority.dbd")
dbLoadDatabase("calcRecord.dbd")
This is because "calcRecord.dbd" uses definitions from the other databases. Finally, we can load in our record:
dbLoadRecords("/wherever/you/put/example.db")
And, the last thing we need to do is call iocInit to start everything up. A template st.cmd file might look like this:
##
## Mark Rivers' kludge for short jumps =Begin=
##
mem = malloc(1024*1024*96)
##

cd "/opt/epics/base/bin/ppc604"
ld < iocCore
ld < calcRecord.o
ld < seq

##
## Mark Rivers' kludge for short jumps =End=
##
free(mem)
##

cd "/opt/epics/base/dbd"
dbLoadDatabase("menuScan.dbd")
dbLoadDatabase("menuYesNo.dbd")
dbLoadDatabase("menuAlarmStat.dbd")
dbLoadDatabase("menuAlarmSevr.dbd")
dbLoadDatabase("menuPriority.dbd")
dbLoadDatabase("calcRecord.dbd")
dbLoadRecords("/home/ioc/crate3/example.db")

iocInit

The following commands are useful to know in order to play with our new calc record:

Useful vxWorks Commands for EPICS
coreRelease prints the version of EPICS running on the IOC.
dbLoadDatabase("/path/to/database/file.dbd", "<var1>=<sub1>", "<var2>=<sub2>", ... , "<varn>=<subn>") Load a database definition file with the variables "var" replaced by the substitutions "sub"
dbLoadRecords("/path/to/database/file.db", "<var1>=<sub1>", "<var2>=<sub2>", ... , "<varn>=<subn>") Load a record instance file with the variables "var" replaced by the substitutions "sub"
dbl ["<record type>"] prints the names of the records in the run time database.
dbgrep ["<pattern>"] prints the names of the records in the run time database that match "<pattern>"
dba ["<record.field>"] lists the attributes of the record and field specified. If no field is given, ".VAL" is assumed.
dbgf ["<record.field>"] prints the value of the record and field specified. If no field is given, ".VAL" is assumed.
dbpf ["<record.field>","<value>"] assigns the specified value to the record and field specified. If no field is given, ".VAL" is assumed.
dbpr ["<record>","<interest level>"] prints all of the fields belonging to the record specified. "interest level" decides what fields to show. Use "0".
dbstat prints the status of all locksets that are suspended or contain breakpoints.
dbior ["<driver>","<interest level>"] prints the report entry for the driver specified. "interest level" decides what fields to show. Use "1".

First, let's see if our record exists:

ioc> dbl
goombah
value = 0 = 0x0
Good. It's there. Let's look at its fields:
ioc> dbpr "goombah"
A: 0                ASG:                B: 0                C: 0
CALC:               D: 0                DESC:               DISA: 0
DISP: 0             DISV: 1             E: 0                F: 0
G: 0                H: 0                I: 0                J: 0
K: 0                L: 0                NAME: goombah       SEVR: INVALID
STAT: UDF           TPRO: 0             VAL: 0
value = 0 = 0x0
Ok, so, here's how we're going to add 2 and 2 to get 4 (you knew it was 4, right?):
ioc> dbpf "goombah.A", "2"
DBR_DOUBLE:         2
value = 0 = 0x0
ioc> dbpf "goombah.B", "2"
DBR_DOUBLE:         2
value = 0 = 0x0
ioc> dbpf "goombah.CALC", "A+B"
DBR_STRING:          A+B
value = 0 = 0x0
ioc> dbgf "goombah"
DBR_DOUBLE:         4
value = 0 = 0x0
ioc>
Congratulations. You just spent a couple of weeks compiling and loading an EPICS client, assembling a VME crate, compiling and loading vxWorks onto the crate, compiling and loading EPICS onto the crate, ... and now you know that 2+2=4.

Something a bit more useful is to use the client tools "caput" and "caget." You should have compiled them with the Linux client. If your IOC and OPI are on the same subnet, you should be able to do this at your OPI's command line:

linux% caput goombah.A 12
Old : goombah.A                 0
New : goombah.A                 12
linux% caput goombah.B 13
Old : goombah.B                 0
New : goombah.B                 13
linux% caput goombah.CALC A+B
Old : goombah.CALC
New : goombah.CALC              A+B
linux% caget goombah.VAL
goombah.VAL              25
So, wonder of wonders, 12+13=25.

Now that we've done this by hand, tune in next week when we streamline the process....


(Installing the EPICS Server) <-BACK | NEXT-> (EPICS References)