Chapter 5. Processing Event Built data

This chapter builds on the examples so far to show how to process events that have come from an event builder. We specifically build this on the framework in the example: Peforming type independent processing.

The only differences between event built and non-event-built data are how the PHYSICS_EVENT ring item bodies are constructed. For event built data, these bodies consist of a size followed by a sequence of fragments. Each fragment has a header and a body. The body of each fragment is, itself, just another PHYSICS_EVENT ring item.

NSCLDAQ supplies the FragmentIndex class. This class is able to unravel event built data for you. In this chapter, we're going to look at the changes to the processor.cpp file needed to make processEvent capable of dealing with an event that has been glued together from fragments by the event builder.

The full code for this example is installed in $DAQROOT/share/recipes/eventbuilt

Here are the additions we needed to make to the top part of processor.cpp


#include <FragmentIndex.h>
#include <CRingItemFactory.h>

            

FragmentIndex.h defines the FragmentIndex class we'll use to pick apart the raw physics items. Once we've done that, we'll use a CRingItemFactory (defined in CRingItemFactory.h) to construct a CPHysicsEventItem from a pointer to the ring item in each fragment.

Let's now look at the processEvent code.


void
CRingItemProcessor::processEvent(CPhysicsEventItem& item)
{
    std::cout << "Event:\n";
    std::cout << "Time stamp: "  << item.getEventTimestamp()
        << " Output SourceId: "  << item.getSourceId() << std::endl;

    FragmentIndex indexer(                         (1)
        static_cast<uint16_t*>(item.getBodyPointer())
    );
    std::cout << "Event has "
        << indexer.getNumberFragments()       (2)
        << " fragments\n";

    CRingItemFactory itemFactory;
    for (size_t i = 0; i < indexer.getNumberFragments(); i++) {
        FragmentInfo fragment(indexer.getFragment(i));   (3)
        std::cout << "Fragment: " << i << " source id : " << fragment.s_sourceId
            << " timestamp: " << fragment.s_timestamp << std::endl;

        // Each item is a ring item.  The itemhdr points to the ring item.
        // s_itembody points to the item body.

        uint8_t* pRawFrag =                            (4)
            reinterpret_cast<uint8_t*>(fragment.s_itemhdr); 

                     (5)

        CRingItem* pRawItem = itemFactory.createRingItem(pRawFrag);
        std::unique_ptr<CPhysicsEventItem>
            fragItem(dynamic_cast<CPhysicsEventItem*>(pRawItem));
        std::cout << "Fragment: \n" << fragItem->toString() << std::endl;

        uint16_t* pBody = fragment.s_itembody;     (6)

    } 
}

            

(1)
The simplest way to construct a FragmentIndex object is to pass it a pointer to the body of the ring item. The FragmentIndex class allows you to iterate over the fragments either using STL like iteration or by getting the size and indexing each fragment.
(2)
We've chosen the indexing version of iteration. This gets us the number of fragments in the event. Each fragment, recall is a fragment header followed by a fragment payload which is a PHYSICS_EVENT ring item.
(3)
Indexing a fragment returns a FragmentInfo structure. This has the following fields:

uint64_t s_timestamp

The timestamp of the fragment. All of the fragment timestamps will be within the Glom coincidence window of the first fragment.

uint32_t s_sourceId

The id of the sourcea that produced this fragment. Note the event builder allows fragments from the same sourcea to appear more than once in the event. This is an intentional feature.

uint32_t s_size

The size of the fragment payload. This should match the size field of the embedded ring item.

uint32_t s_barrier

The barrier type of the fragment. This should always be zero as physics events are not barrier events.

uint16_t* s_itemhdr

Points to the items header. This is the ring item header of the ring item that is the fragment payload.

uint16_t* s_itemBody

Pointer to the body of the ring item. This is correctly computed for items with and without body headers.

(4)
This line extracts a pointer to the ring item from the fragment.
(5)
If the ring item factory is handed a pointer to a raw ring item, it will construct the appropriate concrete ring item class (in this case a CPhysicsEvent). Since that ring item is dynamically allocated, we wrap it in a std::unique_ptr to ensure it is destroyed when the block exits.
(6)
There are actually two ways to get a pointer to the event body for later analysis. s_itembody in the fragment description struct points to it. The CRingItem base class method getBodyPointer also returns a pointer to the body.

Both are correct even if the event has no body header