[programmer/prog-exa-pci.tex] \label{prog-exa-pci-chapter} \section{Overview} Reading data streams from a PCI board into the PC is a common use case for data acquisiton systems. In \dabc~ one can implement access to such boards by means of special \class{Device} and \class{Transport} classes that communicate with the appropriate linux device driver. The \class{Device} represents the board and may do the hardware set-up at \keyw{Configure} time, using dedicated \class{dabc::Parameters}. The \class{Transport} may fill its data buffers via board DMA, and pass the \class{Buffers} to the connected readout \class{Module}. This example treats the \strong{Active Buffer Board } (\ABB) \cite{AbbDescription} \index{Active Buffer Board ! overview}, a PCI express (PCIe) board \index{PCI express} with a {\em Virtex 4} FPGA and optical connectors to receive data from the experiment frontend hardware. It is developed for the CBM experiment \cite{CBM-stat-rep} by the {\em Institut f. Technische Informatik} at Mannheim University. The board developers deliver a kernel module as linux device driver, and the \class{mprace::} C++ library to work with the board from user space. Since this driver software may also be applied for other PCIe boards, the corresponding \dabc~ classes \class{pci::Device} and \class{pci::Transport} are rather generic, using namespace \class{pci::}. The special properties of the \ABB~ board are then implemented in a \class{pci::BoardDevice} subclass and in further classes with namespace \class{abb::}. Besides some simple test executables that read from and write to the \ABB~ on a single machine, there is an example of a \class{bnet::WorkerApplication} that applies the \ABB~ classes for the readout module. \section{PCI Device and Transport} \subsection{pci::BoardDevice} \index{PCI ! pci::BoardDevice} Subclass of \class{dabc::Device}. Adapter to the the \class{mprace::Board} functionality, i.e. the generic PCIe. \begin{compactenum} \item It implements the \strong{Transport factory method} \func{CreateTransport()}. This will create a \class{pci::Transport} and assign a dedicated working thread for each transport. The \dabc~ framework will use this method to establish the data connection of a \class{Port} with the PCI device. \item It defines a \strong{plug-in point} for an abstract board component: The device functionalities may require driver implementations that are more board specific. Because of this, the \class{mprace::} library provides base class \class{Board} with some virtual methods to work on the driver. This is applied here as handle to the actual \class{Board} implementation (e.~g.~ a \class{mprace:ABB}) that must be instantiated in the constructor of the subclass. Note that all functionalites require a real \class{Board} implementation, thus it is not possible to instantiate a mere \class{pci::BoardDevice} without subclassing it! \item It adds \strong{Device specific commands} \class{CommandSetPCIReadRegion} and \class{CommandSetPCIWriteRegion} that define the regions in the PCI address space for reading or writing data, resp. Method \func{ExecuteCommand(Command*)} is extended to handle such commands. \item It manages the \strong{scatter-gather mapping} of userspace \class{dabc::Buffer} objects for the DMA engine. These are taken from a regular \dabc~ memory pool and are each mapped to a \class{mprace::DMABuffer} representation. The DMA mapping is done in method \func{MapDMABuffer()} which gets the reference to the \class{dabc::MemoryPool*} that is used for the \class{pci::Transport}. This is required at \class{Device} initialization time; the mapping must be refreshed on the fly if the memory pool changes though. Method \func{GetDMABuffer(dabc::Buffer*)} will deliver for each \class{dabc::Buffer*} of the mapped memory pool the corresponding \class{mprace::DMABuffer*} object to be used in the underlying \class{mprace::} library. These are associated by the \class{dabc::Buffer} id number which defines the index in the \class{std::vector} keeping the \class{mprace::DMABuffer*} handles. Method \func{DoDeviceCleanup()} is implemented for a proper cleanup of the mapped DMA buffers when the \class{Device} is removed by the framework. \item \strong{Reading data from the board:} Method \func{ReadPCI(dabc::Buffer*)} implements reading one buffer from PCI, using the BAR, the PCI start address, and the read size, as specified before. These read parameters may be either set by method \func{SetReadBuffer(unsigned int bar, unsigned int address, unsigned int length)}, or by submitting the corresponding command \class{CommandSetPCIReadRegion} to the \class{pci::Device} If DMA mode (defined in the constructor) \strong{is not} enabled , this will just use PIO to fill the specified \class{dabc::Buffer} from the PCI address range. If DMA mode \strong{is} enabled, it will perform DMA into the user space \class{dabc::Buffer*}; this must be taken from a memory pool that was mapped before by means of \func{MapDMABuffer()}. This is a synchronous call that will initiate the DMA transfer and block until it is complete. For asynchronous DMA (double buffering of \class{dabc::DataTransport}) \index{PCI ! DMA} following virtual methods are provided: Method \func{ReadPCIStart(dabc::Buffer*)} may start the asynchronous filling of one mapped \class{Buffer} from the configured PCI board addresses. It should not wait for the completion of the data transfer, but return immeadiately without blocking after triggering the DMA. In contrast to this, \func{ReadPCIComplete(dabc::Buffer*)} must wait until the DMA transfer into the specified \class{Buffer} is completely finished. So the \class{pci::Transport} will initiate DMA by \func{ReadPCIStart()} and check for DMA completion by \func{ReadPCIComplete()}. A subclass of \class{pci::BoardDevice} may re-implement these methods with board specific functionalities. \item \strong{Writing data to the board:} Method \func{WritePCI(dabc::Buffer*)} implements writing data from a \dabc~ buffer to the PCI address space, using the BAR, the PCI start address, and the write size, as specified before. These write parameters may be either set by method \func{SetWriteBuffer(unsigned int bar, unsigned int address, unsigned int length)}, or by submitting the corresponding command \class{CommandSetPCIWriteRegion} to the \class{pci::Device}. If DMA mode (defined in the constructor) \strong{is not} enabled , this will just use PIO to transfer the specified \class{dabc::Buffer} to the PCI addresses. If DMA mode \strong{is} enabled, it will perform DMA from the user space \class{dabc::Buffer*}; this must be taken from a memory pool that was mapped before by means of \func{MapDMABuffer()}. This call will initiate the DMA transfer and block until it is complete. Currently there is no asynchronous implementation for data output to PCI, since this is a rare use case for a DAQ system. \end{compactenum} \subsection{pci::Transport} \index{PCI ! pci::Transport} This class handles the connection between the \class{Port} of a module and the PCI device. It is created in \func{CreateTransport()} of \class{pci::BoardDevice} when the user application calls the corresponding \class{Manager} method with the names of the port and the device instances, e.~g.~ \\ {\tt dabc::mgr.CreateTransport("ReadoutModule/Input", "AbbDevice");} It extends the base class \class{dabc::DataTransport} which already provides generic \class{Buffer} queues with a data backpressure mechanism, both for input and output direction. Because this class is also a \class{Worker}, each \class{pci::Transport} object has a dedicated thread that runs the IO actions. The following virtual methods of \class{dabc::DataTransport} were implemented: \begin{description} \item[\em unsigned Read\_Size()] : Returns the size in byte of the next buffer that is to be read from board. Uses the current readout length as set for the \class{pci::BoardDevice} with \func{SetReadBuffer()}, or \class{CommandSetPCIReadRegion}, resp. \item[\em unsigned Read\_Start(dabc::Buffer* buf)] : This initiates the reading into buffer \func{buf} and returns without waiting for completion. The functionality is forwarded to \func{ReadPCIStart()} of \class{pci::BoardDevice}. When \func{Read\_Start()} returns, the transport thread can already push the \strong{previously} filled DMA buffer to the connected \class{Port}, which may wake up the waiting thread of its \class{Module} for further processing. Thus base class \class{dabc::DataTransport} implicitly provides a double-buffering mechanism here. \item[\em unsigned Read\_Complete(dabc::Buffer* buf)] : Will wait until filling the buffer \func{buf} from a DMA read operation is completed. The DMA either must have been started asynchronously by a previous \func{Read\_Start()} call; or it must be started synchronously here. This method is used by the base class for synchronization between transport thread and DMA engine of the PCI board. The functionality is forwarded to \func{ReadPCIComplete()} of \class{pci::BoardDevice}. \item[\em bool WriteBuffer(const dabc::Buffer& buf)] : Write content of \func{buf} to the PCI region as set for the \class{BoardDevice} with \func{SetWriteBuffer()}, or \class{CommandSetPCIWriteRegion}, resp. This is a pure synchronous method, i.~e.~ it will start the DMA transfer and return no sooner than it's completed. The functionality is forwarded to \func{WritePCI()} of \class{pci::BoardDevice}. \item[\em void ProcessPoolChanged(dabc::MemoryPool* pool)] : Is called by the framework whenever the memory pool associated with the transport instance changes, e.~g.~ at transport connection time, pool expansion, etc. It calls \func{MapDMABuffers()} of \class{pci::BoardDevice} to rebuild the scatter-gather mappings for each buffer of the pool. \end{description} \section{Active Buffer Board implementation} \index{Active Buffer Board} \subsection{abb::Device} \index{PCI ! abb::Device} This subclass of \class{pci::BoardDevice} adds some functionality that is rather specific to the \ABB~ hardware and the test environment. \begin{compactenum} \item The constructor instantiates the \class{mprace::Board} component for the \ABB~ functionalities. Additionally, a DMA engine component \class{mprace::DMAEngineWG} is applied for all DMA specific actions. \item It implements the actual asynchronous DMA by overriding methods \func{ReadPCIStart()} and \func{ReadPCIComplete()}. \index{PCI ! DMA} The base class \class{pci::BoardDevice} can provide synchronous DMA only, because the generic \class{mprace::Board} interface does not cover asynchronous features. These are handled by the \class{DMAEngineWG} component. \item The constructor uses several \strong{configuration parameters}: \begin{small} \begin{verbatim} unsigned int devicenum = GetCfgInt(ABB_PAR_BOARDNUM, 0, cmd); unsigned int bar = GetCfgInt(ABB_PAR_BAR, 1, cmd); unsigned int addr = GetCfgInt(ABB_PAR_ADDRESS, (0x8000 >> 2), cmd); unsigned int size = GetCfgInt(ABB_PAR_LENGTH, 8192, cmd); \end{verbatim} \end{small} The parameter names are handled by string definitions in \decl{abb/Factory.h}: \begin{small} \begin{verbatim} #define ABB_PAR_BOARDNUM "ABB_BoardNumber" #define ABB_PAR_BAR "ABB_ReadoutBAR" #define ABB_PAR_ADDRESS "ABB_ReadoutAddress" #define ABB_PAR_LENGTH "ABB_ReadoutLength" \end{verbatim} \end{small} The \func{GetCfgInt()} will look for a parameter of the specified name already existing in the system, e.~g.~ if the \class{Application} object has defined such. If not, a \class{dabc::Parameter} of that name will be created and exported to the control system. If the configuration file specifies a value for this parameter, it will be set; otherwise, the default value (second argument of \func{GetCfgInt()}) is set. If the constructor gets a command object \func{cmd} as argument containing a parameter of the specified name, this command's parameter value will override all other values for this parameter defined elsewhere in the system. The user may pass such a \func{cmd} to the \class{abb::Device} either as third argument of the manager factory method \func{CreateDevice()}; or by means of a \class{dabc::CmdCreateDevice} object which is invoked by \func{Execute()} of the manager. This is useful if the device is to be tested without any configuration or control system, as shown in the examples of section \ref{prog_exapci_simpletest}. \item It provides \strong{pseudo event data} for the Bnet test example in the received DMA buffers: Method \func{ReadPCI()} is extended to copy an event header of the Bnet format (i.e. incrementing event count and unique id) into each output buffer after the base class \func{ReadPCI()} is complete. This workaround is necessary since the \ABB~ data itself does not contain any information in the test setup. Additionally, method \func{DoDeviceCleanup()} will reset the event counters at the end of each DAQ run. \end{compactenum} \subsection{abb::ReadoutModule} \index{PCI ! abb::ReadoutModule} Subclass of \class{dabc::ModuleAsync}; generic implementation of a readout module to use the \class{BoardDevice}. \begin{compactenum} \item It creates the memory pool which is used for DMA buffers in the \class{pci::BoardDevice}; this pool is propagated to the device via the \class{pci::Transport} when module is connected, since device will use the pool associated with the connection port. \item Module runs either in standalone mode (one input port, no output) for testing; or in regular mode (one input port, one output port) \item \func{ProcessItemEvent()} defines the module action for any \dabc~ events, e.~g.~ input port has new buffer. In standalone mode, the received buffer is just released. In regular mode, buffer is send to the output port. \item It has a \class{dabc::Ratemeter} object which is updated for each packet arriving in \func{ProcessItemEvent()}. The average data throughput rate is then printed out to the terminal on stopping the module in \func{AfterModuleStop()}. Alternatively, by means of method \func{CreateRateParameter()} it also defines a rate parameter "DMAReadout" that is linked to the input port "Input" and may export the current data rate to the control system. \end{compactenum} \subsection{abb::WriterModule} \index{PCI ! abb::WriterModule} Subclass of \class{dabc::ModuleSync}; generic implementation of a writer module to use the \class{BoardDevice}. \begin{compactenum} \item Creates the memory pool which is used for DMA buffers in the \class{pci::BoardDevice}; this pool is propagated to the device via the \class{pci::Transport} when module is connected, since device will use the pool associated with the connection port. \item Module runs either in standalone mode (one output port, no input) for testing; or in regular mode (one input port, one output port) \item \func{MainLoop()} defines the module action. In standalone mode, a new buffer is taken from the memory pool and send to the output port. In regular mode, the send buffer is taken from the input port. \item It has a \class{dabc::Ratemeter} object which is updated for each packet arriving in \func{MainLoop()}. The average data throughput rate is then printed out to the terminal on stopping the module in \func{AfterModuleStop()}. Alternatively, by means of method \func{CreateRateParameter()} it also defines a rate parameter "DMAWriter" that is linked to the input port "Output" and may export the current data rate to the control system. \end{compactenum} \subsection{abb::Factory} \index{PCI ! abb::Factory} A subclass of \class{dabc::Factory} to plug in the \ABB~ classes: \begin{compactenum} \item Implements \func{CreateDevice()} for the \class{abb::Device}. The third argument of this factory method is a \class{dabc::Command} that may contain optional setup parameters of the device. \item Implements \func{CreateModule()} for the \class{abb::ReadoutModule} and the \class{abb::WriterModule}. Third argument of this factory method is a \class{dabc::Command}, containing optional setup parameters of the module. \item The factory is created automatically as static (singleton) instance on loading the \verba{libDabcAbb.so}. \end{compactenum} \section{Simple read and write tests} \label{prog_exapci_simpletest} The functionality of the \ABB~ can be tested with several simple executables which are provided in the \decl{test} subfolder of the \decl{abb} plugin package. \subsection{DMA Read from the board} \label{prog_exapci_simpletest_read} The example code \verba{abb\_test\_read.cxx} shows in a simple \func{main()} function how to utilize the \class{abb::} classes \index{Active Buffer Board ! DMA read} for a plain readout with DMA. \begin{compactenum} \item It applies the \class{dabc::StandaloneManager} as most simple \class{Manager} implementation. \begin{small} \begin{verbatim} int nodeid=0; // this node id int numnodes=1; // number of nodes in cluster ... dabc::StandaloneManager manager(0, nodeid, numnodes); \end{verbatim} \end{small} \item The \class{abb::Device} is created by means of a command \class{CmdCreateDevice} which is passed to the manager. The command wraps also some initial parameters for the device which are then evaluated in method \func{CreateDevice()} of \class{abb::Factory}: \begin{small} \begin{verbatim} #define READADDRESS (0x8000 >> 2) #define READSIZE 16*1024 ... std::string devname="ABB"; dabc::CmdCreateDevice dcom("abb::Device", devname.c_str()); // arguments: (class name, device name) // set additional parameters for abb device here: dcom.SetInt(ABB_PAR_BOARDNUM, BOARD_NUM); dcom.SetInt(ABB_PAR_BAR, 1); dcom.SetInt(ABB_PAR_ADDRESS, READADDRESS); dcom.SetInt(ABB_PAR_LENGTH, readsize); bool res=dabc::mgr.Execute(dcom); DOUT1("CreateDevice = %s", DBOOL(res)); \end{verbatim} \end{small} Here the parameter names (e.~g.~ {\tt ABB\_PAR\_ADDRESS}) use the string definitions as set in \decl{abb/Factory.h}. The parameter values are defined locally (e.~g.~ {\tt READADDRESS}); however, the DMA transfer size {\tt readsize} may be set by the executables's first command line parameter. Boolean variable {\tt res} contains the result of the command execution (\keyw{true} or \keyw{false}) which is printed as debut output to the terminal with the {\tt DOUT1()} macro. \item It creates a \class{abb::ReadoutModule} by means of a command \class{CmdCreateModule} which is passed to the manager. The command wraps also some initial parameters for the module which are then evaluated in method \func{CreateModule()} of \class{abb::Factory}: \begin{small} \begin{verbatim} dabc::CmdCreateModule cmd("abb::ReadoutModule", "ABB_Readout", "ReadoutThread"); // arguments: (class name, module name, thread name) cmd.SetInt(ABB_COMPAR_BUFSIZE, readsize); cmd.SetInt(ABB_COMPAR_STALONE,1); cmd.SetInt(ABB_COMPAR_QLENGTH, 10); cmd.SetStr(ABB_COMPAR_POOL,"ABB-standalone-pool"); bool res=dabc::mgr.Execute(cmd); DOUT1("Create ABB readout module = %s", DBOOL(res)); \end{verbatim} \end{small} Again the parameter names (e.~g.~ {\tt ABB\_COMPAR\_QLENGTH}) use common string definitions as set in \decl{abb/Factory.h}, such as: the size of the memory pool buffers {\tt ABB\_COMPAR\_BUFSIZE} which is set to the required DMA transfer size{\tt readsize}; the standalone run mode of the module {\tt ABB\_COMPAR\_STALONE}; the port queue length {\tt ABB\_COMPAR\_QLENGTH}; the name of the module's memory pool {\tt ABB\_COMPAR\_POOL}, which also. \item The transport connection between the input port of the readout module and the \class{abb::Device} is established by a direct method call of the manager: \begin{small} \begin{verbatim} res = manager.CreateTransport("ABB_Readout/Input", devname.c_str()); DOUT1("Connected module to ABB device = %s", DBOOL(res)); \end{verbatim} \end{small} The manager will find the \ABB~ device instance by the string {\tt devname} and use its factory method \func{CreateTransport()} to instantiate a \class{pci::Transport} that will be connected to the port of name "ABB\_Readout/Input". \item The readout module processing is started by name with a manager method: \begin{small} \begin{verbatim} manager.StartModule("ABB_Readout"); DOUT1("Started readout module...."); \end{verbatim} \end{small} Then the main process waits for 5 seconds while the \dabc~ threads and the board DMA performs the data transfer. The module is stopped again then. \begin{small} \begin{verbatim} sleep(5); manager.StopModule("ABB_Readout"); DOUT1("Stopped readout module."); \end{verbatim} \end{small} After the module has stopped, its internal \class{dabc::Ratemeter} will print some average data rate values to the terminal. Finally, all objects are destroyed and the manager is cleaning up the process before the program ends: \begin{small} \begin{verbatim} dabc::mgr.CleanupApplication(); \end{verbatim} \end{small} \end{compactenum} \subsection{DMA Write to the board} \label{prog_exapci_simpletest_write} The example code \verba{abb\_test\_write.cxx} shows in a simple \func{main()} function how to utilize the \class{abb::} classes to write data from the PC to the \ABB~ with DMA \index{Active Buffer Board ! DMA write}. The code is very similar to the read example as described in the above section \ref{prog_exapci_simpletest_read}: \begin{compactenum} \item It applies the \class{dabc::StandaloneManager} as most simple \class{Manager} implementation. \item The \class{abb::Device} is created by means of a command \class{CmdCreateDevice} which is passed to the manager. The command contains the initial parameters for the device. The DMA transfer size {\tt readsize} may be set by the executables's first command line parameter (see section \ref{prog_exapci_simpletest_read} for code example). \item It creates a \class{abb::WriterModule} by means of a command \class{CmdCreateModule} which is passed to the manager. The command contains the initial parameters for the module which are then evaluated in method \func{CreateModule()} of \class{abb::Factory}: \begin{small} \begin{verbatim} dabc::CmdCreateModule cmd("abb::WriterModule", "ABB_Sender", "WriterThread"); cmd.SetInt(ABB_COMPAR_BUFSIZE, readsize); cmd.SetInt(ABB_COMPAR_STALONE,1); cmd.SetInt(ABB_COMPAR_QLENGTH, 10); cmd.SetStr(ABB_COMPAR_POOL,"ABB-standalone-pool"); cmd.SetStr(ABB_PAR_DEVICE,devname.c_str()); bool res=dabc::mgr.Execute(cmd); DOUT1("Create ABB writer module = %s", DBOOL(res)); \end{verbatim} \end{small} Again the parameter names are expressd by common string definitions as set in \decl{abb/Factory.h}. \item The transport connection between the output port of the writer module and the \class{abb::Device} is established by a direct method call of the manager: \begin{small} \begin{verbatim} res = manager.CreateTransport("ABB_Sender/Output", devname.c_str()); DOUT1("Connected module to ABB device = %s", DBOOL(res)); \end{verbatim} \end{small} \item The writer module's processing is started with a manager method: \\ {\tt manager.StartModule("ABB\_Sender")}. \\ The main process waits 5 seconds while the \dabc~ threads and the board DMA perform the data transfer. The module is stopped again then. After the module has stopped, its internal \class{dabc::Ratemeter} will print some average data rate values to the terminal. Finally, the manager is cleaning up all objects and the program terminates. \end{compactenum} \subsection{Simultaneous DMA Read and Write} The example code \verba{abb\_test.cxx} shows in a simple \func{main()} function how to utilize the \class{abb::} classes to write data from the PC to the \ABB~ in one DMA channel, and simultaneously read data back from the board with another DMA \index{Active Buffer Board ! DMA read and write}. channel. It applies the \class{abb::Device} both with a \class{abb::WriterModule} and a \class{abb::ReadoutModule} that run in different threads. So the code is a merger of the above examples \ref{prog_exapci_simpletest_read} and \ref{prog_exapci_simpletest_write}: \begin{compactenum} \item It applies the \class{dabc::StandaloneManager} as most simple \class{Manager} implementation. \item The \class{abb::Device} is created by means of a command \class{CmdCreateDevice} which is passed to the manager. The command contains the initial parameters for the device. The DMA transfer size {\tt readsize} (same for both directions) may be set by the executables's first command line parameter (see section \ref{prog_exapci_simpletest_read} for code example). \item It creates a \class{abb::ReadoutModule} by means of a command \class{CmdCreateModule} which is passed to the manager (see section \ref{prog_exapci_simpletest_read} for code example). \item It creates a \class{abb::WriterModule} by means of a command \class{CmdCreateModule} which is passed to the manager (see section \ref{prog_exapci_simpletest_write} for code example). \item The transport connections of the \class{abb::Device} both with the input port of the reader module, and the output port of the writer module are established by invoking method \func{CreateTransport()} of the manager (see sections \ref{prog_exapci_simpletest_read} and \ref{prog_exapci_simpletest_write} for comments on the code) \item Both modules are started with {\tt manager.StartModule("")}. The main process sleeps for 60 seconds during the DMA transfer, then it stops both modules again. After the modules have stopped, their internal \class{dabc::Ratemeter} instances will print some average data rate values to the terminal. Finally the manager is cleaned up and the program ends. \end{compactenum} \section{Active Buffer Board with Bnet application} \label{prog_exapci_bnet} The DAQ builder network (Bnet) example as described in section \ref{prog_exabnet_test} may optionally utilize the \ABB~ \index{Active Buffer Board ! with Bnet} as input for the Readout module. This is provided in class \class{bnet::TestWorkerApplication} which implements the \class{bnet::WorkerApplication}: The Bnet factory method \begin{small} \begin{verbatim} bool bnet::TestWorkerApplication::CreateReadout(const char* portname, int portnumber) \end{verbatim} \end{small} will instantiate an \class{abb::Device} if the configuration parameter for the portnumber $p$ ("Input$p$Cfg", as delivered by \func{ReadoutPar(p)}) is set to "ABB". This \class{abb::Device} is connected directly to the input port of the standard Bnet combiner module, as specified by the \func{portname} argument of the method: \begin{small} \begin{verbatim} if(ReadoutPar(portnumber) == "ABB") { const char* abbdevname = "ABBDevice"; fABBActive = dabc::mgr.CreateDevice("abb::Device", abbdevname); res = dabc::mgr.CreateTransport(portname, abbdevname); if (!res) EOUT("Cannot create ABB transport"); } \end{verbatim} \end{small} Note that the \class{abb::ReadoutModule} is \strong{not used} here; this is applied for the simple examples only, see section \ref{prog_exapci_simpletest}). Any other value of \func{ReadoutPar(p)}) will apply the \class{TestGeneratorModule} of the standard Bnet example. The parameters for the \ABB~ can be set in the XML configuration file, using the names as defined in \decl{abb/Factory.h}. This may look as follows: \begin{small} \begin{verbatim} ... ... \end{verbatim} \end{small} Note that the ABB plugin library \decl{libDabcAbb.so} must be loaded to instantiate the \class{abb::Factory} and apply its classes on the node.