scanServiceExample

2019.05.21

editors:
Marty Kraimer

This product is available via an open source license

Table of Contents

Introduction

Overview

scanServiceExample is an example service implemented via a PVRecord ( described in developerGuide ).
There are actually two implementation. One uses channelPutGet and the other uses channelRPC.

Terminology:

scanService
This is code that perform a scan. It is used by both server implementations.
scanServerPutGet
A PVRecord server that is implemented via channelPutGet.
scanServerRPC
A PVRecord server that is implemented via channelRPC.
scanServer
Used when the discussion applies to either server.
scanClientPutGet
Client code that communicates with scanServerPutGet.
scanClientRPC
Client code that communicates with scanServerRPC.
scanClient
Used when the discussion applies to either client.

This example has two main goals:

  1. Show an example of a service implemented via a PVRecord.
  2. Make the argument that channelPutGet is better than channeRPC

Background

This example is motivated by pvDatabaseRPC , which was created by David Hickin while he was working at the Diamond Light Source. It implements a scan service via channelRPC.

scanServiceExample is a simplified version of pvDatabaseRPC but also gives an example of using channelPutGet in addition to channelRPC.

Getting Started

scanServiceExample is available at: scanServiceExample

In order to fully understand this example you should clone and build it.

After it is build you can start the following servers:

mrk> pwd
/home/epicsv4/masterCPP/scanServiceExample/iocBoot/scanServerPutGet
mrk> ../../bin/linux-x86_64/scanServerPutGet st.cmd
...
epics> pvdbl
scanServerPutGet
and
mrk> pwd
/home/epicsv4/masterCPP/scanServiceExample/iocBoot/scanServerRPC
mrk> ../../bin/linux-x86_64/scanServerRPC st.cmd
...
epics> pvdbl
scanServerRPC

After starting the two services you can monitor each service via the command:

pvget -r "" -m scanServerPutGet scanServerRPC

To run the client code you can run commands like:

mrk> pwd
/home/epicsv4/masterCPP/scanServiceExample
mrk> bin/linux-x86_64/scanClientPutGet configure .1 .1 0 0
mrk> bin/linux-x86_64/scanClientPutGet start
or
mrk> pwd
/home/epicsv4/masterCPP/scanServiceExample
mrk> bin/linux-x86_64/scanClientRPC configure .1 .1 0 0
mrk> bin/linux-x86_64/scanClientRPC start

Scan Service

The scanService moves through a set of points, where a point is an x,y pair of doubles. It is used by both scanServerPutGet and scanServerRPC.

The public methods of ScanService are:

class Point
{
public:
...
    double x;
    double y;
};

class ScanService::Callback
{
public:
    virtual void update(int flags);
        const static int SETPOINT_CHANGED  = 0x1;
        const static int READBACK_CHANGED  = 0x2;
        const static int SCAN_COMPLETE     = 0x4;
};

class ScanService : public epicsThreadRunable
{
...
public:
    static ScanServicePtr create();
    virtual void run()
...
    void registerCallback(Callback::shared_pointer const & callback);
    bool unregisterCallback(Callback::shared_pointer const & callback);
    Point getPositionSetpoint();
    Point getPositionReadback();
    void configure(const std::vector<Point> & newPoints);
    void startScan();
    void stopScan();
    void setRate(double stepDelay,double stepDistance);
    void setDebug(bool value);
...
}

where:

Callback::update
A scanServer must create a Callback. Whenever the scanService has a change of state to report it calls the update method.
create
A scanServer calls this to create a single instance of ScanService. A thread is created that performs scans.
registerCallback
This is called in order to receive updates. An update occurs whenever a scan starts a new setpoint, each time a move is made, and when a scan stops.
unregisterCallback
This is called to stop updates.
getPositionSetpoint
Get the current setpoint.
getPositionReadback
Get the current position.
configure
Specify a new scan configuration.
startScan
Start a scan with the current configuration. The first move is to the first point. Moves are made in small steps between points.
stopScan
Stop the current scan. This can be called by client code or when a scan completes.
setRate
stepDelay
The number of seconds the scan thread waits between moves.
stepDistance
The distance to move.
setDebug
If true then the service displays a message every time configure, startScan, and stopScan is called.

Scan Server

There are two example servers:

scanServerRPC
This creates the following PVRecord:
mrk> pvinfo scanServerRPC
scanServerRPC
Type:
    structure
        Point positionSP
            point_t value
                double x
                double y
            time_t timeStamp
                long secondsPastEpoch
                int nanoseconds
                int userTag
        Point positionRB
            point_t value
                double x
                double y
            time_t timeStamp
                long secondsPastEpoch
                int nanoseconds
                int userTag
        time_t timeStamp
            long secondsPastEpoch
            int nanoseconds
            int userTag
The client issues scan requests via channelRPC requests.
scanServerPutGet
This creates the following PVRecord:
pvinfo scanServerPutGet
scanServerPutGet
Type:
    structure
        Point positionSP
            point_t value
                double x
                double y
            time_t timeStamp
                long secondsPastEpoch
                int nanoseconds
                int userTag
        Point positionRB
            point_t value
                double x
                double y
            time_t timeStamp
                long secondsPastEpoch
                int nanoseconds
                int userTag
        time_t timeStamp
            long secondsPastEpoch
            int nanoseconds
            int userTag
        structure argument
            string command
            structure configArg
                double[] x
                double[] y
            structure rateArg
                double stepDelay
                double stepDistance
            structure debugArg
                boolean value
        structure result
            string value
Note that the difference from scanServerRPC is argument and result These are fields a client uses for channelPutGet request.

Scan Client

Two Implementations

There are two example clients:

scanClientRPC
This uses channelRPC requests to access record scanServerRPC.
scanClientPutGet
This uses channelPutGet requests to access record scanServerPutGet.

User Interface

Both clients are accessed via the same user interface. If a user enters no arguments then help is displayed. For example:

mrk> pwd
/home/epicsv4/masterCPP/scanServiceExample
mrk> bin/linux-x86_64/scanClientRPC 
if interactive is specified then interactive mode is specified
following are choices for non interactive mode:
   configure x0 y0 ... xn yn
   start
   stop
   setRate stepDelay stepDistance
   setDebug true|false

The following are some non interactive examples:

mrk> pwd
/home/epicsv4/masterCPP/scanServiceExample
mrk> bin/linux-x86_64/scanClientPutGet configure .1 .2 0 0
structure 
    structure result
        string value configure success

mrk> bin/linux-x86_64/scanClientPutGet start
structure 
    structure result
        string value startScan success

mrk> bin/linux-x86_64/scanClientPutGet stop
structure 
    structure result
        string value stopScan success

mrk> bin/linux-x86_64/scanClientPutGet setDebug true
structure 
    structure result
        string value configure success

mrk> bin/linux-x86_64/scanClientPutGet setRate .5 .1
structure 
    structure result
        string value configure success

mrk> bin/linux-x86_64/scanClientPutGet start
structure 
    structure result
        string value startScan success

The following is an interactive example:

mrk> bin/linux-x86_64/scanClientRPC i
enter one of: exit configure start stop setRate setDebug
configure
enter x y values
.1 .2 0 0
response
structure 
    string value configure success

enter one of: exit configure start stop setRate setDebug
start
response
structure 
    string value start success

enter one of: exit configure start stop setRate setDebug
setRate
enter stepDelay
.5
enter stepDistance
.1
response
structure 
    string value setRate success

enter one of: exit configure start stop setRate setDebug
configure
enter x y values
1 2 0 0
response
structure 
    string value configure success

enter one of: exit configure start stop setRate setDebug
start
response
structure 
    string value start success

enter one of: exit configure start stop setRate setDebug
exit

Monitoring

The following monitors setpoint and argument changes:

pvget -m -r "positionSP,argument,result" scanServerPutGet scanServerRPC

The following monitors position changes.

pvget -m -r "positionRB" scanServerPutGet scanServerRPC
Be prepared for lots of updates while scanning is active.

Comparison of channelPutGet vs channelRPC

source code size

Note the size of the source modules:

mrk> wc scanClientPutGet/scanClientPutGet.cpp scanClientRPC/scanClientRPC.cpp
  318   829 11824 scanClientPutGet/scanClientPutGet.cpp
  380   890 12671 scanClientRPC/scanClientRPC.cpp
  693  1714 24265 total
mrk> wc scanServerPutGet/scanServerPutGet.cpp scanServerRPC/scanServerRPC.cpp
  263   506  8235 scanServerPutGet/scanServerPutGet.cpp
  396   759 12054 scanServerRPC/scanServerRPC.cpp
  659  1265 20289 total

For both client and server channelPutGet code is shorter.

argument and result

With channelPutGet a client can see the arguments and result for client scan requests.

pvput

With channelPutGet a client can use pvput to interface to the scanner. For example:

pvput -r "argument" scanServerPutGet  argument='{"command":"configure","configArg":{"x":[".1",".0"],"y":[".1","0"]}}'
pvput -r "argument" scanServerPutGet  argument='{"command":"start"}'