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....
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_type | The 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:
|
| 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.
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.
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.CALC | The infix string for the calculation. |
| goombah.VAL | The result of the calculation. |
| goombah.PREC | The 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.
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.oafter 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 = 0x0Good. 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 = 0x0Ok, 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 25So, wonder of wonders, 12+13=25.
Now that we've done this by hand, tune in next week when we streamline the process....