Chapter 31. Filter framework

31.1. Overview

The data stream in nscldaq is flexibly configured and consists of streaming data between nodes, which are most commonly ringbuffers. Data is pulled from the upstream node, the source, and passed on to the downstream node, the sink. Occasionally, it is useful to be able to insert a program to inspect or manipulate the data in between nodes. Probably the most common example of this is a data integrity checker program. Other scenarios that may find use for a filter program are when a program downstream, like SpecTcl, expects a certain data format and the data is not originally formatted that way or if for diagnostic purposes one needs to implement a program to count events in a channel for which there are no hardware scalers setup.

The nscldaq provides a framework for constructing a filter program that aims to greatly simplify the development of a filter program. A filter program essentially consists of three stages.

Stages of a filter program

Extraction

In this stage the ring items are read from the data source.

Filtering / Handling

The filtering or handling stage deals with how the ring items are to be manipulated. It is here that the differing functionality of filter programs is defined and consists of experiment-specific code.

Insertion

The final stage inserts the filtered ring items back into the data stream to send to the sink.

The first and the last stages of the filter program are completely generic to all filter programs because all must read data from the source before processing it and then subsequently send the data to the sink. Because these are generic, the nscldaq filter framework has already implemented those stages out of the box. The user is left the responsibility of implementing the second stage. Implementation of stage 2 consists of defining C++ classes that instantiate primitive filter objects to be registered to the framework. Though initially this may sound like some work, it is only as complicated as the filter the user desires to construct. For example, the trivial task of counting the number of physics event items in the data stream, can be accomplished with the following primitive filter:


#include <CFilter.h>

class CounterFilter : public CFilter
{
	int m_count;

	public:
	CounterFilter() : m_count(0) {}
  
	CounterFilter* clone() const { return new CounterFilter(*this); }
  
	CRingItem* handlePhysicsEventItem(CPhysicsEventItem* item) 
	{ ++m_count; }
};