Development notes

Structure of the Online Monitoring package

The online monitoring package provides two pieces of functionality. The code to connect to the live data stream and make histograms and plots summarizing the data quality and a viewer for those histograms which enables the shifter to view the histograms, make comparisons to previous results, and be alerted when the histograms have somehow gone "out of spec". The organization of the OnlineMonitoring package reflects this and is divided into three sub-directories:

  1. producer
  2. viewer
  3. util

The "producer" directory contains the code that implements the ART analysis module which connects to the data stream, fills the histograms, and publishes them. The "viewer" sub-directory contains all the code specific for the viewer application. "util" contains code which must be shared by the producer and the viewer. This includes code for histogram management, file management, channel mappings, etc.

Structure of the producer

Producer classes

The structure of the producer is defined by the following classes:

  • OnMonProd - This is the top level ART analyzer module that receives and unpacks the event data from the FlatDAQData object
  • RawEventUnpacker - This class "unpacks" the FlatDAQData spooling through the DataBlocks, MicroSlices, and NanoSlices. As it spools through the data it prepares summaries of the data for histogram makers
  • RawEventSummary - An ntuple-style summary of the event overall
  • DataBlockSummary - An ntuple-style summary of an entire data block
  • MicroSliceSummary - An ntuple-style summary of an entire micro slice
  • NanoSliceSummary - An ntuple-style summary of a nano-slice

The goal of this organization is to allow for a single set of loops over the data to feed multiple histogram makers rather than forcing histogram makers to each re-implement this somewhat tricky set of nested loops. This should not only make the code cleaner, but more efficient.

Connecting histogram makers

Histogram makers need access to the Summary objects which contain the data they will want to plot. To get this access they sub-class the SummarySubscriber base class and set the bits required to get updates for the summaries they want. For example, a class HitCounts wanting to make and fill histograms of hit counts would be defined this way:

In HitCounts.h:

#include "OnlineMonitoring/producer/SummarySubscriber.h" 
namespace om {
  class HitCounts : public SummarySubscriber {
    void Get(const RawEventSummary& res);

In HitCounts.cxx:

#include "OnlineMonitoring/producer/HitCounts.h" 

// Guarantee one instance of this histogram-making class
static HitCounts hitCounts;

HitCounts::HitCounts() : SummarySubscriber(SummarySubscriber::kEvent)
{ }

The subscription is set in the constructor and can be OR'ed combinations of kEvent, kDataBlock, kMicroSlice, and kNanoSlice. For each subscription the subscriber must implement the appropriate Get@s:

Notice that the summaries from the higher-level event objects are made available to the lower-level event object; that is the @NanoSliceSummary
has access to the MicroSliceSummary, the DataBlockSummary, and the RawEventSummary. For example, the event header (trigger type, run, event numbers, date/time) information is contained in the RawEventSummary. Note, however, that some of the event-level information requires the entire event to be processed before its data is complete (for example, hit counts). The headers for the Sumamry objects mark which data can be relied upon by the lower level histogram makers and which will only be complete at the end of processing. For example, quoting from RawEventSummary.h

  // All subscribers can expect this data to be filled correctly
  unsigned int      fRun;        ///< Run number
  unsigned int      fSubrun;     ///< Subrun number
  unsigned int      fEvent;      ///< Event nunber
  unsigned int      fNdatablock; ///< Number of data blocks
  unsigned long int fT0;         ///< Trigger time in nova time units (64 Mhz ticks)
  float             fHour;       ///< A fractional hour of the day
  // Only event summary subscribers can expect this data to be correct
  unsigned int           fNhit;
  std::set<unsigned int> fDiblocks; ///< The list of diblocks in event
  std::set<unsigned int> fDCMs;     ///< The list of DCMs contributing to the event
  std::set<unsigned int> fFEBs;     ///< The list of FEBs contributing to the event

Adding new histograms

To add a new histogram to the producer one first writes a subscriber (see the Connecting histogram makers section above). Assuming that you have the subscriber you want follow these steps to add a new histogram:

  1. Add the description of your histogram to the util/onmon-histos.csv file. This file provides all the descriptions of the histograms required by the producer and viewer programs.
  2. Within your subscriber class, ask for an instance of your histogram from the HistoSet class. For example, suppose you've chosen to name your new 1-D histogram "NfebsVsHour", then in your subscriber's constructor you would do:
    #include "OnlineMonitoring/producer/HistoSet.h" 
    MySubscriber::MySubScriber() : 
      HistoSet& h = HistoSet::Instance();
      fNfebsVsHour = h.GetTH1F("NhitVsHour");

The histogram will be created by HistoSet according to the parameters in the .csv file. HistoSet will manage the writing, mapping, reseting, and deleting of the histogram; your copy is just a reference to a histogram owned and managed by HistoSet. In the subscriber's "Get" method you would fill the fNfebVsHour histogram just as you would any other histogram.

Histogram management

For those who are curious about what is going on under the hood in the above section on Adding new histograms this section provides a few more details.

At the start of the program the histogram data contained in the onmon-histos.csv file is read into util/HistoData objects which are held in a util/HistoTable. When you ask the producer/HistoSet object for an instance of a histogram it checks if the histogram already exists in memory. If it does not, it asks the HistoTable class for the histogram data and creates the histogram based on the specifications. It enters this new histogram into its collection of managed histograms and returns the result to the user.

To test that the histogram data table is being parsed correctly, one can use the util/ program. This will parse a standard dummy .csv file and check that the results are correct.

Hardware address mapping

I would like the presentation of the pixel data from the detector to match as much as possible to what one sees when looking at the physical devices represented in the plots. Thus I would like the diblocks to be presented in order from right to left (1 on the right, 15 on the left) as that is how the detector is seen from the cat walks. Within the diblocks the DCMs would be presented in order from the top down, 1-6 representing the DCMs on the top of the detector, 7-12 representing the DCMs on the side of the detector. Within the DCMs the FEBs should be laid out as are the ethernet jacks on the DCM. Within the FEB, the pixels should be ordered as the APDs are viewed. This is summarized in the following figure:

The relevant sections of code for managing this mapping are in the util directory: HardwareMapping.h, HardwareMapping.cxx,, and testHardwareMapping.C. To test the hardware mapping execute:

% cd util
% gmake tbin
% testHardwareMapping > file.txt
% root -l testHardwareMapping.C

Look for errors reported from the executable and irregularities in the resulting plots.

List of attached figures and files