EPICS V4 Developer's Guide

EPICS V7 Working Draft, 04-December-2017

NOTE: This is the working version of the developerGuide.
EPICS V4 release versions are:
developerGuide_V4Release4_6
developerGuide_V4Release4_5

Editors:
Marty Kraimer

Abstract

This is Developer's Guide for the PVA components of EPICS 7, which is a set of related products:
relatedDocumentsV4.html

Table of Contents

Overview of the PVA components of EPICS 7

This document briefly describes a set of application programming interfaces (APIs) for PVA.

These core APIs provide a toolkit for creating applications that access and/or provide sources of structured data. This document is intended for EPICS PVAs software developers who want to implement applications via the core APIs.

EPICS PVA resources and website

This document is a tutorial rather then a detailed reference manual. Links to the reference manual for each PVA modules are provided in the following overview sections.

In addition the following are standards:

PVA Modules

The current release of EPICS 7 includes the following PVA modules:

pvData
The EPICS PVA type system, which describes and implements structured data.
normativeTypes
A set of standard pvData structures.
pvAccess
Support for connecting a client and server and for transporting pvData between client and server.
pvaClient
pvAccess is a callback based API. pvaClient provides a synchronous interface, simpified callback classes, and a number of convenience methods.
pvDatabase
A memory resident database of PVRecords and a pvAccess channel provider for accessing the PVRecords.
pva2pva
This consists of two components: 1) A PVA gateway, and 2) qsrv. This document only discusses qsrv.
qsrv is a PVA channel provider for EPICS DBRecords.

In addition the following, although not part of the current EPICS 7 release, are based on PVA:

example
A set of examples that use all of the above components.
pvaPy
Python wrapper for pvData and pvAccess. This is not described in this docmument.

pva2pva and pvaPy are implemented only in C++. Each has a single github repository.

The other components are implemented in both C++ and Java, with each language having it's own github repository. For example pvData has the repositories pvDataJava and pvDataCPP

EPICS 7 has separate C++ and Java releases.

Terminology

EPICS 7
This is a combination of epics base and what was previously called EPICS V4, but is now called PVA.
epics base is what has existed since about 1990. It has had many releases: The oldest set of releases are the 3.11 series and the newest is the 3.16 series. epics base is also refered to as EPICS V3.
epics base supports data as a flat structure.
PVA adds structured data and works along side EPICS V3. It's main components are:
pvData
API and implementation for structured data
normativeTypes
A set of structures designed for use by tools like display managers, archivers, etc.
pvAccess
Network support and API for transfering data between clients and servers. All data is handled as pvData objects.
API - Application Programming Interface
The pvData API is how a client accesses the type system and data objects for each type. In Java this consists mainly of Java interfaces. In C++ this is via C++ classes.
Introspection API
The type system supports both introspection and data interfaces. The introspection interfaces allow a client to determine the data types for objects without requiring a data instance.
Data API
These provide access to data instances.
type
Often used when talking about a field. In this case it means the intospection interface for the field.
PVScalarType
Generic name for the data type for a scalar. Thus a generic name for one of PVBoolean, PVByte, ..., PVString.
PVScalarArrayType
Generic name for the data type for a scalarArray. This a generic name for one of PVBooleanArray, PVByteArray, ..., PVStringArray.
PVType
Generic name for any valid Data API type.
Special Field
These are a set standard structure definitions: enum, alarm, timeStamp, control, display, and alarmLimit.

Overview: pvData

Detailed Documentation is available at: pvDataCPP and pvDataJava

pvData supports structured data where a structure is a set of fields. Each field is composed of one of the following types:

scalar
The field holds data that has a single scalarType:
boolean
Can be true or false.
signed Integer
Integers of 8, 16, 32, and 64 bits are supported.
unsigned Integer
Unsigned integers of 8, 16, 32, and 64 bits are supported.
float
IEEE 32 bit floating point.
double
IEEE 64 bit floating point.
string
In C++ std::string and in Java String. On the network UTF8.
scalarArray
An array of one of the scalar types.
structure
A set of fields and a name for each field. Each field can have any valid type but the type can not change.
structureArray
An array of structures. Each element must have the same introspection interface.
union
A field that has a single sub field which can dynamically change type. A variant union allows any valid type. A restricted union allows for a set of valid types.
unionArray
A array of unions. Each element must have the same introspection interface.

Overview: normativeTypes

Detailed Documentation is available at: normativeTypesCPP and normativeTypesJava

Each normative type defines a structure that has a set of standard fields. For example NTScalar defines:

epics:nt/NTScalar:1.0
    double value                        // mandatory and can be any scalar type
    string descriptor                   // optional
    alarm_t alarm                       // optional
        int severity
        int status
        string message
    time_t timeStamp                    // optional
        long secondsPastEpoch
        int nanoseconds
        int userTag
    display_t display                   // optional
        double limitLow
        double limitHigh
        string description
        string format
        string units
    control_t control                   // optional
        double limitLow
        double limitHigh
        double minStep
    string extra1                       // extra
    string[] extra2                     //

Overview: pvAccess

Detailed Documentation is available at: pvAccessCPP and pvAccessJava

A client written in either Java or C++ can communicate with a server written in either Java or C++. All communication between client and server is done via the methods provided by pvAccess and by pvData objects.

pvAccess provides:

channelProviderRegistry
An arbitrary number of channelProviders can be created. Starting with EPICS 7, a separate registry exists for client and server.
channelProvider
Code that provides access to a pvData data source. It creates channels.
A channel:
  • Provides access to a pvData structure.
  • Has an associated channelName.
channel
Methods for each of the following:
getField
Get the pvData introspection information.
channelGet
Get data.
channelPut
Put data.
channelPutGet
Put data, let server process, and then get result.
monitor
Monitor pvData changes.
channelArray
get/put subArray data.
channelRPC
Like channelPutGet but on each request pvData interfaces for put and get can change.

channelPutGet and channelRPC provide the equivalent of a Remote Procedure Call. The client passes a pvData object to the server. This pvData object is the argument for the RPC. The server uses this to decide what to do and sends a pvData object back to the client, which is the RPC result.

Overview: pvaClient

Detailed Documentation is available at: pvaClientCPP and pvaClientJava

pvaClient is a synchronous wrapper for the pvAccess API, which is a callback based API. In addition pvaClient provides many convenience methods. Thus it is easier to use than pvAccess itself.

pvaClientCPP also provides simplified callback classes, which can be used to implement non blocking client code. pvaClientJava will be upgraded to support the same features.

Overview: pvDatabase

Detailed Documentation is available at: pvDatabaseCPP and pvDatabaseJava

A framework for implementing a network accessible database of smart memory resident records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by pvData. A complete implementation of ChannelProvider is provided for accessing the records. The framework can be extended in order to create record instances that implement services. The minimum that an extension must provide is a top level PVStructure, a record name, and a process method.

Overview: pva2pva

Detailed Documentation is available at: pva2pva

qsrv (a major component of pva2pva) is a channel provider for accessing DBRecords in an IOC. qsrv allows clients to get, put and monitor V3 PVs (fields of EPICS DB records) via pvAccess, translating the value and its meta data (graphics limits, alarm status, timestamp) to or from V4 Normative Type (NT) pvData structures (NTScalar, NTScalarArray, and NTEnum).

Overview: pvaPy

A Python wrapper for pvData and pvAccess. This is not discussed in this document.

Overview: channelProvider

The basic requirements for a channel provider are:

  1. Given a channel name, find and connect to the data source
  2. Implement one or more of the channel methods: getField, createChannelGet, etc.
  3. get/put all data as pvData.
  4. Prefer normative types instead of raw pvData

pvAccess implements channelProviderRegistry, which allows an arbitrary number of providers. Starting with EPICS 7 there are separate client and server registries.

A provider is one of the following:

Client Provider
Called by code that wants to access a channel, where each channel has a unique channel name
Server Provider
A provider for the remote side of the pva provider, which is described next.

Before either kind of provider can be used it must first register itself with the channelProviderRegistry

Provider pva

An important component of EPICS V4 is provider pva, which is a provider that implements a connection between a client and a server that uses the pva network protocal as defined in: pvAccess_Protocol_Specification.html

Provider pva has two components:

client
This is what a pvAccess client uses.
remote
This connects to server providers

Network communcation is used between client and server. Remote pva transfers data between the network and channel providers that have registered at the remote node.

Client Providers

An arbitrary number of client providers can be implemented. Each provider must implement the channel interface mentioned above and must register with the channelProviderRegistry before it can be used by a client.

pvAccess itself provides the following client providers:

pva
The client side of provider pva.
ca
This is a connection that uses channel access to access an epics V3 channel.
pipelineService
A pipelineService is a service that provides a lossless channel monitor. A client requests access to a pipelineService via a pvRequest argument.
pvGet and eget provide support.
Other clients could also access a server that implements a pipelineService.

Server Providers

At present the following providers are available for the remote side of pva

local
This is implemented by pvDatabase
qsrv
This is a provider that directly accesses V3 DBRecord.
pipelineService
pvAccess provides a helper class for a service that wants to implement a pipelineService.

Client Provider Choices

At present a client that is not running as part of a V3 IOC or a pvDatabase has two choices for channelProvider: pva and ca. A client running as part of a V3 IOC that is also using pvDatabase has all of the following choices: pva, ca, local, and pvSrv. Note that if a client directly connects to either local or pvSrv no network communication is involved.

And since channelProviderRegistry allows any arbitrary number of providers additional providers can be developed for either the client or remote side of pva.

pvlist, pvget, pvput, pvinfo, and eget

pvAccessCPP provides command line tools. Each provides help. For example:

pvlist -help

The examples shown in this section assume that the example database described in the next secton is running. Also your PATH environment variable must include the appropriate bin directory from pvAccessCPP. For example:

export PATH=$PATH:${EPICSV7}/pvAccessCPP/bin/${EPICS_HOST_ARCH}

pvlist

The options are:

mrk> pvlist -help

Usage: pvlist [options] [server address or GUID starting with '0x']...

options:
  -h: Help: Print this message
  -V: Print version and exit
  -i                 Print server info (when server address list/GUID is given)
  -w <sec>:          Wait time, specifies timeout, default is 3.000000 second(s)
  -q:                Quiet mode, print only error messages
  -d:                Enable debug output

examples:
	pvlist
	pvlist ioc0001
	pvlist 10.5.1.205:10000
	pvlist 0x83DE3C540000000000BF351F

Without any arguments this shows the location of all V4 servers it can locate. For example:

pvlist
GUID 0x7F06B2560000000047B97D25, version 1: tcp@[192.168.124.1:45345, 10.0.0.37:45345]

This only shows one line because only the exampleDatabase server is running. In an operational system the output could be quite extensive.

Once the location of a server is known the list of channel names can be shown by asking for either the GUID or tcp address. Thus either:

pvlist 0x7F06B2560000000047B97D25
or
pvlist 10.0.0.37:45345
produces:
DBRao01
DBRdouble00
...

pvget

pvget provides functionality similar to caget. For example:

mrk> caget DBRdouble01
DBRdouble01                    0

The options are:

mrk> pvget -help

Usage: pvget [options] PVname...

options:
  -h: Help: Print this message
  -V: Print version and exit
  -r <pv request>:   Request, specifies what fields to return and options, default is 'field(value)'
  -w <sec>:          Wait time, specifies timeout, default is 3 seconds for get, inf. for monitor
  -t:                Terse mode - print only value, without names
  -i:                Do not format standard types (enum_t, time_t, ...)
  -m:                Monitor mode
  -p <provider>:     Set default provider name, default is 'pva'
  -v:                Show entire structure
  -q:                Quiet mode, print only error messages
  -d:                Enable debug output
  -F <ofs>:          Use <ofs> as an alternate output field separator
  -f <input file>:   Use <input file> as an input that provides a list PV name(s) to be read, use '-' for stdin
 enum format:
  -n: Force enum interpretation of values as numbers (default is enum string)


example: pvget double01

Some examples are:

mrk> pvget PVRdouble
PVRdouble                      5
mrk> pvget PVRdoubleArray
PVRdoubleArray 5 1 2 3 4 5
mrk> pvget PVRenum01
PVRenum01                         zero
mrk> pvget -i PVRenum01
enum01
epics:nt/NTEnum:1.0 
    enum_t value
        int index 0
        string[] choices [zero,one,two,three]


mrk> pvget -i -r "value,alarm,timeStamp" enum01
PVRenum01
epics:nt/NTEnum:1.0 
    enum_t value
        int index 0
        string[] choices [zero,one,two,three]
    alarm_t alarm
        int severity 3
        int status 0
        string message UDF
    time_t timeStamp
        long secondsPastEpoch 631152000
        int nanoseconds 0
        int userTag 0

Multiple channels can be accessed witn a single command. For example:

mrk> pvget PVRdouble01 PVRdouble02
PVRdouble01                       0
PVRdouble02                       0

The above examples all use channel provider pva, i. e. the pvAccess server for network communication. It is also possible to use ca, i. e. the channel access server that comes with iocCore. Thus it can access a V3 IOC even if the IOC has no V4 support.

mrk> pvget -p ca DBRdouble01
DBRdouble01                       0
mrk> pvget -p ca -r "value,alarm,timeStamp" DBRdouble01
DBRdouble01
epics:nt/NTScalar:1.0 
    double value 0
    alarm_t alarm INVALID RECORD UDF_ALARM
    time_t timeStamp <undefined> 0

pvget can also monitor, i. e. it has functionality similer to camonitor.

mrk> pvget -m -r "value,alarm,timeStamp" PVRdouble02
PVRdouble02
epics:nt/NTScalar:1.0 
    double value 0
    alarm_t alarm INVALID NO_STATUS UDF
    time_t timeStamp <undefine> 0


PVRdouble02
epics:nt/NTScalar:1.0 
    double value 1
    alarm_t alarm NO_ALARM NO_STATUS NO_ALARM
    time_t timeStamp 2015-05-14T08:29:55.769 0

pvput

pvput provides functionality similar to caput. For example:

mrk> caput DBRdouble01 1.0
Old : DBRdouble01                       0
New : DBRdouble01                       1
mrk> pvput DBRdouble01 2.0
Old : DBRdouble01                       1
New : DBRdouble01                       2

The options are:

mrk> pvput -help
Usage: pvput [options] <PV name> <value>
       pvput [options] <PV name> <size/ignored> <value> [<value> ...]
       pvput [options] <PV name> <field>=<value> ...
       pvput [options] <PV name> <json_array>
       pvput [options] <PV name> <json_map>


options:
  -h: Help: Print this message
  -v: Print version and exit
  -r <pv request>:   Request, specifies what fields to return and options, default is 'field(value)'
  -w <sec>:          Wait time, specifies timeout, default is 3.000000 second(s)
  -t:                Terse mode - print only successfully written value, without names
  -p <provider>:     Set default provider name, default is 'pva'
  -v:                Show entire structure
  -q:                Quiet mode, print only error messages
  -d:                Enable debug output
  -F <ofs>:          Use <ofs> as an alternate output field separator
  -f <input file>:   Use <input file> as an input that provides a list PV name(s) to be read, use '-' for stdin
 enum format:
  default: Auto - try value as enum string, then as index number
  -n: Force enum interpretation of values as numbers
  -s: Force enum interpretation of values as strings

 JSON support is present

Examples:

  pvput double01 1.234       # shorthand
  pvput double01 value=1.234

  pvput arr:pv X 1.0 2.0  # shorthand  (X is arbitrary and ignored)
  pvput arr:pv "[1.0, 2.0]"            # shorthand
  pvput arr:pv value="[1.0, 2.0]"

Field values may be given with JSON syntax.

Complete structure

  pvput double01 '{"value":1.234}'

Sub-structure(s)

  pvput group:pv some='{"value":1.234}' other='{"value":"a string"}'

Here are some examples:

mrk> pvput PVRdouble 5
Old : PVRdouble                      0
New : PVRdouble                      5
mrk> pvput PVRdoubleArray 5 1 2 3 4 5
Old : PVRdoubleArray 0
New : PVRdoubleArray 5 1 2 3 4 5
mrk> pvput -r "field(scalar)" PVRBigRecord scalar.boolean='{"value":true}' scalar.byte='{"value":9}'
Old : 
structure 
    structure scalar
        structure boolean
            boolean value false
        structure byte
            byte value 2
        structure long
            long value 0
        structure double
            double value 0
        structure string
            string value 


New : 
structure 
    structure scalar
        structure boolean
            boolean value true
        structure byte
            byte value 9
        structure long
            long value 0
        structure double
            double value 0
        structure string
            string value 

pvinfo

pvinfo retrieves the introspection interface for selected channels

The options are:

mrk> pvinfo -help

Usage: pvinfo [options] <PV name>...


options:
  -h: Help: Print this message
  -V: Print version and exit
  -w <sec>:          Wait time, specifies timeout, default is 3.000000 second(s)
  -p <provider>:     Set default provider name, default is 'pva'
  -d:                Enable debug output
  -c:                Wait for clean shutdown and report used instance count (for expert users)
Example: pvinfo double01

eget

eget provides many of the same features but in addition supports channelRPC and understands some of the normative types.

The options are:

mrk> eget -help

Usage: eget [options] [<PV name>... | -s <service name>]


options:
  -h: Help: Print this message
  -V: Print version and exit
  -s <service name>:   Service API compliant based RPC service name (accepts NTURI request argument)
  -a <service arg>:    Service argument in 'name[=value]' or 'name value' form
  -r <pv request>:     Get request string, specifies what fields to return and options, default is 'field(value)'
  -w <sec>:            Wait time, specifies timeout, default is 3.000000 second(s)
  -z:                  Pure pvAccess RPC based service (send NTURI.query as request argument)
  -N:                  Do not format NT types, dump structure instead
  -i:                  Do not format standard types (enum_t, time_t, ...)
  -t:                  Terse mode
  -T:                  Transpose vector, table, matrix
  -m:                  Monitor mode
  -x:                  Use column-major order to decode matrix
  -p <provider>:       Set default provider name, default is 'pva'
  -q:                  Quiet mode, print only error messages
  -d:                  Enable debug output
  -F <ofs>:            Use <ofs> as an alternate output field separator
  -f <input file>:     Use <input file> as an input that provides a list PV name(s) to be read, use '-' for stdin
  -c:                  Wait for clean shutdown and report used instance count (for expert users)
 enum format:
  -n: Force enum interpretation of values as numbers (default is enum string)


examples:

#! Get the value of the PV corr:li32:53:bdes
> eget corr:li32:53:bdes

#! Get the table of all correctors from the rdb service
> eget -s rdbService -a entity=swissfel:devicenames

#! Get the archive history of quad45:bdes;history between 2 times, from the archive service
> eget -s archiveService -a entity=quad45:bdes;history -a starttime=2012-02-12T10:04:56 -a endtime=2012-02-01T10:04:56

#! Get polynomials for bunch of quads using a stdin to give a list of PV names
> eget -s names -a pattern=QUAD:LTU1:8%:POLYCOEF | eget -f -

example code

NOTE: exampleCPP and exampleJava are not part of the EPICS 7 release. They must be cloned from github:

git clone https://github.com/epics-base/exampleCPP.git
or
git clone https://github.com/epics-base/exampleJava.git

exampleCPP and exampleJava both have examples that use code from all modules described in this document except for pvaPy. In particular they both have examples:

database
Example server code implemented via pvDatabase.
In addition it shows how to have PVRecords and V3 Records both running as part of a V3 IOC.
It also shows how to start provider qsrv so that both ca and pva can be used to access the V3 records.
exampleClient
example client code using the pvaClient API.
exampleLink
Implements PVRecords that monitor, get, and put to channel.

These examples that can be used while learning what is described in this document. Both exampleJava and exampleCPP have other examples.

Examples in exampleJava/exampleClient, exampleCPP/exampleClient, and pvaPy require that the database in exampleCPP/exampleClient or exampleJava/exampleClient is started. If exampleCPP is used the exampleDatabase can be started either as a main program or as part of a V3 IOC. If started as part of a V3 IOC, it also has V3 records and starts qsrv. If started as a main program the result is the same as starting exampleJava/database All the V4 PVRecords have the prefix PVR and all the V3 DBRecords have the prefix DBR.

In linux exampleCPP/database can be started as follows:

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

The example database has both V3 IOC records and V4 PVRecords. In addition qsrv is running. Thus all V3 records are available via either ca or pva. The PVRecords are only available via pva. Examples of using ca and pva command line tools are:

mrk> pvlist 
GUID 0x7F06B2560000000047B97D25, version 1: tcp@[10.0.0.37:45345, 192.168.124.1:45345]
mrk> pvlist 0x7F06B2560000000047B97D25
DBRao01
DBRdouble00
.... many more records
mrk> pvget PVRlong
PVRlong                        0
mrk> caget PVRlong
Channel connect timed out: 'PVRlong' not found.
mrk> caget DBRdouble01
DBRdouble01                       0
mrk> pvget DBRdouble01
DBRdouble01                       0
mrk> caget PVRushort01
Channel connect timed out: 'PVRushort01' not found.
mrk> pvget PVRushort01
PVRushort01                       0
mrk> 

The examples in exampleCPP/exampleClient and exampleJava/exampleClient can now be run. For example:

mrk> pwd
/home/epicsv4/masterCPP/exampleCPP/exampleClient
mrk> ls bin/linux-x86_64/
examplePvaClientGet          examplePvaClientPut  monitorNoBlock
examplePvaClientMonitor      getFieldNoBlock      putNoBlock
examplePvaClientMultiDouble  getNoBlock           putUnion
examplePvaClientNTMulti      helloWorldPutGet
examplePvaClientProcess      helloWorldRPC
mrk> 
mrk> bin/linux-x86_64/examplePvaClientGet
_____examplePvaClientGet starting_______
__exampleDouble__
short way
as double 0
long way
as double 0
... LOTS MORE OUTPUT

pvaClient Tutorial

Some Basic Concepts

Special Field Structures

The following provide PVA definitions for EPICS V3 DBD data other than just scalar and scalar array data associated with a value field:
alarm, timeStamp, enum, display, control, and alarmLimit.
Each is a structure that also has an associated structure id name. For example alarm_t is the structure id name for an alarm structure. For the tutorial alarm, timeStamp, and enum are briefly discussed A later section provides more details about these and the other special fields.

alarm
alarm_t alarm
    int severity
    int status
    string message 

pvData provides code associated with a alarm structure. This code restricts severity to be one of:

NONE,MINOR,MAJOR,INVALID,UNDEFINED

and status to be one of:

NONE,DEVICE,DRIVER,RECORD,DB,CONF,UNDEFINED,CLIENT
timeStamp
time_t timeStamp
    long secondsPastEpoch
    int nanoseconds
    int userTag

pvData provides code associated with a timeStamp structure. This code provides support that makes the timeStamp UTC (Universial Time Coordinated) compliant.

enum
enum_t value
    int index
    string[] choices

pvData provides code associated with an enum structure. This forces the index to select an element of the choices.

pvRequest

Each of the PVChannel create methods has a PVStructure argument named pvRequest. This structure allows the client to 1) select a subset of the fields in the top level structure from the server, 2) provide record options, and 3) provide field specific options.

pvAccess provides a method createRequest that, given a string, creates a pvRequest structure. A later section provides more details but for this tutorial lets just give a simple example:

"field(value, alarm, timeStamp):
or just
"value, alarm, timeStamp"
Specifies that the client wants to receive the top level fields named value, alarm , and timeStamp.

C++ Tutorial

The tutorial is not part of an EPICS 7 release. It can be cloned via the command:

git clone  https://github.com/mrkraimer/pvaClientTutorialCPP.git
cd pvaClientTutorialCPP
make

Clone it in the same directory that holds your EPICS 7 release. Documentation is provided in pvaClientTutorialCPP/documentation/clientTutorialCPP.html.

Java Tutorial

NOT IMPLEMENTED The current plan is to create this after pvaClientJava has been upgraded to support the same features that have been added to pvaClientCPP.

pvDatabase Tutorial

NOT IMPLEMENTED The current plan is to create this for both C++ and Java after pvaClientTutorialJava has been created.

pvAccess Channel Providers.

NOTES:

pvaClient
If you are using pvaClientCPP or pvaClientJava to write client code then pvaClient takes care of starting and destroying client providers.
pvDatabase
If You are using exampleCPP/database or exampleJava/database as a basis for creating your own server then the examples show how to start and destroy the server providers. exampleCPP/exampleLink or exampleJava/exampleLink show how to start and destroy both the client and server providers.
pav2pva
This provides support for starting a remote pva server and provider qsrv as part of an IOC.

pvAccessJava and pvAccessCPP implement the following providers:

pva
This is a provider that trasfers data via the network protocol defined in pvAccess Protocol Specification
This provides the client and server side of the network protocol. The client side is a complete implementation. The server side requires additional code to access data sources but takes care of all network code.
ca
This is client support for transfering data via the EPICS V3 channel access protocol.

Providers can be provided for other data sources. A provider must implement ChannelProvider and Channel. pvAccess supports an arbitrary number of providers. On the server side of remote pvAccess providers must be implemented, because it calls the providers to implement the CHannel methods.

At present C++ provides two server side providers:

qsrv
This is a pvAccess server that accesses V3 records,
local
This is implemented by pvDatabaseCPP

At present Java provides one server side provider:

local
This is implemented by pvDatabaseJava

Special Fields

enum

An enumerated structure is a structure that has fields:

enum_t
    int index
    string[] choices

PVEnumerated

PVEnumerated
    boolean attach(PVField pvField)
    void detach()
    boolean isAttached()
    boolean setIndex(int index)
    int getIndex()
    String getChoice()
    boolean choicesMutable()
    String[] getChoices()
    boolean setChoices(String[] choices)

where

attach
Attempts to attach to pvField It returns (false,true) if pvField (is not, is) an enumerated structure.
detach
Detaches from the pvData structure.
isAttached
Is there an attachment to an enumerated structure?
setIndex
Set the index field in the pvData structure. An exception is thrown if not attached to a pvData structure.
getIndex
Get the index field in the pvData structure.
getChoice
Get the String value corresponding to the current index field in the pvData structure. An exception is thrown if not attached to a pvData structure.
choicesMutable
Can the choices be changed? Note that this is often true. An exception is thrown if not attached to a pvData structure.
getChoices
Get the array of choices. An exception is thrown if not attached to a pvData structure.
getNumberChoices
Get the number of choices. An exception is thrown if not attached to a pvData structure.
setChoices
Change the choices. An exception is thrown if not attached to a pvData structure.

alarm

An alarm structure is defined as follows:

alarm_t alarm
    int severity
    int status
    string message

Note that severity and status are NOT defined as enumerated structures. The reason is performance, i. e. prevent passing the array of choice strings everywhere. The AlarmStatus and AlarmSeverity provide the equivalent of choices for an enumerated structure.

AlarmSeverity

Alarm Severity defines the possible alarm severities

enum AlarmSeverity
    NONE,MINOR,MAJOR,INVALID,UNDEFINED
    AlarmSeverity getSeverity(int value)
    final String[] alarmSeverityNames
    String[] getSeverityNames()
where
getSeverity
Get the alarm severity corresponding to the integer value.
getSeverityNames
Get the array of severity choices.

AlarmStatus

Alarm Status defines the possible alarm status conditions

enum AlarmStatus
    NONE,DEVICE,DRIVER,RECORD,DB,CONF,UNDEFINED,CLIENT;

    AlarmStatus getStatus(int value)
    String[] alarmStatusNames
    String[] getStatusNames()
where
getStatus
Get the alarm status corresponding to the integer value.
getStatusNames
Get the array of status choices.

Alarm

Alarm
    String getMessage()
    void setMessage(String message)
    AlarmSeverity getSeverity()
    void setSeverity(AlarmSeverity alarmSeverity)
    AlarmStatus getStatus()
    void setStatus(AlarmStatus alarmStatus)
}
where
getMessage
Get the message.
setMessage
Set the message.
getSeverity
Get the severity.
setSeverity
Set the severity.
getStatus
Get the status.
setStatus
Set the status.

PVAlarm

PVAlarm 
    boolean attach(PVField pvField)
    void detach()
    boolean isAttached()
    void get(Alarm alarm)
    boolean set(Alarm alarm)

where

PVAlarm
The default constructor. Attach must be called before get or set can be called.
attach
Attempts to attach to pvField It returns (false,true) if it found an appropriate pvData structure. It looks first a pvField itself and if is not an appropriate pvData structure but the field name is value it looks to see if the parent structure has an appropriate sub structure.
detach
Detaches from the pvData structure.
isAttached
Is there an attachment to an alarm structure?
get
Copies data from the pvData structure to an Alarm. An exception is thrown if not attached to a pvData structure.
set
Copies data from Alarm to the pvData structure. An exception is thrown if not attached to a pvData structure.

timeStamp

A timeStamp is represented by the following structure

time_t timeStamp
    long secondsPastEpoch
    int nanoseconds
    int userTag

The Epoch is the POSIX epoch, i.e. Jan 1, 1970 00:00:00 UTC. Both the seconds and nanoseconds are signed integers and thus can be negative. Since the seconds is kept as a 64 bit integer, it allows for a time much greater than the present age of the universe. Since the nanoseconds portion is kept as a 32 bit integer it is subject to overflow if a value that corresponds to a value that is greater than a little more than 2 seconds of less that about -2 seconds. The support code always adjust seconds so that the nanoSecconds part is normalized, i. e. it has is 0<=nanoseconds<nanoSecPerSec..

TimeStamp

The definition of TimeStamp is:

TimeStamp 
    void normalize()
    long getSecondsPastEpoch()
    long getEpicsSecondsPastEpoch()
    int getNanoseconds()
    int getUserTag()
    void setUserTag(int userTag)
    void put(long secondsPastEpoch,int nanoseconds)
    long getMilliSeconds()
    void put(long milliSeconds)
    void getCurrentTime()
    boolean equals(TimeStamp other)
    boolean lt(TimeStamp other)
    boolean le(TimeStamp other)
    void add(long seconds)
    void add(double seconds)
    double diff(TimeStamp a,TimeStamp b)
}

where:

normalize
Adjust secondsPastEpoch and nanoseconds so that 0<=nanoseconds<nanoSecPerSec.
getSecondsPastEpoch
Get the seconds part of timeStamp
getEpicsSecondsPastEpoch
Get seconds relative to the EPICS epoch. The epics epoch starts on Jan 1 1990 00:00:00 UTC.
getNanoseconds
Get the nanoseconds part of timeStamp.
getUserTag
Get the userTag
setUserTag
Set the userTag
put(long secondsPastEpoch,int nanoseconds)
Put a value into the timeStamp.
getMilliSeconds
Get the number of milliseconds since the epoch.
put(long milliSeconds);
Put a value into the timeStamp given the number of milliSeconds since the epoch.
getCurrentTime
Get the current time.
equals
Is this time equal other?
lt
Is this time less than other.
le
Is this time less that or equal to other.
add(long seconds)
Add the specified number of seconds.
add(double seconds)
Add the specified number of seconds.
diff
Compute a-b. The result is in seconds.

The TimeStamp class provides arithmetic and comparison methods for time stamps. The result is always kept in normalized form, which means that the nanosecond portion is 0<=nano<nanoSecPerSec. Note that it is OK to have timeStamps for times previous to the epoch.

PVTimeStamp

PVTimeStamp 
    boolean attach(PVField pvField)
    void detach()
    boolean isAttached()
    void get(TimeStamp timeStamp)
    boolean set(TimeStamp timeStamp)
where
attach
Attempts to attach to pvField It returns (false,true) if a timeStamp structure is found. It looks first at pvField itself and if is not an appropriate pvData structure but the field name is value it looks up the parent structure tree.
detach
Detach from the pvData structure.
isAttached
Is there an attachment to a timeStamp structure?
get
Copies data from the pvData structure to a TimeStamp. An exception is thrown if not attached to a pvData structure.
set
Copies data from TimeStamp to the pvData structure. An exception is thrown if not attached to a pvData structure.

display

Display information is represented by the following structure

display_t display
    double limitLow
    double limitHigh
    string description
    string format
    string units

Display

Display
    double getLow()
    double getHigh()
    void setLow(double value)
    void setHigh(double value)
    String getDescription()
    void setDescription(String value)
    String getFormat()
    void setFormat(String value)
    String getUnits()
    void setUnits(String value)

where

getLow
Get the low limit.
getHigh
Get the high limit.
setLow
Set the low limit.
setHigh
Set the high limit.
getDescription
Get the description.
setDescription
Set the description.
getFormat
Get the format.
setFormat
Set the format.
getUnits
Get the units.
setUnits
Set the units.

PVDisplay

PVDisplay
    boolean attach(PVField pvField)
    void detach()
    boolean isAttached()
    void get(Display display)
    boolean set(Display display)

where

attach
Attempts to attach to pvField It returns (false,true) if it found an appropriate pvData structure. It looks first a pvField itself and if is not an appropriate pvData structure but the field name is value it looks to see if the parent structure has an appropriate sub structure.
detach
Detaches from the pvData structure.
isAttached
Is there an attachment to a display structure?
get
Copies data from the pvData structure to a Display. An exception is thrown if not attached to a pvData structure.
set
Copies data from Display to the pvData structure. An exception is thrown if not attached to a pvData structure.
create
Create a PVDisplay instance. Attach must be called before get or set can be called.

control

Control information is represented by the following structure

structure control
    double limitLow
    double limitHigh
    double minStep

Control

The definition for Control is:
Control
    double getLow()
    double getHigh()
    void setLow(double value)
    void setHigh(double value)

where

getLow
Get the low limit.
getHigh
Get the high limit.
setLow
Set the low limit.
setHigh
Set the high limit.

PVControl

PVControl
    boolean attach(PVField pvField)
    void detach()
    boolean isAttached()
    void get(Control control)
    boolean set(Control control)

where

attach
Attempts to attach to pvField It returns (false,true) if it found an appropriate pvData structure. It looks first a pvField itself and if is not an appropriate pvData structure but the field name is value it looks to see if the parent structure has an appropriate sub structure.
detach
Detaches from the pvData structure.
isAttached
Is there an attachment to a control structure?
get
Copies data from the pvData structure to a Control. An exception is thrown if not attached to a pvData structure.
set
Copies data from Control to the pvData structure. An exception is thrown if not attached to a pvData structure.
create
Create a PVControl instance. Attach must be called before get or set can be called.

alarmLimit

This has unresolved issues.

Standard for Field::ID

The introspection interface for every field has an ID, which is available via method:

class Field {
...
    string getID();
...
};

This section describes how the IDs are assigned.

Scalar

boolean
byte
short
int
long
ubyte
ushort
ulong
float
double
string

Scalar Array

boolean[]
byte[]
short[]
int[]
long[]
ubyte[]
ushort[]
ulong[]
float[]
double[]
string[]

Union

any        // variant union
union      // restricted union

Union Array

any[]
union[]

structure

default

structure

standard fields

alarm_t
time_t
display_t
control_t
enum_t
alarmLimit_t
valueAlarm_t

Issues:

display_t
This has field
string format
The allowed syntax should be defined.
alarmLimit_t,valueAlarm_t
standardField and standardPVField define valueAlarm for boolean, each numeric scalar type, and for enum_t. alarmLimit_t has the same fields as valueAlarm for double. I suspect that most tools will only use alarmLimit_t. What to do?

structure array

Like structure except that [] is appended.

pvRequest and pvCopy

Oveview

Both pvDataJava and pvDataCPP implement the following:

CreateRequest
Creates a PVStructure that is a valid pvRequest to be passed between a client a server. The Channel class of pvAccess has methods that have pvRequest arguments. For example:
ChannelGet createChannelGet(
    ChannelGetRequester channelGetRequester,
    PVStructure pvRequest);
A pvRequest structure allows a client to select:
  1. An arbitrary subset of the fields in the top level data structure for the channel.
  2. Global and field specific options
PVCopy
PVCopy is a facility that copies data between a top level PVStructure for the client and the top level PVStructure from the server. CreateRequest allows the client to select the fields desired. PVCopy is used by the server side of pvAccess to transfer data between the two top level PVStructures.

The following subsections discuss:

CreateRequest
CreateRequest creates a pvRequest structure from a string argument. This subsection describes the syntax for the string argument.
pvRequest
The layout of a valid pvRequest structure is described.
PVCopy
Some details about PVCopy are described.
Guidelines for pvDatabase services
This subsection is of interest for implementing new services for channel providers.

CreateRequest

Goals

CreateRequest is a parser that creates a pvRequest structure from a string. It was created with the following goals:

Create any valid pvRequest
The layout of a valid pvRequest structure is described below.
Concise Syntax
The goal is to allow simple and concise syntax for common requests. For example:
pvget -r "value,alarm,timeStamp" channelName
Simple to parse
This does result in the syntax being somewhat rigid. For example order matters. A more flexible parser could be created but for now only the existing parser is described.

Definition

CreateRequest
    PVStructure createRequest(string request)
    string getMessage()
createRequest
Create a pvRequest PVStructure. If an invalid request is made a null PVStructure is returned.
getMessage
The reason why the last request failed.

Purpose

CreateRequest allows a client to select an arbitrary subset of the fields in the top level structure associated with a channel. It also allows the client to specify options. Thus the client can specify:

Global Options
Global options are options that apply to the record itself.
Desired Fields
An arbitrary set of fields can be selected from the top level structure that holds the data in the record.
Field Options
These are options that apply to a selected field.

NOTE: The term record is adapted from pvDatabase. A pvDatabase has a memory resident database of 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. A pvAcccess server can be implemented that does not use the pvDatabase model but it must provide top level PVStructures to which a pvAcccess Channel can be attached. qsrv, which provides access to iocCore V3 records, is an example that also accepts a pvRequest created by a call to createRequest.

Relationship to Channel methods of pvAccess

Channel has methods:

createChannelGet,createChannelPut,and createMonitor
For these the request string is of the form:
record[option,...]field(fieldDef,...)
OR
field(fieldDef,...)
OR
fieldDef,...
createChannelPutGet
For these the request string is of the form:
record[option,...]putField(fieldDef,...)getField(fieldDef,...)
OR
putField(fieldDef,...)getField(fieldDef,...)
createChannelProcess
Server dependent. Provider local accepts an empty string.
createChannelRPC
Server dependent.
createChannelArray
Server dependent. Provider local accepts an empty string or a string which is the field name.

Syntax

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:

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,putFeld,getField 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.

Assume that the channel has the following data:

pvget -r "field()" -i powerSupply
powerSupply
structure 
    alarm_t alarm
        int severity 2
        int status 0
        string message bad voltage
    time_t timeStamp
        long secondsPastEpoch 0
        int nanoseconds 0
        int userTag 0
    structure power
        double value 0
    structure voltage
        double value 0
    structure current
        double value 0
Then the following selects a subset of the fields:
mrk> pvget -r "field(alarm{severity,message},timeStamp.secondsPastEpoch,power)" -i powerSupply
powerSupply
structure 
    structure alarm
        int severity 2
        string message bad voltage
    structure timeStamp
        long secondsPastEpoch 0
    structure power
        double value 0

The syntax was chosen to be easy to use and to parse:

record[...]
Specifies a set of global options, i. e., options that apply to the record itself.
field(...)
putField(...)
getField(...)
Each selects a set of subfields of the top level structure. Each specifies a comma separated set of fieldDefs.
fieldDef
Selects a single subfield of the current structure.
option
A name=value pair. Both name and value are character strings.
[...]
Holds a comma separated set of options.
{...}
Selects a set of subfields of a substructure within the top level structure. Each defines a comma separated set of fieldDefs. This is a recursive definition. Thus a fullFieldName within {} is relative to structure that is referenced by {}.

Naming conventions

blanks
All blanks are removed before parsing is started.
reserved characters
The following characters may not be used except as used in the above syntax:
{ } ( ) [ ] = ,

The character
.
in a fieldDef separates field names.
field name
A field name can only contain alphanumeric characters and the character _, but must not start with the character _.
This is not enforced by the current parser but should be a rule for field names.
option name
This should follow the same convertion as field name.
This is not enforced by the parser but future changes could.
option value
For now the only illegal characters are the reserved characters. At least the following character should also be allowed:
: 

What other characters should be allowed?

Simple Examples

Clients like CSS, Synoptic Displays, Alarm Managers, and Archive Managers only want access to some combination of the following fields: value, alarm, timeStamp, display, and control. If the request is for a record that has these all as top level fields the request string is just a comma separated list of the field names. For example:

"value,alarm,timeStamp"

If the record is does not have the desired field at the top level then the field can still be accessed but in this case the full structure is preserved. For example:

"alarm,timeStamp,power.value"

Will get the top level alarm and timeStamp and the value from a structure named power. Thus the above works for a record that is structured as follows:

powerSupply
    alarm
    timeStamp
    power
       value
       display
       ...
    ...
What is returned to the client is:
powerSupply
    alarm
    timeStamp
    power
       value

The only option most clients want is to request that a record be processed as part of a get or put requests. This is done via requests like the following:

"record[process=true]field(value,alarm,timeStamp)"

Power Supply Example

The following examples are for a power supply record:

powerSupply
    alarm
    timeStamp
    power
       value
       alarm
       .. other fields
    voltage
       value
       alarm
       .. other fields
    current
       value
       alarm
       .. other fields
    .. other fields

The following request:

"field(alarm,timeStamp,power.value)"

Will return to the client the following:

record psSimple
    alarm_t alarm
        severity NONE status NONE
        message null
    time_t 2013-02-27 06:04:30.997 userTag 0
    structure power
        double value 10.0

Note that if the actual record does not have a requested field than it will not be present in the structure returned to the client.

The following is the same except that a record option and an option for the value field will be given.

"record[process=true]field(alarm,timeStamp,power.value[monitorAlgorithm=onChange])"

The options are to process the record and to cause power.value to cause a monitor only if the value changes.

The following:

"field(alarm,timeStamp,power{value,alarm},current{value,alarm},voltage{value,alarm})"

Will return to the client the following:

record psEmbeded
    alarm_t alarm
        severity NONE status NONE
        message
    time_t 1969-12-31 19:00:00.000 userTag 0
    structure power
        double value 0.0
    structure current
        double value 0.0
        alarm_t alarm
            severity NONE status NONE
            message
    structure voltage
        double value 0.0
        alarm_t alarm
            severity NONE status NONE
            message

Syntax for pvRequest structure created by createRequest

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

record
The options that apply to the entire record.
option
This is of the form string name value
field
Definitions that select fields of the PVRecord and options for the fields. This definition is recursive.
fieldName
The field name that will appear in the PVStructure that is a copy of the fields selected from the PVRecord.

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

PVCopy

This is the interface for mapping between a PVStructure that contain a copy of the data for a subset of the fields in a PVRecord. Note that this interface is NOT for a single PVStructure but for a single PVRecord and a single Structure introspection interface that describes a subset of the fields in the PVRecord. For example if a server supports monitor queues then the server will allocate a PVStructure for each queue element but will create a single PVCopy.

PVCopy
    PVMaster getPVMaster()
    void traverseMaster(PVCopyTraverseMasterCallback callback)
    Structure getStructure()
    PVStructure createPVStructure()
    int getCopyOffset(PVField masterPVField)
    int getCopyOffset(PVStructure masterPVStructure,PVField masterPVField)
    PVField getMasterPVField(int structureOffset)
    void initCopy(PVStructure pvCopy, BitSet bitSet,boolean masterLocked)
    void updateCopySetBitSet(PVStructure copyPVStructure,BitSet bitSet,boolean lockMaster)
    void updateCopyFromBitSet(PVStructure copyPVStructure,BitSet bitSet,boolean lockMaster)
    boolean updateMaster(PVStructure pvCopy,BitSet bitSet,boolean lockMaster)
    PVStructure getOptions(PVStructure copyPVStructure,int fieldOffset)
    String dump()
}

where

getPVMaster
Get the PVMaster to which this PVCopy is attached
traverseMaster
Traverse all the fields in master. The callback is called for each field in master.
getStructure
Get the introspection interface which describes the subset of the fields in the PVMaster.
createPVStructure
Create a PVStructure which can hold a subset of the data from the PVMaster. A client may require multiple PVStructures. For example if a monitor request supports a queue than a PVStructure is required for each queue element.
getCopyOffset(PVField masterPVField)
Given a PVField from the master determine the offset within the PVStructure where the copy of the data is located. PVStructure.getSubField(offset) can be called to locate the PVField within the PVStructure.
getCopyOffset(PVStructure masterPVStructure,PVField masterPVField)
Given a masterPVField within a masterPVStructure determine the offset within the PVStructure where the copy of the data is located. PVStructure.getSubField(offset) can be called to locate the PVField within the PVStructure.
getMasterPVField
Given an offset within a PVStructure return the corresponding PVField in the PVMaster.
initCopy
Initialize PVStructure with the current data from the PVMaster. The bitSet will have offset 0 set to 1 and all other bits set to 0.
updateCopySetBitSet
Update PVStructure from PVMaster. The BitSet shows which fields in PVStructure have changed.
updateCopyFromBitSet
Update PVStructure from PVMaster. Only fields that have the offset in bitSet set to true are modified.
updateMaster
Update the fields in PVMaster with data from PVStructure. Only fields that have the offset in bitSet set to true are modified.
getOptions
Get options for a field in a PVStructure created by pvCopy The arguments are the pvStructure and the offset of the field for which the options are wanted. It returns the options or null if the field did not have options.
dump
Provides a dump of the internal pvCopy nodes. This is useful for debugging monitor algorithms.

Guidelines for pvDatabase Services

This section is for services that use pvDatabaseCPP. A service provides a top level PVStructure and implements methods init, process, and destroy. pvDatabaseCPP and pvAccessCPP provide a complete implementation of the server side of pvAccess. From the PVStructure a PVRecord is created. This record can be locked.

pvDatabaseJava is similar except that the service only implements method process.

A server is providing data for multiple clients. Each client has it's own top level PVStructure that has data for a subset of the fields in the servers top level PVStructure. The service must be careful of how it updates data in it's top leve PVStructure or else data for a client can change after process completes and before it is sent to the client.

Previously the mapping between a PVStructure and it's associated bitSet as shown: bitSet for pvStructure

This can also be used to present guidelines for modifing fields in the top level PVStructure for the service:

The pvRecord must be locked whenever any field is modfied.

Any union, unionArray, or structureArray field that has a bit of the bitSet assigned to it must be replaced. Modifing subFields of the field is not permisible.

pvRequest Options

Overview

As described in the previous section, both record and field options can be specified. For this release of PVA only record options are implemented.

Record Options

At present the following record options are in use:

queueSize
This is used to define the queueSize for monitors. The default is:
record[queueSize=2]
A larger size can be specified.
process
This is used by qsrv and pvDatabaseCPP to specify of records should be processed. The default is false for channelGet and true for channelPut and channelPutGet, An example is:
record[process=false]
block
This is used by qsrv to specify if a request to process a record should block until the record completes processing. It is also used by the ca provider (in pvAccess) for channelPuts. The default is not to block. An example is:
record[block=true]

Convert Facility

Both pvDataJava and pvDataCPP have a Convert Facility. Although they have some common features they are different enough that they will be discussed separately.

They main difference is support for scalar arrays.

The Java Convert facility supports sub-array copies between any two numeric arrays. It does a deep copy except for a complete copy between two array of the same type and the source array is immutable. In this case to calls the shareData method and also makes the destination array immutable.

The C++ faclity only supports complete array copies between two scalar arrays of the same type and only does a shallow copy. It also does a shallow copy for union, unionArray, and structureArray fields. It also has a separate facility that does sub-array copies between two arrays of the same type.

Java

NOTE: copying immutable array fields. If an entire immutable array field is copied to another array that has the same elementType, both offsets are 0, and the length is the length of the source array, then the shareData method of the target array is called and the target array is set immutable. Thus the source and target share the same primitive array.

This section describes the supported conversions between data types.

interface Convert {
    void getFullFieldName(StringBuilder builder,PVField pvField)
    void getString(StringBuilder buf,PVField pv, int indentLevel);
    void getString(StringBuilder buf,PVField pv);
    void fromString(PVScalar pv,String from);
    void fromString(PVScalarArray pv,String from);
    int fromStringArray(PVScalarArray pv,
         int offset, int len, String[]from, int fromOffset);
    int toStringArray(PVScalarArray pv,
         int offset, int len, String[]to, int toOffset);
    boolean isCopyCompatible(Field from, Field to);
    void copy(PVField from,PVField to);
    boolean isCopyScalarCompatible(Field from, Field to);
    void copyScalar(PVField from, PVField to);
    boolean isCopyScalarArrayCompatible(ScalarArray from, ScalarArray to);
    int copyScalarArray(PVScalarArray from, int offset,
         PVScalarArray to, int toOffset, int len);
    boolean isCopyStructureCompatible(Structure from, Structure to);
    void copyStructure(PVStructure from, PVStructure to);
    boolean isCopyUnionCompatible(Union from, Union to);
    void copyUnion(PVUnion from, PVUnion to);
    boolean isCopyStructureArrayCompatible(StructureArray from, StructureArray to);
    void copyStructureArray(PVStructureArray from, PVStructureArray to);
    boolean isCopyUnionArrayCompatible(UnionArray from, UnionArray to);
    void copyUnionArray(PVUnionArray from, PVUnionArray to);
    // For the following the pv Type must be PVByte, ...., PVDouble
    byte toByte(PVField pv);
    short toShort(PVField pv);
    int   toInt(PVField pv);
    long  toLong(PVField pv);
    float toFloat(PVField pv);
    double toDouble(PVField pv);
    String toString(PVScalar pv);
    void  fromByte(PVField pv, byte from);
    void  fromShort(PVField pv, short from);
    void  fromInt(PVField pv, int from);
    void  fromLong(PVField pv, long from);
    void  fromUByte(PVField pv, byte from);
    void  fromUShort(PVField pv, short from);
    void  fromUInt(PVField pv, int from);
    void  fromULong(PVField pv, long from);
    void  fromFloat(PVField pv, float from);
    void  fromDouble(PVField pv, double from);
// For the following the element type must be pvByte, ...., pvDouble
    int toByteArray(PVScalarArray pv,
        int offset, int len, byte[]to, int toOffset);
    int toShortArray(PVScalarArray pv,
        int offset, int len, short[]to, int toOffset);
    int toIntArray(PVScalarArray pv,
        int offset, int len, int[]to, int toOffset);
    int toLongArray(PVScalarArray pv,
        int offset, int len, long[]to, int toOffset);
    int toFloatArray(PVScalarArray pv,
        int offset, int len, float[]to, int toOffset);
    int toDoubleArray(PVScalarArray pv,
        int offset, int len, double[]to, int toOffset);
    int fromByteArray(PVScalarArray pv,
        int offset, int len, byte[]from, fromOffset);
    int fromShortArray(PVScalarArray pv,
        int offset, int len, short[]from, fromOffset);
    int fromIntArray(PVScalarArray pv,
        int offset, int len, int[]from, fromOffset);
    int fromLongArray(PVScalarArray pv,
        int offset, int len, long[]from, fromOffset);
    int fromUByteArray(PVScalarArray pv,
        int offset, int len, byte[]from, fromOffset);
    int fromUShortArray(PVScalarArray pv,
        int offset, int len, short[]from, fromOffset);
    int fromUIntArray(PVScalarArray pv,
        int offset, int len, int[]from, fromOffset);
    int fromULongArray(PVScalarArray pv,
        int offset, int len, long[]from, fromOffset);
    int fromFloatArray(PVScalarArray pv,
        int offset, int len, float[]from, fromOffset);
    int fromDoubleArray(PVScalarArray pv,
        int offset, int len, double[]from, fromOffset);
    void newLine(StringBuilder builder, int indentLevel);
}

The array methods all return the number of elements copied or converted. This can be less than len if the PVField array contains less than len elements.

newLine is a convenience method for code that implements toString It generates a newline and inserts blanks at the beginning of the newline.

The getString methods dump the data in the metadata syntax described in the pvData project overview. Note that the toString methods of PVField are implemented by calling these convert methods.

C++

Many of the Java convert methods have been moved to class PVField and it's extensions. This include all the comparison methods and also conversion between numeric types.

convert.h

class Convert;
typedef std::tr1::shared_ptr<Convert> ConvertPtr;

class Convert {
public:
    static ConvertPtr getConvert();
    ~Convert();
    void getString(std::string * buf,PVFieldPtr const & pvField,int indentLevel);
    void getString(std::string * buf,PVFieldPtr const & pvField);
    void getString(std::string * buf,PVField const * pvField,int indentLevel);
    void getString(std::string * buf,PVField const * pvField);
    std::size_t fromString(
        PVStructurePtr const &pv,
        StringArray const & from,
        std::size_t fromStartIndex = 0);
    void fromString(PVScalarPtr const & pv, std::string const & from);
    std::size_t fromString(PVScalarArrayPtr const & pv, std::string const &from);
    std::size_t fromStringArray(
        PVScalarArrayPtr const & pv,
        std::size_t offset, std::size_t length,
        StringArray const & from,
        std::size_t fromOffset);
    std::size_t toStringArray(PVScalarArrayPtr const & pv,
        std::size_t offset,
        std::size_t length,
        StringArray & to,
        std::size_t toOffset);
    int8 toByte(PVScalarPtr const & pv);
    int16 toShort(PVScalarPtr const & pv);
    int32 toInt(PVScalarPtr const & pv);
    int64 toLong(PVScalarPtr const & pv);
    uint8 toUByte(PVScalarPtr const & pv);
    uint16 toUShort(PVScalarPtr const & pv);
    uint32 toUInt(PVScalarPtr const & pv);
    uint64 toULong(PVScalarPtr const & pv);
    float toFloat(PVScalarPtr const & pv);
    double toDouble(PVScalarPtr const & pv);
    std::string toString(PVScalarPtr const & pv);
    void fromByte(PVScalarPtr const & pv,int8 from);
    void fromShort(PVScalarPtr const & pv,int16 from);
    void fromInt(PVScalarPtr const & pv, int32 from);
    void fromLong(PVScalarPtr const & pv, int64 from);
    void fromUByte(PVScalarPtr const & pv,uint8 from);
    void fromUShort(PVScalarPtr const & pv,uint16 from);
    void fromUInt(PVScalarPtr const & pv, uint32 from);
    void fromULong(PVScalarPtr const & pv, uint64 from);
    void fromFloat(PVScalarPtr const & pv, float from);
    void fromDouble(PVScalarPtr const & pv, double from);
}

extern ConvertPtr getConvert();

pvSubArrayCopy.h

This supports sub-array copying between arrays that have the same type.

template<typename T>
void copy(
    PVValueArray<T> & pvFrom,
    size_t fromOffset,
    size_t fromStride,
    PVValueArray<T> & pvTo,
    size_t toOffset,
    size_t toStride,
    size_t count);

void copy(
    PVScalarArray & from,
    size_t fromOffset,
    size_t fromStride,
    PVScalarArray & to,
    size_t toOffset,
    size_t toStride,
    size_t count);

void copy(
    PVStructureArray & from,
    size_t fromOffset,
    size_t fromStride,
    PVStructureArray & to,
    size_t toOffset,
    size_t toStride,
    size_t count);

void copy(
    PVArray & from,
    size_t fromOffset,
    size_t fromStride,
    PVArray & to,
    size_t toOffset,
    size_t toStride,
    size_t count);

The last copy is the only one most client need to call. It either throws an error if the element types do not match or calls the other copy functions. The arguments are:

from
The source array.
fromOffset
The offset into the source array.
fromStride
The interval between source elements.
to
The destination array. The element type must be the same as for the source array. If the element type is structure then the introspection interface for the element types must be the same.
toOffset
The offset into the destination array.
toStride
The interval between destination elements.
count
The number of elements to copy.

An exception is thrown if:

type mismatch
The element types for the source and destination differ.
immutable
The destination array is immutable.
capacity immutable
The destination array needs to have it's capacity extended but the capacity is immutable.

PVField::copy

PVField has a method copy. It allows copying between any two compatible fields. Both fields must have the same type. For scalar and scalarArray conversion between the various scalarTypes is supported.

The following:

    PVDataCreatePtr create = getPVDataCreate();
    PVDoublePtr pvDouble = create->createPVScalar<PVDouble>();
    PVBytePtr pvByte = create->createPVScalar<PVByte>();
    pvByte->put(10);
    pvDouble->copy(*pvByte);
    cout << "double " << pvDouble << " byte " << pvByte << endl;

    PVStringPtr pvString = create->createPVScalar<PVString>();
    pvString->copy(*pvByte);
    cout << "string " << pvString << " byte " << pvByte << endl;
produces:
double 10 byte 10
string 10 byte 10

Likewise for scalarArray:

    PVDataCreatePtr create = getPVDataCreate();
    PVDoubleArrayPtr pvDoubleArray = create->createPVScalarArray<PVDoubleArray>();
    PVByteArrayPtr pvByteArray = create->createPVScalarArray<PVByteArray>();
    shared_vector<int8> data(2);
    data[0] = 1; data[1] = 10;
    pvByteArray->replace(freeze(data));
    pvDoubleArray->copy(*pvByteArray);
    cout << "double " << pvDoubleArray << " byte " << pvByteArray << endl;

    PVStringArrayPtr pvStringArray = create->createPVScalarArray<PVStringArray>();
    pvStringArray->copy(*pvByteArray);
    cout << "string " << pvStringArray << " byte " << pvByteArray << endl;
produces:
double [1,10] byte [1,10]
string [1,10] byte [1,10]