Event segments are software components that manage the dgitizers associated with a logical part of your experiment. You can create an arbitrary number of event segments and control the order in which they are read. The abstraction of event segments supports the fact that experiments may be composed of re-usable detector subsystems and systems.
At the NSCL for example: One might run an experiment using the SeGA gamma ray spectrometer and the S800 spectrograph. One way to do this is to have a re-usable event segment for the S800, another for SeGA and to register them both with the readout framework to build the experiment.
                Event segments are instances of classes (objects) that are
                derived from the class CEventSegment.
                This section will also describe the CEventPacket
                base class which is an event segment that wraps an event segment
                into an NSCL tagged packet.
            
The methods an event segment can implement are:
initializeIs 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.
                        
disableThis 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.
readThis 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.
Enough theory already. Let's look at a sample implemetation of an event segment. First the header:
Example 2-2. Simple Event Segment Header
#include <stdint.h>#include <CEventSegment.h>
class CAENcard;
class CCAENEventSegment : public CEventSegment
{ private: CAENcard* m_pCard;
public: CCAENEventSegment(uint32_t base, uint8_t id, int crate = 0);
~CCAENEventSegment();
virtual void initialize(); virtual void clear(); virtual size_t read(void* pBuffer, size_t maxwords); private: bool haveEvent(); };


CEventSegment
                        class.
                    
CAENcard is a class that
                        will be defined later.   Using forward class definitions
                        reduces the chances of building circular dependencies
                        between header files.
                    
CCAENEventSegment
                        derives from the CEventSegment class.
                        Only CEventSegment derived
                        classes can be registered as event segments with the
                        framework.
                    
CAENcard.
                        It is going to be a pointer to a CAENcard
                        object we will create in our class constructor.  That
                        object will be used to manipulate the CAEN digitizer.
                    
id).
                        and VME crate number.
                    Lets look at the implementation (.cpp) file of the event segment a chunk at a time:
Example 2-3. Event segment front matter
#include <config.h>#include "CCAENEventSegment.h"
#include <CAENcard.h>
![]()
#include <string> #include <stdlib.h> #include <iostream>


CCAENEventSegment class it
                        needs access to the header so that method prototypes
                        and member data definitions are available to
                        method implementations.
                    
CAENcard  class
                        we made in the header.  Since CAENEventSegment
                        is going to call CAENcard methods,
                        we'll the compiler will need the actual class
                        definition.
                    
The next code section we will look at contains the constructor and destructor of the event segment. The constructor is invoked when the event segment is created (usually in the Skeleton.cpp just prior to registration). The destructor is usually never invoked. However if you have some overarching event segment that, at initialization time, creates other event segments it contains, destructors may be called. In order to allow your code to be embedded in environments you don't initially anticipate, you should write correct constructors for all event segments.
Example 2-4. Event segment constructor and destructor
CCAENEventSegment::CCAENEventSegment(uint32_t base, uint8_t id,
                                     int packet, int crate) :
  m_pCard(new CAENcard(id, 0, false, base))  
{
}
CCAENEventSegment::~CCAENEventSegment()
{
  delete m_pCard;   
}
                
m_pCare member data
                        with a pointer to a CAENcard
                        object that will manage the CAEN tdc we are
                        operating with.
                    At this time, no operations are perfomed on the device itself as we've not yet been asked to initialize it.

CAENcard object the constructor
                        created or memory and SBS mapping resources will
                        be leaked for each construction/destruction cycle.
                    Next lets look at the initialization code. In a production environment, this code might open a file and read some configuration data, using that data to figure out how to initialize the device. In keeping with showing the simplest code possible, we are going to hard code all configuration information.
Example 2-5. Event segment initialize implementation
void
CCAENEventSegment::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);
}
                
sleep is probably somewhat longer
                        than required.  A call to usleep
                        for a few milliseconds is probably more appropriate.
                    

                The clear function is trivial:
            
Example 2-6. Event segment clear implementation
CCAENEventSegment::clear()
{
  m_pCard->clearData();
}
                The heart of the event segment is, of course, the code that reads out the module:
Example 2-7. Event segment read method implementation
size_t
CCAENEventSegment::read(void* pBuffer, size_t maxwords)
{
  // Maximum number of words is 34*2:
  if (maxwords < 34*2 ) {          
    throw
      std::string(
      "CCAENEventSegment - insufficient buffers space for worst case event");
  }
  
  for (int i =0; i < 30; i++) {
    if(haveEvent()) break;            
  }
  int n = (m_pCard->readEvent(p))/sizeof(uint16_t); 
  return n; 
}
bool
CCAENEventSegment::haveEvent()     
{
  return m_pCard->dataPresent();
}
                
read is called it is
                        given a pointer that describes where to put data
                        that has been read; pBuffer
                        and a size_t maxwords
                        that describes the amount of space remaining in the
                        buffer in words (uint16_t sized units).
                        This code ensures that the maximum TDC event size
                        will fit in the remaining buffer space, throwing
                        an exception if it won't.
                    The computation of the largest TDC event size comes from the fact that the TDC has 32 channels, that each event will have a header and trailer, and that each item will be a uint32_t, which uses two uint16_t units of storage.



n is the number of
                        uint16_t words read.  That value is returned.
                    
Before leaving the subject of event segments for the scaler readout, I want to touch on two other important classes you can use to help you organize your code:
CCompoundEventSegment
                            The CCompoundEventSegment
                            class is an event segment that consists of
                            an ordered list of event segments (including
                            other compound event segments).
                        
This can be used to organize a detector system that consists of many detector elements into a single event segment you can hand to your users
CEventPacketNSCL events are often broken into packets. A packet consists of a header that has a size (note in RingDaq the size is a uint32_t while in SPDAQ, it is a uint16_t), a tag, and following the header, the payload of the packet.
                            CEventPacket
                            allows you to wrap an existing event
                            segment (including a compound event segment),
                            in a packet without the event segment knowing it's
                            being wrapped.
                        
                Let's look at a simple example of a compound event segment.
                Earlier in this section, we've built a class;
                CCAENEventSegment that encapsulated
                a CAEN 32 channel TDC.   Suppose we had an experiment
                that consisted of several of these TDCs (I know it's a bad
                example but it prevents me from having to build additional
                event segments and all I really want to show is the mechanics
                of using compound event segments.)
            
We might have code the builds a Compound event segment as follows:
Example 2-8. Using a compound event segment
...
    // Create a few CCAENEventSegment objects:
    CCAENEventSegment tdc1(0x10000000, 1);
    CCAENEventSegment tdc2(0x10010000, 2);
    CCAENEventSegment tdc3(0x10020000, 3);
    
    // Create a compound event segment that reads out tdc1, tdc2, tdc3 in order:
    
    CCompoundEventSegment tdcs;
    tdcs.AddEventSegment(&tdc1);
    tdcs.AddEventSegment(&tdc2);
    tdcs.AddEventSegment(&tdc3);
...
                
                The key to this example is that a CCompoundEventSegment
                has a method named AddEventSegment whose
                parameter is a pointer to an existing event segment.  This
                creates an ordered list of event segments.
            
                Since a CCompoundEventSegment is itself
                an event segment it can be added to another
                CCompoundEventSegment as well.
                Continuing the example above:
            
...
      // Assume we've made more event segments named adcs and qdcs:
      
      CCompoundEventSegment myDetector;
      myDetector.AddEventSegment(&tdcs);
      myDetector.AddEventSegment(&adcs);
      myDetector.AddEventSegment(&qdcs);
...
                The event segment hierarchy you build up can be as deep as you need to capture the needs of your detector system.
                Suppose now that myDetector in the previous
                example is a know NSCL detector that has been assigned a packet
                id of 0x1234.  We can now create an event segment that
                wraps our detector in that packet by using a
                CEventPacket as follows:
            
Example 2-9. Using CEventPacket
...
    CEventPacket myDetectorPacket(myDetector, 0x1234,
                                    "My  Detector",
                                    "Packet for the My Detector device"
                                    "V1.0");
                It's pretty easy to understand what the first two parameters are, a reference to an event packet and the tag. The remaining information are bundled into a documentation event emitted at the start of run that describe the set of event packets that you can expect to see in the run. Each documenation event contains a set of strings, one for each packet you created. Each string is a colon separated set of fields consisting of the short name of the packet (My Detector above), The stringified packet id (0x1234) as a hexadecimal, a long name of the packet (Packet for the My Detector device above), and a packet version which should be changed whenever the format of the packet changes (V1.0 in the example above), and the date and time the packet object was constructed (e.g. Tue Oct 18 15:32:20 2011).
                If you want to see what the description string looks like, you can
                call the Format method of the
                CEventPacket object.