Project

General

Profile

Creating a new Fragment type

One of the first things needed for new experiments is to create a new Fragment Overlay type. Fragment Overlays (such as ToyFragment) provide art with methods to decode the raw data from your hardware.
In real systems, data should not be written through an Overlay type, instead write raw Fragments and set the type field to match the destination overlay.

The Overlay Class

(I will be using artdaq-core-demo/Overlays/ToyFragment.hh as an example)

The Overlay class should provide methods to decode the raw data coming from your hardware. For easier interoperability, most artdaq Overlay types provide some form of dataBegin and dataEnd methods, with return types relevant to the decoded data.
In ToyFragment, these functions are called dataBeginADCs and dataEndADCs, returning atc_t const*. The ToyHardwareInterface emits a preamble before the ADC data, this is captured by the ToyFragment::Header struct. From a raw artdaq::Fragment's point of view, a ToyFragment contains the artdaq::Fragment Header, a ToyFragment::Metadata object and the data payload (which the overlay further breaks down into the ToyFragment::Header and list of ADC values). There are no restrictions on the format of the payload region of the Fragment, but the Metadata must be a fixed-size class. Since Fragments are represented as solid data, container types should be avoided in Fragment Overlays, but C-style arrays are fine.

A possible implementation of a Fragment Overlay:


class MyOverlay : public artdaq::Fragment {

MyOverlay(artdaq::Fragment const& frag) : artdaq_fragment_(frag) {}

struct DataHeader {
uint8_t n_datapoints;
uint8_t fw_version;
uint64_t trigger_num;
}

struct DataPoint {
uint64_t timestamp;
uint16_t adc;
};

DataHeader const* get_header() { return reinterpret_cast<DataHeader const*>(artdaq_fragment_.dataBeginBytes()); }

DataPoint const* dataBegin() { return reinterpret_cast<DataPoint const*>(get_header() + 1); }

DataPoint const* dataEnd() { return dataBegin() + get_header().n_datapoints; }

private:
artdaq::Fragment artdaq_fragment_;

}

Note the presence of the overlaid Fragment reference in the class.

Bookkeeping Classes

The Fragment Overlay class needs to be registered with art to be usable in art modules. This is done through the FragmentType class in your "core" package, and an overload of the RawInputQueueReader art Source in your "daq" package.

I've provided examples of these bookkeeping classes from artdaq_demo and artdaq_core_demo here. FragmentType must be updated to match the overlays provided by your "core" package; the others just need updated class names and namespaces (i.e. DemoInput->MyExperimentsInput).

FragmentType.hh:

#ifndef artdaq_demo_Overlays_FragmentType_hh
#define artdaq_demo_Overlays_FragmentType_hh
#include "artdaq-core/Data/Fragment.hh" 

namespace demo
{
    /**
     * \brief List of names (in the order defined below) of the User types defined in artdaq_core_demo
     */
    std::vector<std::string> const names{"MISSED", "TOY1", "TOY2", "ASCII", "UDP", "UNKNOWN"};

    /**
     * \brief Implementation details namespace
     */
    namespace detail
    {
        /**
         * \brief User type codes defined by artdaq_core_demo
         */
        enum FragmentType : artdaq::Fragment::type_t
        {
            MISSED = artdaq::Fragment::FirstUserFragmentType,
            TOY1,
            TOY2,
            ASCII,
            UDP,
            INVALID // Should always be last.
        };

        // Safety check.
        static_assert(artdaq::Fragment::isUserFragmentType(FragmentType::INVALID - 1),
            "Too many user-defined fragments!");
    }

    using detail::FragmentType;

    /**
     * \brief Lookup the type code for a fragment by its string name
     * \param t_string Name of the Fragment type to lookup
     * \return artdaq::Fragment::type_t corresponding to string, or INVALID if not found
     */
    FragmentType toFragmentType(std::string t_string);

    /**
     * \brief Look up the name of the given FragmentType
     * \param val FragmentType to look up
     * \return Name of the given type (from the names vector)
     */
    std::string fragmentTypeToString(FragmentType val);
}
#endif /* artdaq_demo_Overlays_FragmentType_hh */

RawEventQueueReader.hh:

#ifndef artdaq_demo_ArtModules_detail_RawEventQueueReader_hh
#define artdaq_demo_ArtModules_detail_RawEventQueueReader_hh

#include <string>
#include <map>

#include "artdaq/ArtModules/detail/RawEventQueueReader.hh" 

namespace demo
{
    namespace detail
    {
        struct RawEventQueueReader : public artdaq::detail::RawEventQueueReader
        {
            RawEventQueueReader(RawEventQueueReader const&) = delete;

            RawEventQueueReader& operator=(RawEventQueueReader const&) = delete;

            RawEventQueueReader(fhicl::ParameterSet const& ps,
                                art::ProductRegistryHelper& help,
                                art::SourceHelper const& pm);

            RawEventQueueReader(fhicl::ParameterSet const& ps,
                                art::ProductRegistryHelper& help,
                                art::SourceHelper const& pm,
                                art::MasterProductRegistry&) : RawEventQueueReader(ps, help, pm) {}
        };
    } // detail
} // demo

// Specialize an art source trait to tell art that we don't care about
// source.fileNames and don't want the files services to be used.
namespace art
{
    template <>
    struct Source_generator<demo::detail::RawEventQueueReader>
    {
        static constexpr bool value = true;
    };
}

#endif /* artdaq_demo_ArtModules_detail_RawEventQueueReader_hh */

RawEventQueueReader.cc:

#include "artdaq-demo/ArtModules/detail/RawEventQueueReader.hh" 
#include "artdaq-core-demo/Overlays/FragmentType.hh" 

#include <messagefacility/MessageLogger/MessageLogger.h>

demo::detail::RawEventQueueReader::RawEventQueueReader(fhicl::ParameterSet const& ps,
                                                       art::ProductRegistryHelper& help,
                                                       art::SourceHelper const& pm) :
    artdaq::detail::RawEventQueueReader(ps, help, pm)
{
    mf::LogInfo("RawEventQueueReader") << "DemoInput Constructor!";
    for (auto& name : names)
    {
        mf::LogInfo("RawEventQueueReader") << "Adding fragment type " << name << " to fragment_type_map, and registering with the ProductRegistryHelper";
        fragment_type_map_[toFragmentType(name)] = name;
        help.reconstitutes<artdaq::Fragments, art::InEvent>(pretend_module_name, name);
    }
}

DemoInput_source.cc:

#include "art/Framework/IO/Sources/Source.h" 
#include "artdaq-demo/ArtModules/detail/RawEventQueueReader.hh" 
#include "art/Framework/Core/InputSourceMacros.h" 

#include <string>
using std::string;

/**
 * \brief The artdaq_demo namespace
 */
namespace demo
{
    /**
     * \brief DemoInput is an art::Source using the detail::RawEventQueueReader class
     */
    typedef art::Source<detail::RawEventQueueReader> DemoInput;
}

DEFINE_ART_INPUT_SOURCE(demo::DemoInput)