Each create method of class Channel, in pvAccess, has an argument PVStructure pvRequest. This document discusses how this argument is defined and used.
This product is available via an open source license
pvAccess defines interface Channel, which is a connection between a client and a server. Channel has a number of create methods. Each has an argument pvRequest:
NOTE: The following is pseudo code. ChannelProcess createChannelProcess( ChannelProcessRequester channelProcessRequester, PVStructure pvRequest); ChannelGet createChannelGet( ChannelGetRequester channelGetRequester, PVStructure pvRequest); ChannelPut createChannelPut( ChannelPutRequester channelPutRequester, PVStructure pvRequest); ChannelPutGet createChannelPutGet( ChannelPutGetRequester channelPutGetRequester, PVStructure pvRequest); ChannelRPC createChannelRPC( ChannelRPCRequester channelRPCRequester, PVStructure pvRequest); ChannelArray createChannelArray( ChannelArrayRequester channelArrayRequester, PVStructure pvRequest); Monitor createMonitor( MonitorRequester MonitorRequester, PVStructure pvRequest);
pvAcccess does not define the format of a pvRequest but pvData provides a convenience method:
PVStructure createRequest(String request);This creates a pvRequest that can be used to communicate with a server.
pvDataCPP and pvDataJava both implement CreateRequest.
A pvRequest structure allows a client to select:
In 2006 a project named javaIOC was started by Marty Kraimer. Shortly after Matej Sekoranja also started working on this project. Until about 2000, they were the only javaIOC developers.
The pvAccess API was one of the first projects. How to pass client requests to the server was a big problem. Then it was agreed that, since pvData supports structured data, a single argument can be used to pass client requests to the server, i.e.
PVStructure pvRequest
pvRequest allows a client to pass structured data to the server. But without some additional facility, clents, servers, and tools would not know what to do with a pvRequest structure. Thus a facility CreateRequest was created. It takes a request string and creates a pvRequest.
In about 2000 work started on a C++ implementation of what was in the javaIOC. At this time more people became involved with develpment. This is now what is called the EPICS 7 V4 modules. Both Java, C++, and Python implementations are now provided.
When a client connects to a channel, the client callback has a Structure argument. The Structure is the introspection interface for the PVStructure associated with the channel. The client can use Structure to select the fields it wants to get, put, monitor, etc.
Another way is for the client to guess which fields the channel supports. This is what most clients will do. For example a widget for a display manager knows which fields it requires.
The server decides which fields it supports. It ignores fields that client requests but are not supported. When a get, put, etc, connects the client code must be prepared to accept what the server provides.
A server only provides fields it supports for the operation, e. g. get, put. If it does not support any fields the client requests then it should return an error.
The server looks for record and field options it supports, and just ignores other options.
pvAccess provides the ability to pass structured data between clients and servers. Since the data can be quite complex it could be very difficult for clients to use. Lets consider a few typical types of clients.
Tools like CSS, caqtDM, etc., provide widgets like strip chart , alarm display, slider, etc. These widgets want data like EPICS base provides. That means some combination of value, alarm, timeStamp, display, and control information.
A service that is implemented via a channelPutGet request could provide data via a structure like:
structure recordName structure argument // details are service specific structure result // details are service specificwhere
In this case the client could just say that it wants "putField(argument),getField(result)"
There is support for some normative types like NTScalar and NTArray. Other normative types could provide helper code that either uses the conventions and support described in this document or invent their own support for creating a pvRequest.
For specialized support it is always possible to create private conventions for pvRequest. But then general pupose tools will be much less useful and it may also be necessary for the support to implement various Channel methods.
The following are the two main components related to pvRequest:
PVStructure createRequest(String request);Creates a "PVStructure pvRequest" to be passed from a client to a server. A pvRequest structure allows a client to select:
The purpose is to create a pvRequest structure for accesssing data in a server. It allows the clients to select an arbitrary subset of the fields in the top level structure associated with the channel. It also allows the client to specify options. Thus the client can specify:
NOTE: The term record is adapted from pvDatabase. A pvDatabase is database of a memory resident smart records. A pvAcccess channel is a connection to a record. A record has a top level PVStructure that holds the data for the record.
Other pvAcccess providers must provide a PVStructure for each channel it supports.
qsrv, which provides access to iocCore DBRecords, is an example that also accepts a pvRequest created by a call to createRequest.
Channel has methods:
record[option,...]field(fieldDef,...) OR field(fieldDef,...) OR fieldDef,...
record[option,...]putField(fieldDef,...)getField(fieldDef,...) OR putField(fieldDef,...)getField(fieldDef,...)
This section provides an easy (hopefully) to understand description.
A request argument has the syntax:
record[option,...]field(fieldDef,...) OR field(fieldDef,...) OR fieldDef,... OR record[option,...]putField(fieldDef,...)getField(fieldDef,...) OR putField(fieldDef,...)getField(fieldDef,...)
NOTES:
field() getField() putField()all mean an entire data structure.
Thus a request consists of record options and sets of field definitions or just field definitions. A record option is of the form:
record[name=value,...]
A field(...),getField(...),putField(...) is a comma separated set of fieldDefs which are of the form:
fullFieldName or fullFieldName[option,...] or fullFieldName{fieldDef,...} // recursive definition or fullFieldName[option,...]{fieldDef,...} // recursive definition
fullFieldName selects a subfield of the current sub-structure being accessed. Initially this means the top level structure of the data associated with the channel. In a recursive definition the current sub-structure becomes the location asscociated with fullFieldName
If fieldName{fieldDef,...} is given then the generated data structure will have a structure field with subfields.
The syntax was chosen to be easy to use and to parse:
{ } ( ) [ ] = ,
Thus a request consists of record options and sets of field definitions or
just field definitions.
A record option is of the form:
record[name=value,...]
field,putFeld, and getField each contain a comma separated set of fieldDefs which are of the form:
fullFieldName or fullFieldName[option,...] or fullFieldName{request} // recursive definition
A fullFieldName is the full name of a field in the PVRecord.
If request is null or an empty string than an entire structure is selected.
mrk> pvget -r "" -v PVRdouble // "" means entire PVStructure PVRdouble epics:nt/NTScalar:1.0 double value 0 alarm_t alarm int severity 0 int status 0 string message time_t timeStamp long secondsPastEpoch 0 int nanoseconds 0 int userTag 0 mrk> pvget -r "value" -v PVRdouble PVRdouble structure double value 0 mrk> pvget -r "record[process=true]field(value,timeStamp)" -v PVRdouble PVRdouble structure double value 0 time_t timeStamp 2019-04-17 13:18:55.617 long secondsPastEpoch 1555521535 int nanoseconds 617126872 int userTag 0
Assume that the channel has the following data:
pvget -r "field()" -v PVRdumbPowerSupply PVRdumbPowerSupply structure alarm_t alarm int severity 0 int status 0 string message time_t timeStamp <undefined> long secondsPastEpoch 0 int nanoseconds 0 int userTag 0 structure power double value 0 alarm_t alarm int severity 0 int status 0 string message structure voltage double value 0 alarm_t alarm int severity 0 int status 0 string message structure current double value 0 alarm_t alarm int severity 0 int status 0 string messageThen the following selects a subset of the fields:
mrk> pvget -r "field(alarm{severity,message},timeStamp.secondsPastEpoch,power)" -v PVRdumbPowerSupply PVRdumbPowerSupply structure structure alarm int severity 0 string message structure timeStamp long secondsPastEpoch 0 structure power double value 0 alarm_t alarm int severity 0 int status 0 string message
pvDatabaseCPP and pvDatabaseJava are compliant with pvRequest including record options.
As described below pvDatabaseCPP supports field options via plugin support.
The latest epics-base implements plugin support, but not via pvRequest.
ca is a client provider that uses the channel access network protocol, but presents data to the client as pvData structures.
The latest version of ca in pvAccessCPP has pvRequest support that allows the client to select a combination of of value, alarm, timeStamp, display, control, and valueAlarm information.
It implements record options, but not field options. However the client can use the epics-base plugin support.
qsrv is not compliant with pvRequest. It does honor the record options, but does not honor the field subset options. It does honor the epics-base plugin support.
For example:
mrk> pvget -p ca -r "value" -v DBRdouble DBRdouble structure double value 0 mrk> pvget -p pva -r "value" -v DBRdouble DBRdouble epics:nt/NTScalar:1.0 double value 0 alarm_t alarm int severity 3 int status 2 string message UDF time_t timeStamp long secondsPastEpoch 631152000 int nanoseconds 0 int userTag 0 display_t display double limitLow -10 double limitHigh 10 string description string format string units volts control_t control double limitLow -9 double limitHigh 9 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
At present the following record options are supported:
record[queueSize=2]A larger size can be specified.
record[process=false]
record[block=false]
For details see: pluginSupport
For example:
mrk> pvget -r "value" PVRdoubleArray PVRdoubleArray structure double[] value [1,2,3,4,5,6,7,8,9,10] mrk> pvget -r "value[array=3:5]" PVRdoubleArray PVRdoubleArray structure double[] value [4,5,6]
Beginning with the 3.15 releases of epics-base channel plugin filters are supported. Currently there is support for TimeStamp, Deadband, Array, and Synchronize. In addition code can be written to support other plugins without requiring any changes to epics-base.
Using the channel access client API, the client requests plugins by appending to the channel name.
For example:
mrk> caget DBRdoubleArray DBRdoubleArray 5 1 2 3 4 5 mrk> caget DBRdoubleArray.[2:4] DBRdoubleArray.[2:4] 3 3 4 5 mrk> pvget DBRdoubleArray.[2:4] DBRdoubleArray.[2:4] 2019-04-17 13:49:48.919 [1,2,3] | BUG IN QSRV?? mrk> pvget -p ca DBRdoubleArray.[2:4] DBRdoubleArray.[2:4] [3,4,5]
Some observations about the channel filters:
mrk> caget DBRdoubleArray.[2:4] DBRdoubleArray.[2:4] 3 3 4 5 mrk> caget DBRdoubleArray.[xxx:yyy] Channel connect timed out: 'DBRdoubleArray.[xxx:yyy]' not found.
mrk> pvput DBRdoubleArray [1,2,3,4,5] Old : 2019-04-15 14:28:49.283 [100,200] New : 2019-04-15 14:31:15.022 [1,2,3,4,5] mrk> caget DBRdoubleArray.[2:4] DBRdoubleArray.[2:4] 3 3 4 5 mrk> caput DBRdoubleArray.[2:4] 3 10 20 30 Old : DBRdoubleArray.[2:4] 3 3 4 5 New : DBRdoubleArray.[2:4] 3 3 0 0 mrk> caget DBRdoubleArray DBRdoubleArray 5 3 0 0 0 0 mrk> pvput DBRdoubleArray [1,2,3,4,5] Old : 2019-04-18 05:51:24.438 [100,200,300] New : 2019-04-18 05:52:30.546 [1,2,3,4,5] mrk> pvput DBRdoubleArray.[2:4] [100,200,300] Old : 2019-04-18 05:52:30.546 [1,2,3] New : 2019-04-18 05:53:08.617 [100,200,300] mrk> pvget DBRdoubleArray DBRdoubleArray 2019-04-18 05:53:08.617 [100,200,300]
NOTE: This section is only of interest to someone who is implementing code that has to introspect a structure generated by createRequest.
pvRequest is a PVStructure that describes 1) record options and 2) field requests and options. It has the following structure:
structure structure record structure _options option ... structure field structure fieldName structure _options option ... structure fieldName structure _options option ... ... structure putField structure fieldName structure _options option ... structure fieldName structure _options option ... ... structure getField structure fieldName structure _options option ... structure fieldName structure _options option ... ...
where
Note:
An example of option is process.
Process is a record option:
structure structure record structure _options string process true
For example if process is an option to createGet then the record will be processed before data is fetched. NOTE: scalarType boolean is also supported.
The following is an example of a field option:
structure structure field structure value structure _options string monitorAlgorithm onChange
Some examples are:
request structure request alarm,timeStamp,power.value structure structure alarm structure timeStamp structure power structure value request record[process=true]field(alarm,timeStamp,power.value) structure structure record structure _options string process true structure field structure alarm structure timeStamp structure power structure value request record[process=true]field(alarm,timeStamp[algorithm=onChange,causeMonitor=false],power{value,alarm}) structure structure record structure _options string process true structure field structure alarm structure timeStamp structure _options string algorithm onChange string causeMonitor false structure power structure value structure alarm request record[process=true,xxx=yyy]field(alarm,timeStamp[causeMonitor=true],power.value) structure structure record structure _options string process true string xxx yyy structure field structure alarm structure timeStamp structure _options string causeMonitor true structure power structure value
NOT DONE
<request> :=