EPICS V4 Developer's Guide

EPICS v4 Working Group, Working Draft, 16-Feb-2017

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

Editors:
Marty Kraimer, BNL
David Hickin, Diamond Light Source
Matej Sekoranja, CosyLab

Abstract

This is Developer's Guide for EPICS V4, which is a set of related products:
relatedDocumentsV4.html

Table of Contents

Overview of V4

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

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

V4 Modules

V4 includes the following core components:
pvData
The EPICS V4 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 is a synchronous interface for pvAccess and also provides a number of convenience methods.
pvDatabase
A memory resident database of PVRecords and a pvAccess channel provider for accessing the PVRecords.
pvaSrv
A pvAccess channel provider for EPICS V3 DBRecords.
example
A set of examples that use all of the above components.
pvaPy
Python wrapper for pvData and pvAccess.

pvaSrv 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

Terminology

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. This document uses a combination of psuedo code, Java code, and C++ code for describing APIs. It uses psuedo code when there is equivalent code in Java and in C++. The psuedo code is similar to Java interface definitions. The language specific APIs are provided in separate projects e. g. pvDataJava, and pvDataCPP. This document also describes the APIs for the other V4 core modules.
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

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 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

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 numeric 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

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
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 it, 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

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.

Overview: pvDatabase

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. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. 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 and a process method.

Overview: pvaSrv

pvaSrv is a channel provider for DBRecords in an EPICS V3 IOC. pvaSrv 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).

Overview: pvaPy

A Python wrapper for pvData and pvAccess.

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.

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
pvaSrv
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.

EPICS V4 resources and website

This document is a tutorial rather then a detailed reference manual. The following should be consulted for detailed documentation:

In addition the following are standards:

Current Status

pvData
EPICS V4 Release 4.6 has a complete implementation for both Java and C++.
normativeTypes
EPICS V4 Release 4.6 has C++ and Java helper classes for many, but not all, of the normative types.
pvAccess
EPICS V4 Release 4.6 has a complete implementation for both C++ and Java.
pvaClient
EPICS V4 Release 4.6 has an implementation for both Java and CPP
However getField and channelArray are not implemented.
pvDatabase
EPICS V4 Release 4.6 has an implementation for both C++ and Java. For C++ the pvDatabase can run as part of a V3 IOC.
example
EPICS V4 Release 4.6 has an implementation for both C++ and Java.
pvaSrv
EPICS V4 Release 4.6 has an implementation for C++. Since this is for iocCore a Java implementation is not necessary.
pvaPy
EPICS V4 Release 4.6 has an implementation for C++. Since this is for Python a Java implementation is not necessary.

pvlist, pvget, pvput, 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:${EPICSV4}/pvAccessCPP/bin/${EPICS_HOST_ARCH}

pvlist

The options are:

mrk> pvlist -help

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

  -h: Help: Print this message
options:
  -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
double01                       0
mrk> pvget DBRdouble01
double01                       0

The options are:

mrk> pvget -help

Usage: pvget [options] PVname...

  -h Help: Print this message
options:
  -r pvrequest       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 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'
  -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)

example: pvget double01

Some examples are:

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                       1
New : DBRdouble01                       1
mrk> pvput DBRdouble01 1.0
Old : DBRdouble01                       1
New : DBRdouble01                       1

The options are:

mrk> pvput -help

Usage: pvput [options] PVname values...

  -h Help: Print this message
options:
  -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 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
 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

example: pvput double01 1.234

At the present time it is not possible to send output to multiple channels with one command.

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 -h

Usage: eget [options] [PV_name ... | -s service_name]

  -h  Help: Print this message

options:
  -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 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 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 -

Channel Provider Context

A channel provider normally has "context" associated with it. For example the client side of provider pva needs threads for broadcasts, connecting to a channel, and passing data between client and server. This section discusses how existing providers are initialized so that a provider can create it's context.

If pvaClient is used by a client it helps with initiatizing required providers. This section discusses client provider initialization via pvaClient.

If pvDatabase implements and registers server provider local. This section discusses server provider initialization via pvDatabase.

Fundamental Rules For Initializing Provider Context

Client Providers

A client should only initialize the client providers it uses. Since each provider can have overhead associated with it's context, A client should not be started unless it is actually used.

Server Providers

The remote side of pva is started via a call to:

startPVAServer("providerList",...

providerList should only include the server providers required. and each provider must have registered with channelProviderRegistry before startPVAServer is called.

In particular the list should not include any client providers or network loops may occur.

Context Initialization via pvaClient

PvaClient is a singleton class. It initializes client providers, can cache channel connections, and some other channel methods.

A C++ client starts pvaClient via:

PvaClientPtr pva= PvaClient::get("providerList");

A Java client starts pvaClient via:

PvaClient pva= PvaClient.get("providerList");

providerList is a space separated list of provider names. If this is called by a pure client the the only currently supported options are:

"pva"
The context for the client side of provider pva is started.
"ca"
The context for the client side of provider ca is started.
"pva ca"
The context for the client side of provider pva and ca are started.

Other client providers can also be implemented but each must call provide a method to initialize it's context and each must register itself before PvaClient is started.

It is also possible for PvaClient to be used in a server to access other channels. An example is a PVRecord that has a link to another channel. In this case the providerList can also include service providers, but the service providers must have registered with channelProviderRegistry before PvaClient is started.

Server Context Initialization

Both Java and C++ implement a pvAccess method named startPVAServer, which starts the server context for the remote side of provider pva. The first argument to this method is a string that has a space separated list of provider names. Each provider must be registered with channelProviderRegistry before startPVAServer is called.

pvDatabase implements a provider named local, which is a provider that accesses PVRecords.

pvaSrv implements provider dbPv, which is a provider for accessing V3 DBRecords. This is only available for a V3 IOC.

Other server providers can also be provided but each must register with channelProviderRegistry before startPVAServer is called.

In Java the context for the remote side of provider pva is started via:

ServerContextImpl context = ServerContextImpl.("local",0,true,System.out);

In C++ a server that is not part of a V3 IOC, the context for the remote side of provider. pva is started via:

ServerContextImpl context = ServerContextImpl.startPVAServer("local",0,true,System.out);

In C++ a server that is part of a V3 IOC, the context for the remote side of provider is started via the following statement in a st.cmd file.

startPVAServer "local dbPv"

example code

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 dbPv so that both ca and pva can be used to access the V3 records.
exampleClient
example client code using the API for pvaClient
exampleLink
Implements PVRecords that monitor, get, and put to channel.
For now the other channel must exists or initialization fails.

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 pvaSrv. 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 pvaSrv 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
Channel connect timed out: 'PVRlong' not found.
PVRlong                        0
mrk> caget PVRlong
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/master/exampleCPP/exampleClient
mrk> ls bin/linux-x86_64/
examplePvaClientGet          examplePvaClientNTMulti  helloWorldPutGet
examplePvaClientMonitor      examplePvaClientProcess  helloWorldRPC
examplePvaClientMultiDouble  examplePvaClientPut

mrk> bin/linux-x86_64/examplePvaClientGet
_____examplePvaClientGet starting_______
__exampleDouble__
short way
as double 0
long way
as double 0
... LOTS MORE OUTPUT

An example of running the examples in pvaPy is:

mrk> pwd
/home/epicsv4/pvaPy/examples
mrk> python testPut.py
****************************
Testing string
Got old value:  
Putting value:  ; a
Got new value:  ; a
Putting value:  ; b
Got new value:  ; b

...... lots more output
mrk>

API: pvData

ScalarType

This defines the pvData primitive types:

boolean
Can be either true or false
byte
An 8 bit signed integer.
short
A 16 bit signed integer.
int
A 32 bit signed integer.
long
A 64 bit signed integer.
ubyte
An 8 bit unsigned integer.
ushort
A 16 bit unsigned integer.
uint
A 32 bit unsigned integer.
ulong
A 64 bit unsigned integer.
float
A 32 bit IEEE floating point number.
double
A 64 bit IEEE floating point number.
string
In Java a Java String. In C++ a std::string. On the network a UTF8 encoded string.

Type

A pvData object consists of a set of fields where each field has one on the following types:

scalar
The type of the field is one of the scalar types.
scalarArray
The type of the field is an array one of the scalar types.
structure
The field has a set of sub-fields. Each sub-field has a name and a type that can be any valid Type.
structureArray
The field is an array of structure fields where each element has the same introspection interface.
union
The field has a single sub-field. Two types of union are supported: a restricted union and a variant union. For a variant union the sub-field can be have any valid Type. For a restricted union the sub-field can have any of a specified set of Types.
unionArray
The field is an array of union fields where each element has the same introspection interface.

Data Examples

NOTE: In the examples alarm_t, time_t, and enum_t are used as type names. All have type structure. They are examples of special fields described later.

The following is a top level structure that has three sub-fields:

value
This has type scalar and scalarType double.
alarm
This is a structure field.
timeStamp
This is also a structure field
structure
    double value 5.0
    alarm_t alarm
        int severity 0
        int status 0
        string message no alarm
    time_t timeStamp
        long secondsPastEpoch 0
        int nanoseconds 0
        int userTag 0

The following is an example where the value field is a scalar string array.

structure
    string[] value ["aaa","bbb","ccc","ddd"]
    time_t timeStamp
        long secondsPastEpoch 0
        int nanoseconds 0
        int userTag 0

The following example is a structure with a single subfield that is itself a structure.

structure
    enum_t value
        int index 0
        string[] choices [true,false]

The following has two examples of a restricted union.

structure
    union value
        time_t
            long secondsPastEpoch 1000
            int nanoseconds 0
            int userTag 0

structure
    union value
        double  100000

This is like the previous example but now the value field is a variant union.

structure
    any value
        time_t
            long secondsPastEpoch 1000
            int nanoseconds 0
            int userTag 0

structure
    any value
        double  100000

The first example is a union array where each element is variant union. The second is where each element is a restricted union.

structure
    any[] value
        any
            (none)
        any
            double  1.245
`
structure
    union[] value
        union
            (none)
        union
            double  1.245

This is a structure array where each element is a time_t structure.

structure
    time_t[] value
        time_t
            long secondsPastEpoch 1000
            int nanoseconds 0
            int userTag 0
        time_t
            long secondsPastEpoch 3000
            int nanoseconds 0
            int userTag 0

This is a structure that has data for a power supply.

structure
    alarm_t alarm
        int severity 0
        int status 0
        string message
    time_t timestamp
        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 message

Introspection API

Field
    string getID()
    Type getType()
    serialize/deserialize
    display

    Scalar
        ScalarType getScalarType()

    Union
        size_t getNumberFields()
        Field getField(string fieldName)
        size_t getFieldIndex(string fieldName)
        Field[] getFields()
        Field getField(size_t fieldIndex)
        string[] getFieldNames()
        string getFieldName(size_t fieldIndex)
        boolean isVariant()

        Array
            ArraySizeType getArraySizeType()
            size_t getMaximumCapacity()

        ScalarArray
            ScalarType getElementType()

        StructureArray
            Structure getStructure()

        UnionArray
            Union getUnion()

     Structure
         size_t getNumberFields()
         Field getField(string fieldName)
         Field getField(size_t fieldIndex)
         size_t getFieldIndex(string fieldName)
         Field[] getFields()
         string[] getFieldNames()
         string getFieldName(size_t fieldIndex)

Field

This is the base class for the Introspection API. It has the members:

string getID()
Each field instance has an asociated ID, which is just a string. The core part of pvData and pvAccess do not attach any semantics to the value except that pvAccess transfers the ID when it transfers an introspection object. FieldCreate assigns a default ID to each type but also allows code to assign different values. This feature is used for both special fields and normative types.
Type getType()
Get the type.
serialize/deserialize
These are methods to convert an introspection object to/from a byte stream. These are used by pvAccess to transfer introspection objects between client and server. Other code could also use these methods. This document does not provide the details. See pvDataCPP or pvDataJava for details.
display
These methods are used to display the value of an introspection object. Java provides toString methods. C++ provides C++ steam methods. See pvDataCPP or pvDataJava for details.

Scalar

This is the API for type scalar. It has a single method:

ScalarType getScalarType()
Return the scalarType

Union

This is the API for type union. A union is a field that has a single subField. The subField has no name. A union can be either a variant union or a restricted union. The subField for a variant union can have any supported type. A restricted union has a Field array and a string array. Each element of the string array is associated with the corresponding element of the field array.

Union has the methods:

size_t getNumberFields()
Return the number of elements in the Field and string arrays. This is always 0 for a variant union.
Field getField(string fieldName)
Return the Field associated with the fieldName. A null Field is returned if the fieldName is not valid. For a variant array null is always returned.
size_t getFieldIndex(string fieldName)
Return the index associated with the fieldName. -1 is returned if the fieldName is not valid. For a variant array -1 is always returned.
Field[] getFields()
Get the field types. For a variant array and empty array is returned.
Field getField(size_t fieldIndex)
Get the introspection interface associates with the fieldIndex. A null Field is returned if the fieldIndex is not valid. For a variant array null is always returned.
string[] getFieldNames()
Get the field names. For a variant array and empty array is returned.
string getFieldName(size_t fieldIndex)
Get the name asociated with the fieldIndex. An empty string is returned if the fieldIndex is not valid. For a variant array an empty string is always returned.
boolean isVariant()
returns (false,true) if (restricted, variant) union.

Array

Base class for all array types. It has the methods:

ArraySizeType getArraySizeType()
Size means the number of elements. ArraySizeType is one of:
variable
The size is only limited by what cpu architecture allows.
fixed
The size is determined when an instance is created and can not be changed.
bounded
The size can be changed but only up to a maximum specified when an instance is created.
size_t getMaximumCapacity()
Get the maximum capacity and also size that array can have.

ScalarArray

This is the API for type scalarArray. It has the method:

ScalarType getElementType()
Get the ScalarType for each element.

StructureArray

This is the API for type structureArray. It has the method:

Structure getStructure()
Get the introspection interface that each element must have. Note that all elements MUST have exactly the same introspection interface.

UnionArray

This is the API for type unionArray. It has the method:

Union getUnion()
Get the introspection interface that each element must have. Note that all elements MUST have exactly the same introspection interface.

Structure

This is the API for type structure. It has the methods:

getNumberFields
Get the number of immediate sub-field in the structure. -1 is returned if the structure does not have any fields.
Field getField(string fieldName)
Get the subField with the specified name. A null Field is returned if the structure does not have a subField with the specified name.
size_t getFieldIndex(string fieldName)
Get the index of the subField with the specified name. -1 is returned if the structure does not have a subField with the specified name.
Field[] getFields()
Get the Field array for the subFields of the structure.
Field getField(size_t fieldIndex)
Get the subField with the specified index. A null Field is returned if the fieldIndex is invalid for this structure.
string[] getFieldNames()
Get fieldName array for the subFields.
string getFieldName(size_t fieldIndex)
Get the subField name with the specified index. An empty string is returned if the fieldIndex is invalid for this structure.

Introspection Factory

FieldBuilder is the easiest way to create introspection objects. It calls methods in FieldCreate.

NOTE: Unless there is a compelling need then You should always use FieldBuilder rather then calling methods in FieldCreate.

FieldBuilder
    FieldBuilder setId(string id)
    FieldBuilder add(string name, ScalarType scalarType)
    FieldBuilder addBoundedString(string name, size_t maxLength)
    FieldBuilder add(string name, Field field)
    FieldBuilder addArray(string name, ScalarType scalarType)
    FieldBuilder addFixedArray(string name, ScalarType scalarType, size_t size)
    FieldBuilder addBoundedArray(string name, ScalarType scalarType, size_t bound)
    FieldBuilder addArray(string name, Field field)
    Structure createStructure()
    Union createUnion()
    FieldBuilder addNestedStructure(string name)
    FieldBuilder addNestedUnion(string name)
    FieldBuilder addNestedStructureArray(string name)
    FieldBuilder addNestedUnionArray(string name)
    FieldBuilder endNested()

FieldCreate is a factory for creating introspection objects.

FieldCreate
    static FieldBuilder createFieldBuilder()
    Scalar createScalar(ScalarType scalarType)
    BoundedString createBoundedString(size_t maxLength)
    ScalarArray createScalarArray(ScalarType elementType)
    ScalarArray createFixedScalarArray(ScalarType elementType, size_t size)
    ScalarArray createBoundedScalarArray(ScalarType elementType, size_t bound)
    Structure createStructure() //Java does not implement
    Structure createStructure(Structure structToClone) // CPP does not implement
    Structure createStructure(string[] fieldNames, Field[] fields)
    Structure createStructure(string id, string[] fieldNames, Field[] fields)
    StructureArray createStructureArray(Structure elementStructure)
    Union createVariantUnion()
    Union createUnion(string[] fieldNames, Field[] fields)
    Union createUnion(string id, string[] fieldNames, Field[] fields)
    UnionArray createUnionArray(Union elementUnion)
    UnionArray createVariantUnionArray()
    Structure appendField(Structure structure,string fieldName, Field field)
    Structure appendFields(Structure structure,string[] fieldNames, Field[] fields)

FieldBuilder

This is an easy way to create introspection interfaces. A single instance can be used to create multiple introspection interfaces because everytime createStructure or createUnion is called the internal state is reset to start over. Each method except createStructure and createUnion return the FieldBuilder itself so that methods can be chained together. See the examples below. The methods are:

FieldBuilder setId(string id)
Specify the ID for the next Structure or Union to create.
FieldBuilder add(string name, ScalarType scalarType)
Add a scalar field.
FieldBuilder addBoundedString(string name, size_t maxLength)
Add a bounded string field.
FieldBuilder add(string name, Field field)
Add a field with the specified type.
FieldBuilder addArray(string name, ScalarType scalarType)
Add a scalarArray with the specified element type.
FieldBuilder addFixedArray(string name, ScalarType scalarType, size_t size)
Add a fixed size scalarArray with the specified element type.
FieldBuilder addBoundedArray(string name, ScalarType scalarType, size_t bound)
Add a bounded size scalarArray with the specified element type.
FieldBuilder addArray(string name, Field field)
Add an array with elementType field.
Structure createStructure()
Create a Structure from what has been defined so far. The internal state is reset after creation.
Union createUnion()
Create a Union from what has been defined so far. The internal state is reset after creation.
FieldBuilder addNestedStructure(string name)
Add a nested Structure. All fields added until the corresponding endNested will be in the nested Structure.
FieldBuilder addNestedUnion(string name)
Add a nested Union. All field added until the corresponding endNested will be in the nested Union.
FieldBuilder addNestedStructureArray(string name)
Add a nested StructureArray. All fields added until the corresponding endNested will be in the nested StructureArray.
FieldBuilder addNestedUnionArray(string name)
Add a nested UnionArray. All fields added until the corresponding endNested will be in the nested UnionArray.
FieldBuilder endNested()
End of the current nested Structure, Union, StructureArray, or UnionArray.

FieldCreate

The methods are:

FieldBuilder createFieldBuilder()
Create a FieldBuilder which was described above.
Scalar createScalar(ScalarType scalarType)
Create a Scalar of the specified type.
BoundedString createBoundedString(size_t maxLength)
Create a BoundedString, which is a Scalar with ScalarType pvString and that has a bounded size.
ScalarArray createScalarArray(ScalarType elementType)
Create a ScalarArray that has elements of the specified ScalarType.
ScalarArray createFixedScalarArray(ScalarType elementType, size_t size)
Create a ScalarArray of the specified ScalarType and that will have the specified fixed size.
ScalarArray createBoundedScalarArray(ScalarType elementType, size_t bound)
Create a ScalarArray of the specified ScalarType and that will have the specified maximum size.
Structure createStructure() //Java does not implement
Create a Structure with no subFields.
Structure createStructure(Structure structToClone) // CPP does not implement
Create a new Structure that is a clone of an existing structure.
Structure createStructure(string[] fieldNames, Field[] fields)
Create a Structure with the specified set of fieldNames and fields. The length of the two arrays must be the same.
Structure createStructure(string id, string[] fieldNames, Field[] fields)
Create a Structure with the specified set of fieldNames and fields. The length of the two arrays must be the same. The Structure will have the ID specfied.
StructureArray createStructureArray(Structure elementStructure)
Create a StructureArray where each element will have the specified introspection interface.
Union createVariantUnion()
Create a variant Union.
Union createUnion(string[] fieldNames, Field[] fields)
Create an restricted Union that can have a subField with one of the types specified by fields.
Union createUnion(string id, string[] fieldNames, Field[] fields)
Like the previous method but with the ID specified.
UnionArray createUnionArray(Union elementUnion)
Create a UnionArray where each element will have the specified introspection interface.
UnionArray createVariantUnionArray()
Create a UnionArray where each element can have any valid type.
Structure appendField(Structure structure,string fieldName, Field field)
Create a new Structure that is the old structure with an appended subField.
Structure appendFields(Structure structure,string[] fieldNames, Field[] fields)
Create a new Structure that is the old structure with appended subFields.

Data API

PVField
    serialize/deserialize
    display
    string getFieldName()
    string getFullName()
    size_t getFieldOffset()
    size_t getNextFieldOffset()
    size_t getNumberFields()
    boolean isImmutable()
    void setImmutable()
    Field getField()
    PVStructure getParent()
    void postPut()
    void setPostHandler(PostHandler postHandler)

    PVScalar
        Scalar getScalar()

        PVScalarType // each scalarType, e. g. PVBoolean
            scalarType get()
            void put (scalarType value)

    PVUnion
        Union getUnion()
        PVField get()
        PVType get(Class c)           //PVType is valid PVField extension
        PVField select(int index)
        PVType select(Class c,int index)
        PVField select(string fieldName)
        PVType select(Class c,string fieldName)
        int getSelectedIndex()
        string getSelectedFieldName()
        void set(PVField value)
        void set(int index, PVField value)
        void set(string fieldName, PVField value)

    PVArray
        Array getArray()
        size_t getLength(
        void setLength(size_t length)
        size_t getCapacity(
        void setCapacity(size_t length)
        boolean isCapacityMutable()
        void setCapacityMutable(boolean isMutable)

        PVScalarArray
            ScalarArray getScalarArray()

            PVScalarTypeArray  // each scalarType, e.g. PVBooleanArray
                // C++ and Java have different semantics. See below

        PVUnionArray
            UnionArray getUnionArray()
            // C++ and Java have different semantics. See below

        PVStructureArray
            StructureArray getStructureArray()
            // C++ and Java have different semantics. See below.

PVField

serialize/deserialize
These are methods to convert a data object to/from a byte stream. These are used by pvAccess to transfer data objects between client and server. Other code could also use these methods. This document does not provide the details. See pvDataCPP or pvDataJava for details.
display
These methods are used to display the value of a data object. Java provides toString methods. C++ provides steam methods. See pvDataCPP or pvDataJava for details.
string getFieldName()
Get the name of the field. The name of a top level field is an empty string.
string getFullName()
Fully expand the name of this field using the names of its parent fields with a dot '.' separating each name. For example if an alarm is a top level field named alarm then the status field has the name alarm.status.
size_t getFieldOffset()
Get offset of the PVField field within top level structure. Every field within the PVStructure has a unique offset. The top level structure has an offset of 0. The first field within the structure has offset equal to 1. The other offsets are determined by recursivelys traversing each structure of the tree.
size_t getNextFieldOffset()
Get the next offset. If the field is a scalar or union or array field then this is just offset + 1. If the field is a structure it is the offset of the next field after this structure. Thus (nextOffset - offset) is always equal to the number of fields within the field.
size_t getNumberFields()
Get the total number of fields in this field. This is equal to nextFieldOffset - fieldOffset.
boolean isImmutable()
Is the field immutable, i.e. does it not allow changes.
void setImmutable()
Set the field to be immutable, i. e. it can no longer be modified. This is permanent, i.e. once done the field can not be made mutable.
Field getField()
Get the Field that describes the field.
PVStructure getParent()
Get the parent of this field. A top level field has a null parent.
void postPut()
Called when the field is updated by the implementation.
void setPostHandler(PostHandler postHandler)
Set the handler for postPut. At most one handler can be set.

PVScalar

Scalar getScalar()
Get the Scalar that describes the field.

PVScalarType // each scalarType, e. g. PVBoolean

scalarType get()
Get the value. For example PVDouble has a method double get().
void put (scalarType value)
Change the value. For example PVDouble has a method void put(double value).

PVUnion

Union getUnion()
Get the Union that describes the field.
PVField get()
Get the current data member. This can be null.
PVType get(Class c)
Get the current data member. This returns null if no member or if current member is not of the type sepecified.
PVField select(int index)
If the union is a restricted union than select the field specified by index. If the curent field has a different type than a new field is created. An exception is thrown if the index is out of range or for a variant union. The interface to the data object is returned.
PVType select(Class c,int index)
Like the previous method but null is returned if the data object does not have the specified type.
PVField select(string fieldName)
If the union is a restricted union than select the field specified by fieldName. If the curent field has a different type than a new field is created. An exception is thrown if the fieldName is not valid or for a variant union. The interface to the data object is returned.
PVType select(Class c,string fieldName)
Like the previous method but null is returned if the data object does not have the specified type.
int getSelectedIndex()
Get the index of the current field. A -1 is returned if no field defined or for a variant union.
string getSelectedFieldName()
Get the fieldName of the current field. A null string is returned if no field defined or for a variant union.
void set(PVField value)
Set the data member to the specified value. An exception is thrown if the value is not valid for this PVUnion.
void set(int index, PVField value)
Set the data member to the specified index and value. An exception is thrown if the value is not valid for this index.
void set(string fieldName, PVField value)
Set the data member to the specified fieldName and value. An exception is thrown if the value is not valid for this fieldName.

PVArray

Array getArray()
Get the Array that describes the field.
size_t getLength(
Get the current length of the array.
void setLength(size_t length)
Set the length of the array. An exception is thrown if the value is not legal for this array.
size_t getCapacity(
Get the current capacity of the array.
void setCapacity(size_t length)
Set the capacity. An exception is thrown if the value is not legal for this array.
boolean isCapacityMutable()
Returns (true,false) if the capacity (can,can not) be changed.
void setCapacityMutable(boolean isMutable)
Set capacityMutable to the specified value.

PVScalarArray

Most methods are different for Java and C++. See following sections.

ScalarArray getScalarArray()
Get the ScalarArray that describes the field.

PVUnionArray

Most methods are different for Java and C++. See following sections.

UnionArray getUnionArray()
Get the UnionArray that describes the field.

PVStructureArray

Most methods are different for Java and C++. See following sections.

StructureArray getStructureArray()
Get the StructureArray that describes the field.

Data Factory

PVDataCreate
    PVField createPVField(Field field)
    PVField createPVField(PVField fieldToClone)
    PVScalar createPVScalar(Scalar scalar)
    PVScalar createPVScalar(ScalarType fieldType)
    PVScalar createPVScalar(PVScalar scalarToClone)
    PVScalarType createPVScalar(Class scalarType) // generic 
    PVUnion createPVUnion(Union union)
    PVUnion createPVVariantUnion()
    PVUnion createPVUnion(PVUnion unionToClone)
    PVStructure createPVStructure(Structure structure)
    PVStructure createPVStructure(string[] fieldNames,PVField[] pvFields)
    PVStructure createPVStructure(PVStructure structToClone)
    PVScalarArray createPVScalarArray(ScalarArray array)
    PVScalarArray createPVScalarArray(ScalarType elementType)
    PVScalarArray createPVScalarArray(PVScalarArray arrayToClone)
    PVScalarTypeArray createPVScalarArray(Class element) // generic 
    PVStructureArray createPVStructureArray(StructureArray structureArray)
    PVStructureArray createPVStructureArray(Struture structure)
    PVUnionArray createPVUnionArray(UnionArray unionArray)
    PVUnionArray createPVVariantUnionArray()
    PVUnionArray createPVUnionArray(Union union)
where
PVField createPVField(Field field)
Create a PVField with the specified introspection interface.
PVField createPVField(PVField fieldToClone)
Create a PVField with the same introspection interface as fieldToClone and then copy data from fieldToClone to newly created PVField.
PVScalar createPVScalar(Scalar scalar)
Create a PVScalar with the specified introspection interface.
PVScalar createPVScalar(ScalarType fieldType)
Create a PVScalar with the specified fieldType.
PVScalar createPVScalar(PVScalar scalarToClone)
Create a PVScalar with the same introspection interface as scalarToClone and then copy data from scalarToClone to newly created PVScalar.
PVScalarType createPVScalar(Class scalarType)
Generic version of createPVScalar. See pvDataJava or pvDataCPP for language specific syntax.
PVUnion createPVUnion(Union union)
Create a PVUnion with the specified introspection interface.
PVUnion createPVVariantUnion()
Create a variant PVUnion.
PVUnion createPVUnion(PVUnion unionToClone)
Create a PVUnion with the same introspection interface as unionToClone and then copy data from unionToClone to newly created PVUnion.
PVStructure createPVStructure(Structure structure)
Create a PVStructure with the specified introspection interface.
PVStructure createPVStructure(string[] fieldNames,PVField[] pvFields)
Create a PVStructure with the specified set of fieldNames. The introspection interface for the subFields is the same as the introspection interfaces of pvFields. Why does this method exist?
PVStructure createPVStructure(PVStructure structToClone)
Create a PVStructure with the same introspection interface as structToClone and then copy data from structToClone to newly created PVStructure.
PVScalarArray createPVScalarArray(ScalarArray array)
Create a PVScalarArray with the specified introspection interface.
PVScalarArray createPVScalarArray(ScalarType elementType)
Create a PVScalarArray with the specified elementType.
PVScalarArray createPVScalarArray(PVScalarArray arrayToClone)
Create a PVScalarArray with the same introspection interface as arrayToClone and then copy data from arrayToClone to newly created PVScalarArray.
PVScalarTypeArray createPVScalarArray(Class element)
Generic version. See pvDataJava or pvDataCPP for language specific syntax.
PVStructureArray createPVStructureArray(StructureArray structureArray)
Create a PVStructureArray with the specified introspection interface.
PVStructureArray createPVStructureArray(Struture structure)
Create a PVStructureArray where each element has the specified introspection interface.
PVUnionArray createPVUnionArray(UnionArray unionArray)
Create a PVUnionArray with the specified introspection interface.
PVUnionArray createPVVariantUnionArray()
Create a variant PVUnionArray.
PVUnionArray createPVUnionArray(Union union)
Create a PVUnionArray where each element has the specified introspection interface.

Java Array API

The following is for PVDoubleArray. There are similar definitions for each scalarType.

public class DoubleArrayData {
    public double[] data
    public int offset
}

interface PVDoubleArray extends PVArray {
    int get(int offset, int len, DoubleArrayData data)
    int put(int offset,int len, double[] from, int fromOffset)
    void shareData(double[] from)
}

For PVStructureArray and PVUnionArray the definitions are:

public class StructureArrayData {
    public PVStructure[] data
    public int offset
}

interface PVStructureArray extends PVArray {
    StructureArray getStructureArray()
    int get(int offset, int length, StructureArrayData data)
    int put(int offset,int length, PVStructure[] from, int fromOffset)
    void shareData(PVStructure[] from)
}

public class UnionArrayData {
    public PVUnion[] data
    public int offset
}

interface PVUnionArray extends PVArray{
    UnionArray getUnionArray()
    int get(int offset, int length, UnionArrayData data)
    int put(int offset,int length, PVUnion[] from, int fromOffset)
    void shareData(PVUnion[] from)
}

C++ Array API

The C++ Array API inforces Copy On Write (COW) semantics. The actual definition is template based. But the following is the psuedo code for PVDoubleArray. Each scalarType, PVUnionArray, and PVStructureArray have similar psuedo code.

class PVDoubleArray
    ...
    shared_vector<const double> view()
    void swap(shared_vector<const double> other)
    void replace(const shared_vector<const double> next)
    shared_vector<double> reuse()
    ...
// to change the raw array use code like:
shared_vector<double> data(n)
// put values into data and then
pvDoubleArray->replace(freeze(data))

BitSet and PVStructure

Some important features of pvAccess are:

PVAccess Transmits Top Level PVStructures
For example a channelGet sends data from server to client as a top level PVStructure. For a channelPutGet a top level PVStructure is sent from the client to the server and a different top level PVStructure is returned from the server to the client.
Top Level Introspection Immutable
Once a top level PVStructure is created it's introspection can not be modified. Thus pvAccess has no need to send introspection information with data. Consider channelGet as an example. When a channelGet is created the server creates a top level pvStructure for get requests. The server sends the introspection info back to the client side of pvAccess, which also creates a top level pvStructure. Each time the client issues a get request the data is copied into the top level pvStructure of the server and transmitted to the client side of pvAccess, which puts the data in it's top level pvStructure. This is the data presented to the client.
Only fields that change are transmitted.
Only the data for fields that change value between requests are sent.
Clients can request an arbitrary set of fields from server.
The client does not have to receive all fields provided by the server but can request an arbitrary subset of the fields.

In order to implement the above features pvAccess uses bitSets, where each bit is associated with a field. The following is a top level pvStructure and how bits from a bitSet are assigned:

bit#    field
0    structure
1        double scalarValue
2        double[] scalarArrayValue
3        structure timeStamp
4            long secondsPastEpoch
5            int nanoSeconds
6            int userTag
7        union unionValue
             structure timeStamp
                 long secondsPastEpoch
                 int nanoSeconds
                 int userTag
8        structure[] structureValue
             structure timeStamp
                 long secondsPastEpoch
                 int nanoSeconds
                 int userTag
             structure timeStamp
                 long secondsPastEpoch
                 int nanoSeconds
                 int userTag
9        any[] unionValue
             any
                 structure timeStamp
                     long secondsPastEpoch
                     int nanoSeconds
                     int userTag
             any
                 double value
10       string lastValue

Note that bits for structure subfields are assigned recursively. But each scalar, union, scalarArray, structureArray, and unionArray field are assigned a single bit. This means that each subfield of a union, structuctureArray, or unionArray is treated just like a new top level field. If ANY change is made to a union, scalarArray, structureArray, or unionArray, the complete set of data for that field is sent.

Below the following are discussed:

Convert
A facility that supports copy and convert methods for PVData.
pvCopy
A facility that transfers data between a client top level PVStructure and the top level PVStructure from the server.

An important featured provided by Convert is to copy data between compatible PVStructures.

Java feature: pvDataJava does a deep copy of all fields with the possible exception of scalar array fields. If the source and destination have the same scalarType and the source is immutable then the raw data array is shared and the destination is made immutable.

C++ feature: pvDataCPP does deep copies between scalar and structure fields but shallow copies between scalarArray, structureArray, and unionArray fields. For scalarArray fields this is safe because the implementation of arrays implements COW (Copy on Write) by making the array data const. This forces code to create new raw data in order to change a scalar array field. It also uses const for structureArray and unionArray fields but this does not force all elements of the array to be const. Thus the elements can be changed. Thus any code that wants to share a top level PVStructure must be very careful when changing the value of a union, structureArray, or unionArray field. The rule is that the union, structureArray, or unionArray must be replaced with a new value.

Since the pvCopy facility makes extensive use of Convert the same rules apply to it.

pvDataCPP examples

C++ Introspection Basic Example

The first example creates a Structure with a single subField:

Using FieldBuilder

FieldBuilderPtr fb = fieldCreate->createFieldBuilder();
StructureConstPtr structure =
    fb->add("value", pvDouble)->
    createStructure();
cout << structure << endl;

This produces:

structure
    double value

Using FieldCreate the code is

StringArray fieldName(1);
FieldConstPtrArray field(1);
fieldName[0] = "value";
field[0] = fieldCreate->createScalar(pvDouble);

StructureConstPtr structure =
    fieldCreate->createStructure(fieldName, field);
cout << structure << endl;

This produces:

structure
    double value

C++ Introspection More Complex Example

The next example shows how to create a structure that has three fields: a value field that is a scalarArray with element type double, an alarm structure, and a timeStamp structure.

NOTE: Three different ways to create the same structure are shown.

Using FieldBuilder and StandardField

Prefered way!! This is the easiest way to create the example and also creates the correct introspection interface for standard fields.

cout << "valueAlarmTimeStamp\n";
FieldBuilderPtr fb = fieldCreate->createFieldBuilder();
StandardFieldPtr standardField = getStandardField();
StructureConstPtr structure  =
    fb->addArray("value", pvDouble)->
    add("alarm",standardField->alarm()))->
    add("timeStamp",standardField->timeStamp()))->
    createStructure();
cout << structure << endl;
This produces:
valueAlarmTimeStamp
structure
    double[] value
    alarm_t alarm
        int severity
        int status
        string message
    time_t timeStamp
        long secondsPastEpoch
        int nanoseconds
        int userTag

Using FieldBuilder

cout << "valueAlarmTimeStamp\n";
FieldBuilderPtr fb = fieldCreate->createFieldBuilder();
StructureConstPtr alarm =
   fb->add("severity", pvInt)->
   add("status",pvInt)->
   add("message",pvString)->
   createStructure();
StructureConstPtr timeStamp =
        fb->add("secondsPastEpoch", pvLong)->
        add("nanoseconds",pvInt)->
        add("userTag",pvInt)->
        createStructure();
StructureConstPtr structure  =
    fb->addArray("value", pvDouble)->
    add("alarm",alarm)->
    add("timeStamp",timeStamp)->
    createStructure();
cout << structure << endl;
This produces:
valueAlarmTimeStamp
structure
    double[] value
    structure alarm
        int severity
        int status
        string message
    structure timeStamp
        long secondsPastEpoch
        int nanoseconds
        int userTag

Using FieldCreate

size_t n = 3;
StringArray fieldName(n);
FieldConstPtrArray field(n);
fieldName[0] ="severity";
fieldName[1] ="status";
fieldName[2] ="message";
field[0] = fieldCreate->createScalar(pvInt);
field[1] = fieldCreate->createScalar(pvInt);
field[2] = fieldCreate->createScalar(pvString);
StructureConstPtr alarm = fieldCreate->createStructure(fieldName, field);
fieldName = StringArray(n);
field = FieldConstPtrArray(n);
fieldName[0] = "secondsPastEpoch";
fieldName[1] = "nanoseconds";
fieldName[2] = "userTag";
field[0] = fieldCreate->createScalar(pvLong);
field[1] = fieldCreate->createScalar(pvInt);
field[2] = fieldCreate->createScalar(pvInt);
StructureConstPtr timeStamp = fieldCreate->createStructure(fieldName, field);
fieldName = StringArray(n);
field = FieldConstPtrArray(n);
fieldName[0] = "value";
fieldName[1] = "alarm";
fieldName[2] = "timeStamp";
field[0] = fieldCreate->createScalarArray(pvDouble);
field[1] = alarm;
field[2] = timeStamp;
StructureConstPtr structure = fieldCreate->createStructure(fieldName, field);
cout << structure << endl;
This produces:
structure
    double[] value
    structure alarm
        int severity
        int status
        string message
    structure timeStamp
        long secondsPastEpoch
        int nanoseconds
        int userTag

More C++ Introspection Examples Using FieldBuilder

valueAlarmTimeStampNested
cout << "valueAlarmTimeStampNested\n";
FieldBuilderPtr fb = fieldCreate->createFieldBuilder();
StructureConstPtr structure  =
    fb->addArray("value", pvDouble)->
    addNestedStructure("alarm")->
        add("severity", pvInt)->
        add("status",pvInt)->
        add("message",pvString)->
    endNested()->
    addNestedStructure("timeStamp")->
        add("secondsPastEpoch", pvLong)->
        add("nanoseconds",pvInt)->
        add("userTag",pvInt)->
        endNested()->
    createStructure();
cout << structure << endl;
This produces:
structure
    double[] value
    structure alarm
        int severity
        int status
        string message
    structure timeStamp
        long secondsPastEpoch
        int nanoseconds
        int userTag
union
cout << "union\n";
FieldBuilderPtr fb = fieldCreate->createFieldBuilder();
UnionConstPtr unionx =
    fb->add("short", pvShort)->
    add("long", pvLong)->
    createUnion();
StructureConstPtr structure  =
    fb->add("value",unionx)->
        createStructure();
    cout << structure << endl;
This produces:
union
structure
    union value
        short short
        long long`
unionNested
cout << "unionNested\n";
FieldBuilderPtr fb = fieldCreate->createFieldBuilder();
StructureConstPtr structure  =
    fb->addNestedUnion("value")->
        add("short", pvShort)->
        add("long", pvLong)->
        endNested()->
    createStructure();
cout << structure << endl;
This produces the same result as union.
unionArray
cout << "unionArray\n";
FieldBuilderPtr fb = fieldCreate->createFieldBuilder();
UnionConstPtr unionx =
    fb->add("short", pvShort)->
    add("long", pvLong)->
    createUnion();
StructureConstPtr structure  =
    fb->addArray("value",unionx)->
    createStructure();
cout << structure << endl;
This produces:
unionArray
structure
    union[] value
        union
            short short
            long long
unionArrayNested
cout << "unionArrayNested\n";
FieldBuilderPtr fb = fieldCreate->createFieldBuilder();
StructureConstPtr structure  =
    fb->addNestedUnionArray("value")->
        add("short", pvShort)->
        add("long", pvLong)->
    endNested()->
        createStructure();
    cout << structure << endl;
This produces the same result as unionArray.
structureArray
cout << "structureArray\n";
FieldBuilderPtr fb = fieldCreate->createFieldBuilder();
StructureConstPtr subStruct =
    fb->add("short", pvShort)->
    add("long", pvLong)->
    createStructure();
StructureConstPtr structure  =
    fb->addArray("value",subStruct)->
createStructure();
cout << structure << endl;
This produces:
structureArray
structure
structure[] value
        structure
            short short
            long long
structureArrayNested
cout << "structureArrayNested\n";
FieldBuilderPtr fb = fieldCreate->createFieldBuilder();
StructureConstPtr structure  =
    fb->addNestedStructureArray("value")->
    add("short", pvShort)->
    add("long", pvLong)->
    endNested()->
        createStructure();
    cout << structure << endl;
This produces the same result as structureArray.

C++ Data Examples

simpleExample
cout << "simple\n";
FieldBuilderPtr fb = fieldCreate->createFieldBuilder();
StructureConstPtr structure  =
    fb->add("value", pvDouble)->
    createStructure();
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(structure);
PVDoublePtr pvDouble = pvStructure->getSubField<PVDouble>( "value");
pvDouble->put(10.0);
cout << pvStructure << endl;
This produces:
simple
structure 
    double value 10.0
valueAlarmTimeStamp
cout << "valueAlarmTimeStamp\n";
FieldBuilderPtr fb = fieldCreate->createFieldBuilder();
StandardFieldPtr standardField = getStandardField();
StructureConstPtr structure =
    fb->addArray("value", pvDouble)->
    add("alarm",standardField->alarm())->
    add("timeStamp",standardField->timeStamp())->
    createStructure();
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(structure);
PVDoubleArrayPtr pvDoubleArray = pvStructure->getSubField<PVDoubleArray>("value");
size_t len = 4;
PVDoubleArray::svector data(4);
for(size_t i=0; i<len; ++i) data[i] = i+1;
pvDoubleArray->replace(freeze(data));
    cout << pvStructure << endl;
This produces:
valueAlarmTimeStamp
structure 
    double[] value [1.0,2.0,3.0,4.0]
    alarm_t alarm
        int severity 0
        int status 0
        string message 
    time_t timeStamp
        long secondsPastEpoch 0
        int nanoseconds 0
        int userTag 0
union
cout << "unionNested\n";
FieldBuilderPtr fb = fieldCreate->createFieldBuilder();
StructureConstPtr structure  =
    fb->addNestedUnion("value")->
        add("short", pvShort)->
        add("long", pvLong)->
        endNested()->
    createStructure();
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(structure);
PVLongPtr pvlong = pvDataCreate->createPVScalar<PVLong>();
pvlong->put(1000);
PVUnionPtr pvUnion = pvStructure->getSubField<PVUnion>("value");
pvUnion->set("long",pvlong);
    cout << pvStructure << endl;
This produces:
union
structure 
    union value
        long  1000
unionArray
cout << "unionArrayNested\n";
FieldBuilderPtr fb = fieldCreate->createFieldBuilder();
StructureConstPtr structure  =
    fb->addNestedUnionArray("value")->
        add("short", pvShort)->
        add("long", pvLong)->
    endNested()->
    createStructure();
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(structure);
PVUnionArrayPtr pvValue = pvStructure->getSubField<PVUnionArray>("value");
size_t len = 2;
PVUnionArray::svector pvUnions(len);
for(size_t i=0; i<len; ++i) pvUnions[i] =
    pvDataCreate->createPVUnion(pvValue->getUnionArray()->getUnion());
pvUnions[0]->select("short");
pvUnions[1]->select("long");
PVShortPtr pvshort = pvUnions[0]->get<PVShort>();
pvshort->put(1);
PVLongPtr pvlong = pvUnions[1]->get<PVLong>();
pvlong->put(5);
pvValue->replace(freeze(pvUnions));
cout << pvStructure << endl;
This produces:
unionArray
structure 
    union[] value 
        union 
            short  1
        union 
            long  5
structureArray
cout << "structureArrayNested\n";
FieldBuilderPtr fb = fieldCreate->createFieldBuilder();
StructureConstPtr structure  =
    fb->addNestedStructureArray("value")->
        add("short", pvShort)->
        add("long", pvLong)->
    endNested()->
    createStructure();
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(structure);
PVStructureArrayPtr pvValue = pvStructure->getSubField<PVStructureArray>("value");
size_t len = 2;
PVStructureArray::svector pvStructures(len);
for(size_t i=0; i<len; ++i) {
    pvStructures[i] = pvDataCreate->createPVStructure(
        pvValue->getStructureArray()->getStructure());
    PVStructurePtr pvs = pvStructures[i];
    PVShortPtr pvshort = pvs->getSubField<PVShort>("short");
    pvshort->put(i);
    PVLongPtr pvlong = pvs->getSubField<PVLong>("long");
    pvlong->put(i);
}
pvValue->replace(freeze(pvStructures));
    cout << pvStructure << endl;
This produces:
structureArray
structure 
    structure[] value 
        structure 
            short short 0
            long long 0
        structure 
            short short 1
            long long 1
structureArray
System.out.println("structureArray");
FieldBuilder fb = fieldCreate.createFieldBuilder();
Structure structure = 
   fb.addNestedStructureArray("value").
        add("short", ScalarType.pvShort).
        add("long", ScalarType.pvLong).
    endNested().
    createStructure();
System.out.println(structure);`
This produces the same result as structureArray.

pvDataJava examples

Java Introspection Basic Example

The first example creates a Structure with a single subField:

Using FieldBuilder

FieldBuilder fb = fieldCreate.createFieldBuilder();
Structure structure = 
    fb.add("value", ScalarType.pvDouble).
    createStructure();
System.out.println(structure);

This produces:

structure
    double value

Using FieldCreate

String[] fieldName = new String[1];
Field[] field = new Field[1];
fieldName[0] = "value";
field[0] = fieldCreate.createScalar(ScalarType.pvDouble);
Structure structure = fieldCreate.createStructure(fieldName, field);
System.out.println(structure);

The output is again:

structure
    double value

Java Introspection More Complex Example

This example shows how to create a structure that has three fields: a value field that is a scalarArray with element type double, an alarm structure, and a timeStamp structure.

Using FieldBuilder and StandardField

System.out.println("valueAlarmTimeStamp");
FieldBuilder fb = fieldCreate.createFieldBuilder();
StandardField standardField = StandardFieldFactory.getStandardField();
Structure structure =
     fb.addArray("value", ScalarType.pvDouble).
     add("alarm",standardField.alarm()).
     add("timeStamp",standardField.timeStamp())).
     createStructure();
System.out.println(structure);
This produces:
valueAlarmTimeStamp
structure
    double[] value
    alarm_t alarm
        int severity
        int status
        string message
    time_t timeStamp
        long secondsPastEpoch
        int nanoseconds
        int userTag

Using FieldBuilder

System.out.println("valueAlarmTimeStamp");
FieldBuilder fb = fieldCreate.createFieldBuilder();
Structure alarm =
     fb.add("severity", ScalarType.pvInt).
     add("status",ScalarType.pvInt).
     add("message",ScalarType.pvString).
     createStructure();
Structure timeStamp =
     fb.add("secondsPastEpoch", ScalarType.pvLong).
     add("nanoseconds",ScalarType.pvInt).
     add("userTag",ScalarType.pvInt).
     createStructure();
Structure structure =
     fb.addArray("value", ScalarType.pvDouble).
     add("alarm",alarm).
     add("timeStamp",timeStamp).
     createStructure();
System.out.println(structure);
This produces:
valueAlarmTimeStamp
structure
    double[] value
    structure alarm
        int severity
        int status
        string message
    structure timeStamp
        long secondsPastEpoch
        int nanoseconds
        int userTag

Using FieldCreate

String[] fieldName = new String[3];
Field[] field = new Field[3];
fieldName[0] ="severity";
fieldName[1] ="status";
fieldName[2] ="message";
field[0] = fieldCreate.createScalar(ScalarType.pvInt);
field[1] = fieldCreate.createScalar(ScalarType.pvInt);
field[2] = fieldCreate.createScalar(ScalarType.pvString);
Structure alarm = fieldCreate.createStructure(fieldName, field);
fieldName = new String[3];
field = new Field[3];
fieldName[0] = "secondsPastEpoch";
fieldName[1] = "nanoseconds";
fieldName[2] = "userTag";
field[0] = fieldCreate.createScalar(ScalarType.pvLong);
field[1] = fieldCreate.createScalar(ScalarType.pvInt);
field[2] = fieldCreate.createScalar(ScalarType.pvInt);
Structure timeStamp = fieldCreate.createStructure(fieldName, field);
fieldName = new String[3];
field = new Field[3];
fieldName[0] = "value";
fieldName[1] = "alarm";
fieldName[2] = "timeStamp";
field[0] = fieldCreate.createScalarArray(ScalarType.pvDouble);
field[1] = alarm;
field[2] = timeStamp;
Structure structure = fieldCreate.createStructure(fieldName, field);
System.out.println(structure);
This produces:
structure
    double[] value
    structure alarm
        int severity
        int status
        string message
    structure timeStamp
        long secondsPastEpoch
        int nanoseconds
        int userTag

More Java Introspection Examples Using FieldBuilder

valueAlarmTimeStampNested
System.out.println("valueAlarmTimeStampNested");
FieldBuilder fb = fieldCreate.createFieldBuilder();
Structure structure =
    fb.addArray("value", ScalarType.pvDouble).
    addNestedStructure("alarm").
         add("severity", ScalarType.pvInt).
         add("status",ScalarType.pvInt).
         add("message",ScalarType.pvString).
    endNested().
    addNestedStructure("timeStamp").
         add("secondsPastEpoch", ScalarType.pvLong).
         add("nanoseconds",ScalarType.pvInt).
         add("userTag",ScalarType.pvInt).
    endNested().
    createStructure();
System.out.println(structure);
This produces the same output as valueAlarmTimeStamp.
union
System.out.println("union");
FieldBuilder fb = fieldCreate.createFieldBuilder();
Union union =
    fb.add("short", ScalarType.pvShort).
    add("long", ScalarType.pvLong).
    createUnion();
Structure structure = 
    fb.add("value",union).
    createStructure();
System.out.println(structure);
This produces:
union
structure
    union value
        short short
        long long`
unionNested
System.out.println("unionNested");
FieldBuilder fb = fieldCreate.createFieldBuilder();
Structure structure = 
    fb.addNestedUnion("value").
        add("short", ScalarType.pvShort).
        add("long", ScalarType.pvLong).
        endNested().
    createStructure();
System.out.println(structure);
This produces the same result as union.
unionArray
System.out.println("unionArray");
FieldBuilder fb = fieldCreate.createFieldBuilder();
Union union =
    fb.add("short", ScalarType.pvShort).
    add("long", ScalarType.pvLong).
    createUnion();
Structure structure = 
    fb.addArray("value",union).
    createStructure();
System.out.println(structure);
This produces:
unionArray
structure
    union[] value
        union
            short short
            long long
unionArrayNested
System.out.println("unionArrayNested");
FieldBuilder fb = fieldCreate.createFieldBuilder();
Structure structure = 
    fb.addNestedUnionArray("value").
        add("short", ScalarType.pvShort).
        add("long", ScalarType.pvLong).
    endNested().
    createStructure();
System.out.println(structure);
This produces the same result as unionArray.
structureArray
System.out.println("structureArray");
FieldBuilder fb = fieldCreate.createFieldBuilder();
Structure subStruct =
    fb.add("short", ScalarType.pvShort).
    add("long", ScalarType.pvLong).
    createStructure();
Structure structure = 
    fb.addArray("value",subStruct).
    createStructure();
System.out.println(structure);
This produces:
structureArray
structure
    structure[] value
        structure
            short short
            long long
structureArrayNested
System.out.println("structureArrayNested");
FieldBuilder fb = fieldCreate.createFieldBuilder();
Structure structure = 
   fb.addNestedStructureArray("value").
        add("short", ScalarType.pvShort).
        add("long", ScalarType.pvLong).
    endNested().
    createStructure();
System.out.println(structure);`
This produces the same result as structureArray.

Java Data Examples

simpleExample
System.out.println("simple");
FieldBuilder fb = fieldCreate.createFieldBuilder();
Structure structure = 
    fb.add("value", ScalarType.pvDouble).
    createStructure();
PVStructure pvStructure = pvDataCreate.createPVStructure(structure);
PVDouble pvDouble = pvStructure.getSubField(PVDouble.class, "value");
pvDouble.put(10.0);
System.out.println(pvStructure);
This produces:
simple
structure 
    double value 10.0
valueAlarmTimeStamp
System.out.println("valueAlarmTimeStamp");
FieldBuilder fb = fieldCreate.createFieldBuilder();
StandardField standardField = StandardFieldFactory.getStandardField();
Structure structure =
        fb.addArray("value", ScalarType.pvDouble).
        add("alarm",standardField.alarm()).
        add("timeStamp",standardField.timeStamp()).
        createStructure();
PVStructure pvStructure = pvDataCreate.createPVStructure(structure);
PVDoubleArray pvDoubleArray = pvStructure.getSubField(PVDoubleArray.class, "value");
int length = 4;
double[] data = new double[length];
for (int i=0; <length; ++i) data[i] = i+1;
convert.fromDoubleArray(pvDoubleArray, 0, length, data, 0);
System.out.println(pvStructure);
This produces:
valueAlarmTimeStamp
structure 
    double[] value [1.0,2.0,3.0,4.0]
    alarm_t alarm
        int severity 0
        int status 0
        string message 
    time_t timeStamp
        long secondsPastEpoch 0
        int nanoseconds 0
        int userTag 0
union
System.out.println("union");
FieldBuilder fb = fieldCreate.createFieldBuilder();
Structure structure = 
    fb.addNestedUnion("value").
        add("short", ScalarType.pvShort).
        add("long", ScalarType.pvLong).
        endNested().
    createStructure();
PVStructure pvStructure = pvDataCreate.createPVStructure(structure);
PVUnion pvUnion = pvStructure.getSubField(PVUnion.class,"value");
PVLong pvLong = (PVLong)pvDataCreate.createPVScalar(ScalarType.pvLong);
pvLong.put(1000);
pvUnion.set("long",pvLong);
System.out.println(pvStructure);
This produces:
union
structure 
    union value
        long  1000
unionArray
System.out.println("unionArray");
FieldBuilder fb = fieldCreate.createFieldBuilder();
Structure structure = 
    fb.addNestedUnionArray("value").
        add("short", ScalarType.pvShort).
        add("long", ScalarType.pvLong).
    endNested().
    createStructure();
PVStructure pvStructure = pvDataCreate.createPVStructure(structure);
PVUnionArray pvValue = pvStructure.getSubField(PVUnionArray.class,"value");
int len = 2;
PVUnion[] pvUnions = new PVUnion[len];
for(int i=0; <len; ++i) pvUnions[i] = pvDataCreate.createPVUnion(pvValue.getUnionArray().getUnion());
pvUnions[0].select("short");
pvUnions[1].select("long");
PVShort pvShort = pvUnions[0].get(PVShort.class);
pvShort.put((short)1);
PVLong pvLong = pvUnions[1].get(PVLong.class);
pvLong.put(5);
pvValue.put(0, len, pvUnions, 0);
System.out.println(pvStructure);
This produces:
unionArray
structure 
    union[] value 
        union 
            short  1
        union 
            long  5
structureArray
System.out.println("structureArray");
FieldBuilder fb = fieldCreate.createFieldBuilder();
Structure structure = 
        fb.addNestedStructureArray("value").
        add("short", ScalarType.pvShort).
        add("long", ScalarType.pvLong).
    endNested().
    createStructure();
PVStructure pvStructure = pvDataCreate.createPVStructure(structure);
PVStructureArray pvValue = pvStructure.getSubField(PVStructureArray.class,"value");
int len = 2;
PVStructure[] pvStructures = new PVStructure[len];
for(int i=0; i<len; ++i) {
    pvStructures[i] = pvDataCreate.createPVStructure(pvValue.getStructureArray().getStructure());
    PVStructure pvs = pvStructures[i];
    PVShort pvShort = pvs.getSubField(PVShort.class,"short");
    pvShort.put((short)i);
    PVLong pvLong = pvs.getSubField(PVLong.class,"long");
    pvLong.put(i);
}
pvValue.put(0, len,pvStructures , 0);
System.out.println(pvStructure);
This produces:
structureArray
structure 
    structure[] value 
        structure 
            short short 0
            long long 0
        structure 
            short short 1
            long long 1
structureArray
System.out.println("structureArray");
FieldBuilder fb = fieldCreate.createFieldBuilder();
Structure structure = 
   fb.addNestedStructureArray("value").
        add("short", ScalarType.pvShort).
        add("long", ScalarType.pvLong).
    endNested().
    createStructure();
System.out.println(structure);`
This produces the same result as structureArray.

API: normativeType

normativeTypes defines a set of standard structures. Each normative type consists of a set of mandatory fields, a set of optional fields, and an arbitrary number of extra fields. The mandatory and optional fields are meant for use by standard tools such as Display Managers and Alarm Handlers. The extra fields are for specialized tools.

normativeTypes provides a set of helper classes for implementing and using normative types. For the complete set of normative types see normativeTypesJava or normativeTypesCPP.

Some of the types it supports are:

NTScalar
This has a value field that has type scalar
NTScalarArray
This has a value field that has type scalarArray
NTNameValue
This has a field name that is a string array and a field value that has type scalarArray. Each name[i] is associated with value[i].
NTTable
This has a string array field named labels For each label there is a scalar array field with name = label.
NTMultiChannel
This has a value field that is a union array. A primary use of this type is to access data from a set of pvAccess channels. Each element of value holds data from a channel.
NTNDArray
This type holds NDArray data as defined by the areaDetector facility.

For example NTScalar is defined as :

epics:nt/NTScalar:1.0
    double value                        // mandatory and can be any numeric 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                     //

API: pvaClient

The following sections use psuedo code to describe methods. Consult the Java or C++ documentation for the exact syntax.

PvaClient Overview

The pvAccess Client API is:

PvaClient is a synchronous API for accessing PVData via PVAccess. It also provides a number of convenience methods. It allows the client to request access without checking for failure, but throws an exception when a reuest fails. A client can also check for failues and thus prevent failures.

The PvaClient API has the following features:

  1. Provides a synchronous API rather than the callback API provided by pvAccess.
  2. Makes common requests easy.
  3. Provides full access to the pvAccess API for more demanding applications
  4. Allows efficient client side programs.
  5. Can cache channels, channelGets, and channelPuts.
  6. Takes care of most object resource management problems.

Simple examples of using pvaClient:

This is a C++ example.

// easyGet
PvaClientPtr pvaClient = PvaClient::create();
double value = pvaClient->channel("exampleDouble")->get()->getData()->getDouble();

// easyPut
PvaClientChannelPtr channel = pvaClient->channel("exampleDouble");
PvaClientPutPtr put = channel->put();
PvaClientPutDataPtr putData = put->getData();
putData->putDouble(3.0); put->put();

// easyMonitor
PvaClientMonitorPtr monitor = pvaClient->channel("examplePowerSupply")->monitor("");
PvaClientMonitorDataPtr easyData = monitor->getData();
while(true) {
    monitor->waitEvent();
    cout << "changed\n";
    easyData->showChanged(cout);
    cout << "overrun\n";
    easyData->showOverrun(cout);
    monitor->releaseEvent();
}
// easyProcess
PvaClientChannelPtr channel = pvaClient->channel("exampleDouble");
PvaClientProcessPtr process = channel->createProcess();
process->process();

PvaClient can cache channels, channelGets, and channelPuts.

A simple Java example is:

String channelName = "someChannel";
double value =  pva.channel(channelName).get().getData().getDouble();
...
value = pva.channel(channelName).get().getData().getDouble();
...

The simple example allows the client to get a value from the server with a single call. This example results in a channel and channelGet both being cached. The first call results in connecting to the server, creating a channelGet, and issuing a get request. The second call reuses the channel and channelGet that were cached by the first request. If any part of the request fails an exception occurs.

The same request could also be done by:

String channelName = "someChannel";
PvaClientChannel pvaChannel = pva.createChannel(channelName);
pvaChannel.issueConnect();
Status status = pvaChannel.waitConnect(2.0);
if(!status.isOK()) {System.out.println(" connect failed"); return;}
PvaClientGet pvaGet = pvaChannel.createGet();
pvaGet.issueConnect();
status = pvaGet.waitConnect();
if(!status.isOK()) {System.out.println(" createGet failed"); return;}
PvaClientGetData pvaData = pvaGet.getData();
value = pvaData.getDouble();
...
pvaData = pvaGet.getData();
value = pvaData.getDouble();

In this case no caching occurs but the client is responsible for lots more then in the simple example.

Class PvaClient

class PvaClient
{
    // methods for creating and accessing the singleton instance of PvaClient
    static PvaClient get(string providerNames = "pva ca");
    // method for creating cached instances of PvaClientChannel
    PvaClientChannel channel(
        string channelName,
        string providerName = "pva",
        double timeOut = 5.0);
    // method for creating non-cached instances of PvaClientChannel
    PvaClientChannel createChannel(
        string channelName,
        string providerName = "pva");
    // methods for ChannelRequester and Requester
    string getRequesterName();
    void message(string message, MessageType type);
    void setRequester(Requester requester);
    void clearRequester();
    // methods for channel cache
    void showCache();
    size_t cacheSize();
    // debug methods
    void setDebug(boolean value);
    boolean getDebug();
    // destroy the singleton instance of PvaClient
    void destroy(); // JAVA Only
}
get

For both Java and C++ PvaClient is a singleton class, i. e. there is a single instance created. In Java it is created via:

PvaClient pva= PvaClient.get("providerList");
An example is:
PvaClient pva= PvaClient.get("pva ca");

In C++ it is created via:

PvaClientPtr pva= PvaClient::get("providerList");
An example is:
PvaClientPtr pva= PvaClient::get("pva ca");

For both Java and C++ if no argument is specified for get than the default is "pva ca"

If get is called multiple times only the first call creates the single instance of PvaClient. Additional calls return the same instance.

Two providers are special:

pva
The client side of provider pva is registered and started.
ca
The client side of provider ca is registered and started. V3 implements the server side of the ca network protocal.

Other client providers can be specicified but must be registered and started before PvaClient is started.

PvaClient provides a cache for channels, channelGet, etc. Thus in the following:

PvaClient pva= PvaClient.get();
...
double value = pva.channel(channelName).get().getData().getDouble();
...
value = pva.channel(channelName).get().getData().getDouble();

Only the first call for value will result in connection to channelName and creation of a ChannelGet. The second call uses a cached channel and a cached channelGet. The next section describes when instances are cached.

All cached objects and context for providers pva and ca are destroyed when the destructor for PvaClient is called. For Java the client must call pva.destroy(). For C++ this is done automatically when the destructor for PvaClient is called.

channel
This method creates and connects to cached channels.
If a channel for channelName and providerName already exists in the cache it is just returned.
If it is not found in the cache Then an attempt is made to connect to the channel. If the attempt fails an exception is thrown.
If the attempt succeeds the channel is cached and returned to the caller.
createChannel
A channel is created and returned to the caller, but the channel is not connected to the server.
The caller must call PvaClientChannel methods to connect.
The channel is not cached.
Requester Methods
PvaClient implements interfaces ChannelRequester and Requester, which are required by pvAccess, but also allows the user to implement Requester.
Cache Methods
These are methods for seeing what is in the cache.
PvaClient:: Debug Merhods
This is a way to get debug output while using PvaClient. These are static methods that can be used by all the classes that are part of PvaClient. They can also be used by client code.

Class PvaClientChannel

class PvaClientChannel
{
    // connect and throw if failure
    void connect(double timeOut = 5.0);
    // connect and return Status if failure
    void issueConnect();
    Status waitConnect(double timeOut = 5.0);
    // Create a PvaClientField   NOT IMPLEMENTED
    PvaClientField createField(string subField = "");
    // create a PvaClientProcess
    PvaClientProcess createProcess(string  request = "");
    PvaClientProcess createProcess(PVStructure   pvRequest);
    // create a cached PvaClientGet
    PvaClientGet get(string  request = "value,alarm,timeStamp");
    // create a non cached PvaClientGet
    PvaClientGet createGet(string  request  = "value,alarm,timeStamp");
    PvaClientGet createGet(PVStructure   pvRequest);
    // create a cached PvaClientPut
    PvaClientPut put(string  request = "value");
    // create a non cached PvaClientPut
    PvaClientPut createPut(std::string  request = "value");
    PvaClientPut createPut(PVStructure  pvRequest);
    // create a PvaClientPutGet
    PvaClientPutGet createPutGet(string  request ="putField(argument)getField(result)" );
    PvaClientPutGet createPutGet(PVStructure  pvRequest);
    // create, connect, and start PvaClientMonitor
    PvaClientMonitor monitor(string  request = "value,alarm,timeStamp");
    PvaClientMonitor monitor(PvaClientMonitorRequester  pvaClientMonitorRequester);
    PvaClientMonitor monitor(string  request,PvaClientMonitorRequester pvaClientMonitorRequester);
    // create PvaClientMonitor. client must call PvaClientMonitor methods to create and start.
    PvaClientMonitor createMonitor(request = "value,alarm,timeStamp");
    PvaClientMonitor createMonitor(PVStructure   pvRequest);
    // create a PvaClientArray NOT IMPLEMENTED
    PvaClientArray createArray(string request = "");
    // create a PvaClientArray NOT IMPLEMENTED
    PvaClientArray createArray(PVStructure pvRequest);
    void setStateChangeRequester(PvaClientChannelStateChangeRequester stateChangeRequester);
    oid clearRequester();
    // methods for get and put cache
    void showCache();
    size_t cacheSize();
    // misc methods
    string getChannelName();
    Channel getChannel();
    void destroy(); // Java only

}
connect
This calls issueConnect and waitConnect. An exception is thrown if the connection request fails.
issueConnect
A connection request is issued and the method returns to the caller who must call waitConnect. Note that the caller can issue multiple issueConnect calls, for different PvaChannels, before calling waitConnect for each issueConnect. Thus the issueConnect calls are making parallel requests to connect to the servers.
waitConnect
Wait for the issueConnect request to complete. Status shows the result of the request. If status.isOK() is false then the request failed and status.getMessage() can be called to see why the request failed.
createField
NOT IMPLEMENTED
createProcess
If the channel is not connected call connect.
A PvaClientProcess is created.
The client must call the PvaClientProcess to connect and issue process requests.
get
See if a get for the same request is cached. If so it is returned.
If none is cached, create and connect to a PvaClientGet, cache it, and returns it to the client.
If any part of the request fails an exception is thrown.
createGet
If the channel is not connected call connect.
A PvaClientGet is created.
The client must call the PvaClientGet methods to connect and issue put requests.
The channelGet is not cached.
put
See if a put for the same request is cached. If so it is returned.
If none is cached it creates and connects a PvaClientPut, caches it, and returns it to the client.
If any part of the request fails an exception is thrown.
createPut
If the channel is not connected call connect.
A PvaClientPut is created.
The client must call the PvaClientPut methods to connect and issue put requests.
The channelPut is not cached.
createPutGet
If the channel is not connected call connect.
A PvaClientPutGet is created.
The client must call the PvaClientPutGet methods to connect and issue put requests.
monitor
Create and connect to a new PvaClientMonitor.
Then call the start method.
If any part of the request fails an exception is thrown.
If the caller provides argument PvaClientMonitorRequester, then the caller will be notified when new events are available. Otherwise the caller must call poll and/or waitEvent.
createMonitor
If the channel is not connected call connect.
A PvaClientMonitor is created.
The client must call the PvaClientMonitor methods to connect and start.
createArray
NOT IMPLEMENTED.
setStateChangeRequester
Set a user callback for channel change of state.
clearRequester
Clear the user callback
getChannelName
Get the channel name.
getChannel
get the pvAccess channel interface.
This can be used if the client needs access to methods not provided by pvaClient.
destroy
Java only. C++ implemnts RAII.
Disconnect the channel and destroy the channel context.

PvaClientMonitorRequester

The following is a class that a client can implement it it wants to be notified when the sever sends new monitors to the client. The client can also handle monitors by polling. See PvaClientMonitor below for details.

class PvaClientMonitorRequester
{
    void event(PvaCientMonitor monitor);
}
where
event
One or more new events are available.

PvaClientChannelStateChangeRequester

The following is a class that a client can implement it it wants to be notified when a change of state for a channel occurs.

class PvaClientChannelStateChangeRequester 
{
    void channelStateChange(PvaClientChannel channel, boolean isConnected);
}
where
channelStateChange
A change in state for the channel has occured,

Class PvaClientGet

class PvaClientGet
{
    void connect();
    void issueConnect();
    Status waitConnect();
    void get();
    void issueGet();
    Status waitGet();
    PvaClientGetData getData();
}
where
connect
This calls issueConnect and waitConnect. An exception is thrown if the connection request fails.
issueConnect
A connection request is issued and the method returns to the caller who must call waitConnect.
The caller can issue multiple issueConnect calls, for different PvaChannelGets, before calling waitConnect for each issueConnect. Thus the issueConnect calls are making parallel requests to connect to the servers.
waitConnect
Wait for the issueConnect request to complete. Status shows the result of the request. If status.isOK() is false then the request failed and status.getMessage() can be called to see why the request failed.
get
This calls issueGet and waitGet. An exception is thrown if any error arises.
issueGet

If the channel is not connected then a connect request is issued. If the connect request fails an exception is thrown.

A get request is issued and the method returns to the caller who must call waitGet. Note that the caller can issue multiple issueGet calls before calling waitGet for each issueGet. Thus the issueGet calls are making parallel requests to the servers.

waitGet
Wait for the issueGet request to complete. Status shows the result of the request. If status.isOK() is false then the request failed and status.getMessage() can be called to see why the request failed.
getData
Get the data for the previous get. This must be called after each successful get.

The following is for the data

class PvaClientGetData
{
    Structure getStructure();
    PVStructure getPVStructure();
    BitSet getChangedBitSet();
    showChanged() // different syntax for Java and C++
    void setData(PVStructure pvStructure, BitSet bitSetFrom);
    bool hasValue();
    bool isValueScalar();
    bool isValueScalarArray();
    PVField getValue();
    PVScalar getScalarValue();
    PVArray[] getArrayValue();
    PVScalarArray getScalarArrayValue();
    double getDouble();
    string getString();
    double[] getDoubleArray();
    string[] getStringArray();
    Alarm getAlarm();
    TimeStamp getTimeStamp();
}
where
getStructure
Get the introspection interface for the data.
getPVStructure
Get all the data.
getChangedBitSet
Get the changed bit set for PVStructure,
showChanged
Show all fields that have changed value since the previous get.
setData
Set data into PVStructure.
This is not normally called by the client but by the implementation.
hasValue
Does the Structure have a top level field named value?
isValueScalar
Is the value field a scalar?
isValueScalarArray
Is the value field a scalar array?
getValue
Get the value field as a PVField.
The caller can use introspection to find the actual type.
An exception is thrown if a top level field named value does not exist.
getScalarValue
Get the value field as a scalar.
An exception is thrown if the request is not valid.
getArrayValue
Get the value field as an array.
An exception is thrown if the request is not valid.
getScalarArrayValue
Get the value field as a scalar array.
An exception is thrown if the request is not valid.
getDouble
Get the value field as a double.
An exception is thrown if the request is not valid.
getString
Get the value field as a string.
An exception is thrown if the request is not valid.
getDoubleArray
Get the value field as a double array.
An exception is thrown if the request is not valid.
getStringArray
Get the value field as a string.
An exception is thrown if the request is not valid.
getAlarm
Get the data for a top level alarm field.
An exception is thrown if the request is not valid.
getTimeStamp
Get the data for a top level timeStamp field.
An exception is thrown if the request is not valid.

Class PvaClientPut

class PvaClientPut
{
    void connect();
    void issueConnect();
    Status waitConnect();
    void get();
    void issueGet();
    Status waitGet();
    void put();
    void issuePut();
    Status waitPut();
    PvaClientPutData getData();
}
where
connect
This calls issueConnect and waitConnect. An exception is thrown if the connection request fails.
issueConnect
A connection request is issued and the method returns to the caller who must call waitConnect.
The caller can issue multiple issueConnect calls, for different PvaChannelPuts, before calling waitConnect for each issueConnect. Thus the issueConnect calls are making parallel requests to connect to the servers.
waitConnect
Wait for the issueConnect request to complete. Status shows the result of the request. If status.isOK() is false then the request failed and status.getMessage() can be called to see why the request failed.
get
Get the curent value stored in the server. This calls issueGet and waitGet. An exception is thrown if any error arises.
issueGet
A get request is issued and the method returns to the caller who must call waitGet.
The caller can issue multiple issueGet calls before calling waitGet for each issueGet. Thus the issueGet calls are making parallel requests to the servers.
waitGet
Wait for the issueGet request to complete. Status shows the result of the request. If status.isOK() is false then the request failed and status.getMessage() can be called to see why the request failed.
put
Put data to the server. All fields in PvaClientPutData that have a changed BitSet are sent to the server. This calls issuePut and waitPut. An exception is thrown if any error arises.
issuePut
A put request is issued and the method returns to the caller who must call waitPut.
The caller can issue multiple issuePut calls before calling waitPut for each issuePut. Thus the issuePut calls are making parallel requests to the servers.
waitPut
Wait for the issuePut request to complete. Status shows the result of the request. If status.isOK() is false then the request failed and status.getMessage() can be called to see why the request failed.
getData
Get the PvaClientPutData. This only needs to be called before the first put. The BitSet is cleared automatically after each successful put. If get is issued then the BitSet shows the changes since the last get. In this case the BitSet should be cleared before the next put.
class PvaClientPutData
{
    Structure getStructure();
    PVStructure getPVStructure();
    BitSet getChangedBitSet();
    showChanged() // different syntax for Java and C++
    bool hasValue();
    bool isValueScalar();
    bool isValueScalarArray();
    PVField getValue();
    PVScalar getScalarValue();
    PVArray[] getArrayValue();
    PVScalarArray getScalarArrayValue();
    double getDouble();
    string getString();
    double[] getDoubleArray();
    string[] getStringArray();
    void putDouble(double value);
    void putString(string value);
    void putDoubleArray(double[] value);
    void putStringArray(string[] value);
}
where
getStructure
Get the introspection interface for the data.
getPVStructure
Get all the data.
getChangedBitSet
Get the changed bit set for PVStructure,
showChanged
Show all fields that have changed value since the previous get or put.
hasValue
Does the Structure have a top level field named value?
isValueScalar
Is the value field a scalar?
isValueScalarArray
Is the value field a scalar array?
getValue
Get the value field as a PVField.
The caller can use introspection to find the actual type.
An exception is thrown if a top level field named value does not exist.
getScalarValue
Get the value field as a scalar.
An exception is thrown if the request is not valid.
getArrayValue
Get the value field as an array.
An exception is thrown if the request is not valid.
getScalarArrayValue
Get the value field as a scalar array.
An exception is thrown if the request is not valid.
getDouble
Get the value field as a double.
An exception is thrown if the request is not valid.
getString
Get the value field as a string.
An exception is thrown if the request is not valid.
getDoubleArray
Get the value field as a double array.
An exception is thrown if the request is not valid.
getStringArray
Get the value field as a string.
An exception is thrown if the request is not valid.
putDouble
Get the value field as a double.
An exception is thrown if the request is not valid.
putString
Put the value field as a string.
An exception is thrown if the request is not valid.
putDoubleArray
Put the value field as a double array.
An exception is thrown if the request is not valid.
putStringArray
Put the value field as a string.
An exception is thrown if the request is not valid.

Class PvaClientPutGet

class PvaClientPutGet
{
    void connect();
    void issueConnect();
    Status waitConnect();
    void putGet();
    void issuePutGet();
    Status waitPutGet();
    void getGet();
    void issueGetPut();
    Status waitGetPut();
    void getGet();
    void issueGetGet();
    Status waitGetGet();
    void getPut();
    void issueGetPut();
    Status waitGetPut();
    PvaClientPutData getPutData();
    PvaClientGetData getGetData(); 
}
where
connect
This calls issueConnect and waitConnect. An exception is thrown if the connection request fails.
issueConnect
A connection request is issued and the method returns to the caller who must call waitConnect.
The caller can issue multiple issueConnect calls, for different PvaChannelPutGets, before calling waitConnect for each issueConnect. Thus the issueConnect calls are making parallel requests to connect to the servers.
waitConnect
Wait for the issueConnect request to complete. Status shows the result of the request. If status.isOK() is false then the request failed and status.getMessage() can be called to see why the request failed.
putGet
All fields in PvaClientPutData that have a changed BitSet are sent to the server, the server normally issues a process request, and the data received from the server is put into PvaClientGetData. This calls issuePutGet and waitPutGet. An exception is thrown if any error arises.
issuePutGet
A putGet request is issued and the method returns to the caller who must call waitPutGet.
The caller can issue multiple issuePutGet calls before calling waitPutGet for each issuePutGet. Thus the issuePutGet calls are making parallel requests to the servers.
waitPutGet
Wait for the issuePutGet request to complete. Status shows the result of the request. If status.isOK() is false then the request failed and status.getMessage() can be called to see why the request failed.
getGet
Get the curent PvaClientGetData is retrieved from the server. This calls issueGetGet and waitGetGet. An exception is thrown if any error arises.
issueGetGet
A getGet request is issued and the method returns to the caller who must call waitGetGet.
The caller can issue multiple issueGetGet calls before calling waitGetGet for each issueGetGet. Thus the issueGetGet calls are making parallel requests to the servers.
waitGetGet
Wait for the issueGetGet request to complete. Status shows the result of the request. If status.isOK() is false then the request failed and status.getMessage() can be called to see why the request failed.
getPut
Get the curent PvaClientPutData is retrieved from the server. This calls issueGetPut and waitGetPut. An exception is thrown if any error arises.
issueGetPut
A getPut request is issued and the method returns to the caller who must call waitGetPut.
The caller can issue multiple issueGetPut calls before calling waitGetPut for each issueGetPut. Thus the issueGetPut calls are making parallel requests to the servers.
waitGetPut
Wait for the issueGetPut request to complete. Status shows the result of the request. If status.isOK() is false then the request failed and status.getMessage() can be called to see why the request failed.
getPutData
Get the PvaClientPutData. This only needs to be called before the first putGet. The BitSet is cleared automatically after each successful putGet. If getPut is issued then the BitSet shows the changes since the last getPut. In this case the BitSet should be cleared before the next putGet.
getGetData
Get the PvaClientGetData. This must be called after each successfull putGet and getGet.

Class PvaClientProcess

class PvaClientProcess
{
    void connect();
    void issueConnect();
    Status waitConnect();
    void process();
    void issueProcess();
    Status waitProcessx();
}
where
connect
This calls issueConnect and waitConnect. An exception is thrown if the connection request fails.
issueConnect
A connection request is issued and the method returns to the caller who must call waitConnect.
The caller can issue multiple issueConnect calls, for different PvaChannelProcesss, before calling waitConnect for each issueConnect. Thus the issueConnect calls are making parallel requests to connect to the servers.
waitConnect
Wait for the issueConnect request to complete. Status shows the result of the request. If status.isOK() is false then the request failed and status.getMessage() can be called to see why the request failed.
process
This calls issueProcess and waitProcess. An exception is thrown if the request fails.
issueProcess
A process request is issued and the method returns to the caller who must call waitProcess.
The caller can issue multiple issueProcess calls before calling waitProcess for each issueProcess. Thus the issueProcess calls are making parallel requests to the servers.
waitProcess
Wait for the issueProcess request to complete. Status shows the result of the request. If status.isOK() is false then the request failed and status.getMessage() can be called to see why the request failed.

Class PvaClientMonitor

class PvaClientMonitor
{
    void connect();
    void issueConnect();
    Status waitConnect();
    void setRequester(PvaClientMonitorRequesterpvaClientMonitorrRequester);
    void start();
    void stop();
    bool poll();
    bool waitEvent(double secondsToWait = 0.0);
    void releaseEvent();
    PvaClientMonitorData getData(); 
}
where
connect
This calls issueConnect and waitConnect. An exception is thrown if the connection request fails.
issueConnect
A connection request is issued and the method returns to the caller who must call waitConnect.
The caller can issue multiple issueConnect calls, for different PvaChannelMonitors, before calling waitConnect for each issueConnect. Thus the issueConnect calls are making parallel requests to connect to the servers.
waitConnect
Wait for the issueConnect request to complete. Status shows the result of the request. If status.isOK() is false then the request failed and status.getMessage() can be called to see why the request failed.
setRequester
Set a client requester to be called when a monitor occurs. The argument can be null if the client no longer wants to be called directly.
If the client is directly notified then the client can call poll and releaseEvent.
start
Start monitoring.
stop
Stop moitoring.
poll
Poll for a new monitor event.
If the return value is true then getData is called to get the data and releaseEvent must be called when the client is done with the data.
waitEvent
Wait for an event. A value of 0 means wait forever. Is success the poll is called to get the outstanding events.
releaseEvent
Must be called to release the event.
getData
Get the data for the last successful poll request. This must be called after each successful pull.
class PvaClientMonitorData
{
    Structure getStructure();
    PVStructure getPVStructure();
    BitSet getChangedBitSet();
    BitSet getOverrunBitSet();
    showChanged() // different syntax for Java and C++
    showOverrun() // different syntax for Java and C++
    bool hasValue();
    bool isValueScalar();
    bool isValueScalarArray();
    PVField getValue();
    PVScalar getScalarValue();
    PVArray[] getArrayValue();
    PVScalarArray getScalarArrayValue();
    double getDouble();
    string getString();
    double[] getDoubleArray();
    string[] getStringArray();
    Alarm getAlarm();
    TimeStamp getTimeStamp();
    
}
where
getStructure
Get the introspection interface for the data.
getPVStructure
Get all the data.
getChangedBitSet
Get the changed bit set for PVStructure,
getOverrunBitSet
Get the overrun bit set for PVStructure,
showChanged
Show all fields that have changed value since the previous poll.
showOverrun
Show all fields that have overrun since the previous poll.
hasValue
Does the Structure have a top level field named value?
isValueScalar
Is the value field a scalar?
isValueScalarArray
Is the value field a scalar array?
getValue
Get the value field as a PVField.
The caller can use introspection to find the actual type.
An exception is thrown if a top level field named value does not exist.
getScalarValue
Get the value field as a scalar.
An exception is thrown if the request is not valid.
getArrayValue
Get the value field as an array.
An exception is thrown if the request is not valid.
getScalarArrayValue
Get the value field as a scalar array.
An exception is thrown if the request is not valid.
getDouble
Get the value field as a double.
An exception is thrown if the request is not valid.
getString
Get the value field as a string.
An exception is thrown if the request is not valid.
getDoubleArray
Get the value field as a double array.
An exception is thrown if the request is not valid.
getStringArray
Get the value field as a string.
An exception is thrown if the request is not valid.
getAlarm
Get the data for a top level alarm field.
An exception is thrown if the request is not valid.
getTimeStamp
Get the data for a top level timeStamp field.
An exception is thrown if the request is not valid.

Class PvaClientMultiChannel

class PvaClientMultiChannel
{
    static PvaClientMultiChannel create(
        PvaClient pvaClient,
        string[] channelNames,
        string providerName = "pva",
        size maxNotConnected = 0);
    string[] getChannelNames();
    Status connect(double timeout=5.0);
    bool allConnected();
    bool connectionChange();
    bool[] getIsConnected();
    PvaClientChannel[] getPvaClientChannelArray();
    PvaClient getPvaClient();
    PvaClientMultiGetDouble createGet();
    PvaClientMultiPutDouble createPut();
    PvaClientMultiMonitorDoublePtr createMonitor();
    PvaClientNTMultiPut createNTPut();
    PvaClientNTMultiGet createNTGet(string request = "value,alarm,timeStamp");
    PvaClientNTMultiMonitor createNTMonitor(string request = "value,alarm,timeStamp");
}
where
create
A static method for creating a new instance of PvaMultiChannel, which is a class for connection to a set of channels.
getChannelNames
Get the channel names.
connect
Connect to the channels. The Status provides the result.
allConnected
Are all channels connected?
connectionChange
Is there a change in connection status since the last call?
getIsConnected
Get the connection state of all the channels.
getPvaClientChannelArray
Get the array of PvaClientChannel.
getPvaClient
Get the singleton instance of PvaClient.
createGet
Create a PvaClientMultiGetDouble for getting each channel as a double. This throws an exception if the request is not valid.
createPut
Create a PvaClientMultiPutDouble for getting each channel as a double. This throws an exception if the request is not valid.
createMonitor
Create a PvaClientMultiMonitorDouble for getting each channel as a double. This throws an exception if the request is not valid.
createNTPut
Creates a PvaClientNTMultiPut. This throws an exception if any channel does not have a field named value.
createNTGet
Creates a PvaClientNTMultiGet. This throws an exception if any channel does not have a field named value.
createNTMonitor
Creates a PvaClientNTMultiMonitor. This throws an exception if any channel does not have a field named value.

Class PvaMultiGetDouble

class PvaMultiGetDouble
{
    void connect();
    double[] get();
}
where
connect
Create a channelGet for each channel.
get
Issue a get for each channel. Return the data as an array of doubles.

Class PvaMultiPutDouble

class PvaMultiPutDouble {
    void connect();
    void put(double[] value);
}
where
connect
Create a channelPut for each channel.
get
Issue a put for each channel. Returns when all puts are complete.

Class PvaMultiMonitorDouble

class PvaMultiMonitorDouble
{
    void connect();
    bool poll();
    bool waitEvent(double secondsToWait);
    double[] get();
}
where
connect
Create a monitor for each channel.
poll
Poll for new events.
waitEvent
Wait for events or until timeout. It return is true then poll is called to get the data.
get
Get the data. This call also releases the current event for each channel.

Class PvaClientNTMultiPut

class PvaClientNTMultiPut
{
    void connect();
    PVUnion[] getValues();
    void put();

}
where
connect
Create a channelPut for each channel.
getValues
Get an array of PVUnion for the value field of each channel. The client sets values before making a call to put.
put
Put a value to each channel.

Class PvaClientNTMultiGet

class PvaClientNTMultiGet
{
    void connect();
    void get();
    PvaClientNTMultiData getData();
}
where
connect
Create a channelGet for each channel.
get
Issue a get for each channel.
getData is called to get the data.
getData
Get the data as an PvaClientNTMultiData.
class PvaClientNTMultiData
{
    size_t getNumber();
    void startDeltaTime();
    void endDeltaTime();
    TimeStamp getTimeStamp();
    NTMultiChannel getNTMultiChannel();
}
getNumber
Get the number of channels.
startDeltaTime
Set the timeStamp base for computing deltaTimes.
endDeltaTime
Update NTMultiChannel fields.
getTimeStamp
Get the time when the last get was made.
getNTMultiChannel
Get the NTMultiChannel.

Class PvaClientNTMultiMonitor

class PvaClientNTMultiMonitor
{
    void connect();
    bool poll();
    bool waitEvent(double secondsToWait);
    PvaClientNTMultiData getData();
}
where
connect
Create a channel monitor for each channel
poll
poll each channel.
If result is true than getData returns the data.
waitEvent
Wait until poll returns true.
A thread sleep of .1 seconds occurs between each call to poll.
getData
Get the data.
class PvaClientNTMultiData
{
    size getNumber();
    void startDeltaTime();
    void endDeltaTime();
    TimeStamp getTimeStamp(); 
    NTMultiChannel getNTMultiChannel();
}
where
getNumber
Get the number of channels.
startDeltaTime
Set the timeStamp base for computing deltaTimes.
endDeltaTime
Update NTMultiChannel fields.
getTimeStamp
Get the time when the last get was made or monitor poll returned true.
getNTMultiChannel
Get the NTMultiChannel.

API: pvDatabase

pvDatabase Overview

A brief description of a pvDatabase is that it is a set of network accessible, smart, memory resident records. Each record has data composed of a top level PVStructure. Each record has a name which is the channelName for pvAccess. A local Channel Provider implements the complete ChannelProvider and Channel interfaces as defined by pvAccess. The local provider provides access to the records in the pvDatabase. This local provider is accessed by the remote pvAccess server. A record is smart because code can be attached to a record, which is accessed via a method named process.

Implementations for both Java and C++ are available.

With the C++ implementation (pvDatabaseCPP) the pvDatabase can be provided via a Main program or can be part of a V3IOC. In the later case the IOC has both a database of V3 Records and PVRecords. Thus pvDatabase can be used to develop V4 services for V3IOCs.

Class PVRecord

Note that exampleCPP and exampleJava both provide examples for implementing pvRecords.

Class PVListener

class PVListener // class for local provider
{
    void dataPut(PVRecordField pvRecordField);
    void dataPut(PVRecordStructure requested,PVRecordField pvRecordField); 
    void beginGroupPut(PVRecord pvRecord);
    void endGroupPut(PVRecord pvRecord);
    void unlisten(PVRecord pvRecord);
}

where

dataPut(PVRecordFieldPtr const & pvRecordField)
pvField has been modified. This is called if the listener has called PVRecordField::addListener for pvRecordField.
dataPut( PVRecordStructurePtr const & requested,PVRecordFieldPtr const & pvRecordField)
pvField has been modified. Requested is the field to which the requester issued a pvField->addListener. This is called if the listener has called PVRecordField->addListener for requested.
beginGroupPut
A related set of changes is being started.
endGroupPut
A related set of changes is done.
unlisten
The record is being destroyed. The listener must release all access to the record.

class PVRecordClient

class PVRecordClient // class for local provider
{
   void detach(PVRecord pvRecord)
}

where

detach
The record is being removed from the master database,

Class PVRecord

class PVRecordField // class for pvCopy
class PVRecordStructure // class for pvCopy

class PVRecord
{
    static PVRecord create(string recordName,PVStructure pvStructure);  // C++ only
    PVRecord(string recordName,PVStructure pvStructure);
    ~PVRecord()          // C++ only
    boolean init();      // C++ only
    void initPVRecord(); // C++ only
    void start();        // C++ only
    void destroy() 
    // Override to implement semantics
    void process();
    // Override to implement channelRPC
    Service getService(PVStructure pvRequest)
    // methods called by code that accesses a record
    void lock();
    void unlock();
    boolean tryLock()
    void lockOtherRecord(PVRecordPtr const & otherRecord);
    // method called when record is deleted.
    void detachClients();
    // methods called by code that changes data in a record.
    void beginGroupPut();
    void endGroupPut();
    // methods called by local provider
    boolean addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
    boolean removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
    // methods for pvCopy, i.e. to find fields that have changed value
    bool addListener(PVListener pvListener,PVCopy pvCopy);
    void nextMasterPVField(PVField pvField);
    boolean removeListener(PVListener pvListener,PVCopy pvCopy);
    // misc methods
    string getRecordName();
    PVStructure getPVStructure();
    PVRecordStructure getPVRecordStructure();
    PVRecordField findPVRecordField(PVField pvField);
    // methods for debugging output
    int getTraceLevel();
    void setTraceLevel(int level)
}
where
create - C++ only
Can be called to create a soft PVRecord. A soft record implements process by setting an optional top level timeStamp to the current time.
A derived class should have it's own static create method.
PVRecord
Constructor.
Create a PVRecord named recordName and that has pvStructure as it's top level structure. A derived class must call the base class constructor before performing any other initialization.
~PVRecord - C++ only
This calls destroy.
init - C++ only
Virtual method. Derived classes must implement this method. This method Must call initPVRecord.
start - C++ only
Virtual method. Optional method for derived class. It is called before record is added to database. The base method does nothing.
destroy
Destroy the PVRecord and any context.
Release any resources used and get rid of listeners and requesters.
If a derived class overrides this, it must call the base class destroy after it has destroyed any resorces it uses.
Destroy can be called for several reasons. Some examples are:
  • By pvDatabase when it is destroyed.
  • By other code that wants to destroy the record.
process
Virtual method. Derived classes usually implement this method. It implements the semantics for the record.
If a top level timeStamp exists the base class set it equal to the current time.
getService
A derived class implements this method if it supports channelRPC. It implements the semantics for the channelRPC. The base class returns null.
lock and unlock
Lock and Unlock the record. Any code accessing the data in the record or calling other PVRecord methods must have the record locked.
tryLock
If true then just like lock. If false client can not access record. A client can try to simultaneously hold the lock for more than two records by calling this method. But must be willing to accept failure.
lockOtherRecord
A client that holds the lock for one record can lock one other record. A client must not call this if the client already has the lock for more then one record.
detachClients
Ask all clients to detach from the record
beginGroupPut
Begin a group of puts. This results in all registered PVListeners being called
endGroupPut
End a group of puts. This results in all registered PVListeners being called.
addPVRecordClient
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
removePVRecordClient
Client is no longer accessing the record.
addListener
Add a PVListener. This must be called before calling pvRecordField.addListener.
nextMasterPVField
Called by pvCopy monitor support.
removeListener
Removes a listener. The listener will also be removed from all fields to which it is attached.
getRecordName
Return the recordName.
getPVStructure
Get the top level PVStructure.
getPVRecordStructure
Get the top level PVRecordStructure.
findPVRecordField
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
getTraceLevel
This can be used for debugging. There are currently three levels that are used by existing code.
0
Produce no trace messages.
1
Issue a message to std::cout whenever anything is created or destroyed.
2
In addition to lifetime messages also issue a message whenever the record is accessed by pvAccess client.
setTraceLevel
Set the trace level. Note that special, described below. provides a record support that allows a pvAccess client to set the trace level of a record.

Class PVDatabase

class PVDatabase
{
     PVDatabase getMaster();  // C++ pnly
     PVDatabase();
     ~PVDatabase();      // C++ only
     destroy();
     PVRecord findRecord(String recordName);
     boolean addRecord(PVRecord record);
     boolean removeRecord(PVRecord record);
     string[] getRecordNames();
}

class PVDatabaseFactory
{
     PVDatabase getMaster();  // Java only
}
where
getMaster
Get the master database.
The master database is what provider local uses.
PVDatabase
Constructor.
~PVDatabase C++ only
Calls destroy;
destroy
This is called by remote channelAccess when process exits. This destroys and removes all records in the PVDatabase.
findRecord
Find a record. An empty pointer is returned if the record is not in the database.
addRecord
Add a record to the database. If the record already exists it is not modified and false is returned.
getRecordNames
Returns an array of all the record names.
removeRecord
Remove a record from the database. If the record was not in the database false is returned.

TraceRecord

An instance of TraceRecord provides the ability to remove another record in the local PVDatabase. exampleCPP and exampleJava each has a submodule named database that creates a PVDatabase that has a record named PVRtraceRecord. It has the structure:

structure 
    structure argument
        string recordName 
        int level
    structure result
        string status 

It is designed to be used via a channelPutGet request but can also be used via pvget and pvput. For example:

pvget -m PVRtraceRecord

Then in another window:

pvput -r "argument{recordName,level}" PVRtraceRecord name level
where
name
The name of the record
level
The trace level, which should be one of 0, 1, 2.

For example if the command is:

pvput -r "argument{recordName,level}" PVRtraceRecord PVRdouble 2

Then the monitor will show:

pvget -m PVRtraceRecord
PVRtraceRecord
structure 
    structure argument
        string recordName 
        int level 0
    structure result
        string status 


PVRtraceRecord
structure 
    structure argument
        string recordName PVRdouble
        int level 2
    structure result
        string status success

Record PVRdouble will now show debug infomation when the record is accessed.

RemoveRecord

An instance of RemoveRecord provides the ability to remove another record in the local PVDatabase. exampleCPP and exampleJava each has a submodule named database that creates a PVDatabase that has a record named PVRremoveRecord. It has the structure:

structure 
    structure argument
        string recordName 
    structure result
        string status 

It is designed to be used via a channelPutGet request but can also be used via pvget and pvput. For example:

pvget -m PVRremoveRecord

Then in another window:

pvput -r "argument.recordName" PVRremoveRecord name
where
name
The name of the record to remove

For example if the command is:

pvput -r "argument.recordName" PVRremoveRecord PVRdouble

Then the monitor will show:

pvget -m PVRremoveRecord
PVRremoveRecord
structure 
    structure argument
        string recordName 
    structure result
        string status 


PVRremoveRecord
structure 
    structure argument
        string recordName PVRdouble
    structure result
        string status success

Record PVRdouble is no longer in the PVDatabase.

Channel Provider local

This is code that provides an implementation of channelProvider as defined by pvAccess. It provides access to PVRecords and is accessed by the server side of remote pvAccess. It uses the copy and monitor facilities from pvDataCPP and connects them to a PVRecord.

The implementation is a complete implementation of channelProvider and channel.

The following provides a brief description of each channel method that is implemented.

channelProcessLocal

Implements channelProcess.

channelGetLocal

Implements channelGet.

channelPutLocal

Implements channelPut.

channelPutGetLocal

Implements channelPutGet.

channelArrayLocal

Implements channelArray.

ChannelRPCLocal

Implements channelRPC.

MonitorLocal

This is the code that implements monitors on changes to fields of a PVRecord. Because it is called by pvAccess client (monitor methods) and by PVRecord (when postPut is called), it must be careful to prevent deadlocks. The implementation is via class MonitorLocal (implemented in monitorFactory.cpp) and PVCopyMonitor. MonitorLocal is the interface between pvAccess and PVCopyMonitor. PVCopyMonitor is the interface between MonitorLocal and PVRecord. MonitorLocal manages a MonitorElement queue. While monitoring is active (between start and stop) it keeps an active element for use by PVCopyMonitor. While monitoring is active PVCopyMonitor updates the active monitor element whenever a postPut is issued to any field being monitored.

The following two sections provide a few more details about MonitorLocal and PVCopyMonitor.

MonitorLocal

MonitorLocal implements the following abstract base classes:

Monitor
This is described by pvDataCPP. It has methods start, stop, poll, and release. These methods are called by the pvAccess client
PVCopyMonitorRequester
This has methods releaseActiveElement and unlisten. These methods are called by PVCopyMonitor.
MonitorLocal manages the following:
MonitorElementQueue
This is a queue of monitor elements. A Queue is implemented by pvDataCPP and used by MonitorLocal. It is a finite queue. A monitor element is described by pvDataCPP. It has fields pvStructure, changedBitSet, and overrunBitSet. The pvStructure holds data for a subset of the fields in a PVRecord. The changedBitSet and overrunBitSet describe changes between monitor event. MonitorLocal creates an instance of PVCopy (implemented by pvDataCPP), which manages the interaction between the set of fields being monitored and the fields in the top level PVStructure of the PVRecord. pvCopy is also used to create the pvStructure for each monitor element.
activeElement
Whenever monitoring is active monitorLocal keeps an active element for use by pvCopyMonitor. It changes the active element based on calls to poll (by the client) and calls to releaseActiveElement (by pvCopyMonitor). If there are no free element when releaseActiveElement is called the current active element is returned. If a free element is available the client is notified that a new monitor element is available and the free element becomes the active element.

A brief description on each method in MonitorLocal is:

start
Called by client. With a lock held it clears the monitorElement queue and allocates an active element. With no lock held calls pvCopyMonitor->startMonitoring(activeElement)
stop
Called by client. With no lock held calls pvCopyMonitor->stopMonitoring(activeElement)
poll
Called by client. With a lock held it calls queue->getUsed();
release
Called by client. With a lock held it calls queue->releaseUsed();
releaseActiveElement
Called by PVCopyMonitor with no locks held. With a lock held it tries to get a new free element. If it can't it just returns the current active element. Otherwise it does the following. Using the activeElement it updates the pvStructure and compresses the changed and overrun bitSet. It then calls queue->setUsed(activeElement); It then sets the active element to the new free element. With no lock held it calls monitorRequester->monitorEvent(getPtrSelf()) and finally returns the new active element,
unlisten
With no lock held it calls monitorRequester->unlisten(getPtrSelf());
PVCopyMonitor

pvCopyMonitor is the code that manages changes to fields in the record. It is called by PVRecord whenever a postPut is issued to a field. pvCopyMonitor uses the active monitor element provided by monitorFactory. Note that this method is called with the record locked. It only modifies the changedBitSet and overrunBitSet of the active element but never modifies the pvStructure.

A brief description of the pvCopyMonitor methods is:

startMonitoring
With no lock held it sets its monitorElement to the startElement passed by monitorLocal and calls pvRecord->addListener(getPtrSelf()). It locks the pvRecord. It calls calls addListener for every field in the record that is being monitored. It clears the overrun and changed bit sets. It sets bit 0 of the changed bit set and calls pvCopyMonitorRequester->releaseActiveElement(); Thus the client will get the initial values for every field being monitored. The record is unlocked and the method returns to the caller.
stopMonitoring
With no lock held it calls pvRecord->removeListener(getPtrSelf());
dataPut
This is called because of a call to postPut. It is called with the record locked. It updates the changed and overrun bitSets. It isGroupPut is false it calls pvCopyMonitorRequester->releaseActiveElement(). Otherwise it sets dataChanged true.
beginGroupPut
With a lock held it sets isGroupPut true and dataChanged false.
endGroupPut
With a lock held it sets isGroupPut false. With no lock held and dataChanged true it calls pvCopyMonitorRequester->releaseActiveElement()
unlisten
Just calls pvCopyMonitorRequester->unlisten();

exampleJava and exampleCPP

exampleJava and exampleCPP both have example databases. Project exampleCPP has a database that is similar to exampleJava/ This section will mainly describe exampleCPP/database. It shows how create a V3 IOC that has both V3 Records, PVRecords, and pvaSrv. After reading this section the examples in exampleJava and exampleCPP should be easy to understand.

Starting the example

The example database can be run either as a standalone main program or as part of a V3 IOC (Input Output Controller). If as part of a V3 IOC the IOC has both V3 and V4 records. It also starts pvaSrv so that the V3 records can be accessed via either ca or pva.

To run the example as a main program:

mrk> pwd
/home/epicsv4/exampleCPP/database
mrk> bin/linux-x86_64/exampleDatabaseMain 

To start the example as part of a V3 IOC:

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

Once the database is running then the following can access exampleHello:

pvput -r "argument" PVRhelloPutGet World
pvget -r "result.value" PVRhelloPutGet
exampleHello
structure 
    structure result
        string value Hello World

source code

exampleCPP/database/src has the following examples:

exampleHello
This is an hello world example that is meant to be accesed via a channelPutGet request. It can also be accessed via pvput followed by a pvget.
exampleHelloRPC
The is an hello world example that is accessed via a channelRPC request.
exampleDatabase
This creates an exampleHello record. It also creates a large set of records of all possible types that have only the default process semantics, which only sets the timeStamp when the record is processed.
The src directory also has the following files:
exampleDatabaseMain.cpp
code to run example as main program
exampleDatabaseInclude.dbd
exampleDatabaseRegister.cpp
code to run example as part of V3 IOC

exampleDatabaseMain.cpp

This is the file that runs the example as a main program. The main program is:

int main(int argc,char *argv[])
{
    PVDatabasePtr master = PVDatabase::getMaster();
    ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
    ExampleDatabase::create();
    ServerContext::shared_pointer ctx =
        startPVAServer("local",0,true,true);
    cout << "exampleDatabase\n";
    string str;
    while(true) {
        cout << "Type exit to stop: \n";
        getline(cin,str);
        if(str.compare("exit")==0) break;

    }
    ctx->destroy();
    return 0; 
}
The only code that is not generic is:
    ExampleDatabase::create();

exampleDatabaseRegister.cpp

This is used to create an iocshell command to start the example database as part of a V3 IOC. It is almost generic. The guts of the code is:
static const iocshFuncDef exampleDatabaseFuncDef = {"exampleDatabase", 0};

static void exampleDatabaseCallFunc(const iocshArgBuf *args)
{
    ExampleDatabase::create();
}

static void exampleDatabaseRegister(void)
{
    static int firstTime = 1;
    if (firstTime) {
        firstTime = 0;
        iocshRegister(&exampleDatabaseFuncDef, exampleDatabaseCallFunc);
    }
}

extern "C" {
    epicsExportRegistrar(exampleDatabaseRegister);
}
Other than exampleDatabase as part of the names it is boiler plate code.

exampleDatabaseInclude.dbd

This is just:
registrar("exampleDatabaseRegister")
It again is generic except for exampleDatabase as part of the name.

Makefile

Look at the Makefile to see how other files.

exampleDatabase

exampleDatabase.h is almost completely generic. It has the code:
class epicsShareClass  ExampleDatabase{
public:
    static void create();
};
It defines a factory methods for creating the example database.

exampleDatabase.cpp is not generic. It shows how to create records of various types. Most of the records have only the default process method. The default process does nothing except that if a timeStamp field is present the time is set to the current time. Look at the code to see examples of how to create records.

exampleCPP/database/ioc

This directory has two subdirectories:
Db
This creates some V3 records.
src
This has to files to load the V3 and V4 support for a V3 IOC.

src

exampleDatabaseMain.cpp is standard code to start a V3 IOC

exampleDatabaseInclude.dbd contains:

include "base.dbd"
include "PVAServerRegister.dbd"
include "registerChannelProviderLocal.dbd"
include "dbPv.dbd"
registrar("exampleDatabaseRegister")

base.dbd is standard way to include the base V3 code. PVAServerRegister.dbd is how the pvAccess Server code is included. registerChannelProviderLocal.dbd is how the local pva server is started. The local server is implemented by pvDatabaseCPP. dbPv.dbd is how dbPv, the server that accesses V3 revords, is started.

registrar("exampleDatabaseRegister") is the only example specific statement. It is used to start the example database.

exampleCPP/database/iocBoot/exampleDatabase

This is where the V3 IOC is started. The st.cmd starts with normal way to start a V3 IOC. What is different is the statements after iocInit:

exampleDatabase
startPVAServer

The first statement creates the example database. The second statement starts the pva server. After it is started clients can access the V4 records and the V3 records via pva.

API: pvAccess

ChannelProvider

ChannelProviderRegistry
    ChannelProvider getProvider(string providerName)
    ChannelProvider createProvider(string providerName)
    string[] getProviderNames()

ChannelProvider
    string getProviderName()
    ChannelFind channelFind(
        string channelName,
        ChannelFindRequester channelFindRequester)
    ChannelFind channelList(
        ChannelListRequester channelListRequester)
    Channel createChannel(
         string channelName,
         ChannelRequester,
         short priority)
    Channel createChannel(
         string channelName,
         ChannelRequester channelRequester,
         short priority,
         string address)

ChannelFindRequester
    void channelFindResult(
        Status status,
        ChannelFind channelFind,
        boolean wasFound)

ChannelFind
    ChannelProvider getChannelProvider()
     void cancelChannelFind()

ChannelProviderRegistry

getProvider
This gets the requested provider.Two providers are registered automatically: pva, which uses the pvAccess network protocol, and ca, which uses the Channel Access network protocol.
createProvider
This registers a ChannelProvider. User code does not call this, e.g. it is called via code that implements the ChannelProvider interface.
getProviderNames
Returns the names of the providers that have been created.

ChannelProvider

destroy
The channel provider will shutdown and remove all resources it is using.
getProviderName
Get the name of the provider.
channelFind
Find a channel. The details are described in this section.
createChannel
Create a channel. A Channel is described in the next section.

ChannelFindRequester

channelFindResult
This is called by the implementation. It may or may not get called. For a remote call it is normally called only if the channel is found. A local provider will usually call it immediately and report if it has the requested channel. Thus this can be called before the channelFind method returns.

ChannelFind

This method is to be used to by local pvAccess implementations (e.g. pvAccess server queries pvIOCJava. A client can determine if a channel exists without creating a channel. The client must implement a requester interface and the implementation provides an interface that the client can use to cancel the request.

getChannelProvider
Get the channel provider.
cancelChannelFind
Cancel the find request.

Channel

ChannelRequester extends Requester
    void channelCreated(
        Status status,
        Channel channel)
    void channelStateChange(
        Channel channel,
        ConnectionState connectionState)

enum AccessRights {
    none,
    read,
    readWrite
}

Channel extends Requester
    ChannelProvider getProvider()
    string getRemoteAddress()
    ConnectionState getConnectionState()
    void destroy()
    string getChannelName()
    ChannelRequester getChannelRequester()
    boolean isConnected()
    AccessRights getAccessRights(PVField pvField)
    void getField(GetFieldRequester requester,string subField)
    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)

ChannelRequester

The caller must implement the interface ChannelRequester.

Requester
This is defined in package org.epics.pvdata.pv. It has two methods: getRequesterName and message.
channelCreated
This is called when a channel has been created. The argument provides the channel unless status was not success.
channelStateChange
A channel connection state change has occurred. This is called the first time when a channel has been created and connected to a server or destroyed.

Channel

where

getProvider
Get the provider.
getRemoteAddress
Get the network address of the server.
destroy
Destroy the channel and all resources used by the channel.
getChannelName
The name of the channel, e.g. the name if the PVRecord.
getChannelRequester
Get the channel requester. This is normally called by the implementation rather than the client.
isConnected
Is the channel connected?
getAccessRights
Get the access rights for the channel.

The remaining methods are described in later sections of this package overview.

Many of the interface described in the later sections are created via a create call that has as one of it's arguments:

PVStructure pvRequest

This is also discussed in a later section.

GetField

The method:

    Channel.getField(GetFieldRequester requester, String subField);

Gets the introspection interface for the specified sub field of the record it which the channel is connected. The subField can be null, which means get the introspection interface for the entire record, or is of the form "name.name..." . Thus it can be a request for any field within a record. The requester must implement the interface:

interface GetFieldRequester extends Requester {
    void getDone(Status status, Field field);
}

where

getDone
A getField request has finished. A null is returned if the request fails and status contains a reason of a failure. If the request succeeds Field is the introspection interface.

ChannelRequest

Many of the interfaces described in later sections extend the following interface.

interface ChannelRequest extends Lockable, Destroyable {
    Channel getChannel();
    void cancel();
    void lastRequest();

where

destroy
Destroy whatever the extended interface implements. It will free all resources it uses.
getChannel
Get the channel instance to which this request belongs.
cancel
Cancel the current request.
lastRequest
The next request will be the last request.

ChannelProcess

A ChannelProcess is created via a call to:

interface Channel extends Requester {
   ChannelProcess createChannelProcess(
       ChannelProcessRequester channelProcessRequester);
}

The definition is:

ChannelProcessRequester extends Requester
    void channelProcessConnect(
        Status status,
        ChannelProcess channelProcess)
    void processDone(
        Status status,
        ChannelProcess channelProcess)

ChannelProcess extends ChannelRequest
    void process()

ChannelProcessRequester

The requester must implement this interface.

channelProcessConnect
This returns the interface for requesting that a record be processed. Status is the result for the create request. channelProcess is null if status is not success.
processDone
This is called when a process request is done. Status is the result for the process request.

ChannelProcess

process
Process the record. Another process request must not be issued until processDone is called.

ChannelGet

A ChannelGet is created via a call to:

interface Channel extends Requester {
    ChannelGet createChannelGet(
        ChannelGetRequester channelGetRequester,
        PVStructure pvRequest);
}

The definition is:

ChannelGetRequester extends Requester
    void channelGetConnect(
        Status status,
        ChannelGet channelGet,
        Structure structure)
    void getDone(
        Status status,
        ChannelGet channelGet,
        PVStructure pvStructure,
        BitSet bitSet)

ChannelGet extends ChannelRequest
    void get()

ChannelGetRequester

channelGetConnect
This is called if the createProcess fails or when the client request is connected to the server. Status provides the result of the createChannel request. If status is not success than the other arguments are null. channelGet is the interface for requesting gets. structure is the introspection interface for data that is returned to getDone.
getDone
The get request is done. status provides the result of the get request. If successful the pvStructure and bitSet contain the data returned as a result of a get request. bitSet shows which fields have new data. If a bit of bitSet is set for a structure field that means that all fields of the structure have new values. For example of bit 0 of bitSet is set then all fields of pvStructure have new data. Note that each PVField implements a method getFieldOffset(). this can be used with bitSet to determine which fields have new data values. See BitSet and PVField in org.epics.pvdata for details.

ChannelGet

get
Get data. Another get request must not be issued until getDone is called.

ChannelPut

A ChannelPut is created via a call to:

interface Channel extends Requester {
    ChannelPut createChannelPut(
        ChannelPutRequester channelPutRequester,
        PVStructure pvRequest);
}

The definition is:

ChannelPutRequester extends Requester
    void channelPutConnect(
        Status status,
        ChannelPut channelPut,
        Structure structure)
    void putDone(
        Status status,
        ChannelPut channelPut)
    void getDone(
        Status status,
        ChannelPut channelPut,
        PVStructure pvStructure,
        BitSet bitSet)

ChannelPut extends ChannelRequest
    void put(PVStructure pvPutStructure, BitSet bitSet)
    void get()

ChannelPutRequester

The requester must implement this interface.

channelPutConnect
This is called if the createChannelPut fails or when the client request is connected to the server. Status provides the result of the createChannelPut request. If status is not success than the other arguments are null. channelPut is the interface for requesting puts and gets. Structure is the introspection interface that must be used for data that is put to the server or that is returned by a get request.
putDone
The put request is done. status provides the result of the put request.
getDone
The get request is done. status provides the result of the put request. pvStructure has the data returned from the server. bitSet to shows which fields have new data before making a put request. If a bit of bitSet is set for a structure field that means that all fields of the structure have new values. For example of bit 0 of bitSet is set then all fields of pvStructure have new data. Note that each PVField implements a method getFieldOffset(). this can be used with bitSet to determine which fields have new data values. See BitSet and PVField in org.epics.pvdata for details.

ChannelPut

put
Put data. pvPutStructure is the data to send to the server, bitSet determines which fields are sent.
get
Get the current data from the record. The record is never processed. The request just gets the current values which is put into the pvStructure returned in the call to channelPutConnect.

NOTE: Only one of put and get can be outstanding at the same time. Another get or put must not be issued until getDone or putDone is called.

ChannelPutGet

A channelPutGet request puts data into a record, optionally processes the record, and gets data from the record.

A channel putGet is created via a call to:

interface Channel extends Requester {
    ChannelPutGet createChannelPutGet(
        ChannelPutGetRequester channelPutGetRequester,
        PVStructure pvRequest);
}

The definition is:

ChannelPutGetRequester extends Requester
    void channelPutGetConnect(
        Status status,
        ChannelPutGet channelPutGet,
        Structure putStructure,
        Structure getStructure)
    void putGetDone(
        Status status,
        ChannelPutGet channelPutGet,
        PVStructure getPVStructure,
        BitSet getBitSet)
    void getPutDone(
        Status status,
        ChannelPutGet channelPutGet,
        PVStructure putPVStructure,
        BitSet putBitSet)
    void getGetDone(
        Status status,
        ChannelPutGet channelPutGet,
        PVStructure getPVStructure,
        BitSet getBitSet)

ChannelPutGet extends ChannelRequest
    void putGet(PVStructure pvPutStructure, BitSet putBitSet)
    void getPut()
    void getGet()

ChannelPutGetRequester

The requester must implement this interface.

channelPutGetConnect
This is called if the createChannelPutGet fails or when the client request is connected to the server. Status provides the result of the createChannelPutGet request. If status is not success than the other arguments are null. channelPutGet is the interface for requesting putGet, getPut and getGet. putStructure is the introspection interface that is used for data sent by the client and getStructure the introspection interface for data received by the client.
putGetDone
The putGet request is done. If status is not success then the other arguments may be null. getPVStructure is the data returned to the client and getBitSet shows which fields have changed value since the last putGet request.
getPutDone
The getPut request is done. If status is not success then the other arguments may be null. putPVStructure is the data returned to the client and putBitSet shows which fields have changed value since the last getPut request.
getGetDone
The getGet request is done. If status is not success then the other arguments may be null. getPVStructure is the data returned to the client and getBitSet shows which fields have changed value since the last getGet request.

ChannelPutGet

putGet
First put the pvPutStructure data into the record. Then if process is true process the record. Finally get data from the record and put it into pvGetStructure.
getPut
Get current put data from the record and put it into pvPutStructure. The record is never processed.
getGet
Get current data from the record and put it into pvGetStructure. The record is never processed.

NOTE: Only one of putGet, getPut, or getGet can be outstanding at the same time. Another request must not be issued until the appropriate xxxDone is called.

ChannelArray

ChannelArray provides the ability to read or write a sub-array of an array field in a record. Note that all the other transfer methods can also read or write arrays but always transfer entire arrays. ChannelArray provides the ability to transfer a subarray. A ChannelArray is created via a call to:

interface Channel extends Requester {
    ChannelArray createChannelArray(
        ChannelArrayRequester channelArrayRequester,
        PVStructure pvRequest);
}

The definition is:

ChannelArrayRequester extends Requester
    void channelArrayConnect(
        Status status,
        ChannelArray channelArray,
        Array array)
    void putArrayDone(
        Status status,
        ChannelArray channelArray)
    void getArrayDone(
        Status status,
        ChannelArray channelArray,
        PVArray pvArray)
    void getLengthDone(
        Status status,
        ChannelArray channelArray,
        int length,
        int capacity)
    void setLengthDone(
        Status status,
        ChannelArray channelArray)

ChannelArray extends ChannelRequest
    void putArray(
        PVArray putArray,
        int offset,
        int count,
        int stride)
    void getArray(
        int offset,
        int count,
        int stride)
    void getLength()
    void setLength(
        int length)

ChannelArrayRequester

The requester must implement this interface.

channelArrayConnect
This is called if the createChannelArray fails or when the client request is connected to the server. Status provides the result of the createChannelArray request. If status is not success than the other arguments are null. channelArray is the interface for requesting puts and gets. array is the interface for the data that transfered between client and server.
putArrayDone
The put request is done.
getArrayDone
The get request is done. pvArray holds the data.
getLengthDone
The getLength request is done. length and capacity hold the result.
setLengthDone
The setLength request is done.

ChannelArray

putArray
Put array data. The offset and stride are the offset and stride in the server and the count is the total number of elements to write.
getArray
Get array data. The offset and stride are the offset and stride in the server and the count is the total number of elements to read.
getLength
Get the current length and capacity of the array in the server.
setLength
Set the length and capacity of the array in the server.

Monitor

Monitor provides the ability to set monitors on data in a PVRecord. What triggers a monitor depends on the monitor support in the server. The standard server provides support for the following: onPut, onChange, onAbsoluteChange, and onPercentChange.

A Monitor is created via a call to:

    void createMonitor(
        MonitorRequester monitorRequester,
        PVStructure pvRequest);
}

The definition is:

MonitorRequester extends Requester
    void monitorConnect(Status status, Monitor monitor, Structure structure))
    void monitorEvent((Monitor monitor)
    void unlisten()

Monitor
    Status start()
    Status stop()
    MonitorElement poll()
    void release(MonitorElement monitorElement)

MonitorElement
    PVStructure getPVStructure()
    BitSet getChangedBitSet()
    BitSet getOverrunBitSet()

MonitorRequester

The requester must implement this interface.

monitorConnect
This is called if the createMonitor fails or when the client request is connected to the server.
status
The result of the createMonitor request. If status is not success than the other arguments are null.
monitor
The interface for the Monitor.
structure
The introspection interface for the data that will be returned for each monitor event.
monitorEvent
monitorEvent is called when a monitor occurs. The client must call monitor.poll to get data for the monitor. See below.
unlisten
The server has issued an unlisten request.

The following is the Monitor interface which is defined in project pvData

interface Monitor extends Destroyable {
    Status start();
    Status stop();
    MonitorElement poll();
    void release(MonitorElement monitorElement);
}

where

start
Start monitoring
stop
Stop monitoring
poll
Poll for monitor event. Null is returned when no more events are available. The client must call this method in order to get monitor data.
release
When the client has processed the monitor event returned by poll the client must call release before again calling poll.

A monitorElement is defined as:

interface MonitorElement {
    PVStructure getPVStructure();
    BitSet getChangedBitSet();
    BitSet getOverrunBitSet();
}

where

getPVStructure
The data structure.
getChangedBitSet
A bitset which has a bit set for each field of the data structure which has changed since the last monitor.
getOverrunBitSet
A bitset which has a bit set for each field of the data structure which has changed more than once since the last monitor.

Monitor

start
Start monitoring
stop
Stop monitoring
poll
Poll for monitor event. Null is returned when no more events are available. The client must call this method in order to get monitor data.
release
When the client has processed the monitor event returned by poll the client must call release before again calling poll.

ChannelRPC

A ChannelRPC is like a channelPutGet except that a completely different pvStructure can be sent and returned for each request. It is created via a call to:

interface Channel extends Requester {
    ChannelRPC createChannelRPC(
        ChannelRPCRequester channelRPCRequester,
        PVStructure pvRequest);
}

The definition is:

ChannelRPCRequester extends Requester
    void channelRPCConnect(
        Status status,
        ChannelRPC channelRPC)
    void requestDone(
        Status status,
        ChannelRPC channelRPC,
        PVStructure pvResponse)

ChannelRPC extends ChannelRequest
    void request(PVStructure pvArgument)

ChannelRPCRequester

The requester must implement this interface.

channelRPCConnect
Status provides the result of the createChannelRPC request. If status is not success than the other argument is null.
requestDone
The request is done. status provides the result of the request. If successful a pvResponse is returned.

ChannelRPC

request
pvArgument is the structure that is sent to the server. If lastRequest is true than it is a one time request, i.e. Send it is the same as calling destroy after the request is complete.

API: pvaSrv

pvaSrv implements channel provider dbPv that runs in the EPICS V3 IOC

dbPv allows you to get, put and monitor V3 PVs (fields of EPICS DB records) over pvAccess, translating the value and its meta data (graphics limits, alarm status, timestamp) to or from V4 Normative Type (NT) pvData structures (NTScalar, NTScalarArray).

API: pvaPy

pvaPy provides a python interface to pvData and pvAccess.

The following:

#!/usr/bin/env python

from pvaccess import Channel

c = Channel('double01')
oldValue = c.get().getDouble()
print 'Got old value: ', oldValue
value = oldValue + 1.1
print 'Putting value via putDouble(): ', value
c.putDouble(value) 
newValue = c.get().getDouble()
print 'Got new value: ', newValue
produces:
python examplePut.py
Got old value:  5.3
Putting value via putDouble():  6.4
Got new value:  6.4

pvAccess Channel Providers.

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:

pvaSrv
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

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]

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
Just 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
Just 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
Just 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
Just 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. pvaSrv, 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?

Standard 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 pvaSrv and pvDatabaseCPP to specify of records should be processed. The default is false channelGet and true for channelPut and channelPutGet, An example is:
record[process=false]
block
This is used by pvaSrv to specify if a request to process a record should block until the record completes processing. The default is the same as the value ofi the process option. An example is:
record[block=false]

Simple Examples

Clients like CSS, Synoptic Displays, Alarm, and Archive 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.

Appendix I What has been tested

exampleJava/database

The following tests are using the database:

mrk> pwd
/home/epicsv4/master/exampleJava/database/shell
mrk> ./exampleDatabase 

exampleJava/exampleClient

Test Result
examplePvaClientGet Success
examplePvaClientMonitor Success
examplePvaClientMultiDouble Success
examplePvaClientNTMulti Success
examplePvaClientProcess Success
examplePvaClientPut Success
helloWorldPutGet Success
helloWorldRPC Success
getForever pva PVRdouble Success
monitorForever pva PVRdouble Success

exampleCPP/exampleClient

Test Result
examplePvaClientGet Success
examplePvaClientMonitor Success
examplePvaClientMultiDouble Success
examplePvaClientNTMulti Success
examplePvaClientProcess Success
examplePvaClientPut Success
helloWorldPutGet Success
helloWorldRPC Success
getForever pva PVRdouble Success
monitorForever pva PVRdouble Success
testRAII pva PVRdouble Success
testRAIIMultiDouble Only works with DBRecords
testRAIINTMulti Only works with DBRecords

exampleCPP/database

The following tests are using the database:

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

exampleJava/exampleClient

Test Result
examplePvaClientGet Success
examplePvaClientMonitor Success
examplePvaClientMultiDouble Success
examplePvaClientNTMulti many extra 0 elements on array
examplePvaClientProcess Success
examplePvaClientPut Success
helloWorldPutGet Success
helloWorldRPC Success
getForever pva PVRdouble Success
getForever pva DBRdouble00 Success
getForever ca DBRdouble00 Success
monitorForever pva PVRdouble Success
monitorForever pva DBRdouble00 Success
monitorForever ca DBRdouble00 Success

exampleCPP/exampleClient

Test Result
examplePvaClientGet Success
examplePvaClientMonitor Success
examplePvaClientMultiDouble Success
examplePvaClientNTMulti Success
examplePvaClientProcess Success
examplePvaClientPut Success
helloWorldPutGet Success
helloWorldRPC Success
getForever pva PVRdouble Success
getForever pva DBRdouble00 Success
getForever ca DBRdouble00 Success
monitorForever pva PVRdouble Success
monitorForever pva DBRdouble00 Success
monitorForever ca DBRdouble00 Success
testRAII pva PVRdouble Success
testRAII pva DBRdouble00 Success
testRAII ca DBRdouble00 Success
testRAIIMultiDouble pva Success
testRAIIMultiDouble ca Success
testRAIINTMulti pva Success
testRAIINTMulti ca Success

exampleJava/exampleLink

Starting either:

mrk> pwd
/home/epicsv4/master/exampleJava/exampleLink
mrk> scripts/exampleLinkMain pva doubleArray true
or
mrk> pwd
/home/epicsv4/master/exampleJava/exampleLink
mrk> scripts/exampleLinkMain local doubleArray true

exampleLinkClient works in both exampleJava and exampleCPP.

Starting both

mrk> pwd
/home/epicsv4/master/exampleJava/exampleLink
mrk> scripts/doubleArrayMain
and
mrk> pwd
/home/epicsv4/master/exampleJava/exampleLink
mrk> scripts/exampleLinkMain pva doubleArray true

exampleLinkClient works in both exampleJava and exampleCPP.

exampleCPP/exampleLink

Success

Starting either:

mrk> pwd
/home/epicsv4/master/exampleCPP/exampleLink/iocBoot/exampleLink
mrk> ../../bin/linux-x86_64/exampleLink st.cmd 
or
mrk> pwd
/home/epicsv4/master/exampleCPP/exampleLink/iocBoot/exampleLink
mrk> ../../bin/linux-x86_64/exampleLink st.local

exampleLinkClient works in both exampleJava and exampleCPP.

Starting:

mrk> pwd
/home/epicsv4/master/exampleCPP/exampleLink/iocBoot/doubleArrayExternal
mrk> ../../bin/linux-x86_64/exampleLink st.cmd
and either
mrk> pwd
/home/epicsv4/master/exampleCPP/exampleLink/iocBoot/exampleLink
mrk> ../../bin/linux-x86_64/exampleLink st.extca
or
mrk> pwd
/home/epicsv4/master/exampleCPP/exampleLink/iocBoot/exampleLink
mrk> ../../bin/linux-x86_64/exampleLink st.extpva

exampleLinkClient works in both exampleJava and exampleCPP.

But exampleLinkClient must be run with the arguments "pva doubleArrayExternal"

exampleJava/helloPutGet and exampleCPP/helloPutGet

The following servers were tested:

  1. Java implementation of helloPutGetMain
  2. C++ implementation of helloPutGetMain
  3. helloPutGet as part of a V3 IOC

For each server both the Java and C++ versions of helloPutGetClient were tested

All tests were successfull.

exampleJava/helloRPC and exampleCPP/helloRPC

The following servers were tested:

  1. Java implementation of helloService
  2. C++ implementation of HelloService

For each server both the Java and C++ versions of helloClient were tested

All tests were successfull.

But neither server terminated wnen exit was issued.

exampleJava/powerSupply and exampleCPP/powerSupply

The following servers were tested:

  1. Java implementation of powerSupplyMain
  2. C++ implementation of powerSupplyMain
  3. powerSupply as part of a V3 IOC

For each server both the Java and C++ versions of powerSupplyMonitore were tested. After both were started then the following was run:

pvput -r "power.value,voltage.value" powerSupply 1 2
This test was successful.

However when powerSupplyClient was run a failure occurs. See the next section for details.

exampleJava/pvDatabaseRPC and exampleCPP/pvDatabaseRPC

The following servers were tested:

  1. Java implementation of exampleRPCMain
  2. C++ implementation of exampleRPCMain
  3. exampleRPC as part of a V3 IOC

The following was started to see the result of run requests:

pvget -m mydevice

Next the following commands were issued on both Java and C++:

control configure 1 1
control run

All tests were successful.

exampleJava/serviceAPI

The server was started and then the client.

The test was successful but the server does not accept an exit command.

Appendix II Known Problems

pvDataJava

Field
Does not implement getNumberFields
PVDataCreate
Why does the following method exist?
PVStructure createPVStructure(string[] fieldNames,PVField[] pvFields)

pvaClient

getField
Not implemented
channelArray
Not implemented

C++ Server Segmentation Fault on exit

Sometimes a segmantation fault occurs when a C++ server terminates.

exampleCPP/ChannelArchiverService

Needs documentation.

exampleJava

exampleClient

helloRPC
helloWorldRPC does not terminate.
provider ca
numeric arrays are filled out with 0s

helloRPC

powerSupply

The powerSupplyClient ends with setting voltage to 0. This causes the PVRecord to throw an exception, which pvAccess passes back to the client. All appears OK except that if another client has a monitor on the PVRecord, that client no longer gets an monitor updates.

rdbService and serviceAPI

These are left almost unchanged since release 4.5. rdbService needs lots of work.

exampleCPP

monitors

When a monitor is issued to a channel that is served by dbPv (pvaSrv), Each monitor shows that all requested fields have changed.

helloRPC

helloService does not terminate.

exampleLink

st.ca
Starting:
mrk> pwd
/home/epicsv4/master/exampleCPP/exampleLink/iocBoot/exampleLink
mrk> ../../bin/linux-x86_64/exampleLink st.ca
Does not work.
When started this way the LinkRecord sets a monitor via provider "ca", which means that pvAccess sets a monitor via provider ca on a V3 record that is in the same IOC. The linkRecord never receives monitors.
Using provider dbPv
Attempting to use provider dbPv, i.e. pvaSrv, does not work.

powerSupply

The powerSupplyClient ends with setting voltage to 0. This causes the PVRecord to throw an exception, which pvAccess passes back to the client. All appears OK except that if another client has a monitor on the PVRecord, that client no longer gets monitor updates.

memory leaks

When any of the examples terminate valgrind shows memory leaks. Clients not so bad but servers not so good.

epicsbase: 3_14 vs 3_15

The following is from the exampleLink in https://github.com/mrkraimer/exampleCPP.

I also added some print statement in pvAccessCPP/src/server/responseHandlers.cpp

The example is started as part of an IOC with two channel providers started: dbPv and local.

Then I did:

 pvget exampleLink

Note that the provider for exampleLink is local.

With base 3.14 I get

epics> ServerChannelFindRequesterImpl::channelFindResult _responseCount 0 _expectedResponseCount 2 wasFound 0
ServerChannelFindRequesterImpl::channelFindResult _responseCount 1 _expectedResponseCount 2 wasFound 1
ServerChannelRequesterImpl::channelCreated status 1 channel exampleLink
epics> ServerChannelFindRequesterImpl::channelFindResult _responseCount 0 _expectedResponseCount 2 wasFound 0
ServerChannelFindRequesterImpl::channelFindResult _responseCount 1 _expectedResponseCount 2 wasFound 1
ServerChannelRequesterImpl::channelCreated status 1 channel exampleLink

With base 3.15 I get

epics> ServerChannelFindRequesterImpl::channelFindResult _responseCount 0 _expectedResponseCount 2 wasFound 0
ServerChannelFindRequesterImpl::channelFindResult _responseCount 1 _expectedResponseCount 2 wasFound 1
ServerChannelRequesterImpl::channelCreated status 0

NOTE that it never tried the second provider, i. e. provider local. Instead something tried to create channel from provider dbPv.

I did some more looking and it does not appear that pvaSrv is the problem. It looked like something in base3.15. src/ioc/db/dbChannel.c was calling dbChannelCreate as a result of calling dbChannelTest. But I never got to the final problem.

Also I created an example application for both 3.14 and for 3.15. Running the example ioc via valgrind shows some memory problems under both 3.14 and 3.15. BUT 3.15 has LOTs more problems.

I saw the same thing when I ran exampleCPP/exampleLink/iocBoot/exampleLink.