[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}
...
<Context host="node01" name="Worker2:42">
  <Run>
   <lib value="${DABCSYS}/lib/libpcidriver.so"/>          
   <lib value="${DABCSYS}/lib/libmprace.so"/>
   <lib value="${DABCSYS}/lib/libDabcAbb.so"/>          
   <lib value="libBnetTest.so"/>
 </Run> 
 <Application class="bnet::TestWorker">
   <NumReadouts value="1"/>
   <Input0Cfg value="ABB"/>
 </Application>
 <Devices>        
    <Device name="ABBDevice">
      <ABB_BoardNumber value="0"/>
      <ABB_ReadoutBAR value="1"/> 
      <ABB_ReadoutAddress value="8192"/>
      <ABB_ReadoutLength value="16384"/> 
    </Device>
 </Devices>      
</Context>
...
\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.


