3.2. Porting existing event segments to RingDaq

In this section we will look at how to take an existing event segment for the SPDAQ production readout framework and port it to RingDaq. Let's start by comparing the interfaces of the two types of event segments

The CEventSegment class in the SPDAQ system looks like this:

Initialize

Provides initialization code that is called at the start of data taking.

Read

Reads data in response to the trigger. rBuffer is a reference to the spectrodaq buffer pointer object that describes where to put the data to be read. The return value is a spectrodaq buffer pointer objedt that has been advanced so that it points to the first word (uint16_t) following the data read by this event segment

Clear

Called prior to being able to accept a trigger (including the first trigger). This method is supposed to do any cleanup to make the digitizers able to accept a new event.

MaxSzie

This is supposed to returnt he maximum number of uint16_t data alements the event segment will read.

By comparison, the RingDaq CEventSegment, which has the same purpose has the following interface:

initialize

Is called before data taking starts and is expected to initialize the data taking devices to prepare them and enable them to take data. This method is optional. If omitted the framework does nothing to initialize this event segment.

clear

Is called to clear digitizers to prepare them to respond to the next trigger. It is called just prior to waiting for a trigger (at the start of the run after initialize is called as well as after each event). This method is optional and if not implemented the framework does nothing for this event segment at clear time.

disable

This method is called as data taking is being shutdown. If your devices require any actions to disable them you can perform those actions in this method. One place you might use this would be if you have programmed a user specific trigger based on VME interrupts. You could use the method to disable the interrupts on your trigger device.

This method is optional and the framework will do nothing if it is not implementerd.

read

This method is called on each trigger it is expected to read the data from the devices managed by this event segment from the digitizer hardware. Parameters are as follows:

type: void*

parameter: pBuffer

Purpose: Pointer to storage into which this event segment should store its data. Usually the first thing you will need to do is re-cast this pointer to the appropriate data type.

type: size_t

parameter: maxWords

Purpose: The maximum number of uint16_t units that can fit in the space pointed to by pBuffer. Very bad things will happen if you read more than this number of words.

The return value is expected to be the number of uint16_t units of data read by this segment.

The following are a few general remarks about how to port from SPDAQ to RingDaq for each method in CEventSegment. It is important to note that the header spectrodaq.h does not exist in RingDaq and #include directives for it should be removed:

Initialize

Change the name of this function to initialize. Typically no other changes will be needed.

Clear

Change the name of this method to clear. Typically no other changes are needd.

MaxSize

This method has no counterpart in the RingDaq system. Remove it from your event segment.

Read

This method needs the most work:

  1. Rename the method to read changing the parameter signature to match that of the RingDaq event segment (accepting a void* and a size_t).

  2. Usually you will need to cast the input pointer to a uint16_t*. Then replace all arithmetic involving DAQWordBufferPtr::GetIndex() with direct pointer arithmetic

  3. Return the number of words read rather than a pointer to the next location.

  4. Peform a test at the top of the function to see if your worst case event (or if you can determine it your atual event size) is less than or equal to the maxWords parameter and throw an exception if not.

Let's see how this works in practice. The following two examples show a header and an implementation of an SPDAQ event segment that manages a CAEN V775 TDC.

Example 3-2. SPDAQ Production readout event segment header


#include <CEventSegment.h>
#include <stdint.h>

using namespace std;
#include <spectrodaq.h>


class CAENcard;

class MyEventSegment :  public CEventSegment
{
 private:
  CAENcard* m_pCard;
 public:
  MyEventSegment(uint32_t base,  uint8_t id, int crate= 0);
  virtual ~MyEventSegment();

  virtual void Initialize();
  virtual DAQWordBufferPtr& Read(DAQWordBufferPtr& rBuffer);
  virtual void Clear();
  virtual unsigned int MaxSize();

};

                

Example 3-3. SPDAQ production readout event segment implementation


#include <config.h>
#include "MyEventSegment.h"
#include <CAENcard.h>


MyEventSegment::MyEventSegment(uint32_t base, uint8_t id, int crate) :
  m_pCard(new CAENcard(id, 0, false, base))
{}

MyEventSegment::~MyEventSegment() 
{
  delete m_pCard;
}

void MyEventSegment::Initialize()
{
  m_pCard->reset();
  sleep(2);
  for(int i =0; i < 32; i++) {
    m_pCard->setThreshold(i, 0);
  }
  m_pCard->commonStart();
  m_pCard->keepOverflowData();
  m_pCard->keepUnderThresholdData();
  m_pCard->setRange(0x1e);

  m_pCard->clearData();
}

void MyEventSegment::Clear()
{ 
  m_pCard->clearData();

}

DAQWordBufferPtr&
MyEventSegment::Read(DAQWordBufferPtr& rBuffer)
{
  for (int i =0; i < 30; i++) {
    if(m_pCard->dataPresent()) break;
  }
  rBuffer +=(m_pCard->readEvent(rBuffer))/sizeof(int16_t);

  return rBuffer;
}

unsigned int
MyEventSegment::MaxSize() 
{
  return 34*2;
}

                

After following the previous suggestions the resulting header for the event segment looks like this:

Example 3-4. Porting the event segment to RingDaq


#include <CEventSegment.h>
#include <stdint.h>


(1)

using namespace std;


class CAENcard;

class MyEventSegment :  public CEventSegment
{
 private:
  CAENcard* m_pCard;
 public:
  MyEventSegment(uint32_t base,  uint8_t id, int crate= 0);
  virtual ~MyEventSegment();

  virtual void initialize();  (2)
  virtual size_t read(void* pBuffer, size_t maxWords); (3)
  virtual void clear();      (4)
(5)

};

                
(1)
This note is for what is not there. The #include for spectrodaq.h has been removed as there is no corresponding header in RingDaq.
(2)
In RingDaq the name of this method is entirely lower case rather than starting with an upper case letter.
(3)
Note the change not only in name but in parameter types and number.
(4)
The clear function has been renamed to be fully lower case.
(5)
The MaxWords method is not used by RingDaq and has therefore been removed.

Let's take the implementation file in two pieces. First we'll look at all methods other than the read method. Then we'll look at the read method by itself.

Example 3-5. Porting the Event segment to RingDaq II


#include <config.h>
#include "MyEventSegment.h"     (1)
#include <CAENcard.h>
#include <string>


MyEventSegment::MyEventSegment(uint32_t base, uint8_t id, int crate) :
  m_pCard(new CAENcard(id, 0, false, base))  (2)
{}

MyEventSegment::~MyEventSegment()   (3)
{
  delete m_pCard;
}

void MyEventSegment::initialize()   (4)
{
  m_pCard->reset();
  sleep(2);
  for(int i =0; i < 32; i++) {
    m_pCard->setThreshold(i, 0);
  }
  m_pCard->commonStart();
  m_pCard->keepOverflowData();
  m_pCard->keepUnderThresholdData();
  m_pCard->setRange(0x1e);

  m_pCard->clearData();
}

void MyEventSegment::clear()   (5)
{ 
  m_pCard->clearData();

}
...
                

As you can see not very many modifications were required for this part of the code:

(1)
The <string> header was included so that the read method (see below) can throw a std::string exception.
(2)
No changes required to the consturctor.
(3)
No changes were required to the destrutor.
(4)
The only change required here was to change the first letter of the function name to lower case.
(5)
the only change required to this function was to change the first letter of the function name to lower case.

The bulk of the changes are due to the change in the parameter signature of the read method:

Example 3-6. Porting the Event segment to RingDaq II


...
size_t
MyEventSegment::read(void* pBuffer, size_t maxWords) (1)
{

  if (34*2 > maxWords) {       (2)
    throw std::string("Insuficient space remaining in buffer");
  }

  for (int i =0; i < 30; i++) {
    if(m_pCard->dataPresent()) break;  (3)
  }
  size_t n = (m_pCard->readEvent(pBuffer))/sizeof(int16_t); (4)

  return n;  (5)
}
(6)
                
(1)
This line had to be changed. The first character of the name of the method was changed to lower case and, instead of a DAQWordBufferPtr& return value, a size_t is returned indicating the number of words read. Furthermore, instead of a DAQWordBufferPtr& parameter, the function now is passed a void* and a size_t.
(2)
As recommended earlier, if the largest amount of data we will produce is larger than our remaining event storage space (maxWords), a std::string exception is thrown.
(3)
This code is unchanged.
(4)
This code is only slightly changed. Instead of computing the next buffer location, we just need to know how much data were read.
(5)
Return the numger of words read.

A few points need to be covered prior to leaving this section: