EPICS pvAccessJava

Release 4.0.2, 10-Nov-2014

Editors:
Marty Kraimer, BNL
Matej Sekoranja, CosyLab

Abstract

pvAccessJava is the Java implementation of pvAccess, which is one of a related set of products:
relatedDocumentsV4.html

Status of this Document

This is the 10-Nov-2014 version of the Java implementation of pvAccess. It is a complete implementation of pvAccess as currently defined.

Table of Contents

Introduction

This product is available via an open source license

This document is the project and package overviews for pvAccessJava. The javaDOC is available at JavaDoc

The javaDOC package overview documents for this project are not complete but the packages of interest to people writing client code or connecting an application that wants to start a pvAccess server is complete. What remains is to fully document the implementation. This project overview gives a brief description of the packages provided by this project.

This project implements pvAccess, which is an implementation of Channel Access that fully supports pvData. Package org.epics.pvaccess.client is of interest to anyone who wants to write client code for pvAccess and is also of interest to anyone who wants to write a server that interfaces to other system. The other packages are mainly of interest to those who want to understand the internals of the implementation.

This project also implements client code that uses the Channel Access network protocol to communicate with servers. It converts between the DBR ChannelAccess data and pvData so that the client only sees pvData.

If your only interest is to write client code then look at the example in the next section and then go directly to Pachkage org.epics.pvaccess.client in this document.

This project provides the client and server network support for the interfaces defined in org.epics.pvaccess.client. The pvIOCJava provides a local implementation of pvAccess API, which registers itself with the server support provided by this project. Other implementations can also be provided.

This package also allows the following:

  1. An application can be created that provide a pvAccess server that provides full support for pvData.This is done by starting a local PVDatabase, calling org.epics.pvaccess.LocalFactory.start(), and calling org.epics.pvaccess.ServerFactory.start().
  2. An extensible set of monitoring algorithms is supported. Monitoring is defined by project pvData. pvAccess provides access to this facility. A default set of monitoring algorithms is provided by pvData but other algorithms can be implemented. The code that implements an algorithm must extend org.epics.pvdata.monitor.AbstractMonitor and register with org.epics.pvaccess.server.ChannelServer.
  3. A gateway between pvAccess and other systems can be implemented.

Example

What the example does

This section shows an example of issuing a channel get request. the example is a java main program that can be executed as follows:

java org.epics.pvaccess.client.example.ExampleChannelGet <channelName> <request>

For example if the arguments are:

    counter field(value)

The output is:

2012-09-11T12:48:44.831 Channel 'counter' created with status: StatusImpl [type=OK]. 
2012-09-11T12:48:45.631 Channel 'counter' CONNECTED. 
2012-09-11T12:48:45.708 ChannelGet for 'counter' connected with status: StatusImpl [type=OK]. 
2012-09-11T12:48:45.710 getDone for 'counter' called with status: StatusImpl [type=OK]. 
structure 
    int value 1
2012-09-11T12:48:45.735 Channel 'counter' DISCONNECTED. 
2012-09-11T12:48:45.736 Channel 'counter' DESTROYED. 

If the arguments are:

    counter field(value,timeStamp,alarm)

The output is:

2012-09-11T12:51:12.113 Channel 'counter' created with status: StatusImpl [type=OK]. 
2012-09-11T12:51:12.156 Channel 'counter' CONNECTED. 
2012-09-11T12:51:12.163 ChannelGet for 'counter' connected with status: StatusImpl [type=OK]. 
2012-09-11T12:51:12.164 getDone for 'counter' called with status: StatusImpl [type=OK]. 
structure 
    int value 4
    timeStamp_t timeStamp
        long secondsPastEpoch 1347360671
        int nanoseconds 660000000
        int userTag 0
    alarm_t alarm
        int severity 0
        int status 0
        string message 
2012-09-11T12:51:12.167 Channel 'counter' DISCONNECTED. 
2012-09-11T12:51:12.168 Channel 'counter' DESTROYED.

If the request is not given then the entire record is shown.

Example Source

main:

public class ExampleChannelGet {

    public static void main(String[] args) throws Throwable {
        
    int len = args.length;
        if (len == 0 || len > 2)
        {
            System.out.println("Usage: <channelName> <pvRequest>");
            return;
        }
        
        final String channelName = args[0];
        final String pvRequestString = args[1];
        
        // initialize console logging
        ConsoleLogHandler.defaultConsoleLogging(Level.INFO);
        Logger logger = Logger.getLogger(ExampleChannelGet.class.getName());
        logger.setLevel(Level.ALL);

        // setup pvAccess client
        org.epics.pvaccess.ClientFactory.start();

        // get pvAccess client provider
        ChannelProvider channelProvider =
            ChannelProviderRegistryFactory.getChannelProviderRegistry()
                .getProvider(org.epics.pvaccess.ClientFactory.PROVIDER_NAME); 
        //
        // create channel and channelGet
        //
        CountDownLatch doneSignal = new CountDownLatch(1);

        ChannelRequesterImpl channelRequester = new ChannelRequesterImpl(logger);
        Channel channel = channelProvider.createChannel(
            channelName, channelRequester, ChannelProvider.PRIORITY_DEFAULT);
        ChannelGetRequester channelGetRequester = new
            ChannelGetRequesterImpl(logger, channel, doneSignal);
        CreateRequest createRequest = CreateRequest.create();
        PVStructure pvRequest = createRequest.createRequest(pvRequestString);
        if(pvRequest==null) {
                String message = "createRequest failed " + createRequest.getMessage();
                logger.info(message);
        } else {
                channel.createChannelGet(channelGetRequester,pvRequest);

                // wait up-to 3 seconds for completion
                if (!doneSignal.await(3, TimeUnit.SECONDS))
                        logger.info("Failed to get value (timeout condition).");
        }
        // stop pvAccess client
        org.epics.pvaccess.ClientFactory.stop();
    }

The main routine first checks for valid arguments, initializes console logging, creates a channel and channel get and then waits for up to 3 seconds for response.

Implementation of ChannelRequester:

    static class ChannelRequesterImpl implements ChannelRequester
    {
        private final Logger logger;
        public ChannelRequesterImpl(Logger logger)
        {
            this.logger = logger;
        }

        public String getRequesterName() {
            return getClass().getName();
        }

        public void message(String message, MessageType messageType) {
            logger.log(LoggingUtils.toLevel(messageType), message);
        }

        public void channelCreated(Status status, Channel channel) {
            logger.info("Channel '" + channel.getChannelName() + "' created with status: " + status + ".");
        }

        public void channelStateChange(Channel channel, ConnectionState connectionState) {
            logger.info("Channel '" + channel.getChannelName() + "' " + connectionState + ".");
        }
    
    }

ChannelRequester.channelCreated method gets called when channel instance is created. ChannelRequester.channelStateChange gets called on very channel state change. The implementation above just logs all the information.

Implementation of ChannelGetRequester:

    
    static class ChannelGetRequesterImpl implements ChannelGetRequester
    {
        private final Logger logger;
        private final Channel channel;
        private final CountDownLatch doneSignaler;
    
        private volatile PVStructure pvStructure = null;
   
        public ChannelGetRequesterImpl(Logger logger, Channel channel, CountDownLatch doneSignaler)
        {
            this.logger = logger;
            this.channel = channel;
            this.doneSignaler = doneSignaler;
        }

        public String getRequesterName() {
            return getClass().getName();
        }

        public void message(String message, MessageType messageType) {
            logger.log(LoggingUtils.toLevel(messageType), message);
        }

        public void channelGetConnect(Status status, ChannelGet channelGet,Structure structure
        {
            logger.info("ChannelGet for '" + channel.getChannelName() +
                "' connected with status: " + status + ".");
            if (status.isSuccess())
            {
                this.pvStructure = pvStructure;
                channelGet.get(true);
            }
            else
                doneSignaler.countDown();
        }

        public void getDone(Status status, PVStructure pvStructure, BitSet changedBitSet)s
        {
            logger.info("getDone for '" + channel.getChannelName() +
                "' called with status: " + status + ".");
            if (status.isSuccess())
            {
                System.out.println(pvStructure.toString());
            }

            doneSignaler.countDown();
        }
    }

ChannelGetRequester.channelGetConnect gets called when ChannelGet request is created on server side. Client must always check status for errors. The implementation above issues actual get request on success by calling "channelGet.get()". User can always destroy a request by calling "channelGet.destroy()".
ChannelGetRequester.getDone gets called on get operation completion. On success indicated by status parameter, pvStructure holds the latest data.

User Interface Packages

This section briefly describes the interfaces used by a pvAccess client and by an application that supports a pvAccess server. It only describes how things are connected, i.e. it does not describe pvAccess API or any of its related interfaces because they are described in the package overview for org.epics.pvaccess.client.

org.epics.pvaccess

This package provides the factories that start the client and server that use the pvAccess network protocol to communicate between client and server.

ClientFactory
This starts the client side of the network support for pvAccess. This is the only support that needs to be started by a stand-alone client. The example shown in the previous section calls this.
ServerFactory
This must be started in order to allow pvAccess clients to access the local pvAccess data source. For example the pvIOCJava (PVDatabase) can start this.

A client normally only needs to start the channelProvider as follows:

org.epics.pvaccess.ClientFactory.start();

This starts the channelProvider that uses the pvAccess network protocol. The name of the provider is pva.

For starting servers see package overview below for details.

org.epics.ca

This provides an implementation of ChannelProvider that uses the Channel Access network protocol instead of the pvAccess network protocol. It converts between DBR data and pvData. It has a ClientFactory that has methods start and stop.

A client starts the channelProvider as follows:

org.epics.ca.ClientFactory.start();

The name of the provider is ca.

The channelProvider implements ChannelGet, ChannelPut, ChannelArray, and Monitor. It does not implement ChannelProcess, ChannelPutGet, or ChannelRPC.

org.epics.pvaccess.client

This package provides everything required for a pvAccess client. See the package overview for details. This section only describes how to connect to a channel.

To connect to a channel a client does the following:

    // get wanted channel provider 
    
    ChannelProvider channelProvider = 
        ChannelProviderRegistryFactory.getChannelProviderRegistry().getProvider("pva");

    // connect to a channel with given name
    Channel channel = channelProvider.createChannel(
        "someChannel",
         channelRequester,
         ChannelProvider.PRIORITY_DEFAULT);

This package defines the interfaces a client uses to communicate with a pvAccess server. This project fully implements all the interfaces. If a server is implemented for other systems, it is OK to implement a subset of the interfaces.

Except for ChannelAccessFactory and CreateRequest, this package contains only Java interface and enum definition.

Package org.epics.pvaccess

Overview

This package provides:

Factories
ClientFactory and ServerFactory
PVAConstants
A set of constants used by the pvAccess implementation.
PVAException
A extension of Exception for pvAccess.
PVFactory
A convenience class for accessing pvData facilities.
Version
Administrative class to keep track of the version number.

Factories

Each factory has a public method:

    public static synchronized void start();

For example the pvAccess client can be started by calling:

org.epics.pvaccess.ClientFactory.start();

The following factories are available:

ClientFactory
This starts the client side pvAccess API of the network support for pvAccess. This is the only support that needs to be started by a stand-alone client.
ServerFactory
This starts the server side pvAccess API of the network support for pvAccess. For example the pvIOCJava can start this.

The ClientFactory has a public method:

org.epics.pvaccess.ClientFactory.stop();

PVAConstants

This defines a number of constants used by the implementation. It is not of interest to users. Look at the source for details.

PVAException

This is a simple extension of the Java Exception class. It is defined so that channel access specific exceptions can be thrown.

Version

This is an administrative class to keep track of the version number. See the code for details.

Package org.epics.pvaccess.client

Overview

This package defines the client interfaces for pvAccess, which is a version of channel access that fully supports structured data as defined by PVData. It is assumed that the reader understands PVData, which is implemented by the package "org.epics.pvdata".

A Channel provides a communication path between a client and a PVRecord. A channel access server, which must reside in the same process as the PVRecord, provides access to the record. The client and server can be part of the same process or can be in different processes on the same network node or on different network nodes. Channel Access provides the connection between the client and server.

A client creates a channel via a call to ChannelProvider.createChannel.

This overview discusses:

Many of the methods described below return or have a argument Status. This is defined in project pvData as follows:

interface Status extends Serializable {
    public enum StatusType {OK,WARNING,ERROR,FATAL}
    StatusType getType();
    String getMessage();
    String getStackDump();
    boolean isOK();
    boolean isSuccess();
}

Unless isSuccess is true the client must realize that the request failed. When a failure occurs, other arguments may be null.

ChannelProviderRegistry, and ChannelProvider

In order to connect to a channel a client must:

  1. Call ChannelProviderRegistryFactory.getChannelProviderRegistry().getProvider(String providerName) to get a ChannelProvider.
  2. Call ChannelProvider.createChannel(String channelName, ...) to create a Channel.

A client must know the channel name and the name of the channel provider.

ChannelProviderRegistry

Class org.epics.pvaccess.client.ChannelAccessFactory has the following public methods:

interface ChannelProviderRegistry {
    ChannelProvider getProvider(String providerName);
    ChannelProvider createProvider(String providerName);
    String[] getProviderNames();
}

where

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

interface ChannelProvider {
    static final public short PRIORITY_MIN = 0;
    static final public short PRIORITY_MAX = 99;
    static final public short PRIORITY_LINKS_DB = PRIORITY_MAX;
    static final public short PRIORITY_ARCHIVE = (PRIORITY_MAX + PRIORITY_MIN) / 2;
    static final public short PRIORITY_OPI = PRIORITY_MIN;

    void destroy();
    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);
}

where

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.

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. The interfaces are:

interface ChannelFindRequester {
    void channelFindResult(
        Status status,
        ChannelFind channelFind,
        boolean wasFound);
}

interface ChannelFind {
     ChannelProvider getChannelProvider();
     void cancelChannelFind();
} 

where

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.
getChannelProvider
Get the channel provider.
cancelChannelFind
Cancel the find request.

Channel

The Channel interface provides access to the services a channel access provider implements for a channel. The principal services are: process, get, put, putGet, RPC, array, and monitor. Each of these and some other services are described in later sections of this package overview.

As described in the previous section a Channel is created via a call to the method:

    ChannelProvider.createChannel(String channelName,ChannelRequester channelRequester)

The caller must implement the interface:

interface ChannelRequester extends Requester {
    void channelCreated(
        Status status,
        Channel channel);
    void channelStateChange(
        Channel channel,
        ConnectionState connectionState);
}

where

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.

The Channel interface is:

interface Channel extends Requester{
    public enum ConnectionState {
         NEVER_CONNECTED, CONNECTED, DISCONNECTED, DESTROYED
    };

    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);
}

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
See pvDataJava for a description of pvRequest.

AccessRights

Access Rights are not currently implemented.

enum AccessRights {
    none,
    read,
    readWrite
}

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 requester must implement the interface:

interface ChannelProcessRequester extends Requester {
    void channelProcessConnect(
        Status status,
        ChannelProcess channelProcess);
    void processDone(
        Status status,
        ChannelProcess channelProcess);
}

where

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.

The following is the interface for requesting that a record be processed.

interface ChannelProcess extends ChannelRequest {
    void process();
}

where

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);
}

where

channelGetRequester
Described next.
pvRequest
This was described in a previous section. BUT for channelGet the request string for createRequest.createRequest has the form: "record[options]field(fieldOptions)". An example of a record options is "process=true". The fieldOptions are as defined by pvCopy.

The requester must implement the interface.

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

where

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.

The following is the interface for requesting data from a record.

interface ChannelGet extends ChannelRequest {
    void get();
}

where

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);
}

where

channelPutRequester
Described next.
pvRequest
This was described in a previous section. BUT for channelPut the request string for createRequest.createRequest has the form: "record[options]field(fieldOptions)". An example of a record options is "process=true". The fieldOptions are as defined by pvCopy.

The requester must implement the interface.

interface 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);
}

where

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.

The following is the interface for requesting data from a record.

interface ChannelPut extends ChannelRequest {
    void put(PVStructure pvPutStructure, BitSet bitSet);
    void get();
}

where

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);
}

where

channelPutGetRequester
Described next.
pvRequest
This was described in a previous section. BUT for channelPutGet the request string for createRequest.createRequest has the form: "record[...]putField(fieldOptions)getField(fieldOptions)". An example of a record options is "process=true". The fieldOptions are as defined by pvCopy.

The requester must implement the interface.

interface 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);
}

where

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.

The following is the interface for requesting data from a record.

interface ChannelPutGet extends ChannelRequest {
    void putGet(PVStructure pvPutStructure, BitSet putBitSet);
    void getPut();
    void getGet();
}

where

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.

ChannelRPC

A ChannelRPC is like a putProcessGet except that a completely different PVStructure can be returned for each request. It is created via a call to:

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

where

channelRPCRequester
Described next.
pvRequest
This was described in a previous section. BUT for channelRPC the request string for createRequest.createRequest has the form: "record[options]field(fieldOptions)". Normally no record options are entered. The fieldOptions are as defined by pvCopy.

The requester must implement the interface.

interface ChannelRPCRequester extends Requester
{
    void channelRPCConnect(
        Status status,
        ChannelRPC channelRPC);
    void requestDone(
        Status status,
        ChannelRPC channelRPC,
        PVStructure pvResponse);
}

where

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.

The following is the interface for requesting data from a record.

interface ChannelRPC extends ChannelRequest {
    void request(PVStructure pvArgument);
}

where

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.

Only one request at a time can outstanding (this will be allowed in a future).

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);
}

where

channelArrayRequester
Described next.
pvRequest
This must be a PVStructure created as follows:
CreateRequest createRequest = CreateRequest.create();
PVStructure pvRequest = createRequest.createRequest("subField");
if(pvRequest==null) {
    String message = createRequest.getMessage();
    // take drastic action
}
    
subField is the full path name to the desired field. For example "value" selects a top level field named value.

The requester must implement the interface.

interface 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);
}

where

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.

The following is the interface for getting or putting array data from a record.

interface 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);
}

where

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);
}

where

monitorRequester
Described next.
pvRequest
This is defined by pvCopy and pvMonitor. See their package overviews for details (org.epics.pvdata.pvCopy and org.epics.pvdata.pvMonitor). The format of the request for CreateRequest.createRequest has the form "record[options]field(fieldOptions)". Note that you can specify monitor options for individual fields. See the package overview for pvMonitor for details.

The requester must implement the interface.

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

where

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.

Problems and Future Plans

Access Security

Something like how CA implements access security is required. A complication is that for CA a client attaches to a single scalar or array field. For pvAccess a client attaches to an arbitrary set of fields in a record. Takes some thought to decide what to do.

User Controlled Send Queues

Currently a user can only ask to send a message immediately, i. e. a client has no control over when a message is sent. This can result in many short network packets. Perhaps the following should be provided: Allow a client, for each send request, to specify the following options:

immediate
Immediately queue a request to the send thread.
periodically
Put requests on a periodic send queue. At some periodic rate the queue is transfered to the send thread.
user queue
Client keeps a private queue for requests. The client decides when the queue should be transfered to the send thread.

Helper Packages

This is set of packages that are used by other parts of this project

org.epics.pvaccess

This has the following:

ClientFactory
Starts the client side of remote pvAccess.
ServerFactory
Starts the server side of remote pvAccess.
PVAConstants
A set of constants for pvAccess
PVAException
An extension to Exception for pvAccess
Version
Defines the version.
PVFactory
Factory proxy for all the standard pvData factories used by pvAccess.

org.epics.pvaccess.util.logging

A logging facility based on java.util.logging. This has the following:

LoggerProvider
Defines an interface that provides a logger.
LoggingUtils
Contains various logging helper methods, e.g. mapping from pvData MessageType to Java Logging API Level.
ConsoleLogFormatter
Java API formatter that produces single line log reports meant to go to the console.
ConsoleLogHandler
Java API log handler output logs to the System.out. By default it uses ConsoleLogFormatter.

To setup a simple logging in you console application, use the following code:

        ConsoleLogHandler.defaultConsoleLogging(Level.INFO);
        Logger logger = Logger.getLogger(MyApplicationClass.class.getName());

        ...
        logger.info("Hello world.");

Implementation Packages

These packages provide implementation classes and interfaces shared by both the client and server code for pvAccess

org.epics.pvaccess.util

This has the following:

CircularBuffer
A fixed size circular buffer. If full a new element replaces the oldest.
GrowingCircularBuffer
A circular buffer that holds an unlimited number of elements.
HexDump
Converts a byte array to hex and writes to System.out.println.
InetAddressUtil
Convience methods for INET address conversion.
IntHashMap
An integer hash map. This is used instead of java.util.HashMap so that Integer objects do not have to be allocated.
Mailbox
Optimized usage of concurrent linked list queue.
ShortHashMap
A short hash map. This is used instead of java.util.HashMap so that Short objects do not have to be allocated.

org.epics.pvaccess.util.sync

This package contains thread synchronization utility classes, e.g. named lock pattern implementation (lock guarding a named instance).

org.epics.pvaccess.util.configuration

This package defines interfaces for configuration framework used by pvAccess.

org.epics.pvaccess.util.configuration.impl

This package implements default configuration method for pvAccess.

org.epics.pvaccess.impl.remote

This package contains enums, interface, and abstract class definitions common to both the client and server implementation.

org.epics.pvaccess.impl.remote.codec

This package implement pvAccess protocol (codec) in a way that can be plugged into the blocking or non-blocking IO system.

org.epics.pvaccess.impl.remote.codec.impl

This package partly implements codec to be used by blocking or non-blocking IO system.

org.epics.pvaccess.impl.remote.io

This package currently provides interfaces of a non-blocking IO system.

org.epics.pvaccess.impl.remote.io.impl

This package contains an implementation of non-blocking IO system using Selector.

org.epics.pvaccess.impl.remote.request

This pakcage contains interfaces pvAccess uses to handle requests.

org.epics.pvaccess.impl.remote.tcp

This package contains the code that implements pvAccess codec over TCP transport.

org.epics.pvaccess.impl.remote.udp

his package contains the code that implements pvAccess codec over UDP transport.

org.epics.pvaccess.impl.remote.utils

Some network utility support.

Client Implementation Packages

These packages provide the implementation classes and interfaces for the client code for pvAccess.

org.epics.pvaccess.client.impl.remote

This implements the client side pvAccess API of passing the various ChannelXXX requests over the network, where XXX is Get, Put, PutGet, etc. This code uses the code in the following two packages to send requests and receive responses.

org.epics.pvaccess.client.impl.remote.handlers

This implements handlers for receiving responses from network requests.

org.epics.pvaccess.client.impl.remote.requests

This implements code that sends a network request.

org.epics.pvaccess.client.impl.remote.search

This implement pvAccess discovery mechanism over UDP.

org.epics.pvaccess.client.impl.remote.tcp

This interfaces the client code to package org.epics.pvaccess.impl.remote.tcp.

Server Implementation Packages

These packages provide the implementation classes and interfaces for the server code for pvAccess.

org.epics.pvaccess.server.impl.remote

This implements the server side pvAccess API of passing the various ChannelXXX requests over the network, where XXX is Get, Put, PutGet, etc. This code uses the code in the following two packages to send requests and receive responses. It also has the code which issues beacons.

org.epics.pvaccess.server.impl.remote.handlers

This implements handlers for receiving requests from the client.

org.epics.pvaccess.server.impl.remote.plugins

Currently has just code related to beacons. User can attach its own data to the beacon messages (e.g. to report IOC status).

org.epics.pvaccess.server.impl.remote.tcp

This interfaces the server code to package org.epics.pvaccess.impl.remote.tcp.

org.epics.pvaccess.server.impl.rpc

This package contains interfaces to implement a service based on pvAccess RPC mechanism.

org.epics.pvaccess.server.impl.rpc.impl

This package is the implementation of the org.epics.pvaccess.server.impl.rpc package.

Package org.epics.pvaccess.server

Overview

This package defines the server interfaces for pvAccess, which is the version of channel access that fully supports structured data as defined by PVData.

Server Context

This is an interface implemented by pvAccess server:

interface ServerContext {
    Version getVersion();
    void initialize(ChannelProviderRegistry channelAccess) throws PVAException;
    void initialize(ChannelAccess channelAccess) throws PVAException, IllegalStateException;
    void run(int seconds) throws PVAException, IllegalStateException;
    void shutdown() throws PVAException, IllegalStateException;
    void destroy() throws PVAException, IllegalStateException;
    void printInfo();
    void printInfo(PrintStream out);
    void dispose();
    void setBeaconServerStatusProvider(BeaconServerStatusProvider beaconServerStatusProvider);
}

where

getVersion
Get server implementation version.
initialize
Force to initialize server immediately (user does not need to call this).
run
Run the server for specified amount of time (seconds), 0 stands for forever.
shutdown
Shutdown the server.
destroy
Destroy server instance.
printInfo
Print server info to the System.out.
dispose
Silently destroy the server instance.
setBeaconServerStatusProvider
Set BeaconServerStatusProvider instance to be used by the server (optional).

To start pvAccess server you can simply use the following code:

org.epics.pvaccess.ServerFactory.start();