ca provider

Working Draft, 20-November-2018

editors:
Marty Kraimer

This product is available via an open source license

Status

The ca provider is available as part of: pvAccessCPP

The current status is that, as far as I know, everything described in this document is implemented.

Table of Contents

Introduction

Overview

Since the very beginning of EPICS, channel access, both network protocol and API, provided client access to DBRecords. With EPICS V4 two new methods are available:

ca
A channel provider that uses the channel access network protocol but uses the pvAccess API,
qsrv
A server side channel provider that uses dbAccess to access DBRecords. The pva network protocol is used for communication.

Using V4 terminology, data in a PVRecord consists of some combination of:

value
alarm
timeStamp
display
control
valueAlarm

Different types of clients want different combinations of the above.

This document describes channel provider ca, which is implemented in pvAccessCPP. It uses the channel access network protocol to communicate with a server, i. e. the network protocol that has been used since 1990 to communicate with EPICS IOCs. But instead of the channel access API, the client uses the pvAccess API.

The following discussion only refers to the C++ implementation. However any changes in ca provider should also be make in the Java implementation.

pva is another way to connect to a DBRecord, but this only works if the IOC has qsrv installed. qsrv, which is provided with pva2pva , has full support for communicating with a DBRecord.

ca has the advantage that it does require any changes to an existing IOC.

Overview of Channel Access

channel access, which is provided with epics-base, defines and implements a protocol for client/server network communication.

epics-base provides both a client and a server implementation This document only discusses the client API.
For details see: EPICS Channel Access 4.13.1 Reference Manual

channel access allows a client to get, put, and monitor monitor data from a server. The data is defined by various DBD types.

The following, in epics-base/include, are the main include files that show the channel access API:

cadef.h
db_access.h

The client requests data via one of the DBR types. For example

DBR_STS_DOUBLE	returns a double status structure (dbr_sts_double)
where
struct dbr_sts_double{
	dbr_short_t	status;	 		/* status of value */
	dbr_short_t	severity;		/* severity of alarm */
	dbr_long_t	RISC_pad;		/* RISC alignment */
	dbr_double_t	value;			/* current value */
};

The server converts data between the native type of the field being accessed and the DBR type.

Overview of ca provider

ca is a pvAccess Channel Provider that uses channel access to connect a client to a server.

With ca, the client data appears as pvData objects, i. e. ca converts the data provided by channel access to/from pvData

Thus a pvAccess client can communicate with an existing V3 EPICS IOC without making any changes to existing IOCs.

For an overview of pvData and pvAccess see: EPICS V4 Developer's Guide

ca requests data from the server with a DBR type that matches the native type. See the next section for more details.

All conversion to/from other types must be done by the client.

Client API

Brief Description

The primary goal of provider ca is to allow a client to use the pvAccess API instead of the channel access API.

The channel access API allows the client to pick any DBR type it wants and if the catype, which is the type for the DBRecord, does not match the DBR type the server converts between the types.

With ca the client uses pvData types that match the catype. Thus all conversions will be done on the client side.

Other than having the server perform conversion, ca attempts to let the client do everthing the client can now do with the channel access API.

ca implements the following pvAccess methods : getField, channelGet, channelPut and monitor.

For channelPut the only field that can be accessed is value. A client can issue puts with and without a callback from the server. The default is no callback. If createChannelPut has the option record[block=true] then a put callback used.

All of the other pvAccess methods provide access to fields value, alarm and timeStamp.

Depending on the type associated with the value field the following fields may also be available: display, control , and valueAlarm.

Thus a client can make requests like:

pvget -p ca -r "value,alarm,timeStamp,display,control,valueAlarm" names ...

ca will create a structure that has the fields requested but only for fields that are supported by the server.

Lets discuss the various fields.

value

This can be a scalar, scalarArray, or an enumerated structure.

For a scalar or scalarArray the ScalarType is one of the following: pvString, pvByte, pvShort, pvInt, pvFloat, or pvDouble.

Note that channel access does not support unsigned integers or 64 bit integers. But field request options, described in the next section, allow a client to receive data as pvUByte, pvUShort, pvUInt, pvLong, or pvULong.

A enumerated structure is created if the native type is DBR_ENUM.

Some examples are:

pvget -p ca -r value -i DBRlongout
DBRlongout
structure 
    int value 0
mrk> pvget -p ca -r value -i DBRdoubleout
DBRdoubleout
structure 
    double value 0
mrk> pvget -p ca -r value -i DBRshortArray
DBRshortArray
structure 
    short[] value []
mrk> pvget -p ca -r value -i DBRstringArray
DBRstringArray
structure 
    string[] value [aa,bb,cc]
mrk> pvget -p ca -r value -i DBRmbbo01
DBRmbbo01
epics:nt/NTEnum:1.0 
    enum_t value
        int index 1
        string[] choices [zero,one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve,thirteen,fourteen,fifteen]
mrk> 

alarm,timeStamp,display,control, and valueAlarm

Each of these is one of the property structures defined in pvData.

Examples

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

mrk> pvget -p ca -r timeStamp -i DBRdoubleout
DBRdoubleout
structure 
    time_t timeStamp
        long secondsPastEpoch 1529923341
        int nanoseconds 314916189
        int userTag 0
mrk> pvget -p ca -r display -i DBRdoubleout
DBRdoubleout
structure 
    display_t display
        double limitLow -10
        double limitHigh 10
        string description 
        string format F8.2
        string units volts
mrk> pvget -p ca -r control -i DBRdoubleout
DBRdoubleout
structure 
    control_t control
        double limitLow -1e+29
        double limitHigh 1e+29
        double minStep 0
mrk> pvget -p ca -r valueAlarm -i DBRdoubleout
DBRdoubleout
structure 
    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

pvRequest

Each create method of class Channel, in pvAccess, has an argument PVStructure pvRequest, i. e. it is an argument for createChannelGet, createChannelPut, etc.

pvDataCPP/include/pv/createRequest.h provides a method:

PVStructurePtr createRequest(string const & request);
Thus given a request string it creates a pvRequest structure. In examples, this document uses the request string. It is assumed that client code calls the above method to create a pvRequest structure.

A pvRequest structure allows a client to select:

an arbitrary set of fields.
When a client connects to channel, it receives a Structure that describes the PVStructure the channel supports. From the Structure, or just by anticipating what the server supports, the client can select the fields it wants to access.
global and field specific options
The client can pass global and field specific options to the server.
An option is just a name=value pair, where both name and value are a string.

The following provides a fuller description pvRequest

For ca a valid request string for creating a pvRequest is one of the following:

field,...
Field is any of value, alarm, timeStamp, display. control, and valueAlarm.
value[options],field,...
This allows options for the value field.
record[options]field(value[options],fieldname,...)
This allows record and value options.

record options

At present the following record options are supported:

queueSize

This is used to define the queueSize for monitors. The default is:

record[queueSize=2]

A larger size can be specified.

block

This is used to specify if a process request should block until the record completes processing. This is only valid for channelPut.

record[block=true]

If not specified the default is false.

monitor event mask

An event mask can be specified for createMonitor.

record[DBE=value]
value can be any combination of DBE_VALUE|DBE_ALARM|DBE_ARCHIVE|DBE_PROPERTY

field options

unsigned integer support

channel access has no support for unsigned integers. The following allows a client to ask that ca ask convert from signed to unsigned.

DBF_UCHAR
field(value[dbtype=DBF_UCHAR])
Only valid for DBR_CHAR.
DBF_USHORT
field(value[dbtype=DBF_USHORT])
Only valid for DBR_LONG.
DBF_ULONG
field(value[dbtype=DBF_ULONG])
Only valid for DBR_DOUBLE.
DBF_INT64
field(value[dbtype=DBF_INT64])
Only valid for DBR_DOUBLE.
DBF_UINT64
field(value[dbtype=DBF_UINT64])
Only valid for DBR_DOUBLE.

Support for waveform, lsi and lso record types

These can be used to access strings longer than MAX_STRING_SIZE.

field(value[pvtype=pvString])
NOTES:

Developing applications that use ca

This section provides guidelines for code developers that use ca to connect a client to a server.

This includes plugins for things like MEDM, EDM, caqtDM, etc. But also means any code that use ca: pvget, pvput, pvaClientCPP, exampleCPP/exampleClient, etc.

Guidelines for using ca

display,control,valueAlarm

If any combination of display, control, and valueAlarm are monitored then it is suggested that two monitors are created. One has a request:

record[DBE=DBE_PROPERTY]field(display,control,valueAlarm)
The other has a request:
value,alarm,timeStamp

The above two examples show the complete set of fields that can be requested. An application can uses a subset of the fields shown.

alarm handler, archiver, etc

An alarm handler might make create a monitor with a request like:

record[DBE=DBE_ALARM]field(alarm)

An archiver might make create a monitor with a request like:

record[DBE=DBE_ARCHIVE]field(value,alarm,timeStamp)

Sample Code

exampleCPP Has example client and server code for using pvData and pvAccess.

In particular look at: exampleClient for example client code.

The examples all use pvaClientCPP It has examples:

get.cpp
An example that uses channelGet.
put.cpp
An example that uses channelPut.
monitor.cpp
An example that creates a monitor.

Each example has a create method that accepts the following arguments:

channelName
The name of the channel to access.
provider
The name of the provider. EPICS V4 has support for providers ca and pva.
request
A request string to pass to createRequest.

The above examples work with any channel provider. The rest of this section is for clients using provider ca.

CAClientFactory::start

NOTE: If pvaClientCPP is used then it automatically calls CAClientFactory::start.

The channel access reference manual describes channel context: CA Client Contexts and Application Specific Auxiliary Threads

A brief summary of channel context is that only the thread that calls CAClientFactory::start() and associated auxillary threads can call ca_xxx functions.

The public access to ca is:

class epicsShareClass CAClientFactory
{
public:
    static void start();
    ...
};

Any code that uses ca must call CAClientFactory::start() before making any pvAccess client requests. ca_context_create is called for the thread that calls CAClientFactory::start(). If the client creates auxillary threads the make pvAccess client requests then the auxillary threads will automatically become a ca auxilary thread.

Client Callback Threads

Deadlock in ca_clear_subscription() describes a problem with monitor callbacks. A test was created that shows that the same problem can occur with a combination of rapid get, put and monitor events.

In order to prevent this problem ca creates the following threads: channelConnectThread, getDoneThread, putDoneThread, and monitorEventThread. All client callbacks are made via one of these threads. For example a call to the requester's monitorEvent method is made from the monitorEventThread.

Background

This section provides background material that helps understand design decisions for ca.

DBD to pvData

Type Conversion

Three type systems are involved in accessing data in a DBRecord and converting it to/from pvData:
DBF
The type system used for a DBRecord.
DBR
The type system used by channel access.
pvData
The type system used by pvData/pvAccess.
The following gives a summary of the conversions between the type systems:
rawtype               DBF          DBR         pvData ScalarType

char[MAX_STRING_SIZE] DBF_STRING   DBR_STRING  pvString
epicsInt8             DBF_CHAR     DBR_CHAR    pvByte
epicsUint8            DBF_UCHAR    DBR_CHAR    pvByte
epicsInt16            DBF_SHORT    DBR_SHORT   pvShort
epicsUInt16           DBF_USHORT   DBR_LONG    pvInt
epicsInt32            DBF_LONG     DBR_LONG    pvInt
epicsUInt32           DBF_ULONG    DBR_DOUBLE  pvDouble
epicsInt64            DBF_INT64    DBR_DOUBLE  pvDouble
epicsUInt64           DBF_UINT64   DBR_DOUBLE  pvDouble
float                 DBF_FLOAT    DBR_FLOAT   pvFloat
double                DBF_DOUBLE   DBR_DOUBLE  pvDouble
epicsUInt16           DBF_ENUM     DBR_ENUM    enum structure
epicsUInt16           DBF_MENU     DBR_ENUM    enum structure
Notes:

Accessing data in a DBRecord

An IOC database is a memory resident database of DBRecord instances. Each DBRecord is an instance of one of an extensible set of record types. Each record type has an associated dbd definition which defines a set of fields for each record instance. For example an aoRecord.dbd has the definition:
recordtype(ao) {
    include "dbCommon.dbd" 
    field(VAL,DBF_DOUBLE) {
        ...
    }
    field(OVAL,DBF_DOUBLE) {
        ...
    }
    ... many more fields
In addition each record type has a associated set of support code defined in recSup.h
/* record support entry table */
struct typed_rset {
    long number; /* number of support routines */
    long (*report)(void *precord);
    long (*init)();
    long (*init_record)(struct dbCommon *precord, int pass);
    long (*process)(struct dbCommon *precord);
    long (*special)(struct dbAddr *paddr, int after);
    long (*get_value)(void); /* DEPRECATED set to NULL */
    long (*cvt_dbaddr)(struct dbAddr *paddr);
    long (*get_array_info)(struct dbAddr *paddr, long *no_elements, long *offset);
    long (*put_array_info)(struct dbAddr *paddr, long nNew);
    long (*get_units)(struct dbAddr *paddr, char *units);
    long (*get_precision)(const struct dbAddr *paddr, long *precision);
    long (*get_enum_str)(const struct dbAddr *paddr, char *pbuffer);
    long (*get_enum_strs)(const struct dbAddr *paddr, struct dbr_enumStrs *p);
    long (*put_enum_str)(const struct dbAddr *paddr, const char *pbuffer);
    long (*get_graphic_double)(struct dbAddr *paddr, struct dbr_grDouble *p);
    long (*get_control_double)(struct dbAddr *paddr, struct dbr_ctrlDouble *p);
    long (*get_alarm_double)(struct dbAddr *paddr, struct dbr_alDouble *p);
};
The methods that support accessing data from the record include:
cvt_dbaddr          Implemented by record types that determine VAL type at record initialization
*array_info         Implemented by array record types
get_units           Implemented by numeric record types
get_precision       Implemented by float and double record types
*_enum_*            Implemented by enumerated record types
get_graphic_double  NOTE Always returns limits as double
get_control_double  NOTE Always returns limits as double 
get_alarm_double    NOTE Always returns limits as double
Each of these methods is optional, i. e. record support for a particular record type only implements methods that make sense for the record type. For example the enum methods are only implemented by records that have the definition:
...
    field(VAL,DBF_ENUM) {
...
}
...

Channel Access Data

A client can access any public field of a DBRecord; Each DBRecord instance has a record name that is unique within the local area network. A channel name is a recordname.fieldName. If the fieldname is not specified then .VAL is assumed and the record support methods shown above can also be used to get additional data from the record. Any field that is accessable by client code must have a vald DBF_ type. A client gets/puts data via a DBR_* request. The basic DBR types are:
rawtype               DBR

char[MAX_STRING_SIZE] DBR_STRING
epicsInt8             DBR_CHAR
epicsInt16            DBR_SHORT
epicsInt32            DBR_LONG
float                 DBF_FLOAT
double                DBF_DOUBLE
epicsUInt16           DBR_ENUM
In addition to the DBR basic types the following DBR types provide additional data:
DBR             one of the types above.
DBR_STATUS_*    adds status and severity to DBR.
DBR_TIME_*      adds epicsTimeStamp to DBR_STATUS.
DBR_GR_*        adds display limits to DBR_STATUS. NOTE: no epicsTimeStamp
DBR_CTRL_       adds control limits to DBR_GR.     NOTE: no epicsTimeStamp
DBR_CTRL_ENUM   This is a special case.
NOTES: Some examples:
DBR_STS_DOUBLE	returns a double status structure (dbr_sts_double)
where
struct dbr_sts_double{
	dbr_short_t	status;	 		/* status of value */
	dbr_short_t	severity;		/* severity of alarm */
	dbr_long_t	RISC_pad;		/* RISC alignment */
	dbr_double_t	value;			/* current value */
};

DBR_TIME_DOUBLE	returns a double time structure (dbr_time_double)
where
struct dbr_time_double{
	dbr_short_t	status;	 		/* status of value */
	dbr_short_t	severity;		/* severity of alarm */
	epicsTimeStamp	stamp;			/* time stamp */
	dbr_long_t	RISC_pad;		/* RISC alignment */
	dbr_double_t	value;			/* current value */
};

DBR_GR_SHORT	returns a graphic short structure (dbr_gr_short)
where
struct dbr_gr_short{
	dbr_short_t	status;	 		/* status of value */
	dbr_short_t	severity;		/* severity of alarm */
	char		units[MAX_UNITS_SIZE];	/* units of value */
	dbr_short_t	upper_disp_limit;	/* upper limit of graph */
	dbr_short_t	lower_disp_limit;	/* lower limit of graph */
	dbr_short_t	upper_alarm_limit;	
	dbr_short_t	upper_warning_limit;
	dbr_short_t	lower_warning_limit;
	dbr_short_t	lower_alarm_limit;
	dbr_short_t	value;			/* current value */
};

DBR_GR_DOUBLE	returns a graphic double structure (dbr_gr_double)
where
struct dbr_gr_double{
	dbr_short_t	status;	 		/* status of value */
	dbr_short_t	severity;		/* severity of alarm */
	dbr_short_t	precision;		/* number of decimal places */
	dbr_short_t	RISC_pad0;		/* RISC alignment */
	char		units[MAX_UNITS_SIZE];	/* units of value */
	dbr_double_t	upper_disp_limit;	/* upper limit of graph */
	dbr_double_t	lower_disp_limit;	/* lower limit of graph */
	dbr_double_t	upper_alarm_limit;	
	dbr_double_t	upper_warning_limit;
	dbr_double_t	lower_warning_limit;
	dbr_double_t	lower_alarm_limit;
	dbr_double_t	value;			/* current value */
};

DBR_CTRL_DOUBLE	returns a control double structure (dbr_ctrl_double)
where
struct dbr_ctrl_double{
	dbr_short_t	status;	 		/* status of value */
	dbr_short_t	severity;		/* severity of alarm */
	dbr_short_t	precision;		/* number of decimal places */
	dbr_short_t	RISC_pad0;		/* RISC alignment */
	char		units[MAX_UNITS_SIZE];	/* units of value */
	dbr_double_t	upper_disp_limit;	/* upper limit of graph */
	dbr_double_t	lower_disp_limit;	/* lower limit of graph */
	dbr_double_t	upper_alarm_limit;	
	dbr_double_t	upper_warning_limit;
	dbr_double_t	lower_warning_limit;
	dbr_double_t	lower_alarm_limit;
	dbr_double_t	upper_ctrl_limit;	/* upper control limit */
	dbr_double_t	lower_ctrl_limit;	/* lower control limit */
	dbr_double_t	value;			/* current value */
};


DBR_CTRL_ENUM	returns a control enum structure (dbr_ctrl_enum)
where
struct dbr_ctrl_enum{
	dbr_short_t	status;	 		/* status of value */
	dbr_short_t	severity;		/* severity of alarm */
	dbr_short_t	no_str;			/* number of strings */
	char	strs[MAX_ENUM_STATES][MAX_ENUM_STRING_SIZE];
					/* state strings */
	dbr_enum_t	value;		/* current value */
};

PVData for a DBRecord via ca provider

pvAccessCPP/src/ca has files dbdToPv.h and dbdToPv.cpp. This is the code that converts between DBR data and pvData.

This code must decide which of the many DBR_* types to use.

There is a static method:

static DbdToPvPtr create(
    CAChannelPtr const & caChannel,
    epics::pvData::PVStructurePtr const & pvRequest,
    IOType ioType);  // one of getIO, putIO, and monitorIO

When this is called the first thing is to determine which fields are requested by the client. This is from the set value, alarm, timeStamp. display, control , and valueAlarm.

If the channel type is DBR_ENUM a one time ca_array_get_callback(DBR_GR_ENUM... request is issued to get the choices for the enumerated value.

Depending or which fields are still valid, the DBR type is obtained via:

Where caValueType is one of DBR_STRING, DBR_SHORT, DBR_FLOAT, DBR_ENUM, DBR_CHAR, DBR_LONG, or DBR_DOUBLE.

DBRecord data

Each field of a record, that can be accessed by clients, has one of the following types:

integer
Signed or unsigned integer of lenth 8, 16, 32, and 64 bits.
Array of any integer type.
float
IEEE32 or IEEE64 bit floating point.
Array of either float type.
enum
An unsigned 32 bit integer plus get_enum_strs record support.
string
A fixed length character string.
For the value field the length is MAX_STRING_SIZE.
An array of strings is supported but each element is a fixed length character string of size MAX_STRING_SIZE.

A client can access the value field of a record and some of other fields. The value field can be read and modified. What a client can do with other fields depends on the dbd definition for the field.

For the value field clients can receive but not modify the following type of information:

DBRstatus    // all in dbCommon; no client support for getting directly
    epicsUInt16	status    // available to client via other means
    epicsUInt16	severity  // available to client via other means
    epicsUInt16	acks      // available to client only via channel.ACKS
    epicsUInt16	ackt      // available to client only via channel.ACKT
DBRunits                  // rset get_units
    char units[DB_UNITS_SIZE]
DBRprecision              // rset get_precision
    epicsInt32 precision
DBRtime                   // in dbCommon; no client support for getting directly
    epicsTimeStamp time   // available to client via other means
DBRenumStrs               // rset get_enum_strs
    epicsUInt32 no_str
    char strs[DB_MAX_CHOICES][MAX_STRING_SIZE];	/* string values    */
DBRgrLong                        //NOTE NO rset method
    epicsInt32  upper_disp_limit
    epicsInt32  lower_disp_limit
DBRgrDouble                      // rset get_graphic_double
     epicsFloat64 upper_disp_limit
     epicsFloat64 lower_disp_limit
DBRctrlLong                        //NOTE NO rset method
     epicsInt32 upper_ctrl_limit
     epicsInt32 lower_ctrl_limit
DBRctrlDouble                      // rset get_control_double
     epicsFloat64 upper_ctrl_limit
     epicsFloat64 lower_ctrl_limit
DBRalLong                        //NOTE NO rset method
     epicsInt32   upper_alarm_limit
     epicsInt32   upper_warning_limit
     epicsInt32   lower_warning_limit
     epicsInt32   lower_alarm_limit
DBRalDouble                      // rset get_alarm_double
     epicsFloat64  upper_alarm_limit
     epicsFloat64  upper_warning_limit
     epicsFloat64  lower_warning_limit
     epicsFloat64  lower_alarm_limit

The following are the rset methods related to client data access:

    long (*get_array_info)(struct dbAddr *paddr, long *no_elements, long *offset);
    long (*put_array_info)(struct dbAddr *paddr, long nNew);
    long (*get_units)(struct dbAddr *paddr, char *units);
    long (*get_precision)(const struct dbAddr *paddr, long *precision);
    long (*get_enum_str)(const struct dbAddr *paddr, char *pbuffer);
    long (*get_enum_strs)(const struct dbAddr *paddr, struct dbr_enumStrs *p);
    long (*put_enum_str)(const struct dbAddr *paddr, const char *pbuffer);
    long (*get_graphic_double)(struct dbAddr *paddr, struct dbr_grDouble *p);
    long (*get_control_double)(struct dbAddr *paddr, struct dbr_ctrlDouble *p);
    long (*get_alarm_double)(struct dbAddr *paddr, struct dbr_alDouble *p);

question about DBRgrLong, DBRctrlLong, and DBRalLong

These can not be accessed by client code. Are they ever used?

channel access API

Each field of a channel that can be accessed by a client has a chtype which is one of the following types:

integer
Signed integer of lenth 8, 16, and 32 bits.
Array of any integer type.
float
IEEE32 or IEEE64 bit floating point.
Array of either float type.
enum
An unsigned 32 bit integer plus DBR_CTRL_ENUM.
string
A fixed length character string.
For the value field the length is MAX_STRING_SIZE.
A array of strings is supported but each element is a fixed length character string of size MAX_STRING_SIZE.

For put channel access only allows the client to access the above types of data.

For get and monitor the client can access the following additional data:

DBR             one of the types above.
DBR_STATUS_*    adds status and severity to DBR.
DBR_TIME_*      adds epicsTimeStamp to DBR_STATUS.
DBR_GR_*        adds display and alarm limits to DBR_STATUS.
DBR_CTRL_*      adds control limits to DBR_GR.
DBR_CTRL_ENUM   Same info as DBRenumStrs from rset get_enum_strs 
NOTES:

In addition channel access defines a monitor event mask:

DBE_VALUE
Trigger an event when a significant change in the channel's value occurs. Relies on the monitor deadband field under DCT.
DBE_ARCHIVE (DBE_LOG)
Trigger an event when an archive significant change in the channel's value occurs. Relies on the archiver monitor deadband field under DCT.
DBE_ALARM
Trigger an event when the alarm state changes
DBE_PROPERTY
Trigger an event when a property change (control limit, graphical limit, status string, enum string ...) occurs.

A few comments about the monitor event mask:

default
If a mask is not specified the default is DBE_VALUE | DBE_ALARM.
DBE_VALUE
This will cause the most events.
DBE_PROPERTY
If only this is specified then events are normally rare.

When caqtDm connects to a channel it creates a monitor specifying DBR_CTRL_... and mask DBE_PROPERTY. When the callback for that request occurs, It then creates a monitor specifying DBR_STS_... and an empty mask. Thus for rare events it asks for a lot and for frequent events it asks only for value and alarm status and severity.

channel access and DBRecord

Code in epics-base converts between channel access data and the data in a DBRecord. Since these do not match some problems exist.

int64 and uint64

channel access does not support 64 bit integers. Record access makes these fields appear as a double. Thus the field appears to channel access as a DBR_DOUBLE. This works for both scalar and array. But once the 64 bit value exceeds the number of mantissa bits in a double it will loose lower order bits.

unsigned integers

channel access does not support unsigned integers

uint8
The client can not determine if a DBR_CHAR signed or unsigned.
uint16
The catype appears as an int32.
uint32
The catype appears as a double.

DBR_STRING

Most DBR_STRING fields are limited to MAX_STRING_SIZE. If a field is larger then, unless something is done, a channel access client can only access MAX_STRING_SIZE characters.

Clients have two ways to get longer strings but each relies on the client and server using the same convention. The two ways are:

int8 array
Normally a waveform record with type DBF_CHAR It has to be configured to hold enough bytes for how it is used. A client can get and put long strings by converting from/to byte arrays.
lsi and lso record types
For these record types the value field has catype DBR_STRING unless the client specifies channelName.VAL$. In this case the catype is a DBR_CHAR array.

standard fields

This section shows the relationship between pvData standard fields and DBR data.

enum

enum_t value
    int index 0
    string[] 

alarm

alarm_t alarm
    int severity
    int status
    string message       // not in any DBR types

timeStamp

time_t timeStamp
    long secondsPastEpoch   // seconds since 0000 Jan 1, 1970 UTC
    int nanoseconds
    int userTag             // not in epicsTimeStamp

Note that epicsTimeStamp is:

typedef struct epicsTimeStamp {
    epicsUInt32    secPastEpoch; /* seconds since 0000 Jan 1, 1990 UTC */
    epicsUInt32    nsec;         /* nanoseconds within second */
} epicsTimeStamp;

control

control_t control
    double limitLow
    double limitHigh
    double minStep       // not in any DBR type

display

display_t display
    double limitLow
    double limitHigh
    string description   // not in any DBR type; field DESC is in dbCommon
    string format        // DBR types only define precision
    string units         // provided by DBRunits

format is a problem but what to do?
DBRecord has field PREC.

valueAlarm

valueAlarm_t valueAlarm
    boolean active           // not in any DBR type
    double lowAlarmLimit
    double lowWarningLimit
    double highWarningLimit
    double highAlarmLimit
    int lowAlarmSeverity     // not in any DBR type
    int lowWarningSeverity   // not in any DBR type
    int highWarningSeverity  // not in any DBR type
    int highAlarmSeverity    // not in any DBR type
    double hysteresis        // not in any DBR type
                             // recordTypes that support valueAlarm
                             // should have field HYST

The above is OK. But there is a version for each numeric type. control and display only have double for limit fields. Also record support only has a method for DBRalDouble.


ca only uses valueAlarm with limits as double.

miscellaneous issues

alarm acknowledgement

Is this a problem?
Is it handled by a client issuing gets and puts to fields ACKS and ACKT?

bitMask for standard fields

ca always sets the bit for field value for each get and each monitor event. This seems like the correct semantics since DBRecords already have support for not raising monitors if a scalar value field does not change by a significant amount. It could be quite expensive to check for changes to array value fields.

alarm status

Both epics-base and pvdata have support code for alarms. The definition of status is different between them. ca converts from the epics-base version to the pvdata version.

DBRecord plugin support

Beginning with the 3.15 releases of epics-base channel filters are supported.

Using the channel access client API, the client requests plugins by appending to the channel name.
An example requesting the array plugin is:

caget test:channel test:channel.[3:5] test:channel.[3:2:-3]
 test:channel 10 0 1 2 3 4 5 6 7 8 9
 test:channel.[3:5] 3 3 4 5
 test:channel.[3:2:-3] 3 3 5 7

This works using the channel access client API and also for channel providers qsrv and ca.

Thus ca does not support any plugin options.