PVService: gather
Overview
2010.07.16


Introduction


Gather is a sevice that "gathers" data from a set of channels and makes the data available via arrays of data.

This package provides two services:

createGather
This service creates new gather records. The service is implemented via a javaIOC record.
gather
This is based on a record that has links to a set of records. This service is also implemented via a javaIOC record.

This overview discusses the following topics:

Create Gather
The methods for CreateGather and CreateGatherFactory.
Gather
The methods for Gather and GatherFactory.
Example
A description of how to execute the examples for CreateGather and Gather.
Create Gather Client Implementation
A description of the implementation code for the client side of CreateGather.
Create Gather Record Implementation
A description of the implementation code for the server side of CreateGather.
Gather Client Implementation
A description of the implementation code for the client side of Gather.
Gather Record Implementation
A description of the implementation code for the server side of Gather.

Create Gather


CreateGatherFactory

CreateGatherFactory creates a client that connects to a createGather record, which implements the createGather service.

class CreateGatherFactory {
    public static CreateGather create(String serverName,Requester requester);
}

The arguments to create are:

serverName
The name of a javaIOC record with support that implements the createGather service.
requester
The caller must implement interface Requester which is defined in project pvData.

CreateGather

interface CreateGather {
    void destroy();
    Status connect();
    Status waitConnect(double timeout);
    Status sendRequest(String[] channelNames,boolean channelsAreArrays,String gatherRecordName,
        boolean temporary,boolean monitor);
    Status waitRequest();
}

where:

destroy
This is called by the client to cleanup all client resources used by createGather. It destroys the connection to the createGather record.
connect
This is called once by the client to create a connection to the createGather record.
waitConnect
Wait until connect is done.
sendRequest
Send a request to create a gather record. The arguments to sendRequest are:
channelNames
An array of caV3 channel names. This is the set of channels that are gathered by the gather record created by this service.
channelsAreArrays
Are the caV3 channels expected to be arrays?
gatherRecordName
The name of the gather record created by this request.
temporary
If this is true then the gather record will be destroyed when it is no longer being used.
monitor
If this is true then the gather record will monitor each of the caV3 channels.
waitRequest
Wait until the request is done. The client can then connect to the gather record.

After creating a new implementation of createGather the client must connect and then wait for connection to complete. Connect and waitConnect are separate calls so the client can do other things while connecting.

Once the connection is complete the client can call sendRequest and waitRequest. Again these are separate requests so that the client can do other things while sendRequest is active.

When the client is done with createGather.destroy should be called.

Thus life cycle for a client must follow the following order:

     CreateGather createGather = CreateGatherFactory.create(...)
     createGather.connect(...)
     createGather.waitConnect(...)
     REPEAT
         createGather.sendRequest(...)
         gather.waitRequest(...)
     ENDREPEAT
     createGather.destroy()

If the order is violated an exception is thrown.

The Status return should always be checked to see that status.isOK() returns true.

If waitRequest returns status.isOK() true then the gather record has been created. If the record already exists status.isOK() is true but requester.message is called with a warning message stating that the record already exists.

Example

public class CreateGatherRecords {
private static final String requesterName = "createGatherRecords";
private static final String serverName = "createGather";

private static String putGetPrefix = "putGetExample";
private static String putGetRecordName = putGetPrefix;
private static int numberPutGetRecords = 5;

private static String gatherPrefix = "gatherExample";
private static String gatherRecordName = gatherPrefix;
private static int numberGatherRecords = 5;

private static String arrayPrefix = "array";
private static String arrayRecordName = arrayPrefix;
private static int numberArrayRecords = 3;

public static void main(String[] args) {
    
    org.epics.pvaccess.ClientFactory.start();
    Requester requester = new RequesterImpl();
    CreateGather create = CreateGatherFactory.create(serverName, requester);
    Status status = create.connect();
    if (!status.isSuccess()) messageAndExit(status);
    status = create.waitConnect(1.0);
    if (!status.isSuccess()) messageAndExit(status);

    String[] channelNames = new String[numberPutGetRecords];
    for (int i = 0; i < numberPutGetRecords; i++){
        channelNames[i] = putGetPrefix + String.format("%04d", i);
    }
    status = create.sendRequest(channelNames, putGetRecordName,false,false,false);
    if (!status.isSuccess()) messageAndExit(status);
    status = create.waitRequest();
    if (!status.isSuccess()) messageAndExit(status);

    channelNames = new String[numberGatherRecords];
    for (int i = 0; i < numberGatherRecords; i++){
        channelNames[i] = gatherPrefix + String.format("%04d", i);
    }
    status = create.sendRequest(channelNames, gatherRecordName,false,true,false);
    if (!status.isSuccess()) messageAndExit(status);
    status = create.waitRequest();
    if (!status.isSuccess()) messageAndExit(status);

    channelNames = new String[numberArrayRecords];
    for (int i = 0; i < numberArrayRecords; i++){
        channelNames[i] = arrayPrefix + String.format("%04d", i);
    }
    status = create.sendRequest(channelNames, arrayRecordName,false,true,true);
    if (!status.isSuccess()) messageAndExit(status);
    status = create.waitRequest();
    if (!status.isSuccess()) messageAndExit(status);

    create.destroy();
}

private static void messageAndExit(Status status) {
    System.out.printf("status %s message %s%n", status.getType().name(),
    status.getMessage(), status.getStackDump());
     System.exit(1);
}

private static class RequesterImpl implements Requester {
    public String getRequesterName() {return requesterName;}

    public void message(String meage, MessageType messageType) {
        System.out.printf("%s %s%n", messageType.name(), message);
    }
}
}


Gather


GatherFactory

The definitions for are:

class GatherFactory {
    public static Gather create(String serverName,boolean channelsAreArrays,Requester requester,
        String commands,String properties);
} 

The GatherFactory is called to create a new client that connects to a gather record with the name serverName. The caller must implement interface Requester. Once a Gather is created the client can call it as many times as desired.

The arguments to create are:

serverName
The name of the record that implements the gather service.
channelsAreArrays
Are the caV3 channels expected to be arrays?
requester
The client must implement interface Requester, which is described in pvData.
commands
This is a list of commands the client will issue to gather. This is any combination of "get,processGet,putValue,putBad". If a command not given in this list is issued an exception is thrown.
properties

The properties the client will access from GatherData. This can be any combination of "value,isBad,isConnected,severityIndex". If a property is not specified here then GatherData will return a null value for that property when it is requested. For each of isBad, isConnected, and severityIndex the gather client creates a monitor on the corresponding field of the gather record if the property is requested.

Gather

interface GatherMonitorRequester {
    void monitorEvent();
}

interface GatherMonitor {
    boolean poll();
}

interface Gather {
    void destroy();
    Status connect();
    Status waitConnect(double timeout);
    GatherDataScalar getGatherDataScalar();
    GatherDataArray getGatherDataArray();
    Status createMonitor(GatherMonitorRequester gatherMonitorRequester);
    Status waitCreateMonitor();
    GatherMonitor getGatherMonitor();
    Status processGet();
    Status waitProcessGet();
    Status get();
    Status waitGet();
    Status putBad(boolean[] isBad);
    Status waitPutBad();
    Status putValue(double[] value);
    Status waitPutValue();
}

where:

destroy
This should be called when the client is done using gather. It disconnects from the gather record.
connect
This is called one time by the client to connect to the gather record. It also creates GatherDataScalar or GatherDataArray. This method does not block
waitConnect
Wait for connect to complete. This method blocks until connect is done or until timeout.
getGatherDataScalar
Get the interface for gatherDataScalar. This must not be called until waitConnect returns with status.isOK() is true. It returns a non null value only if waitConnect returned status OK and the client requested scalar data.
getGatherDataArray
Get the interface for gatherDataArray. This must not be called until waitConnect returns with status.isOK() is true. It returns a non null value only if waitConnect returned status OK and the client requested array data.
createMonitor
Create a monitor for the gather record. See below for details about monitoring. The client can call this at most once.
waitCreateMonitor
Wait for the monitor to be created.
getGatherMonitor
Get the channelMonitor that was created.
processGet
Issue a processGet to the gather record. this will make the gather record issue a caV3 get to each of the channels.
waitProcessGet
Wait for processGet to complete.
get
Get the current data stored in the gather record.
waitGet
Wait for get to complete.
putBad
Write to the isBad array of the gather record.
waitPutBad
Wait for putBad to complete.
putValue
Change the value array. This will change the value array in the gather record AND cause the value of each caV3 channel to be changed. Note that this is only supported if the caV3 channels are scalars.
waitPutValue
Wait for putValue to complete.

After creating a new implementation of gather the client must connect and then wait for connection to complete. This is done via separate calls so the the client can do other things while connecting.

Once the connection is complete the client can issue a single createMonitor request and multiple get and wait requests. Each has a separate request and wait. These are separate so that the client can do other things while the request is being implemented.

When the client is done with the gather destroy should be called.

Thus life cycle for a client must follow the following order:

     Gather gather = GatherFactory.create(...)
     gather.connect(...)
     gather.waitConnect(...)]
     gather.getGatherDataScalar()
     or
     gather.getGatherDataArray()
     IF_WANT_MONITOR
         gather.createMonitor()
         gather.waitCreateMonitor()
         gather.getMonitor()
     ENDIF_WANT_MONITOR
     REPEAT
         gather.get(...)
         gather.waitGet(...)
         and/or
         gather.processGet()
         gather.waitProcessGet()
         and/or
         gather.putBad()
         gather.waitPutBad()
         and/or
         gather.putValue()
         gather.waitPutValue()
     ENDREPEAT
     gather.destroy()

If the order is violated an exception is thrown.

The Status returns should always be checked to see that status.isOK() returns true.

GatherData

After the client has successfull connected to the gather record either a GatherDataScalar or a GatherDataArray is created. It holds data obtained for the client. For isBad, isConnected, and severityIndex a monitor is placed on the corresponding field of the gather record. GatherData is updated when a monitor occurs. The value field is updated either because of a get request or because gatherMoitor.poll() is called. Thus the update of the value field is under the complete control of the client.

interface GatherData {
    Alarm getAlarm();
    TimeStamp getTimeStamp();
    int getNumberChannels();
    boolean anyBad();
    boolean isBad(int index);
    boolean badChange();
    boolean allConnected();
    boolean isConnected(int index);
    boolean connectionChange();
    int getSeverityIndex(int index);
    boolean severityIndexChange();
}

interface GatherDataScalar extends GatherData {
    double[] getValue();
}

interface GatherDataArray extends GatherData {
    double[] getValue(int index);
}

where

getAlarm
Get the alarm for the gather record.
getTimeStamp
Get the timeStamp for the gather record.
getNumberChannels
Get the number of channels the gather record is gathering.
anyBad
Have any of the channel been set bad.
isBad
Is the channel with the specified index bad.
badChange
Has any channel changed bad status since the last call to badChange? This initially is true When this is called it is set back to false.
allConnected
Are all channels connected.
isConnected
Is the channel with the specified index connected.
connectionChange
Has any channel changed connection status since the last call to this method? This initially is true When this is called it is set back to false.
getSeverityIndex
Get the severity index for the specified channel.
severityIndexChange
Has the severity index for any channel changed since the last call to this method? This initially is true When this is called it is set back to false.

GatherDataScalar extends GatherData. It is implemented only if the gather record accesses channels that are each a scalar. It implements the following method:

getValue
This returns and array of doubles.

GatherDataArray extends GatherData. It is implemented only if the gather record accesses channels that are each an array. It implements the following method:

getValue
This method has an argument that specifies the channel value desired and returns the data array from that channel.

GatherMonitor

As shown above a client can ask to monitor a gather record. In this case the client must implement interface GatherMonitorRequester, which has a single method monitorEvent. This method is called whenever a monitor is sent from the gather record. The client calls gatherMonitor.poll() to see if more monitior events are queued for the client. If there is more, the data is put into GatherData. Thus the client has code like:

  if(monitorEvent) {
      while(true)
          if(!gatherMonitor.poll()) break;
          // data is in gatherDataScalar or gatherDataArray
      }
  }

Example


An example client and a caV3 database is provided in gather/test/org/epics/pvService/gather/example/gather.zip. Somewhere unzip this file and:

File gather/test/org/epics/pvService/gather/example/package.html provides more details about running the examples.


Create Gather Client Implementation


The client side of createGather implements the methods of CreateGather as follows:

delete
Calls channel.destroy which destroys all pvAccess connections to the createGather record.
connect
Asks pvAccess to connect to the server record that create gather records. When pvAccess has connected it creates a ChannelPutGet with option to process. After the ChannelPutGet is created it verifies that the record has the fields required for creating a gather record. All this is done without blocking the client.
waitConnect
Waits until pvAccess has connected and created the ChannelPutGet. It sets status indicating if it has properly created a ChannelPutGet for requesting creation of gather records.
sendRequest
It uses the arguments supplied by the client to set fields in the put PVStructure of the ChannelPutGet request. It then issues a putGet request. This is a non blocking call.
waitRequest
Waits until the putGet request completes and then returns a status showing the results of the request. If the gather record already exists requester.message is called with a warning message but returns an OK status.

Create Gather Record Implementation


The gather create service is implemented via support. The xml definitions for the gather create support and record are defined in pvService/xml/structure/gather.xml:

<structure structureName = "org.epics.pvService.createGatherFactory">
  <scalar name = "supportFactory" scalarType = "string">
     org.epics.pvService.gather.support.CreateGatherFactory</scalar>
</structure>

<structure structureName = "org.epics.pvService.createGather" extends = "generic">
  <structure name = "alarm" extends = "alarm"/>
  <structure name = "timeStamp" extends = "timeStamp" />
  <structure name = "arguments">
      <auxInfo name = "supportFactory" scalarType = "string">org.epics.pvService.createGatherFactory</auxInfo>
      <array name = "channelNames" scalarType = "string" />
      <scalar name = "gatherRecordName" scalarType = "string" />
      <scalar name = "temporary" scalarType = "boolean" />
      <scalar name = "channelsAreArrays" scalarType = "boolean" />
      <scalar name = "monitor" scalarType = "boolean" />
  </structure>
  <structure name = "result">
      <scalar name = "status" scalarType = "string" />
  </structure>
  <scalar name = "removeRecordRecord" scalarType = "string" />
</structure>

An example of creating records for createGather is given in the zip file for the example. The file is gather.xml:

<database>
<import name = "org.epics.ioc.*" />
<import name = "org.epics.pvData.*" />
<record recordName = "recordRemoveRPC" extends = "recordRemove" />
<record recordName = "createGather" extends = "org.epics.pvService.createGather" >
    <scalar name = "removeRecordRecord">recordRemoveRPC</scalar>
</record>
</database>

The two records are:

recordRemoveRPC
This is a record that removes an installed record from a javaIOC database. It is used by the gather service the delete temporary gather records. It can be accessed by any pvAcess client to remove records. For example it can be used to remove a non-tempry gather record.
createGather
This is the record with support for creating and installing a gather record.

When a gather create record is processed the gather support does the following:



Gather Client Implementation


The client must call GatherFactory.create, which has the arguments:

serverName
The name of the gather record created by CreateGather.
requester
An interface the client must implement. It is defined in project pvData.
commands
A list of the commands the client will call. It must be some combination of: get,getNew,putValue,putBad. Each of these is a method of Gather.
properties
A list of the properties the client will access via the methods of GatherData. This is some combination of: value,isBad,isConnected,severityIndex

Create returns interface Gather. The client side of gather implements the methods of Gather as follows:

destroy
Calls channel.destroy which destroys all pvAccess connections to the gather record.
connect
Asks pvAccess to connect to the gather record. When pvAccess has connected it creates a separater object for each of the commands the client gave when creating Gather. Each results on a ChannelGet or ChannelPut than implements the associated command. In addition a Monitor is created for each property (except value) specified in the create call. Thus isBad, isConnected, and severityIndex all implemened via monitors. The implementation also create an object that implements GatherData.
waitConnect
Waits until connect is complete.
getGatherDataScalar
getGatherDataArray
Get the interface for the gather data. This can only be called after waitConnect returns status.isOK() true. Note that the data will be updated as the result of commands or monitors. This is the only way the client accesses gather data.
processGet
Issue a get command asking that the record be processed. This will result in a get to each of the caV3 channels.
waitProcessGet
Wait until the processGet request completes. The data is put into GatherData.
get
Issue a get command that does not ask that the record be processed, i.e. the current values stored in the gather record are returned.
waitGet
Wait until the get request completes. The data is put into GatherData.
putBad
Write the bad status (true or false) for each channel. The values are put into the gather record.
waitPutBad
Wait until putBad is finished.
putValue
Put a value for each channel. The gathe support will issues a caV3 put for each channel that is connected.
waitPutValue
Wait until putValue completes, which means the values have also been written to the caV3 channels.

Gather Record Implementation


The gather service is implemented via support. The xml definitions for the gather support and record are defined in pvService/xml/structure/gather.xml:

<structure structureName = "org.epics.pvService.gatherFactory">
  <scalar name = "supportFactory" scalarType = "string">
    org.epics.pvService.gather.support.GatherFactory</scalar>
</structure>

<structure structureName = "org.epics.pvService.gatherScalar" extends = "generic">
  <structure name = "alarm" extends = "alarm"/>
  <structure name = "timeStamp" extends = "timeStamp" />
  <scalar name = "command" scalarType = "string">get</scalar>
  <scalar name = "options" scalarType = "string" />
  <array name = "value" scalarType = "double" />
  <array name = "isBad" scalarType = "boolean" />
  <array name = "isConnected" scalarType = "boolean" />
  <array name = "severityIndex" scalarType = "byte" />
  <array name = "channelNames" scalarType = "string">
      <auxInfo name = "supportFactory" scalarType = "string">org.epics.pvService.gatherFactory</auxInfo>
  </array>
</structure>

<structure structureName = "org.epics.pvService.gatherArrayElement">
    <scalar name = "value" scalarType = "double" />
</structure>

<structure structureName = "org.epics.pvService.gatherArray" extends = "generic">
  <structure name = "alarm" extends = "alarm"/>
  <structure name = "timeStamp" extends = "timeStamp" />
  <scalar name = "command" scalarType = "string">get</scalar>
  <scalar name = "options" scalarType = "string" />
  <array name = "value" scalarType = "structure" extends = "org.epics.pvService.gatherArrayElement" />
  <array name = "isBad" scalarType = "boolean" />
  <array name = "isConnected" scalarType = "boolean" />
  <array name = "severityIndex" scalarType = "byte" />
  <array name = "channelNames" scalarType = "string">
      <auxInfo name = "supportFactory" scalarType = "string">org.epics.pvService.gatherFactory</auxInfo>
  </array>
</structure>

These definitions must be part of the structures defined in the javaIOC master database. The gather create support uses these definitions to create gather records. Note that a gather record has array fields for the data of interest to clients.

For each channelName, the gather support creates a caV3 channel for communication with the caV3 channel. The support also has support to get and put values to the caV3 channels.