As already mentioned, alibava-gui allows to load plugins that will enable the end-user to perform non-standard actions at very specific points of the data acquisition process. These stages are:
Figure 20 shows the places, during the acquisition loop, in which the plugin methods are called.
The plugins can be written in C++, as shared libraries, or as Python scripts. Examples can be found in the test folder of the distribution.
In C++ the plugins are nothing but a class that derives from Plugin in Plugin.h, which is shown in Example 1. The test folder in the distribution bundle has an example of a C++ plugin together with a make file (UserMakefile). The example is described in Example 3. In the make files use the pkg-config program to get the compilation flags and the path to to the alibava include files.
class Plugin { : Plugin(); virtual: ~Plugin(); : enum BlockType = {NewFile=0, StartOfRun, DataBlock, CheckPoint, EndOfRun}; string new_file(); int start_of_run(int run_type, int nevts, int sample_size); bool new_event(int ievt); string new_point(); string end_of_run(); string filter_event(const EventData & data); }
The main methods in a Plugin class are:
: string new_file();
This method is called at the beginning of a file. This will only happen when data logging is activated. The method returns a string that will be included in the NewFile data block of the data file. See Section 8.1 ― Data format to understand the data blocks.
: int start_of_run(int run_type, int nevts, int sample_size);
This method is called at the beginning of each run. The parameters are:
start_of_run returns an integer value that is the actual number of events that alibava-gui will consider. If the method is not superseeded by your plugin it will just return nevts. The idea behind this bizarre implementation is to allow the user to perform scans on different parameters and redefine this way the total number of events from the number of scan poitns and the number of events per point.
: string end_of_run();
This is called at the end of each run. It can return a buffer with some user data that will be included in the EndOfRun data block of the data file. See Section 8.1 ― Data format to understand the data blocks.
: bool new_event(int evt);
This method is called at the beggining of each event. The argument evt is the current event number.
It returns a boolean which is
The idea behind this is first, to have a handle right at the beginning of an event and, second, to decide whether we want to add extra information before this event on the data file. This extra information could be the time of the day or, more interesting, the values of the parameters of a scan when a new scan point is going to start.
: string new_point();
This method is called only when new_event has returned true as explained above. It returns a string that will be included in a CheckPoint data block right before the current event DataBlock (See Section 8.1 ― Data format to understand the data blocks). This is usefull to store in the file the parameters of a user-defined scan or some information that you would like to write periodically, like humidity (if you can measure it), detector current, etc.
If you use AsciiRoot (see Section 8.3 ― The AsciiRoot class ), to access the CheckPoint data you will have to write your own class deriving from AsciiRoot implementing the method check_point.
: string filter_event(const EventData & data);
This is called at the end of an event. The idea is that the user can change the information and the format of a normal DataBlock (see Section 8.1 ― Data format). A good example could be a laser scan in which you would only be interested in very few channels. The method returns a string which, if not empty, will be writen in the DataBlock instead of the normal data.
If you change the BlockData format then you will have to use a class which derives from AsciiRoot (see Section 8.3 ― The AsciiRoot class ) and implements the new_data_block method. Also the pedestal and noise values sotred int he data file will loose their meaning and will be unsusable.
In Python the plugin class is as shown in Example 2.
class Plugin(extends object): def new_file(self) -> string def start_of_run(self, run_type, nevts, sample_size) -> int def new_event(self, ievt) -> bool def new_point(self) -> string def end_of_run(self) -> string def filter_event(self, time, temperature, value, data) -> string
The parameters, return values and names of the Python methods are like in C++. The only different method is filter_event since it has a different signature.
: string filter_event(self, time, temperature, value, data);
The parameters of this method are:
Note that the values of time, temperature and value are not decoded and therefore their meaning is as described in Table 4. See the description of the C++ method for more information and warnings.
Plugin examples can be found in the folder test on the distribution bundle.
In order to make a useful plugin, you have to create your own class implementing some of the methods in Plugin. An example of such a class implementing a user defined scan is shown in Example 3.
/* * test_plugin.cc * * This is an example of a plugin writen in C++. * Look at the documentation in PLugin.h * * Created on: Jul 24, 2009 * Author: lacasta */ #include <iostream> #include <sstream> #include <Plugin.h> #include "NewPoint.h" /** * This is an implementation of the Plugin class. * It is a simple example that will make a scan. * We may use the new_file method to store the parameters * of the scan, new_event to determine when a new * point is the scan is needed and new_point to store * the actual values of * the scan variables. */ class MyPlugin : public Plugin { private: int npoints; // N. of pts we want for the scan int nevt_per_point; // N. of pts acquired in each point int run_type; // type of run int current_event; // current event number EventCntr handler; // The object that decides when // to change to the next point public: // Constructor with default values MyPlugin() : npoints(50), nevt_per_point(1000), run_type(-1), handler(nevt_per_point), current_event(0) {} // destructor ~MyPlugin() {} /** * Declaration of Plugin methods to be implemented */ std::string new_file(); int start_of_run(int run_type, int nevts, int sample_size); std::string end_of_run(); bool new_event(int evt); std::string new_point(); }; std::string MyPlugin::new_file() { std::string rc("New file"); std::cout << "new_file" << std::endl; return rc; } int MyPlugin::start_of_run(int runtype, int nevts, int sample_size) { run_type = runtype; std::cout << "start_of_run " << nevts << " events. " << "Runtype " << run_type << std::endl; if (sample_size > handler.value()) { handler.value(sample_size); nevt_per_point = sample_size; } handler.reset(); return npoints*nevt_per_point; } std::string MyPlugin::end_of_run() { std::string rc("end_of_run"); std::cout << "end_of_run" << std::endl; return rc; } bool MyPlugin::new_event(int ievt) { current_event = ievt; return handler(ievt); } std::string MyPlugin::new_point() { std::ostringstream ostr; ostr << "new point: " << current_event << std::endl; std::cout << ostr.str(); return ostr.str(); } /* * This is the factory function or "hook" in terms of the * Plugin dialog box where the instance of you Plugin * implementation is created. */ extern "C" { Plugin *create_plugin() { MyPlugin *plugin = new MyPlugin(); return plugin; } }
In addition to that, we need a factory function that will create the class instance. The name of that function should be specified in the Plugin dialog box when the hook name is required. An example of such a factory function is shown at the very end of Example 3. Note that the function is declared as extern "C". This is important since otherwise alibava-gui will not be able to find it when the shared library is loaded. See the complete example, together with the make file (UserMakefile) in the test folder of the distributed software. In your make files use the pkg-config program to get the compilation flags and the path to to the alibava include files.
As already mentioned, plugins can also be written as Python scripts. An example similar to the previous C++ plugin is shown in
""" An example of an alibava plugin This example implements a user defined scan """ import time import inspect # # Define some usefull constants # # Block types NewFile,StartOfRun, DataBlock, CheckPoint, EndOfRun = range(0,5) # Run types Unknown,Calibration,LaserSync,Laser,RadSource,Pedestal,LastRType = range(0,7) class MyPlugin(object): """ This is an object that can be loaded by alibava to be called at certain stages of the DAQ process. """ def __init__(self): """ Initialization """ self.current_point = 0 self.current_event = 0 self.npoints = 50 self.nevt_per_point = 1000 self.handler = EventCounter(self.nevt_per_point) self.run_type = -1 def new_file(self): """ This is called at the beginning of each file It should return a string with information that will be stored in the file header """ print "new_file" return "Hola !!!" def start_of_run(self, run_type, nevts, sample_size): """ This is called at the beginning of each run. It should return the total number of events that we want to acquire. As an extra input we have the size of the data chunk that Alibava acquires each time we activate the acquisition. Run types are predefined in the variables: Unknown,Calibration,LaserSync,Laser,RadSource,Pedestal """ info_msg("Starting a new run") self.handler.start() self.run_type = run_type write_msg( "start_of_run %d events. Run type: %d" % (nevts, run_type ) ) write_msg( "...sample size %d" % (sample_size) ) self.handler.reset() if sample_size > self.handler.nevts: print "Changing handler.nevts" self.handler.nevts = sample_size print "...new value", self.handler.nevts self.nevt_per_point = sample_size if run_type!= RadSource and run_type!=Laser: return nevts else: # Here we return the number of events we really # want to acquire given that we need to scan npoints # with nevt_per_point events per point. return self.npoints * self.nevt_per_point def end_of_run(self): """ Called at the end of a run """ write_msg("end_of_run") return "end_of_run" def new_event(self, ievt): """ This is called at the beginning of each event. Should return True if we want alibava to call the method new_point. The input parameter is the current event number """ self.current_event = ievt if self.handler.check(ievt): return True else: return False def move_axis(self): """ A dummy function where we could, for instance, move the axis that hole the laser or the source """ pass def new_point(self): """ Called every time that new_event returns True """ self.current_point += 1 self.move_axis() print "new_point %d event %d" % (self.current_point, self.current_event) return "Current axis position is %d" % self.current_point def create_plugin(): """ This is the 'hook'. This is the method called to create an instance of the MyPlugin class """ write_msg("Loading %s" % __name__) plugin = MyPlugin() return plugin
Note that as in the case of C++ we also need here a factory function or hook to create the instance of the Plugin and pass it to Alibava.