SOAP: Communicating with alibava-gui

alibava-gui can also work as a SOAP server. This means that we can send a number (very limited) of commands to the alibava-gui process to start or stop a run and, also, to retrieve some information about the status of the acquisition. The SOAP server and client libraries are implemented using the gSOAP toolkit for Web Services1

The SOAP commands are summarized below:

:
    void getStatus(in int request, out Status status);

Returns the status of the acquisition in a structure of type Status, described in Example 10. The request parameter can be ignored and one can send any value, usually 0.

:
    void startRun(in DAQParam daqParam, out base64binary data);

Starts a run with the parameters specified in a structure of type DAQParam described in Example 11. It returns a data block which contains various histograms.

:
    void stopRun(in int value, out int response);

Stops the current run. The value parameter has no meaning and any value is accepted. The output parameter, response, with return 0 if the operation was succesful and an error code otherwise.

:
    void setDataFile(in string fileName, out int response);

Sets the name of the data file and forces alibava-gui to log data into that file. The output parameter, response, with return 0 if the operation was succesful and an error code otherwise.

Example 10The Status structure
class Status
{
public:
    string status;
    time time;
    int nexpected;
    int ntrigger;
    double rate;
    string run_type;
    double value;
}

Example 11The DAQParam structure
class DAQParam
{
public:
    int runType;
    int numEvents;
    int sampleSize;
    int nbin;
    double xmin;
    double xmax;
}

The data sent by the startRun consists of 2 histograms. The first contains the spectrum and the second the mean value of the signal seen by each channel. The format of the data is described in Table 5. See

Table 5Format of the data from startRun
Size and type Description
int32 number of histograms
For each histogram
int32 number of bins
double xmin
double xmax
nbin * double data chunk with nbin doubles

9.1. Monitoring Alibava runs from abroad

The SOAP server also provides a web service where you can monitor the current status of your run. The interface is really simple. The addres you have to give to the browser is

http://address_of_your_computer:nnnn

where nnn is the port you have given to the SOAP server (10000 by default). You may need to enable that port in order to do that from another computer. Figure 22 shows an exmple of what the server will provide. It will, essentially, be the histograms that are shown at the main window of the alibava-gui application.

Figure 22Web server

9.2. SOAP examples

The test folder in the distribution contains some examples that may help understanding the procedure to communicate with alibava-gui via SOAP. The examples are written in two languages, python and C++.

9.2.1. Python example

In the case of python, we recomend to have SOAPpy installed in the system. It can be downloaded from http://sourceforge.net/projects/pywebsvcs/files/SOAP.py. It is also in most of the Linux distributions so the best is to use the one provided by your particular distribution if you are using Linux. The example below uses this python package. There are two parts. One that hides all the complexity of the SOAP data types intrinsic to Albava and the other the one that contains your actual commands.

The first one is shown in Example 12 and the second in Example 13.

Example 12alibavaSOAP: internals to alibava SOAP structures in Python

#!/usr/bin/env python
"""
Basic methods to communicate via SOAP with the Alibava gui.
It defines the special types: DAQParam and Status
It also defines a couple of conveniece classes. Ones is Hist, that helps
to access the data in form of histograms and Alibava.that encapsulates the 
SOAP commands
"""
import base64
import struct
import cStringIO
from SOAPpy import SOAPProxy
from SOAPpy import WSDL
import SOAPpy
import traceback


#SOAPpy.Config.debug = 1
ALIBAVA_NS="http://alibavasystems.com/alibava/"
        
# modify SOAPBuilder
# This is needed for SOAPpy versions before 0.12,
# otherwise, the class daqParam can handle it
def dump_DAQParam(self, obj, tag, typed = 1, ns_map = {}):
    """ This is a helper function for SOAPpy that will construct
        the request for a ParameterValue.
    """
    if SOAPpy.Config.debug: 
        print "In dump_DAQParam. tag=", tag
    tag = tag or "daqParam"
    tag = SOAPpy.wstools.XMLname.toXMLname(tag) # convert from SOAP 1.2 XML name encoding

    id = self.checkref(obj, tag, ns_map)
    if id == None:
        return
    
    try: a = obj._marshalAttrs(ns_map, self)
    except: a = ''

    try:
        ns = ns_map[ALIBAVA_NS]
        tnm = 'xsi:type="%s:DAQParam"' % (ns)
    except KeyError:
        tnm = ''
    
    self.out.append('<%s %s>\n' % (tag, tnm))
    self.out.append('<runType xsi:type="xsd:int">%d</runType>\n' % obj.runType)
    self.out.append('<numEvents xsi:type="xsd:int">%d</numEvents>\n' % obj.numEvents)
    self.out.append('<sampleSize xsi:type="xsd:int">%d</sampleSize>\n' %obj.sampleSize)
    self.out.append('<nbin xsi:type="xsd:int">%d</nbin>' % obj.nbin)
    self.out.append('<xmin xsi:type="xsd:double">%f</xmin>' % obj.xmin)
    self.out.append('<xmax xsi:type="xsd:double">%f</xmax>' % obj.xmax)
    self.out.append('</%s>\n' % tag)
    
    
def funcToMethod(func,clas,method_name=None):
    """ Helper function to add dynamically methods to a class
    """
    import new
    method = new.instancemethod(func,None,clas)
    if not method_name: method_name=func.__name__
    clas.__dict__[method_name]=method
    
funcToMethod(dump_DAQParam, SOAPpy.SOAPBuilder, "dump_daqParam")        

(PEDESTAL, RADIOACTIVE, LASER, LASERSCAN, CALIBRATION) = range(0,5)


class daqParam(dict):
    def __init__(self, run_type, num_events, sample_size, nbin=1024, xmin=-512.0, xmax=512.0):
        self['runType'] = int(run_type)
        self['numEvents'] = int(num_events)
        self['sampleSize'] = int(sample_size)
        self['nbin'] = int(nbin)
        self['xmin'] = float(xmin)
        self['xmax'] = float(xmax)
    
    def __getattr__(self, name):
        if name in self:
            return self[name]
        else:
            raise AttributeError, name 
            

        
        
class Status(object):
    """ The status of a DAQObject.
    """
    attr_list = {'status': lambda x:x, 
                 'time': lambda x:x, 
                 'nexpected': int, 
                 'ntrigger': int, 
                 'rate': float, 
                 'run_type': lambda x:x, 
                 'value': float}
    def __init__(self, response):
        for attr, cast in Status.attr_list.items():
            print attr, getattr(response, attr)
            setattr(self, attr, cast(getattr(response, attr)))
            
    def __str__(self):
        rate = self.rate;
        rate_units=" Hz";
        thr_units = " Mb/s";

        if  rate>1.e6:
            rate /=1.e6
            rate_units="MHz"
   
        elif rate>1000.0: 
            rate /=1000.
            rate_units = "kHz"

        out = "Evts %7d time %s rate %8.1f %s value %f" % \
            (self.ntrigger, self.time, rate, rate_units, self.value)

        return out

        
class Hst(object):
    """ This is a histogram
    """
    def __init__(self, nx, xmin, xmax, buff=None):
        self.nx = nx
        self.xmin = xmin
        self.xmax = xmax
        if buff:
            self.buff = buff
        else:
            buff = [ 0.0 for i in range(0, nx) ]
            
    def __str__(self):
        ss = "Nbin: %d xmin %f xmax %f\n" % (self.nx, self.xmin, self.xmax)
        i=0
        for val in self.buff:
            ss += "%d - %f\n" %(i, val)
            i += 1
        return ss

def decode_data(data):
    """
    This method decodes the data received from alibava-gui and 
    returns a list of histograms.
    """
    val = cStringIO.StringIO(base64.decodestring(data))
    
    fmt ='i'
    sz = struct.calcsize(fmt)
    v = struct.unpack(fmt, val.read(sz))
    nhst = v[0]
    hst_list = []
    
    for i in range(0, nhst):
        fmt = 'i'
        sz = struct.calcsize(fmt)
        nbin = struct.unpack(fmt, val.read(sz))[0]

        fmt = 'd'
        sz = struct.calcsize(fmt)
        xmin = struct.unpack(fmt, val.read(sz))[0]

        fmt = 'd'
        sz = struct.calcsize(fmt)
        xmax = struct.unpack(fmt, val.read(sz))[0]
        
        fmt = "%dd" % nbin
        sz = struct.calcsize(fmt)
        hst = struct.unpack(fmt, val.read(sz))
        
        hst_list.append( Hst(nbin, xmin, xmax, hst) )
    
    return hst_list


class soapClient(object):
    """ Proxy to talk to Alibava
        It encapsulates the SOAP client
    """
    def __init__(self, host, port):
        self.server = SOAPpy.SOAPProxy("http://%s:%d/alibava" %(host, port), namespace=ALIBAVA_NS, noroot=1)
        
    def getStatus(self):
        return Status( self.server.getStatus() )
    
    def stopRun(self):
        self.server.stopRun(0)
        
    def startPedestalRun(self, nevt, nbin=512, xmin=-512, xmax=-512, nsample=100):
        R = self.server.startRun(daqParam=daqParam(PEDESTAL, nevt, nsample, nbin, xmin, xmax));
        return decode_data(R)
    
    def startSourceRun(self, nevt, nbin=512, xmin=-512, xmax=-512, nsample=100):
        R = self.server.startRun(daqParam=daqParam(RADIOACTIVE, nevt, nsample, nbin, xmin, xmax));
        return decode_data(R)
    
    def startLaserRun(self, nevt, nbin=512, xmin=-512, xmax=-512, nsample=100):
        R = self.server.startRun(daqParam=daqParam(LASER, nevt, nsample, nbin, xmin, xmax));
        return decode_data(R)
    
    def setDataFile(self, fnam):
        self.server.setDataFile(dataFile=fnam)

Example 13testSoap.py: an example of use

#!/usr/bin/env python
"""
Example of a soap client for alibava
"""
import sys
from alibavaSOAP import soapClient, Status


def main(host="localhost", port=10000):
    server = soapClient(host, port)
    
    S = server.getStatus()
    print S
    R = server.stopRun()
    
    R = server.startPedestalRun(1000)
    R = server.startLaserRun(1000, nbin=32, xmin=-512, xmax=512)
    for hst in R:
        print hst
    
    
    server.setDataFile( "alibava_data.dat" )
    R= server.startSourceRun(1000, nbin=32)
    for hst in R:
        print hst
    
    
if __name__ == "__main__":
    try:
        host = sys.argv[1]
    except IndexError:
        host = "localhost"
        
    try:
        port = int(sys.argv[2])
    except IndexError:
        port = 10000
        
    main(host, port)

9.2.2. C++ example

The alibava package provides libraries for communicating with alibava-gui. The test folder of the distribution contains an example, which also shown in Example 14. Look at the given Makefile to see which are the requered includes and libraries. The example shown here also uses the Histogram class whose implementation can also be found in the test folder of the distribution.

Example 14C++ soap client

#include <iostream>
#include <iomanip>
#include <sstream>
#include <vector>
#include <algorithm>
#include <soapalibavaSOAPProxy.h>
#include "Histogram.h"


std::ostream &operator<<(std::ostream &os, const ns1__Status &st)
{
    double rate = st.rate;
    std::string rate_units = " Hz";
    std::string thr_units;

    if (rate > 1.e6)
    {
        rate /= 1.e6;
        rate_units = "MHz";
    }
    else if (rate > 1000.)
    {
        rate /= 1000.;
        rate_units = "kHz";
    }

    os << "Evts " 
       << std::setiosflags(std::ios::fixed | std::ios::right)
       << std::setw(7) << st.ntrigger 
       << " time " << std::setw(6)
       << std::setprecision(1) << st.time 
       << " rate " << std::setw(8)
       << std::setprecision(1) << rate << rate_units 
       << " eff " << std::setw(5)
       << "[" << st.status << "]"
       << std::flush;
    return os;
}



class soapClient
{
    public:
        typedef std::vector<Histogram *> HstList;
    private:
        alibavaSOAPProxy *server;
        HstList histograms;

        void reset_histograms();
    public:
        soapClient();
        ~soapClient();

        void connect(const char *uri);

        int getStatus(ns1__Status &status);
        int setDataFile(const char *ofile);
        int stopRun();
        int startRun(ns1__RunType type, 
                     int nevt, 
                     int nbin, int xmin, int xmax, 
                     int nsample);
        int startPedestalRun(int nevt, 
                             int nbin= 512, 
                             int xmin=-512, 
                             int xmax= 512, 
                             int nsample=100);
        int startSourceRun(int nevt, 
                           int nbin= 512, 
                           int xmin=-512, 
                           int xmax=512, 
                           int nsample=100);
        int startLaserRun(int nevt, 
                          int nbin=512, 
                          int xmin=-512, 
                          int xmax=512, 
                          int nsample=100);

        Histogram *get_histogram(int ix) 
        { 
           return histograms[ix]; 
        }
};

soapClient::soapClient()
: server(0)
{

}

void soapClient::connect(const char *uri)
{
    if (server)
        delete server;

    server = new alibavaSOAPProxy(uri);
}
soapClient::~soapClient()
{
    if (server)
        delete server;

    reset_histograms();
}

struct HstEraser : public std::unary_function<Histogram *, void>
{
        HstEraser() {}
        void operator() (Histogram *hst) {
            delete hst;
        }
};

void soapClient::reset_histograms()
{
    std::for_each(histograms.begin(), 
                  histograms.end(), 
                  HstEraser() );
    histograms.clear();
}

int soapClient::setDataFile(const char *ofile)
{
    if (!server)
        return 1;

    ns1__setDataFileResponse response;
    return server->setDataFile(ofile, response);
}

int soapClient::getStatus(ns1__Status &status)
{
    if (!server)
        return 1;
    ns1__getStatusResponse response;
    server->getStatus(0, response);

    ns1__Status *ss = response.status;
    status.status = ss->status;
    status.nexpected = ss->nexpected;
    status.ntrigger = ss->ntrigger;
    status.rate = ss->rate;
    status.time = ss->time;
    status.value = ss->value;

    return 0;
}

int soapClient::stopRun()
{
    if (!server)
        return 1;

    ns1__stopRunResponse response;
    return server->stopRun(0, response);
}



int soapClient::startRun(ns1__RunType type, 
                         int nevt, 
                         int nbin, int xmin, int xmax, 
                         int nsample)
{
    ns1__startRunResponse response;
    ns1__DAQParam param;

    param.nbin = nbin;
    param.xmin = xmin;
    param.xmax = xmax;
    param.numEvents = nevt;
    param.sampleSize = nsample;
    param.runType = type;
    server->startRun(&param, response);

    std::istringstream is( 
       std::string(
          (const char *)response.startRunResponse.__ptr,
          (size_t)response.startRunResponse.__size ) 
    );




    int ihst, nhst;
    const char *titles[] = { "Spectrum", "Channel Profile", 0 };
    is.read((char *)&nhst, sizeof(int));
    for(ihst=0; ihst<nhst;ihst++)
    {
        Histogram *hst = new Histogram(titles[ihst % 2], 
                                       titles[ihst % 2], 
                                       1, 0., 1.);
        hst->import_from_soap(is);

        histograms.push_back(hst);
    }


    return 0;
}

int soapClient::startPedestalRun(int nevt, 
                                 int nbin, 
                                 int xmin, 
                                 int xmax, 
                                 int nsample)
{
    if (!server)
        return 1;

    return startRun(ns1__RunType__Pedestal, 
                    nevt, 
                    nbin, xmin, xmax, nsample);

}


int soapClient::startSourceRun(int nevt, 
                               int nbin, int xmin, int xmax, 
                               int nsample)
{
    if (!server)
        return 1;

    return startRun(ns1__RunType__RadioactiveSource, 
                    nevt, nbin, xmin, xmax, nsample);

}


int soapClient::startLaserRun(int nevt, 
                              int nbin, int xmin, int xmax,
                              int nsample)
{
    if (!server)
        return 1;

    return startRun(ns1__RunType__Laser, 
                    nevt, nbin, xmin, xmax, nsample);

}

int main(int argc, char **argv)
{
    const char *uri = "http://localhost:10000";
    soapClient client;
    ns1__Status status;

    if (argv[1])
        uri = argv[1];

    client.connect(uri);
    client.getStatus(status);

    std::cout << "Status:" << std::endl;
    std::cout << status << std::endl;

    client.startPedestalRun(5000);

    client.startLaserRun(5000);
    client.getStatus(status);
    std::cout << "Status:" << std::endl;
    std::cout << status << std::endl;

    client.setDataFile("/tmp/data_file.dat");
    client.startSourceRun(10000);
    client.getStatus(status);
    std::cout << "Status:" << std::endl;
    std::cout << status << std::endl;
    client.get_histogram(0)->Print(std::cout);

    return 0;
}

1

Robert A. van Engelen and Kyle Gallivan, The gSOAP Toolkit for Web Services and Peer-To-Peer Computing Networks, in the proceedings of the 2nd IEEE International Symposium on Cluster Computing and the Grid (CCGrid2002), pages 128-135, May 21-24, 2002, Berlin, Germany.