Gather Service Status
2012.01.19


Overview


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

The implementation of the gather service is a pvData/pvAccess client/server service. The server side is implemented via a pvIOCJava record. The client side is implemented in Java, C++, and Python. The gather facility actually consists of two services:

createGather
This service creates new gather records. The service is implemented via a javaIOC record. The newly created gather records are in the same pvIOCJava as the createGather record. Thus a typical gather IOC will consist of a single createGather record and an arbitarary number of gather records.
gather
Each gather server instance is a pvIOCJava record. A gather record has links to a set of V3 channels.

An arbitrary number of clients can share the gather server. createGather requests are automatically queued. This means that if a new create request arrives while another create request is active the new request will wait until the previous request is complete.

Once a gather record is created it can also be used by multiple clients. If get and put requests specify processing the requests are queued.


Status


When Available

The date on the java implementation of gather is 2010.07.16. Thus it has been available for about 1 1/2 years. As far as I know it has never been used except for testing.

The date on the Python client implementation is 2011.08.04. Thus it has been available for C++ and Python since then.

Documentation

pvServiceJava/src/org/epics/pvService/gather/package.html
pvServiceCPP/documentation/pvServicePy.html
pvServiceCPP/documentation/pvServiceCPP.html

TODO

These are just some suggestions.

Currently gather is part of pvServiceJava and pvServiceCPP. It should be taken from them and made into a separate mercurial project with the following directory structure:

documentation
cpp
   src
     client
     server
   test
java
   src
     org
       epics
           pvService
               client
               server
   test
python
   src
      client
   test

Gather was implemented before the idea of normative types. Perhaps it should be changed to use NTNameValue for arguments to create and NTTable for the result.

Currently gather only gathers double values. Each V3 channel can be either a scalar or an array but DBR_TIME_DOUBLE is always the request type.

What else is desirable?

DBR_TIME_STRING?

DBR_TIME_INT?

What if the set of channels does not have a compatible set of DBR types?


Create Gather


Interface

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

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