Package org.epics.pvaccess.client.rpc

Support code for implementing channelRPC.

PVService
This page documentation needs to be updated!
2011.08.22

CONTENTS

Introduction


This package provides support for services that are implemented as an RPC (Remote Procedure Call). PVAccess provideds two flavors of RPC: putProcessGet and channelRPC.

If the service is accessed via a putProcessGet the record that implements the RPC has the structure

record serviceName
    // may be optional fields
   structure arguments
      // sevice specific
  structure result
     // service specific

If the service is accessed via a channelRPC the record that implements the RPC has the structure:

record serviceName
    // may be optional fields
   structure arguments
      // sevice specific

The main difference between the two types of service is that for a putProcessGet the structure of the result is fixed and for a channelRPC a completely new structure is returned for each request.

This package provides support for channelRPC: It provides client and server support for implementing a network accessable service that uses PVData for all data communication between a client and the service. The service is implemented via a PVRecord that is present in a javaIOC. The client communicates with the server via a ChannelRPC (Remote Procedure Call) as defined by pvAccess.

The record name is the name of the service. The record has the following fields:

factoryRPC
A string field that is the name of a factory that implements the specific RPC service.
arguments
A structure that defines the arguments for the RPC.

This package provides:

  • Client - The client code that interfaces to PVAccess, i.e. manages channel access communication with the service
  • Server - Code that implements Support code for a record instance that implements a service. It calls service specific code.

A sevice implementation must implement the following interfaces, which are described in detail below.

  • Server - Defined in javaIOC. package org.epics.ioc.pvAccess
    interface RPCServer {
        void destroy()
        Status initialize(...);
        void request()
    }
  • Client
    interface ServiceClientRequester extends Requester{
        void connectResult(...);
        void requestResult(...);
    }

The rest of this document describes the following:

  • client
    The java interfaces and factories this project provides for a service client.
  • server
    The java interfaces and factories this project provides for a service server.
  • Examples:
    • table
      An example that returns data that can be interpeted as a table.
    • example
      A example of a service that returns a somewhat complex structure.

Client


The client is implemented via the following interfaces and factory:

interface ServiceClientRequester extends Requester{
    void connectResult(Status status,PVStructure pvArguments,BitSet bitSet);
    void requestResult(Status status,PVStructure pvResult);
}

interface ServiceClient {
    void destroy();
    void waitConnect(double timeout);
    void sendRequest();
    void waitRequest();
}

class ServiceClientFactory {
    public static ServiceClient create(String serviceName,ServiceClientRequester requester);
}

ServiceClientRequester, which must be implemented by each client, has the following methods:

connectResult
This is called when ServiceClient has connected to the service or when a timeout occurs. It has the methods:
status
The status as descibed in project pvData.
pvArguments
The arguments structure defined in the service record.
bitSet
A bitSet for pvArguments. The client must call bitSet.set for any fields it changes in pvAguments before issuing a request.
requestResult
This is called when the ServiceClient receives the request data from the service. It has the methods:
status
The status of the request as described in project pvData.
pvResult
The pvStructure created by the service.

ServiceClient is created by ServiceClientFactory. It has the methods:

destroy
Called by the client when it no longer requires the service.
waitConnect
Called by the client to wait until a connection has been made to the service or a timeout occurs.
sendRequest
Called by the client to send a request to the service.
waitRequest
Called by the client to wait until the request response has been returned by the service.

ServiceClientFactory, which is also implemented by this project, has the following method:

create
Creates a ServiceClient and connects to the service. It has the arguments:
serviceName
The name of the channel, i.e. record, that implements the service.
requester
The serviceChannelRequester as described above.

Server


The service is implemented via the following interfaces and factory:

//RPCServer defined in org.epics.ca.server.impl.local
public interface RPCServer {
    void destroy();
    Status initialize(
         Channel channel,
         PVRecord pvRecord,
         ChannelRPCRequester channelRPCRequester,
         PVStructure pvArgument,
         BitSet bitSet,
         PVStructure pvRequest);
    void request();
}

class XXXServiceFactory {
    public static RPCServer create();
}

RPCService, which is implemented by each service, has the following methods:

destroy

Called when a client disconnects from the ChannelRPC.

initialize
Called when a client creates a ChannelRPC. It has the arguments:
channel
The channel that is connecting to the service.
pvRecord
The record that implements the service.
channelRPCRequester
The remote pvAccess server implements this and passes the information back to the client.
pvArgument
The arguments structure as defined in the record.
bitSet
A bitSet for arguments. The service can use this to determine what has changed since the last request.
pvRequest
An optional pvStructure than can be passed between client an d server. See pvAccess for details.
request
Called when a client calls channelRPC.request. The service is expected to create a new pvStructure and call:
channelRPCRequester.requestDone(okStatus, pvTop);
where pvTop is the newly created pvStructure.

Also the service must implement a XXXServiceFactory, which is specified in the record.factory field of the PVRecord. It has the following method:

create
Called by org.epics.ca.server.impl.local.ChannelServerFactory when the PVRecord for the service is initialized.

Examples


The project provides examples in package (org.epics.pvService.example). This section describes ExampleClient and ExampleServiceFactory, which are the client and server sides of the example. The example is skeleton code for a service that, given a search string, returns a set of pvnames. Each pvName can have associated properties. The example ignores the search request. Instead it just makes up two pvnames (pvName0 and pvName1) and makes up some properties for each pvname.

Running the examples

The example package has a file pvService.zip that runs the example. To run the example do the following:

  1. Copy pvService.zip to some test directory and unzip it.
  2. change directory location to pvService.
  3. Edit the source file so that it has the correct locations for WORKSPACE and for the org.eclipse.swt definitions.
  4. Open two windows in the pvService directory
  5. In one window execute the command:
        ./serviceExample
        
  6. In other window execute the command:
        ./clientExample
        
  7. In other window execute the command:
        ./clientTable
        

In the window where clientExample is run you should see:

structure 
{
    0 = structure
    {
        name = pvName0
        properties = structure
        {
            a = structure
            {
                value = avalue
                owner = aowner
            }
            b = structure
            {
                value = bvalue
                owner = bowner
            }
        }
    }
    1 = structure
    {
        name = pvName1
        properties = structure
        {
            a = structure
            {
                value = avalue
                owner = aowner
            }
            b = structure
            {
                value = bvalue
                owner = bowner
            }
        }
    }
}
all done

In the window where clientTable is run you should see:

mrk> ./clientTable 
structure table
    int nrows 2
    structure columns
        string[] name [name0,name1]
        double[] value [10.0000,20.0000]
        structure[] timeStamp 
            structure timeStamp
                long secondsPastEpoch 946702800
                int nanoseconds 582000000
            structure timeStamp
                long secondsPastEpoch 946702801
                int nanoseconds 582000000
timeStamp[] [ 2000-01-01 00:00:00.582, 2000-01-01 00:00:01.582]
structure table
    int nrows 5
    structure columns
        string[] name [name0,name1,name2,name3,name4]
        double[] value [10.0000,20.0000,30.0000,40.0000,50.0000]
        structure[] timeStamp 
            structure timeStamp
                long secondsPastEpoch 946702800
                int nanoseconds 582000000
            structure timeStamp
                long secondsPastEpoch 946702801
                int nanoseconds 582000000
            structure timeStamp
                long secondsPastEpoch 946702802
                int nanoseconds 582000000
            structure timeStamp
                long secondsPastEpoch 946702803
                int nanoseconds 582000000
            structure timeStamp
                long secondsPastEpoch 946702804
                int nanoseconds 582000000
timeStamp[] [ 2000-01-01 00:00:00.582, 2000-01-01 00:00:01.582, 2000-01-01 00:00:02.582, 2000-01-01 00:00:03.582, 2000-01-01 00:00:04.582]
all done

The service record

The xml file that implements the example service is:

<database>
<record recordName = "exampleService" extends = "org.epics.pvService.service">
    <scalar name = "factoryRPC">org.epics.pvService.example.ExampleServiceFactory</scalar>
    <structure name = "arguments">
        <scalar name = "search" scalarType = "string" />
    </structure>
</record>
<record recordName = "tableService" extends = "org.epics.pvService.service">
    <scalar name = "factoryRPC">org.epics.pvService.example.TableServiceFactory</scalar>
    <structure name = "arguments">
        <scalar name = "search" scalarType = "string" />
        <scalar name = "number" scalarType = "int" />
    </structure>
</record>
</database>

This creates two records.

exampleService
This is the record for clientExample
tableSevice
This is the record for clientTable

Note that both records are created by extending org.epics.pvService.service. This is defined in pvService.xml.structure.service.xml:

<structure structureName = "service">
  <scalar name = "factoryRPC" scalarType = "string" />
  <structure name = "arguments" />
</structure>