Network Providers for DBRecords

Author: Marty Kraimer
Date: 2018.05.09

Abstract

WARNING: This is a draft version of this document.

The following provide network access to ioc DBrecords:

channel access
This is the network provider that has existed since the late 1980's and is provided with epics-base.
qsrv
This is a network provider that is provided with pva2pva It runs in an ioc and provides access to DBRecords. A client accesses it via the pva network protocol.
ca
This is a client side provider that is implemented in pvAccessCPP and pvAccessJava.
This document only discuses C++.
ca uses the channel access network protocol to communicate with a server.

This document describes compatability issues between these network providers.

Table of Contents


Goals

Immediate

The immediate goal is to start a discussion about possible changes to ca and qsrv. At the BNL FTF meeting in May 2018 we did discuss the Known issues. This document has been updated to give the current status.

Final

Starting with Overview, this document will descibe compatability issues between the three network providers.

Known Issues

Issues for both ca and qsrv

format
Current Status: unresolved. We need to get input from tool developers.

The definition of display has a string field named format
How should this be defined?
NOTE: channel access uses precision. But this has problems. cstdio states:
precision	description
For integer specifiers (d, i, o, u, x, X):
precision specifies the minimum number of digits to be written. 
...
For a, A, e, E, f and F specifiers:
this is the number of digits to be printed after the decimal point.
...
If a channel access client makes a DBR_STRING request for reading a channel with native type DBF_DOUBLE or DBF_FLOAT then the server will use a Fl.p conversion where l is the total number of characters and p is the precision. But this is only done if the value is such that an F style format makes sense. If it does not it uses an E style format and ignores precision.
For all other DBF types precision is ignored.
byte array for long strings
Current Status:
syntax
Need input about syntax
ca
Will be implemented
qsrv
Will be implemented via info tags instead of via pvRequest


channel access enforces MAX_STRING_SIZE. A workaround is to create a waveform record of type DBF_CHAR. The client must be aware that the record actually holds a character string instead of an array of bytes. For pva clients this means that instead of reading and writing byte array data it should read and write string data. Neither ca or qsrv currently provides support for this.
Perhaps both could support a field option like:
field(value[bytearray=string])
limit number of elements for arrays
Current Status:
syntax
has been changed from maxelements to elements
ca
Will be implemented
qsrv
undecided if anything will be done


channel access gets and monitors allow the caller to specify the maximum number of array elements to be sent to the client. Neither ca or qsrv curently provides support for this.
Perhaps both could support a field option like:
field(elements=value])
valueAlarm
Current Status: unresolved. We need to get input from tool developers.

This has definitions for each numeric scalarType. display and control only support double.
Should valueAlarm only have support for double.

Issues for ca

Current Status: Will be implemented.

channel access allows the client to specify the data types to be received from the server. The necessary conversions are done on the server. This is especially a problem for DBF_CHAR fields. A DBRecord can have a DBF_CHAR or DBF_UCHAR value field, but channel access has no concept of signed and unsigned types. In the description of channel access below an example is given that demonstrates the problem:

Note that the client does get correct values for DBF_CHAR but not for DBF_UCHAR

Perhaps there could be a field option:

field(value[byte=signed])
or
field(value[byte=unsigned])

Issues for qsrv

Does not allow the user to select desired set of fields
Current Status: Michael has serious objections and does not think it is necessary. But when I try pvget -p pva -r "alarm" DBRdoubleArray It returns all fields including all elements for the value field.

If the client specifies:
mrk> pvget -m -r "alarm" -i DBRdoubleArray
The client will also get value,display,control, and valueAlarm fields.
If the client specifies:
mrk> pvget  -p ca -m -r "alarm" -i DBRdoubleArray
The client will only receive the requested fields.

Overview

EPICS has existed since the early 1990's. It evolved from a control system named GTACS which was developed at LANL in the second half of the 1980's. Two major features are:

Memory Resident Database of Smart Records
An IOC has a database consisting of DBRecord instances. Each instance has a record name that should be unique in the local area network. A DBRecord has a "flat" record structure, i. e., it has a set of fields that hold the data for the record instance.
channel access
This is the software that allows a client to read, write, and monitor data in a DBRecord instance.
It implements a network based client/server model.

An extensible set of record types can be created. Each record type has associated code, which is what makes the record "smart". Many record types are designed to interact with hardware like ADCs, DACs, etc. This document only discusses how a client, via channel access, or qsrv or ca can read, write, a monitor data from DBRecord instances.

exampleCPP

exampleCPP has a number of examples. This document uses two of the examples.

database

database supports an IOC that has a combination of PVRecords and DBRecords. In this document we are only interested in DBRecords.

If an IOC is started as follows:

mrk> pwd
/home/epicsv4/masterCPP/exampleCPP/database/iocBoot/exampleDatabase
mrk> ../../bin/linux-x86_64/exampleDatabase st.cmdtypes 

Then a set of DBRecords are available for every possible DBF type:

epics> dbnr
Records  Aliases  Record Type
     1        0    longout
     2        0    mbbi
     1        0    bi
     1        0    floatout
     1        0    ucharout
     1        0    mbbo
     1        0    ao
     1        0    simpleBusy
     1        0    bo
     1        0    charout
     9        0    waveform
     1        0    stringout
     1        0    ushortout
     1        0    ai
     1        0    calc
     1        0    longin
     1        0    stringin
     1        0    shortout
     1        0    calcout
     1        0    ulongout
Total 29 records, 0 aliases
epics> dbl longout
DBRlongout
... // similar for other scalar types
epics> dbl waveform
DBRbyteArray
DBRshortArray
DBRintArray
DBRubyteArray
DBRushortArray
DBRuintArray
DBRstringArray
DBRfloatArray
DBRdoubleArray
epics> 

The examples in this document access these records.

exampleClient

This has examples for issuing client get, put and monitor requests. For example:

mrk> exampleClient/get -help
 -h -p provider -r request - d debug channelNames 
default
-p pva -r value,alarm,timeStamp -d false PVRdouble

IOC DBRecord database

An IOC database consists of a set of DBRecords. Each is an instance of an extensible set of record types. Each record type has associated record support code.

DBRecord structure

A DBRecord instance is a structure with a set of fields defined by the record type. Each field name consists of at most 4 characters. For the database the characters are all lower case and for client access the same name but in upper case. There is a set of fields that are common to all record types and each record type defines an additional set of fields. Some of the fields accessable to clients are:

char           name[61];   /* Record Name */
char           desc[41];   /* Descriptor */
...
epicsTimeStamp time;       /* Time */
...

Each record type must define a field name val, must be one of the types defined in the next subsection. Many fields are declared NOACCESS, which means that clients can not directly access these fields. Each DBRecord instance has a record name. A client can access a record via a channel name. A channel name is identical to the record name with an optional .fieldname. If a field name is not specified then recordname.VAL is implied. A field accessible to clients is one of the following:

numeric type
One of the numeric types defined in the next section.
character string
A fixed length character string.
timeStamp
This is the only field, accessable by clients, that is a structure.
It is defined as follows:
typedef struct epicsTimeStamp {
    epicsUInt32    secPastEpoch;   /* seconds since 0000 Jan 1, 1990 */
    epicsUInt32    nsec;           /* nanoseconds within second */
} epicsTimeStamp;
NOTE secPastEpoch overflows 136 years after 1900, i. e. in 2126.
array
This is an array of one of the numeric types or an array of strings of size MAX_STRING_SIZE.

VAL types

Each record type must define a value field. Client code refers to this field with the name VAL and record support code the name val. Each record type must define the value field to be one of the following types.

typedef enum {
	DBF_STRING,    // char[40];
	DBF_CHAR,      // epicsInt8
	DBF_UCHAR,     // epicsUInt8
	DBF_SHORT,     // epicsInt16
	DBF_USHORT,    // epicsUInt16
	DBF_LONG,      // epicsInt32
	DBF_ULONG,     // epicsUInt32
	DBF_INT64,     // epicsInt64     NOTE: Only starting with base 3.15 releases
	DBF_UINT64,    // epicsUInt64    NOTE: Only starting with base 3.15 releases
	DBF_FLOAT,     // epicsFloat32
	DBF_DOUBLE,    // epicsFloat64
	DBF_ENUM,      // epicsEnum16
	...
}dbfType;

record structure

struct aRecord {
    char                name[61];   /* Record Name */
    char                desc[41];   /* Descriptor */
    ...                             /* other fields */
    epicsEnum16         stat;       /* Alarm Status */
    epicsEnum16         sevr;       /* Alarm Severity */
    ...                             /* other fields */
    epicsTimeStamp      time;       /* Time */
    <type>              val;        /* Current value. Type is record type specific */
    ...                             /* record type specific fields */
}

The val field type can be:

scalar
any of epicsInt8, ..., epicsFloat64 except epicsEnum16.
for DBF_STRING it is "val char[MAX_STRING_SIZE]".
scalar array
Is defined as void *val.
The record support must make it a array of any of epicsInt8, ..., epicsFloat64 except epicsEnum16. Thus arrays of enum are not supported.
For an array of strings it must be an array of char[MAX_STRING_SIZE]
enumerated
These are record types like the epics-base record types bi, bo, mbbi, and mbbo.
These must have value type epicsEnum16, which is actually epicsUInt16.

record support methods related to data access

Each record support can implement a combination of the methods described in this section. Only the methods that make sense for the record type need to be implemented.

This section only describes the methods used for client access. Methods related to record processing are not described.

get_units
long get_units(DBADDR *, char * units);
get_precision
long get_precision(const DBADDR *, long *precision);
get_graphic_double
long get_graphic_double(DBADDR *, struct dbr_grDouble *);
struct dbr_grDouble {
        epicsFloat64    upper_disp_limit;       /*upper limit of graph*/
        epicsFloat64    lower_disp_limit;       /*lower limit of graph*/
}
get_control_double
long get_control_double(DBADDR *, struct dbr_ctrlDouble *)
struct dbr_ctrlDouble {
        epicsFloat64    upper_ctrl_limit;       /*upper limit of graph*/
        epicsFloat64    lower_ctrl_limit;       /*lower limit of graph*/
}
get_alarm_double
long get_alarm_double(DBADDR *, struct dbr_alDouble *);
struct dbr_alDouble {
        epicsFloat64    upper_alarm_limit;
        epicsFloat64    upper_warning_limit;
        epicsFloat64    lower_warning_limit;
        epicsFloat64    lower_alarm_limit;
}
get_enum_str
long get_enum_str(const DBADDR *, char *choice);
get_enum_strs
long get_enum_strs(const DBADDR *, struct dbr_enumStrs *)
struct dbr_enumStrs {
	epicsUInt32	no_str;		/* number of strings*/
	epicsInt32	padenumStrs;	/*padding to force 8 byte align*/
	char		strs[DB_MAX_CHOICES][MAX_STRING_SIZE];	/* string values    */
}
#define MAX_STRING_SIZE 40
#define DB_MAX_CHOICES 30
put_enum_str
long put_enum_str(const DBADDR *, const char *choice);
get_array_info
long get_array_info(DBADDR *, long *no_elements, long *offset);
put_array_info
long put_array_info(DBADDR *, long nNew);

Overview of channel access

The basic functionality of channel access is defined in the header files db_access.h and cadef.h.

Data types

The primitive types supported are:

DBR_STRING    char[MAX_STRING_SIZE]   // MAX_STRING_SIZE=40
DBR_CHAR      epicsInt8
DBR_SHORT     epicsInt16
DBR_LONG      epicsInt32
DBR_FLOAT     epicsFloat32
DBR_DOUBLE    epicsFloat64
DBR_ENUM      epicsUInt16 // SEE BELOW FOR MORE DETAILS

Except for DBR_ENUM, an array of each type is also supported.

Notice that, other than DBR_ENUM, there is no concept of unsigned integer types. When a client connects to a channel, i.e recordname or recordname.fieldname, the client can get the type defined in the record. If the record holds an unsigned type then the type reported to the client is promoted to a wider type as follows:

BBR_CHAR  NO PROMOTION
DBR_SHORT => DBR_LONG
DBR_LONG  => DBR_DOUBLE

Note that since there is no promotion for DBR_CHAR the client can have strange behavior as follows:

mrk> caput DBRucharout -1
mrk> caget DBRucharout
DBRucharout                    -1
mrk> caget -d DBR_CHAR DBRucharout
DBRucharout
    Native data type: DBF_CHAR
    Request type:     DBR_CHAR
    Element count:    1
    Value:            -1
mrk> caget -d DBR_LONG DBRucharout
DBRucharout
    Native data type: DBF_CHAR
    Request type:     DBR_LONG
    Element count:    1
    Value:            255

Conversion between types

The client can ask for any type it wants and the server side will convert between requested and actual types. In particular if the client ask for DBR_STRING then most conversion will be what the client desires. But this can be quite expensive, especially for arrays.

If the client specifies a type that is not the native type than the server converts between the native type and the type the client requests. If the client requests a type that is narrower than the native type then overflow can happen. The client will not be notified when this happens. Also note the problem mentioned above about unsigned integer types.

limit conversions

Note that the record support methods for limits, e. g. get_graphic_double, get_control_double, and get_alarm_double only get limit values as double. This means that for other types conversion are made when a client makes a request that involves limits.

For example consider a longout record and the client issues a DBR_CNTL_LONG request. The limit values in the record are type epicsInt32. When the limit values are read from the record they are converted to double. But the client requested limit values as epicsInt32 so the values are converted from double to epicsInt32. This works but causes some cpu usage.

caget request type

caget is used in many of the examples below. It allows you to specify the request type:

mrk> caget -help
...
  -d <type>: Request specific dbr type; use string (DBR_ prefix may be omitted)
      or number of one of the following types:
 DBR_STRING     0  DBR_STS_FLOAT    9  DBR_TIME_LONG   19  DBR_CTRL_SHORT    29
 DBR_INT        1  DBR_STS_ENUM    10  DBR_TIME_DOUBLE 20  DBR_CTRL_INT      29
 DBR_SHORT      1  DBR_STS_CHAR    11  DBR_GR_STRING   21  DBR_CTRL_FLOAT    30
 DBR_FLOAT      2  DBR_STS_LONG    12  DBR_GR_SHORT    22  DBR_CTRL_ENUM     31
 DBR_ENUM       3  DBR_STS_DOUBLE  13  DBR_GR_INT      22  DBR_CTRL_CHAR     32
 DBR_CHAR       4  DBR_TIME_STRING 14  DBR_GR_FLOAT    23  DBR_CTRL_LONG     33
 DBR_LONG       5  DBR_TIME_INT    15  DBR_GR_ENUM     24  DBR_CTRL_DOUBLE   34
 DBR_DOUBLE     6  DBR_TIME_SHORT  15  DBR_GR_CHAR     25  DBR_STSACK_STRING 37
 DBR_STS_STRING 7  DBR_TIME_FLOAT  16  DBR_GR_LONG     26  DBR_CLASS_NAME    38
 DBR_STS_SHORT  8  DBR_TIME_ENUM   17  DBR_GR_DOUBLE   27
 DBR_STS_INT    8  DBR_TIME_CHAR   18  DBR_CTRL_STRING 28

Example conversions

Some examples are:

DBRecord has value of type DBF_DOUBLE

mrk> caput DBRdoubleout 1.33333
mrk> caget DBRdoubleout
DBRdoubleout                   1.33333
mrk> caget -d DBR_STRING DBRdoubleout
DBRdoubleout
    Native data type: DBF_DOUBLE
    Request type:     DBR_STRING
    Element count:    1
    Value:            1.33               // NOTE PREC=2
mrk> caget -d DBR_SHORT DBRdoubleout
DBRdoubleout
    Native data type: DBF_DOUBLE
    Request type:     DBR_SHORT
    Element count:    1
    Value:            1
mrk> caput DBRdoubleout 1e6
mrk> caget DBRdoubleout
DBRdoubleout                   1e+06
mrk> caget -d DBR_STRING DBRdoubleout
DBRdoubleout
    Native data type: DBF_DOUBLE
    Request type:     DBR_STRING
    Element count:    1
    Value:            1000000.00
mrk> caget -d DBR_SHORT DBRdoubleout
DBRdoubleout
    Native data type: DBF_DOUBLE
    Request type:     DBR_SHORT
    Element count:    1
    Value:            16960          // NOTE OVERFLOW
mrk> 

DBRecord has value of type DBF_USHORT

mrk> caput DBRushortout 65535
mrk> caget DBRushortout
DBRushortout                   65535
mrk> caget -d DBR_STRING DBRushortout
DBRushortout
    Native data type: DBF_LONG        // NOTE PROMOTION TO WIDER TYPE
    Request type:     DBR_STRING
    Element count:    1
    Value:            65535
mrk> caget -d DBR_SHORT DBRushortout
DBRushortout
    Native data type: DBF_LONG
    Request type:     DBR_SHORT
    Element count:    1 
    Value:            -1              // NOTE CONVERSION TO SIGNED
mrk> caget -d DBR_DOUBLE DBRushortout
DBRushortout
    Native data type: DBF_LONG
    Request type:     DBR_DOUBLE
    Element count:    1
    Value:            65535

Workaround for MAX_STRING_SIZE

A way to pass strings longer than MAX_STRING_SIZE is to create a record with a value field that is an array of type DBF_UCHAR. The client must know that this is why the record exists and must convert the array of bytes to a string.

For example:

mrk> caput -a -S DBRbyteArray test
mrk> caget -S DBRbyteArray
DBRbyteArray test
mrk> 

DBR_ENUM

This is how channel access provides access to record types like bi, bo, mbbbi,and mbbo. The value field is just an epicsUInt16.

The value is the index of a set of choices. The choices can be obtained as follows:

mrk> caget -d DBR_GR_ENUM DBRmbbout
DBRmbbout
    Native data type: DBF_ENUM
    Request type:     DBR_GR_ENUM
    Element count:    1
    Value:            one
    Status:           UDF
    Severity:         NO_ALARM
    Enums:            (16)
                      [ 0] zero
                      [ 1] one
                      [ 2] two
                      [ 3] three
                      [ 4] four
                      [ 5] five
                      [ 6] six
                      [ 7] seven
                      [ 8] eight
                      [ 9] nine
                      [10] ten
                      [11] eleven
                      [12] twelve
                      [13] thirteen
                      [14] fourteen
                      [15] fifteen

DBR_STS

This is the prefix to obtain the status and severity in addition to a value. The complete set of types are DBR_STS_STRING, DBR_STS_STRING, DBR_STS_CHAR, DBR_STS_SHORT, DBR_STS_LONG, DBR_STS_FLOAT,DBR_STS_DOUBLE and DBR_STS_ENUM.

Some examples are:

mrk> caget -d DBR_STS_ENUM DBRmbbout
DBRmbbout
    Native data type: DBF_ENUM
    Request type:     DBR_STS_ENUM
    Element count:    1
    Value:            2
    Status:           NO_ALARM
    Severity:         NO_ALARM
mrk> caget -d DBR_STS_STRING DBRmbbout
DBRmbbout
    Native data type: DBF_ENUM
    Request type:     DBR_STS_STRING
    Element count:    1
    Value:            two
    Status:           NO_ALARM
    Severity:         NO_ALARM
mrk> caget -d DBR_STS_DOUBLE DBRdoubleout
DBRdoubleout
    Native data type: DBF_DOUBLE
    Request type:     DBR_STS_DOUBLE
    Element count:    1
    Value:            1.5
    Status:           NO_ALARM
    Severity:         NO_ALARM

DBR_TIME

This is similar to DBR_STS except that the time is also returned.

Some examples are:

mrk> caget -d DBR_TIME_ENUM DBRmbbout
DBRmbbout
    Native data type: DBF_ENUM
    Request type:     DBR_TIME_ENUM
    Element count:    1
    Value:            2
    Timestamp:        2018-04-17 13:43:38.936139
    Status:           NO_ALARM
    Severity:         NO_ALARM
mrk> caget -d DBR_TIME_STRING DBRmbbout
DBRmbbout
    Native data type: DBF_ENUM
    Request type:     DBR_TIME_STRING
    Element count:    1
    Value:            two
    Timestamp:        2018-04-17 13:43:38.936139
    Status:           NO_ALARM
    Severity:         NO_ALARM
mrk> caget -d DBR_TIME_DOUBLE DBRdoubleout
DBRdoubleout
    Native data type: DBF_DOUBLE
    Request type:     DBR_TIME_DOUBLE
    Element count:    1
    Value:            1.5
    Timestamp:        2018-04-17 13:43:27.019340
    Status:           NO_ALARM
    Severity:         NO_ALARM

DBR_GR

This returns value, status, severity, units, precision, display limits, and alarm limits. Note that it is similar to DBR_CTRL except that it does not return control limits,

DBR_CTRL

This returns value, status, severity, units, precision, display limits, alarm limits, and control limits.
Note that epicsTimeStamp is not returned.

Some examples are:

mrk> caget -d DBR_CTRL_DOUBLE DBRdoubleout
DBRdoubleout
    Native data type: DBF_DOUBLE
    Request type:     DBR_CTRL_DOUBLE
    Element count:    1
    Value:            1.5
    Status:           NO_ALARM
    Severity:         NO_ALARM
    Units:            volts
    Precision:        2
    Lo disp limit:    -10
    Hi disp limit:    10
    Lo alarm limit:   -8
    Lo warn limit:    -6
    Hi warn limit:    6
    Hi alarm limit:   8
    Lo ctrl limit:    -1e+29
    Hi ctrl limit:    1e+29
mrk> caget -d DBR_CTRL_LONG DBRlongout
DBRlongout
    Native data type: DBF_LONG
    Request type:     DBR_CTRL_LONG
    Element count:    1
    Value:            100
    Status:           HIHI
    Severity:         MAJOR
    Units:            volts
    Lo disp limit:    -2147483648
    Hi disp limit:    2147483647
    Lo alarm limit:        -80
    Lo warn limit:         -60
    Hi warn limit:          60
    Hi alarm limit:         80
    Lo ctrl limit:    -2147483648
    Hi ctrl limit:    2147483647
mrk> 

DBR_CHAR behavior

As mentioned above the database has field types DBF_CHAR and DBF_UCHAR but channel access only has DBR_CHAR. In addition DBF_UCHAR is not promoted to DBR_SHORT.

This leads to strange behavior as the following examples show.

For channel access

mrk> caget -d DBR_GR_CHAR DBRcharout
DBRcharout
    Native data type: DBF_CHAR
    Request type:     DBR_GR_CHAR
    Element count:    1
    Value:            -127
    Status:           NO_ALARM
    Severity:         NO_ALARM
    Units:            
    Lo disp limit:        -128
    Hi disp limit:         127
    Lo alarm limit:       -100
    Lo warn limit:         -80
    Hi warn limit:          80
    Hi alarm limit:        100
mrk> caget -d DBR_GR_CHAR DBRucharout
DBRucharout
    Native data type: DBF_CHAR
    Request type:     DBR_GR_CHAR
    Element count:    -127        //NOT CORRECT
    Value:            0           //NOT CORRECT
    Status:           NO_ALARM
    Severity:         NO_ALARM
    Units:            
    Lo disp limit:           0
    Hi disp limit:          -1 //NOT CORRECT
    Lo alarm limit:         20
    Lo warn limit:          50
    Hi warn limit:        -106 //NOT CORRECT
    Hi alarm limit:        -56 //NOT CORRECT
mrk> caget -d DBR_GR_DOUBLE DBRcharout
DBRcharout
    Native data type: DBF_CHAR
    Request type:     DBR_GR_DOUBLE
    Element count:    1
    Value:            -127
    Status:           NO_ALARM
    Severity:         NO_ALARM
    Units:            
    Precision:        0
    Lo disp limit:    -128
    Hi disp limit:    127
    Lo alarm limit:   -100
    Lo warn limit:    -80
    Hi warn limit:    80
    Hi alarm limit:   100
mrk> caget -d DBR_GR_DOUBLE DBRucharout
DBRucharout
    Native data type: DBF_CHAR
    Request type:     DBR_GR_DOUBLE
    Element count:    1
    Value:            129
    Status:           NO_ALARM
    Severity:         NO_ALARM
    Units:            
    Precision:        0
    Lo disp limit:    0
    Hi disp limit:    255
    Lo alarm limit:   20
    Lo warn limit:    50
    Hi warn limit:    150
    Hi alarm limit:   200

If the client specifies DBR_GR_DOUBLE the client always gets correct values for limits and value. If the client specifies DBR_GR_CHAR the client does not always get correct values for limits and value.

pvData for DBRecords

value

The mapping from the DBF types for the value field of DBRecord to pvData is:

DBF_STRING  pvString
DBF_CHAR    pvByte
DBF_SHORT   pvShort
DBF_UCHAR   pvUByte
DBF_USHORT  pvUShort
DBF_LONG    pvInt
DBF_ULONG   pvUInt
DBF_INT64   pvLong
DBF_UINT64  pvULong
DBF_FLOAT   pvFloat
DBF_DOUBLE  pvDouble
DBF_ENUM    SEE BELOW
Except for enum each of these is either type scalar or scalarArray.

pvDataCPP defines property structures for enum,alarm,timeStamp,display,control, and valueAlarm.

enum

This is a structure for representing DBF_ENUM DBRecord fields.

An example is:

mrk> pvget -i -p ca -r "value" DBRmbbout
DBRmbbout
epics:nt/NTEnum:1.0 
    enum_t value
        int index 2
        string[] choices [zero,one,two,three,four,five,six,
            seven,eight,nine,ten,eleven,twelve,thirteen,fourteen,fifteen]

alarm

A structure with fields for status, severity, message, and userTag.

An example is:

mrk> pvget -i -p ca -r "alarm" DBRdoubleout
DBRdoubleout
structure 
    alarm_t alarm
        int severity 2
        int status 3
        string message HIHI

pvDataCPP provides alarm.h and pvAlarm.h to make it easier to work with alarms.

timeStamp

A structure with fields for a timeStamp and userTag.

An example is:

mrk> pvget -i -p ca -r "timeStamp" DBRdoubleout
DBRdoubleout
structure 
    time_t timeStamp
        long secondsPastEpoch 1523989803
        int nanoseconds 829527650
        int userTag 0

Note that secondsPastEpoch is a 64 bit integer and the base year is the posix base year which is Jan 1, 1970 UTC.

qsrv and ca both convert the DBRecord time stamp so that the client has the correct posix base.
Note also that, since secondsPastEpoch is a 64 bit integer, it will not overflow for a number of years greater than the age of our universe.

pvDataCPP provides timeStamp.h and pvTimeStamp.h to make it easier to work with timeStamps.

display

A structure of the form:

mrk> pvget -i -p ca -r "display" DBRdoubleout
DBRdoubleout
structure 
    display_t display
        double limitLow -10
        double limitHigh 10
        string description 
        string format F8.2
        string units volts

pvDataCPP provides display.h, which makes it easier to work with display.

control

A structure of the form:

mrk> pvget -i -p ca -r "control" DBRdoubleout
DBRdoubleout
structure 
    control_t control
        double limitLow -1e+29
        double limitHigh 1e+29
        double minStep 0           //NOTE that channel access does not define this

pvDataCPP provides control.h, which makes it easier to work with control.

valueAlarm

A structure of the form:

mrk> pvget -i -p ca -r "valueAlarm" DBRdoubleout
DBRdoubleout
structure 
    valueAlarm_t valueAlarm
        boolean active false
        double lowAlarmLimit -8
        double lowWarningLimit -6
        double highWarningLimit 6
        double highAlarmLimit 8
        int lowAlarmSeverity 0    //NOTE that channel access does not define this
        int lowWarningSeverity 0  //NOTE that channel access does not define this 
        int highWarningSeverity 0 //NOTE that channel access does not define this
        int highAlarmSeverity 0   //NOTE that channel access does not define this
        double hysteresis 0

DBRecord Providers qsrv and ca

qsrv is a server side channel provider that directly accesses the DBRecord database, i. e. it does NOT use channel access.

ca is a client side channel provider. It uses the channel access network protocol to communicate with an IOC. Thus no pvData related code needs to be in the IOC. It converts between pvData and the data used by channel access.

A major difference between both qsrv/ca and pure channel access is that all conversion between datatypes is done by the client instead of by the server. For example there is no equivalent to:

mrk> caget -d DBR_STRING DBRdoubleout
DBRdoubleout
    Native data type: DBF_DOUBLE
    Request type:     DBR_STRING
    Element count:    1
    Value:            100.00

With qsrv and ca the following happens:

mrk> pvget -i -p pva -r "value" DBRdoubleout
DBRdoubleout
epics:nt/NTScalar:1.0 
    double value 100
mrk> pvget -i -p ca -r "value" DBRdoubleout
DBRdoubleout
structure 
    double value 100

qsrv

Since qsrv interfaces directly to the DBRecord database, it supports all DBF types including unsigned integers.

ca

Since ca uses channel access to communicated with the IOC DBRecords, it is subject to the same restrictions as channel access itself. In particular it does not support 64 bit integers. It also has behavior similar to channel access for unsigned integers,

Examples

A signed 32 bit integer value with alarm and timeStamp

mrk> caget -d DBR_TIME_STRING DBRlongout
DBRlongout
    Native data type: DBF_LONG
    Request type:     DBR_TIME_STRING
    Element count:    1
    Value:            100
    Timestamp:        2018-04-17 13:53:55.932364
    Status:           HIHI
    Severity:         MAJOR
mrk> caget -d DBR_TIME_DOUBLE DBRlongout
DBRlongout
    Native data type: DBF_LONG
    Request type:     DBR_TIME_DOUBLE
    Element count:    1
    Value:            100
    Timestamp:        2018-04-17 13:53:55.932364
    Status:           HIHI
    Severity:         MAJOR
mrk> pvget -p pva -r "value,alarm,timeStamp" DBRlongout
DBRlongout
epics:nt/NTScalar:1.0 
    int value 100
    alarm_t alarm MAJOR DEVICE HIHI
    time_t timeStamp 2018-04-17T13:53:55.932 0 // NOTE trailing 0 is userTag
    display_t display                    // NOTE THAT THIS WAS NOT REQUESTED
        double limitLow -2.14748e+09
        double limitHigh 2.14748e+09
        string description longout
        string format 
        string units volts
    control_t control                    // NOTE THAT THIS WAS NOT REQUESTED
        double limitLow -2.14748e+09
        double limitHigh 2.14748e+09
        double minStep 0
    valueAlarm_t valueAlarm              // NOTE THAT THIS WAS NOT REQUESTED
        boolean active false
        int lowAlarmLimit -80
        int lowWarningLimit -60
        int highWarningLimit 60
        int highAlarmLimit 80
        int lowAlarmSeverity 0
        int lowWarningSeverity 0
        int highWarningSeverity 0
        int highAlarmSeverity 0
        int hysteresis 0
mrk> pvget -p ca -r "value,alarm,timeStamp" DBRlongout
DBRlongout
structure 
    int value 100
    alarm_t alarm MAJOR RECORD HIHI
    time_t timeStamp 2018-04-17T13:53:55.932 0 // NOTE trailing 0 is userTag

An unsigned 32 bit integer value with alarm and timeStamp

mrk> caget -d DBR_TIME_STRING DBRulongout
DBRulongout
    Native data type: DBF_DOUBLE
    Request type:     DBR_TIME_STRING
    Element count:    1
    Value:            10
    Timestamp:        2018-04-18 06:21:31.829911
    Status:           LOLO
    Severity:         MAJOR
mrk> caget -d DBR_TIME_DOUBLE DBRulongout
DBRulongout
    Native data type: DBF_DOUBLE
    Request type:     DBR_TIME_DOUBLE
    Element count:    1
    Value:            10
    Timestamp:        2018-04-18 06:21:31.829911
    Status:           LOLO
    Severity:         MAJOR
mrk> pvget -p pva -r "value,alarm,timeStamp" DBRulongout
DBRulongout
epics:nt/NTScalar:1.0 
    uint value 10
    alarm_t alarm MAJOR DEVICE LOLO
    time_t timeStamp 2018-04-18T06:21:31.830 0
    display_t display                    // NOTE THAT THIS WAS NOT REQUESTED
        double limitLow 0
        double limitHigh 4.29497e+09
        string description ulongout
        string format 
        string units 
    control_t control                    // NOTE THAT THIS WAS NOT REQUESTED
        double limitLow 0
        double limitHigh 4.29497e+09
        double minStep 0
    valueAlarm_t valueAlarm              // NOTE THAT THIS WAS NOT REQUESTED
        boolean active false
        uint lowAlarmLimit 20
        uint lowWarningLimit 40
        uint highWarningLimit 60
        uint highAlarmLimit 80
        int lowAlarmSeverity 0
        int lowWarningSeverity 0
        int highWarningSeverity 0
        int highAlarmSeverity 0
        uint hysteresis 0
mrk> pvget -p ca -r "value,alarm,timeStamp" DBRulongout
DBRulongout
structure 
    double value 10
    alarm_t alarm MAJOR CONF LOLO
    time_t timeStamp 2018-04-18T06:21:31.830 0

A double value with alarm and timeStamp.

mrk> caget -d DBR_TIME_STRING DBRdoubleout
DBRdoubleout
    Native data type: DBF_DOUBLE
    Request type:     DBR_TIME_STRING
    Element count:    1
    Value:            90.00
    Timestamp:        2018-04-18 06:25:04.045084
    Status:           HIHI
    Severity:         MAJOR
mrk> caget -d DBR_TIME_DOUBLE DBRdoubleout
DBRdoubleout
    Native data type: DBF_DOUBLE
    Request type:     DBR_TIME_DOUBLE
    Element count:    1
    Value:            90
    Timestamp:        2018-04-18 06:25:04.045084
    Status:           HIHI
    Severity:         MAJOR
mrk> pvget -p pva -r "value,alarm,timeStamp" DBRdoubleout
DBRdoubleout
epics:nt/NTScalar:1.0 
    double value 90
    alarm_t alarm MAJOR DEVICE HIHI
    time_t timeStamp 2018-04-18T06:25:04.045 0
    display_t display                    // NOTE THAT THIS WAS NOT REQUESTED
        double limitLow -10
        double limitHigh 10
        string description ao
        string format 
        string units volts
    control_t control                    // NOTE THAT THIS WAS NOT REQUESTED
        double limitLow -1e+29
        double limitHigh 1e+29
        double minStep 0
    valueAlarm_t valueAlarm              // NOTE THAT THIS WAS NOT REQUESTED
        boolean active false
        double lowAlarmLimit -8
        double lowWarningLimit -6
        double highWarningLimit 6
        double highAlarmLimit 8
        int lowAlarmSeverity 0
        int lowWarningSeverity 0
        int highWarningSeverity 0
        int highAlarmSeverity 0
        double hysteresis 0
mrk> pvget -p ca -r "value,alarm,timeStamp" DBRdoubleout
DBRdoubleout
structure 
    double value 90
    alarm_t alarm MAJOR RECORD HIHI
    time_t timeStamp 2018-04-18T06:25:04.045 0

An array of doubles with alarm and timeStamp

mrk> caget -d DBR_TIME_STRING DBRdoubleArray
DBRdoubleArray
    Native data type: DBF_DOUBLE
    Request type:     DBR_TIME_STRING
    Element count:    5
    Value:            1.11 2.22 3.33 4.44 5.56
    Timestamp:        2018-04-18 06:28:26.644374
    Status:           NO_ALARM
    Severity:         NO_ALARM
mrk> caget -d DBR_TIME_DOUBLE DBRdoubleArray
DBRdoubleArray
    Native data type: DBF_DOUBLE
    Request type:     DBR_TIME_DOUBLE
    Element count:    5
    Value:            1.11111 2.22222 3.33333 4.44444 5.55555
    Timestamp:        2018-04-18 06:28:26.644374
    Status:           NO_ALARM
    Severity:         NO_ALARM
mrk> pvget -p pva -r "value,alarm,timeStamp" DBRdoubleArray
DBRdoubleArray
epics:nt/NTScalarArray:1.0 
    double[] value [1.11111,2.22222,3.33333,4.44444,5.55555]
    alarm_t alarm NO_ALARM NO_STATUS NO_ALARM
    time_t timeStamp 2018-04-18T06:28:26.644 0
    display_t display                    // NOTE THAT THIS WAS NOT REQUESTED
        double limitLow -1000
        double limitHigh 1000
        string description doubleArray
        string format 
        string units 
    control_t control                    // NOTE THAT THIS WAS NOT REQUESTED
        double limitLow -1000
        double limitHigh 1000
        double minStep 0
    valueAlarm_t valueAlarm               // NOTE THAT THIS WAS NOT REQUESTED
        boolean active false
        double lowAlarmLimit nan
        double lowWarningLimit nan
        double highWarningLimit nan
        double highAlarmLimit nan
        int lowAlarmSeverity 0
        int lowWarningSeverity 0
        int highWarningSeverity 0
        int highAlarmSeverity 0
        double hysteresis 0
mrk> pvget -p ca -r "value,alarm,timeStamp" DBRdoubleArray
DBRdoubleArray
structure 
    double[] value [1.11111,2.22222,3.33333,4.44444,5.55555]
    alarm_t alarm NO_ALARM NO_STATUS <no message>
    time_t timeStamp 2018-04-18T06:28:26.644 0

An enumerated value with alarm and timeStamp

mrk> caget -d DBR_TIME_STRING DBRbinaryout
DBRbinaryout
    Native data type: DBF_ENUM
    Request type:     DBR_TIME_STRING
    Element count:    1
    Value:            one
    Timestamp:        2018-04-18 06:31:47.470022
    Status:           LINK
    Severity:         INVALID
mrk> caget -d DBR_TIME_ENUM DBRbinaryout
DBRbinaryout
    Native data type: DBF_ENUM
    Request type:     DBR_TIME_ENUM
    Element count:    1
    Value:            1
    Timestamp:        2018-04-18 06:31:47.470022
    Status:           LINK
    Severity:         INVALID
mrk> pvget -p pva -r "value,alarm,timeStamp" DBRbinaryout
DBRbinaryout
epics:nt/NTEnum:1.0 
    enum_t value one
    alarm_t alarm INVALID RECORD LINK
    time_t timeStamp 2018-04-18T06:31:47.470 0
mrk> pvget -p ca -r "value,alarm,timeStamp" DBRbinaryout
DBRbinaryout
epics:nt/NTEnum:1.0 
    enum_t value one
    alarm_t alarm INVALID 14 LINK
    time_t timeStamp 2018-04-18T06:31:47.470 0
mrk> exampleClient/get -p ca -r "value,alarm,timeStamp" DBRbinaryout
...
Type exit to stop: 
// enter key pressed
channelGetDone DBRbinaryout status Status [type=OK]
changed DBRbinaryout
 = epics:nt/NTEnum:1.0 
    enum_t value
        int index 1
        string[] choices [zero,one]
    alarm_t alarm
        int severity 3
        int status 14
        string message LINK
    time_t timeStamp
        long secondsPastEpoch 1524047507
        int nanoseconds 470022134
        int userTag 0

bitSet {0}
Type exit to stop: 
exit

display,control,valueAlarm

mrk> caget -d DBR_CTRL_STRING  DBRdoubleout
DBRdoubleout
    Native data type: DBF_DOUBLE
    Request type:     DBR_CTRL_STRING
    Element count:    1
    Value:            0.00
    Status:           UDF
    Severity:         INVALID
mrk> caget -d DBR_CTRL_DOUBLE  DBRdoubleout
DBRdoubleout
    Native data type: DBF_DOUBLE
    Request type:     DBR_CTRL_DOUBLE
    Element count:    1
    Value:            0
    Status:           UDF
    Severity:         INVALID
    Units:            volts
    Precision:        2
    Lo disp limit:    -10
    Hi disp limit:    10
    Lo alarm limit:   -8
    Lo warn limit:    -6
    Hi warn limit:    6
    Hi alarm limit:   8
    Lo ctrl limit:    -1e+29
    Hi ctrl limit:    1e+29
mrk> pvget -p pva -r "display,control,valueAlarm" DBRdoubleout
DBRdoubleout
epics:nt/NTScalar:1.0 
    double value 0                   // NOT REQUESTED
    alarm_t alarm INVALID DRIVER UDF // NOT REQUESTED
    time_t timeStamp <undefined> 0   // NOT REQUESTED
    display_t display
        double limitLow -10
        double limitHigh 10
        string description ao
        string format 
        string units volts
    control_t control
        double limitLow -1e+29
        double limitHigh 1e+29
        double minStep 0
    valueAlarm_t valueAlarm
        boolean active false
        double lowAlarmLimit -8
        double lowWarningLimit -6
        double highWarningLimit 6
        double highAlarmLimit 8
        int lowAlarmSeverity 0
        int lowWarningSeverity 0
        int highWarningSeverity 0
        int highAlarmSeverity 0
        double hysteresis 0
mrk> pvget -p ca -r "display,control,valueAlarm" DBRdoubleout
DBRdoubleout
structure 
    display_t display
        double limitLow -10
        double limitHigh 10
        string description 
        string format F8.2
        string units volts
    control_t control
        double limitLow -1e+29
        double limitHigh 1e+29
        double minStep 0
    valueAlarm_t valueAlarm
        boolean active false
        double lowAlarmLimit -8
        double lowWarningLimit -6
        double highWarningLimit 6
        double highAlarmLimit 8
        int lowAlarmSeverity 0
        int lowWarningSeverity 0
        int highWarningSeverity 0
        int highAlarmSeverity 0
        double hysteresis 0

Monitor double value with alarm and timeStamp using provider pva

mrk> pvget -i -p pva -m -r "value,alarm,timeStamp" DBRdoubleout
DBRdoubleout
epics:nt/NTScalar:1.0 
    double value 0
    alarm_t alarm
        int severity 0
        int status 0
        string message NO_ALARM
    time_t timeStamp
        long secondsPastEpoch 1524049231
        int nanoseconds 383230407
        int userTag 0
    display_t display            // NOT REQUESTED
        double limitLow -10
        double limitHigh 10
        string description ao
        string format 
        string units volts
    control_t control            // NOT REQUESTED
        double limitLow -1e+29
        double limitHigh 1e+29
        double minStep 0
    valueAlarm_t valueAlarm      // NOT REQUESTED
        boolean active false
        double lowAlarmLimit -8
        double lowWarningLimit -6
        double highWarningLimit 6
        double highAlarmLimit 8
        int lowAlarmSeverity 0
        int lowWarningSeverity 0
        int highWarningSeverity 0
        int highAlarmSeverity 0
        double hysteresis 0
// NOTE A PUT WAS ISSUED
DBRdoubleout
epics:nt/NTScalar:1.0 
    double value 1
    alarm_t alarm          // DID NOT CHANGE
        int severity 0
        int status 0
        string message NO_ALARM
    time_t timeStamp
        long secondsPastEpoch 1524049280
        int nanoseconds 90405898
        int userTag 0
    display_t display    // DID NOT CHANGE AND NOT REQUESTED
        double limitLow -10
        double limitHigh 10
        string description ao
        string format 
        string units volts
    control_t control    // DID NOT CHANGE AND NOT REQUESTED
        double limitLow -1e+29
        double limitHigh 1e+29
        double minStep 0
    valueAlarm_t valueAlarm  // DID NOT CHANGE AND NOT REQUESTED
        boolean active false
        double lowAlarmLimit -8
        double lowWarningLimit -6
        double highWarningLimit 6
        double highAlarmLimit 8
        int lowAlarmSeverity 0
        int lowWarningSeverity 0
        int highWarningSeverity 0
        int highAlarmSeverity 0
        double hysteresis 0

Monitor double value with alarm and timeStamp using provider ca

With pvget

mrk> pvget -m -p ca -r "value,alarm,timeStamp" -i DBRdoubleout
DBRdoubleout
structure 
    double value 0
    alarm_t alarm
        int severity 0
        int status 0
        string message 
    time_t timeStamp
        long secondsPastEpoch 1524049508
        int nanoseconds 416616659
        int userTag 0
// NOTE THAT WHEN A PUT CAUSES AN EVENT pvput still displays all fields.
DBRdoubleout
structure 
    double value 1
    alarm_t alarm
        int severity 0
        int status 0
        string message 
    time_t timeStamp
        long secondsPastEpoch 1524049828
        int nanoseconds 542712998
        int userTag 0

Now with exampleClient

mrk> exampleClient/monitor -p ca -r "value,alarm,timeStamp" DBRdoubleout
event DBRdoubleout
monitor 
changed
 = structure 
    double value 1
    alarm_t alarm
        int severity 0
        int status 0
        string message 
    time_t timeStamp
        long secondsPastEpoch 1524049423
        int nanoseconds 322158828
        int userTag 0
overrun
// NOTE THAT WHEN PUT IS ISSUED ONLY CHANGED FIELDS ARE DISPLAYED
event DBRdoubleout
monitor 
changed
value = 0
timeStamp.secondsPastEpoch = 1524049508
timeStamp.nanoseconds = 416616659
overrun