Difference between revisions of "V4 DBD Statement Syntax"

From EPICSWIKI
Line 1: Line 1:
DBD Statement Syntax Sept 21 2005
Record Instance Syntax Sept 22 2005
= Overview =
= Overview =
This document presents the syntax for V4 database definition files.
<center>
= General Statements =
</center>


== include ==


include "filename"
The syntax used for record instances has to change in EPICS V4, since we now have to support structured data. While it would have been possible to modify the V3 syntax to allow for this, a complete redesign of the syntax has been done to help improve parsing, and to provide commonality between the syntax of a DB file and the string representation of structured data values passed through Channel Access.


where


; <tt>filename</tt>
= Document Conventions =
: must be a valid filename


The file system search path that will be used to look for the file is determined by the build system, and cannot be modified by the DBD file itself.
This syntax is presented below in the form of a grammar. The conventions I'm using are as follows:


Include statements can be used at the top level, or inside the braces of a <tt>menu</tt>, <tt>struct</tt> or <tt>record</tt> definition.
;''symbolBeingDefined:''
: ''otherSymbol''
: ''alternateSymbolFollowedBy'' <tt>literal</tt>
: ''one of:'' <tt>list of posible literal values</tt>


== # comment ==


# anything
= Common Symbols =


Anything on a line after a <tt>#</tt> character is a comment, and will be ignored. Comments may appear on a line by themselves, or at the end of another statement.  They may not appear inside the parentheses belonging to another statement, but they are permitted inside braces. Inside a single- or double- quoted string the <tt>#</tt> character has no special meaning.
The symbols described in this section are used in the grammar, but may be implemented as lexical tokens.


;''identifier:''
: A legal C99 identifier. Note that C99 permits implementations to allow extended characters to be used in identifiers, but does not require it, so the use of extended characters may reduce portability and is not recommended.


== menu ==
== Integer Constants ==


A menu is an enumerated type where the choice strings are defined once for each IOC.  Menus are defined like this:
;''integerConstant:''
: ''positiveInteger''


menu(menuName) {
;''positiveInteger:''
    choice(choiceName, "choiceValue")
: ''octalConstant''
    ...
: ''hexConstant''
}
: ''decimalConstant''


where
;''octalConstant:''
: <tt>0</tt>
: ''octalConstant'' ''octalDigit''


; <tt>menuName</tt>
;''octalDigit:''
: Must be a valid, unique C identifier.
: ''one of:'' <tt>0-7</tt>
; <tt>choiceName</tt>
: Must be a valid, unique C identifier. By convention, every choiceName should start with the menuName it belongs to. These names are only available to C/C++ source code using the header file generated from the menu definition; they are not stored in the IOC itself.
; <tt>choiceValue</tt>
: Can be any UTF-8 compatible string, which should be unique within the context of this menu.


In general menus should only be definable once, in the DBD file (first definition seen wins in the event of duplicates, give a warning for duplicates and error if subsequent definition is different).  However we may want to include a syntax that allows specific menu definitions to be extended at database load time.
;''hexConstant:''
: <tt>0x</tt> ''hexDigit''
: <tt>0X</tt> ''hexDigit''
: ''hexConstant'' ''hexDigit''


Example:
;''hexDigit:''
: ''one of:'' <tt>0-9 a-f A-F</tt>


menu(menuScan) {
;''decimalConstant:''
    choice(menuScanPassive, "Passive")
: ''one of:'' <tt>1-9</tt>
    choice(menuScanEvent, "Event")
: ''decimalConstant'' ''decimalDigit''
    choice(menuScanInterrupt, "Interrupt")
    choice(menuScan10second, "10 second")
    choice(menuScan5second, "5 second")
    choice(menuScan2second, "2 second")
    choice(menuScan1second, "1 second")
    choice(menuScan_5second, ".5 second")
    choice(menuScan_2second, ".2 second")
    choice(menuScan_1second, ".1 second")
}


<center>
;''decimalDigit:''
= Structures, Record types, Fields and Views =
: ''one of:'' <tt>0-9</tt>
</center>


Structures and record types have significant commonality in that they both define a data structure type containing fieldsThe main difference is that you can't create or populate an instance of a structure outside of a record; only records can appear at the very top level.  Record types also define views of the record, which is not possible for a structure.
This was meant to be a description of the C99 standard integer representation, but I made it up myself so it may be flawedNote that we will not accept the C99 numeric suffixes u/U and l/L since (unlike a C compiler) we know the type of the number we're expecting.


== struct ==
== Floating Point Constants ==


A structure is defined as follows:
;''realConstant:''
:'' positiveReal''
: <tt>-</tt> ''positiveReal''


struct(name) {
;''positiveReal:''
    field(fieldName, fieldType) {
: ''digitSequence''
        fieldAttribute(attributeValue)
: ''digitSequence'' <tt>.</tt>
        ...
: ''digitSequence'' <tt>.</tt> ''exponentPart''
    }
: ''digitSequence'' <tt>.</tt> ''digitSequence''
    ...
: ''digitSequence'' <tt>.</tt> ''digitSequence'' ''exponentPart''
}
: <tt>.</tt> ''digitSequence''
: <tt>.</tt> ''digitSequence'' ''exponentPart''
: ''digitSequence'' ''exponentPart''


where
;''digitSequence:''
: ''decimalDigit''
: ''digitSequence'' ''decimalDigit''


; <tt>name</tt>
;''exponentPart:''
: The structure name must be a valid, unique C identifier.
: <tt>e</tt> ''signedExponent''
; <tt>fieldName</tt>
: <tt>E</tt> ''signedExponent''
: Must be a valid C identifier, unique within the context of this particular structure.
; <tt>fieldType</tt>
: See [[#fieldType|fieldType]] below.
; <tt>fieldAttribute</tt> and <tt>attributeValue</tt>
: See [[#fieldAttribute|fieldAttribute]] below.


== record ==
;''signedExponent:''
: <tt>-</tt> ''digitSequence''
: <tt>+</tt> ''digitSequence''
: ''digitSequence''


A record type is defined as follows:
In ANSI C source code, a sequence of decimal digits with neither a decimal point nor an exponent is an integer constant, not a floating-point constant.  We will permit this however, since we always know the field type in advance.


include "dbCommon.dbd"
== Boolean Constants ==
record(name) extends RecordCommon {
    field(fieldName, fieldType) {
        fieldAttribute(attributeValue)
        ...
    }
    ...
    view(viewName) {
        property(propName, fieldPath)
        ...
    }
    ...
}


; <tt>name</tt>
We can afford to be generous in what we accept as a boolean value:
: The record type name. It must be a valid, unique C identifier.
; <tt>extends RecordCommon</tt>
: This states that the record type extends the set of fields defined in "V4 DB RecordCommon". It should be permissable to name other record types instead of RecordCommon here, as long as the inheritance tree starts at RecordCommon.  Inheritance from multiple record types is not supported; you can only have one <tt>extends</tt> phrase.
; <tt>fieldName</tt>
: Must be a valid C identifier, unique within the context of this particular record type and its parents (extends ...).
; <tt>fieldType</tt>
: See [[#fieldType|fieldType]] below.
; <tt>fieldAttribute</tt> and <tt>attributeValue</tt>
: See [[#fieldAttribute|fieldAttribute]] below.
; <tt>viewName</tt>, <tt>propName</tt> and <tt>fieldPath</tt>
: See [[#Views of a record|Views of a record]] below.


== fieldType ==
;''booleanConstant:''
: ''booleanTrue''
: <tt>"</tt> ''booleanTrue'' <tt>"</tt>
: ''booleanFalse''
: <tt>"</tt> ''booleanFalse'' <tt>"</tt>


Both <tt>struct</tt> and <tt>record</tt> define a field as:
;''booleanTrue:''
    field(fieldName, fieldType) {
: ''one of:'' <tt>1 T TRUE t true True Y YES Yes y yes</tt>
        fieldAttribute(attributeValue)
        ...
    }


The syntax for <tt>fieldType</tt> depends of the field type.  For the more complex types the field definition needs additional information to be provided, which is given inside parentheses following the type name.
;''booleanFalse:''
: ''one of:'' <tt>0 F FALSE f false False N NO No n no</tt>


<blockquote>
'''I'm proposing all these possibilities for true/false as they are all obvious in meaning, and will allow a CA Put of any of these strings to a boolean field. We might even want to allow registration of boolean strings in other languages...'''


=== Basic types: Boolean, numerics, octet, and string ===
== String Constants ==


The following field types have no arguments: <tt>bool</tt>, <tt>int16</tt>, <tt>int32</tt>,  <tt>int64</tt>, <tt>float32</tt>, <tt>float64</tt>, <tt>octet</tt>, and <tt>string</tt>.
;''stringConstant:''
: <tt>"</tt> ''escapedCharacterList'' <tt>"</tt>


Examples:  
;''escapedCharacterList:''
    field(description,string)
: A series of characters, using the C99 ''escapeSequence'' syntax defined below:
    field(value,float64)


=== enum ===
;''escapeSequence:''
: ''simpleEscapeSequence''
: ''octalEscapeSequence''
: ''hexEscapeSequence''
: ''universalCharacterName''


An <tt>enum</tt> field takes one argument after the type name:
;''simpleEscapeSequence:''
: ''one of:'' <tt>\' \" \? \\ \a \b \f \n \r \t \v</tt>


    enum(fieldName)
;''octalEscapeSequence:''
: <tt>\</tt> ''octalDigit''
: <tt>\</tt> ''octalDigit'' ''octalDigit''
: <tt>\</tt> ''octalDigit'' ''octalDigit'' ''octalDigit''


where fieldName is the name of another field in the same record that must be of type <tt>array(string[])</tt>.  The values in this other field at runtime define the available choice strings for the enum field.
;''hexEscapeSequence:''
: <tt>\x</tt> ''hexDigit''
: ''hexEscapeSequence'' ''hexDigit''


Examples:
Note: C99 does not limit the number of hexadecimal digits that can appear in a ''hexEscapeSequence'', but it does state that the behaviour is undefined if the resulting character value exceeds that of the largest character.


    field(choices, array(string[])
;''universalCharacterName:''
    field(value, enum(choices))
: <tt>\u</tt> ''hexQuad''
: <tt>\U</tt> ''hexQuad'' ''hexQuad''


=== menu ===
;''hexQuad:''
: ''hexDigit'' ''hexDigit'' ''hexDigit'' ''hexDigit''


A <tt>menu</tt> field is defined like this:
    menu(menuName)


where <tt>menuName</tt> is the name of the menu.
= Database File =


Example:
This section will eventually define what can appear in a .db file.  That currently means:
    field(scan, menu(menuScan))


=== struct ===
* record instances
* comments
* macro instances, including where they will be allowed
* template files and substitution macro definitions
* port definitions for template instances
* data for tools such as VDCT, that will not be discarded by .db processing tools.


A <tt>struct</tt> field is declared using the type name
The templates, macros and ports design should be very similar to the ideas produced for R3.14 VDCT templates.
    struct(structName)


where <tt>structName</tt> is the name of a struct which must have been previously defined.
= Record Definitions =


;''recordDefinition:''
: ''recordType'' ''recordName'' <tt>= {</tt> ''recordBody'' <tt>}</tt>


Example:
;''recordType:''
      struct(Point) {
: ''identifier''
          field(x,float64)
          field(y,float64)
          field(z,float64)
      }
      ...
      record(haspoint) extends RecordCommon {
          ...
          field(point, struct(point))
      }


=== array ===
;''recordName:''
: ''recordNameChar''
: ''recordName'' ''recordNameChar''


An array field uses definitions like these:
;''recordNameChar:''
: ''one of:'' <tt>0-9 A-Z a-z _ - : ; < > [ ]</tt>
: Any Unicode/UTF-8 character outside of the Basic Latin set


; 1-dimensional array
This extends the character set available to a V3 record name, adding all possible multi-byte characters.  However, EPICS sites are strongly advised to confirm that such record names can be processed using all of their database and CA client tools before actually making use of this particular extension.
: <tt>array(arrayType[capacity])</tt>
; multi-dimensional array
: <tt>array(arrayType[capacityX, capacityY, ...])</tt>
; arbitrarily dimensioned array
: <tt>array(arrayType)</tt>


; 1-dimensional array of unknown size
;''recordBody:''
: <tt>array(arrayType[])</tt>
: ''recordBodyItem''
; 3-dimensional array of unknown size
: ''recordBody'' ''recordBodyItem''
: <tt>array(arrayType[,,])</tt>


; 1-dimensional array of unknown type
Record instance definitions in EPICS V4 look very similar to a C99 structure definition with initialization. For example:
: <tt>array([capacity])</tt>
; multi-dimensional array of unknown type
: <tt>array([capacityX, capacityY, ...])</tt>
; arbitrarily dimensioned array of unknown type
: <tt>array()</tt>


; 1-dimensional array of unknown type or size
  ai foo:bar:temperature = {
: <tt>array([])</tt>
      ...
; 2-dimensional array of unknown type or size
  }
: <tt>array([,])</tt>


* <tt>arrayType</tt> may be any fieldType except <tt>device</tt> or <tt>array</tt>, or may be omitted completely in which case the data type stored is determined by record instanceIf the record instance defines the type, it can only be one of the types listed above under [[#Basic types: Boolean, numerics, octet, and string|Basic types]]
Inside the body of the record definition, there are three possible kinds of statements, similar to a C assignment statement.  Note these statements must be terminated with a semi-colon (which is different from inside a struct)The reason for this difference is to prevent database instance files from becoming dependent on the order of fields in a record; if we permit record instances to be created from a single comma-separated list of field values without the field names, it could lead to significant confusion if the field order ever changes.
* <tt>capacity</tt> is the array's size in the relevent dimension, and must be specified for all dimensions or for none.  If not specified by the record type, the record instance determines an array's dimensionality and size.


Examples:
;''recordBodyItem:''
    field(VAL1D,array(float64[]))  #1d array with arbitrary capacity
: ''infoAssignment''
    field(VAL2D,array(float64[,])) #2d array with arbitrary capacities
: ''fieldAssignment''
    field(anyTypeAnyD,array())    #arbitrary type,number of dimensions, and capacities
: ''extraFieldAssigment''


=== link and device ===
== Information Fields ==


These field types that can get or put data from/to some source outside of the record.  Link fields replace the DBF_INLINK and DBF_OUTLINK fields from EPICS V3, other than INP or OUT which were special.  A device field replaces the INP or OUT and DTYP fields from EPICS V3.
;''infoAssignment:''
: <tt>info</tt> ''infoName'' <tt>=</tt> ''stringConstant'' <tt>;</tt>


A link field's choices come from link definitions, and a device field from device definitions - see the [[#link and device|link and device]] section below for more details.  The main difference between links and devices is that iocCore and the record support communicate with all the link implementations via a standard link support interface, but communication with device support uses an explicitly named interface.
;''infoName:''
: ''identifier''
: ''stringConstant''


The syntax is:
Info items provide additional configuration data about this record that can be accessed by other software running on the IOC.


    link(linkDirection)
  info savePeriod = "30.0";
    device(linkDirection, deviceInterfaceName)
  info restorePhase = "1";
  info "my favourite things" = "raindrops on roses";


where <tt>linkDirection</tt> is <tt>none</tt>, <tt>in</tt>, <tt>out</tt>, <tt>process</tt>, or <tt>inout</tt>. The direction <tt>none</tt> means there is no direction associated with this interface.  For <tt>inout</tt> the direction is determined by the particular <tt>link</tt> or <tt>device</tt> that the record instance selects.
== Field Assignment ==
<tt>deviceInterfaceName</tt> selects compatible device support fom the DBD
<tt>device</tt> definitions.


Examples:
;''fieldAssignment:''
    field(disableLink, link(in))
: ''fieldName'' <tt>=</tt> ''initializer'' <tt>;</tt>
    field(process, link(process))
    field(inp, device(in, digitalIO))


</blockquote>
;''fieldName:''
: ''identifier''


== fieldAttribute ==
;''initializer:''
: ''constant''
: ''structInitializer''
: ''arrayInitializer''
: ''devlinkInitializer''


Each field definition has several associated attributes, the values of which are set like this:
;''initializerList:''
: ''initializer''
: ''initializerList'' <tt>,</tt> ''initializer''


    default("fieldValue")
The ''initializer'' in a field assignment is also the exact same syntax that will be used when converting a string value from a CA client for example into a field value that is being put into a field.
    readonly(yesNo)
    design(yesNo)
    special(yesNo)
    dynamic(yesNo)
    asl(securityLevel)
    link(yesNo)


'''Marty thinks we should get rid of these two:'''
=== Basic and Enumerated Initializers ===
    prompt("promptString")
    group("promptGroup")


'''I am thinking about combining readonly and design into a single attribute called access, which takes one of four choices: design (the default), runtime, readonly, or none.'''
;''constant:''
: ''booleanConstant''
: ''integerConstant''
: ''realConstant''
: ''stringConstant''


The attribute parameter values have the following meanings:
The syntax for the field initializer depends on the data type represented by fieldName.  Basic types (numeric or string) should need no comment other than to note that values for numeric fields must not be given inside quotes (unlike EPICS V3).  Menu field values may be given as either a string or an integer. For enum fields, if the related field that contains the strings is defined first, the enum field may be specified using a string; otherwise it can only be set using an integer value.


; <tt>default("fieldValue")</tt>
Examples:
: Default value for an instance of this field, using the [[V4 DB Record Instance Syntax|record instance value syntax]]. If a default is not specified, the field will initialize to all zero bits.
  ai foo:bar:temperature = {
: If the field is itself a structure, the default value for the instance of the whole structure can override default values declared for individual fields inside that structure.  This can occur at multiple levels.
      inputSmoothing = 0.98;
      invalidValue = 1000;
      units = "Celcius";
      scan = "Interrupt";
      ...
  }


; <tt>readonly(yesNo)</tt>
: Can this field be modified via channel access or database links?  Takes the value No if not specified.


; <tt>design(yesNo)</tt>
=== Structure Initializers ===
: Should a Database Configuration Tool allow the field to be configured at design time?  If No, values for the field cannot be set when loading record instance data at startup.  Takes the value Yes if not specified.


; <tt>special(yesNo)</tt>
;''structInitializer:''
: Does the record have to take special action if the field is modified?  If this is Yes, the record types special processing will be invoked to actually change the field value, which will allow it to perform value checks or additional processing.  Takes the value No if not specified.
: <tt>{</tt> ''structAssignmentList'' <tt>}</tt>


; <tt>dynamic(yesNo)</tt>
;''structAssignmentList:''
: Can the field change as a result of record processing?  If Yes, then changes will be posted to channel access automatically.  If No, only DB or CA puts can change the field, and these post monitors automatically.
: ''initializerList''
: ''fieldName'' <tt>=</tt> ''initializerList''
: ''structAssignmentList'' <tt>;</tt> ''fieldName'' <tt>=</tt> ''initializerList''


; <tt>asl(securityLevel)</tt>
Initializers for a structure field look similar to a nested record body, but the rules are slightly different:
: Channel Access security level for this field, 0 or 1.  Takes the value 1 if not specified.
* You can give a series of values for adjacent items using a simple comma-separated list (for a record body, you ''must'' name each field)
* Semi-colons are required between a value and a following named item.


; <tt>link</tt>
For example:
: This is only valid for string fields. It signifies the the field is the name of an external link. This is for use by Database Configuration Tools.


''These attributes may disappear, see comment above:''
  ai foo:temperature:sensor = {
      linearConvert = {
          mode = "Linear";
          low = -12.5, 133.5
      };
      displayLimit = { 0, 100 };
      ...
  }


; <tt>prompt("promptString")</tt>
=== Link and Device Initializers ===
: A description of this field for the database designer, this string will be displayed by a Database Configuration Tool.  Empty if not specified.  Not used within the IOC.


; <tt>group("promptGroup")</tt>
;''devlinkInitializer:''
: A name that can be used by a Database Configuration Tool to group similar or related fields together.  Empty if not specified.  Not used within the IOC.
: ''choiceName'' <tt>(</tt> ''structAssignmentList'' <tt>)</tt>


== view ==
;''choiceName:''
: ''identifier''


There needs to be more than one way to look at a record remotely (via Channel Access or some other similar network protocol).  Often we just want to get the contents of the value field and some metadata associated with that value, but there are often several fields which can share metadata - engineering units for example.  We can't do metadata using structures because that would mean replicating this metadata, so we add a level of indirection to allow us to group fields together.
These select a particular link or device support for the field, and set its address according to the structure type defined for that link or device type.


A view of a record provides a hierarchical mapping of some of the record's fields from a named Data Access property catalog that can be reached using Channel Access.  Records automatically get a view named "field" that provides direct access to the individual public fields of the record, with no metadata. Beyond that, record types can declare additional hierarchical views and define the fields that appear in them inside the DBD file. The first view defined for a record type is used as its default view (if no views are defined, the field view will become the default view; view parameters may not be permitted).
  calcout foo:temperature:controller = {
      output = ca("fum:baz:heater"; pp=y);
      input = [2] {
          {link = {
              MonitorLink{
                  pvname = "foo:temperature:setpoint";
                  process = false
              }
          }
          {link = {
              InputLink{
                  pvname = "foo:temperature:sensor";
                  process = true;
                  inheritSeverity = true
              }
          }
      };
      expression = "(setpoint - current) > 0";
      ...
  }


A simple view looks like this:
  mbbi foo:bar:door = {
      input = acro9440(0, 5);
      ...
  }


view(viewName) {
=== Array Initializers ===
    property(propName) {
        property(propName, fieldPath)
        ...
    }
    property(propName, fieldPath)
    ...
}


; <tt>viewName</tt>
;''arrayInitializer:''
: The view name must be a valid C identifier, which must be unique in the context of the particular record type.
: <tt>{</tt> ''arrayAssignmentList'' <tt>}</tt>
; <tt>propName</tt>
: ''arrayType'' <tt>{</tt> ''arrayAssignmentList'' <tt>}</tt>
: A Data Access property name, which must be a valid C identifier.
: <tt>[</tt> ''arrayCapacity'' <tt>] {</tt> ''arrayAssignmentList'' <tt>}</tt>
; <tt>fieldPath</tt>
: ''arrayType'' <tt>[</tt> ''arrayCapacity'' <tt>] {</tt> ''arrayAssignmentList'' <tt>}</tt>
: The path to a field in this record type. To use a field inside a structure field, give the full path to that field: <tt>controlLimit.upper</tt> for example.
: If <tt>fieldPath</tt> resolves to a structure, a property catalog containing the whole structure will be sent, with property names matching the structure's field names.
: The <tt>fieldPath</tt> may be omitted as long as there is a subordinate property catalog below this property.


Example:
;''arrayAssignmentList:''
: ''initializerList''
: <tt>[</tt> ''integerConstant'' <tt>] =</tt> ''initializerList''
: ''arrayAssignmentList'' <tt>; [</tt> ''integerConstant'' <tt>] =</tt> ''initializerList''


record(ao) extends RecordCommon {
;''arrayType:''
    field(value, float64) { ... }
: ''one of:'' <tt>bool</tt> <tt>int16</tt> <tt>uint16</tt> <tt>int32</tt> <tt>uint32</tt>
    field(outValue, float64) { ... }
: ''one of:'' <tt>float32</tt> <tt>float64</tt> <tt>octet</tt> <tt>string</tt>
    field(rawValue, int32) { ... }
    field(units, string) { ... }
    field(displayLimit, struct(displayLimit)) { ... }
    ...
    view(value)
        property(value, value) {
            property(units, units)
            property(timeStamp, time)
            property(alarmSeverity, alarmSeverity)
            property(alarmStatus, alarmStatus)
            property(displayLimit, displayLimit)
        }
    }
    view(outValue)
        property(value, outValue) {
            property(units, units)
            property(timeStamp, time)
            property(alarmSeverity, alarmSeverity)
            property(alarmStatus, alarmStatus)
        }
    }
    view(rawValue)
        property(value, rawValue) {
            property(timeStamp, time)
        }
    }
    ...
}


<center>
;''arrayCapacity:''
= link and device =
: ''integerConstant''
</center>
: ''arrayCapacity'' <tt>,</tt> ''integerConstant''


: '''Can we do something to define the interfaces here by using struct methods and member function pointers, rather than making up our own separate interface code?'''
If the definition of the array field being set did not do so, an array field initialization must include the size of the array and/or the type of the data stored in it. Inside the braces data values are given in a comma-separated list; the index can also be set to initialize individual values, and any mixture of the two can be used as desired:


A <tt>link</tt> statement describes an implementation of the generic link interface, which will be available for use in matching link field instances.
  mbbi foo:bar:door = {
 
      stateNames = [4] {"Broken", "Closed", "Open", "Moving"};
A <tt>device</tt> statement describes an implementation of a device choice for device fields.
      stateSeverity = [4] {"Major"; [3] = "Minor"};
 
      ...
The syntax for these is:
  }
 
link(dir, choiceName, dataStructName)
device(dir, choiceName, deviceInterfaceName, dataStructName)
 
where
 
; <tt>deviceInterfaceName</tt>
: The name of an interface via which record support communicates with device support.
 
; <tt>choiceName</tt>
: UTF-8 string that describes the choice


; <tt>dir</tt>
For multi-dimensional arrays, data values can only appear inside the inner-most sets of braces, although index settings are permitted outside of these. These two definitions give the same result:
: Must be one of <tt>none</tt>,<tt>in</tt>,<tt>out</tt>,<tt>process</tt>, or <tt>inout</tt>. Compatible checks are made to match the interface with a field.
: '''Q: What are the rules?'''
:: ''It certainly doesn't seem to make sense to have a none or inout link for instance.''


; <tt>dataStructName</tt>
  matrix identity1 = {
: The name of a <tt>struct</tt> that the interface understands. This interface can contain data and configuration information. The <tt>struct</tt> must be defined before record instances can be created that assign values to it.
      value = float32 [3,3] { {1, 0, 0}, {0, 1, 0}, {1, 0, 0}};
:: ''Need to rewrite this paragraph to clarify.''
  }
  matrix identity2 = {
      value = float32 [3,3] { {1}, {[1] = 1}, {[2] = 1}};
  }

Revision as of 13:04, 22 September 2005

Record Instance Syntax Sept 22 2005

Overview

The syntax used for record instances has to change in EPICS V4, since we now have to support structured data. While it would have been possible to modify the V3 syntax to allow for this, a complete redesign of the syntax has been done to help improve parsing, and to provide commonality between the syntax of a DB file and the string representation of structured data values passed through Channel Access.


Document Conventions

This syntax is presented below in the form of a grammar. The conventions I'm using are as follows:

symbolBeingDefined:
otherSymbol
alternateSymbolFollowedBy literal
one of: list of posible literal values


Common Symbols

The symbols described in this section are used in the grammar, but may be implemented as lexical tokens.

identifier:
A legal C99 identifier. Note that C99 permits implementations to allow extended characters to be used in identifiers, but does not require it, so the use of extended characters may reduce portability and is not recommended.

Integer Constants

integerConstant:
positiveInteger
positiveInteger:
octalConstant
hexConstant
decimalConstant
octalConstant:
0
octalConstant octalDigit
octalDigit:
one of: 0-7
hexConstant:
0x hexDigit
0X hexDigit
hexConstant hexDigit
hexDigit:
one of: 0-9 a-f A-F
decimalConstant:
one of: 1-9
decimalConstant decimalDigit
decimalDigit:
one of: 0-9

This was meant to be a description of the C99 standard integer representation, but I made it up myself so it may be flawed. Note that we will not accept the C99 numeric suffixes u/U and l/L since (unlike a C compiler) we know the type of the number we're expecting.

Floating Point Constants

realConstant:
positiveReal
- positiveReal
positiveReal:
digitSequence
digitSequence .
digitSequence . exponentPart
digitSequence . digitSequence
digitSequence . digitSequence exponentPart
. digitSequence
. digitSequence exponentPart
digitSequence exponentPart
digitSequence:
decimalDigit
digitSequence decimalDigit
exponentPart:
e signedExponent
E signedExponent
signedExponent:
- digitSequence
+ digitSequence
digitSequence

In ANSI C source code, a sequence of decimal digits with neither a decimal point nor an exponent is an integer constant, not a floating-point constant. We will permit this however, since we always know the field type in advance.

Boolean Constants

We can afford to be generous in what we accept as a boolean value:

booleanConstant:
booleanTrue
" booleanTrue "
booleanFalse
" booleanFalse "
booleanTrue:
one of: 1 T TRUE t true True Y YES Yes y yes
booleanFalse:
one of: 0 F FALSE f false False N NO No n no

I'm proposing all these possibilities for true/false as they are all obvious in meaning, and will allow a CA Put of any of these strings to a boolean field. We might even want to allow registration of boolean strings in other languages...

String Constants

stringConstant:
" escapedCharacterList "
escapedCharacterList:
A series of characters, using the C99 escapeSequence syntax defined below:
escapeSequence:
simpleEscapeSequence
octalEscapeSequence
hexEscapeSequence
universalCharacterName
simpleEscapeSequence:
one of: \' \" \? \\ \a \b \f \n \r \t \v
octalEscapeSequence:
\ octalDigit
\ octalDigit octalDigit
\ octalDigit octalDigit octalDigit
hexEscapeSequence:
\x hexDigit
hexEscapeSequence hexDigit

Note: C99 does not limit the number of hexadecimal digits that can appear in a hexEscapeSequence, but it does state that the behaviour is undefined if the resulting character value exceeds that of the largest character.

universalCharacterName:
\u hexQuad
\U hexQuad hexQuad
hexQuad:
hexDigit hexDigit hexDigit hexDigit


Database File

This section will eventually define what can appear in a .db file. That currently means:

  • record instances
  • comments
  • macro instances, including where they will be allowed
  • template files and substitution macro definitions
  • port definitions for template instances
  • data for tools such as VDCT, that will not be discarded by .db processing tools.

The templates, macros and ports design should be very similar to the ideas produced for R3.14 VDCT templates.

Record Definitions

recordDefinition:
recordType recordName = { recordBody }
recordType:
identifier
recordName:
recordNameChar
recordName recordNameChar
recordNameChar:
one of: 0-9 A-Z a-z _ - : ; < > [ ]
Any Unicode/UTF-8 character outside of the Basic Latin set

This extends the character set available to a V3 record name, adding all possible multi-byte characters. However, EPICS sites are strongly advised to confirm that such record names can be processed using all of their database and CA client tools before actually making use of this particular extension.

recordBody:
recordBodyItem
recordBody recordBodyItem

Record instance definitions in EPICS V4 look very similar to a C99 structure definition with initialization. For example:

  ai foo:bar:temperature = {
      ...
  }

Inside the body of the record definition, there are three possible kinds of statements, similar to a C assignment statement. Note these statements must be terminated with a semi-colon (which is different from inside a struct). The reason for this difference is to prevent database instance files from becoming dependent on the order of fields in a record; if we permit record instances to be created from a single comma-separated list of field values without the field names, it could lead to significant confusion if the field order ever changes.

recordBodyItem:
infoAssignment
fieldAssignment
extraFieldAssigment

Information Fields

infoAssignment:
info infoName = stringConstant ;
infoName:
identifier
stringConstant

Info items provide additional configuration data about this record that can be accessed by other software running on the IOC.

  info savePeriod = "30.0";
  info restorePhase = "1";
  info "my favourite things" = "raindrops on roses";

Field Assignment

fieldAssignment:
fieldName = initializer ;
fieldName:
identifier
initializer:
constant
structInitializer
arrayInitializer
devlinkInitializer
initializerList:
initializer
initializerList , initializer

The initializer in a field assignment is also the exact same syntax that will be used when converting a string value from a CA client for example into a field value that is being put into a field.

Basic and Enumerated Initializers

constant:
booleanConstant
integerConstant
realConstant
stringConstant

The syntax for the field initializer depends on the data type represented by fieldName. Basic types (numeric or string) should need no comment other than to note that values for numeric fields must not be given inside quotes (unlike EPICS V3). Menu field values may be given as either a string or an integer. For enum fields, if the related field that contains the strings is defined first, the enum field may be specified using a string; otherwise it can only be set using an integer value.

Examples:

  ai foo:bar:temperature = {
      inputSmoothing = 0.98;
      invalidValue = 1000;
      units = "Celcius";
      scan = "Interrupt";
      ...
  }


Structure Initializers

structInitializer:
{ structAssignmentList }
structAssignmentList:
initializerList
fieldName = initializerList
structAssignmentList ; fieldName = initializerList

Initializers for a structure field look similar to a nested record body, but the rules are slightly different:

  • You can give a series of values for adjacent items using a simple comma-separated list (for a record body, you must name each field)
  • Semi-colons are required between a value and a following named item.

For example:

  ai foo:temperature:sensor = {
      linearConvert = {
          mode = "Linear";
          low = -12.5, 133.5
      };
      displayLimit = { 0, 100 };
      ...
  }

Link and Device Initializers

devlinkInitializer:
choiceName ( structAssignmentList )
choiceName:
identifier

These select a particular link or device support for the field, and set its address according to the structure type defined for that link or device type.

  calcout foo:temperature:controller = {
      output = ca("fum:baz:heater"; pp=y);
      input = [2] {
          {link = {
              MonitorLink{
                  pvname = "foo:temperature:setpoint";
                  process = false
              }
          }
          {link = {
              InputLink{
                  pvname = "foo:temperature:sensor";
                  process = true;
                  inheritSeverity = true
              }
          }
      };
      expression = "(setpoint - current) > 0";
      ...
  }
  mbbi foo:bar:door = {
      input = acro9440(0, 5);
      ...
  }

Array Initializers

arrayInitializer:
{ arrayAssignmentList }
arrayType { arrayAssignmentList }
[ arrayCapacity ] { arrayAssignmentList }
arrayType [ arrayCapacity ] { arrayAssignmentList }
arrayAssignmentList:
initializerList
[ integerConstant ] = initializerList
arrayAssignmentList ; [ integerConstant ] = initializerList
arrayType:
one of: bool int16 uint16 int32 uint32
one of: float32 float64 octet string
arrayCapacity:
integerConstant
arrayCapacity , integerConstant

If the definition of the array field being set did not do so, an array field initialization must include the size of the array and/or the type of the data stored in it. Inside the braces data values are given in a comma-separated list; the index can also be set to initialize individual values, and any mixture of the two can be used as desired:

  mbbi foo:bar:door = {
      stateNames = [4] {"Broken", "Closed", "Open", "Moving"};
      stateSeverity = [4] {"Major"; [3] = "Minor"};
      ...
  }

For multi-dimensional arrays, data values can only appear inside the inner-most sets of braces, although index settings are permitted outside of these. These two definitions give the same result:

  matrix identity1 = {
      value = float32 [3,3] { {1, 0, 0}, {0, 1, 0}, {1, 0, 0}};
  }
  matrix identity2 = {
      value = float32 [3,3] { {1}, {[1] = 1}, {[2] = 1}};
  }