57.4. ringFragmentSource - a prepackaged client for ringbuffer data sources

The ringFragmentSource program provides a mostly pre-packaged program that efficiently gets data from a ring buffer and efficiently submits it to an event builder. Since event data in a ring buffer may have arbitrary format, the user can supply software to extract the timestamp from each event. This software is supplied in the form of a shared object library.

A pre-packaged timestamp extraction library for the S800 is supplied and will be maintained in $DAQROOT/lib/libS800TimeExtractor.so

In addition to the standard ring client framework command options, the following switch is required in all circumstances.

--ring=ring-uri

Defines the ring from which data will be acquired. This can be local (tcp://localhost/ringname), or remote (tcp://somwhere.nscl.msu.edu/ringname)

The data that is submitted to the event builder by this client are the original ring items with fragment headers appended to them. The ringFragmentSource provides the user the ability to specify the values to insert into the fragment header by means of the command line switches. However, the ringFragmentSource honors the values in a ring item's BodyHeader if one is present. It will simply append a FragmentHeader to the item using the BodyHeader information. In this process and the command line options provided by the user will be ignored. For this reason, there is a command line switch called --expectbodyheaders that will allow the user to forego the creation of a timestamp extractor library. It is still possible for the user to specify one of these libraries however. When the user does this it allows the program to continue even in the presence of data without body headers. The semantics of the --timestampextractor and --expectbodyheaders switches are described below.

--timestampextactor=file-path

Specifies the path to the shared library you have built that extracts timestamps from ring events. This is mandatory if the --expectbodyheaders switch is not present. For the s800 pre-built extractor this should be $DAQHOST/lib/libS800TimeExtractor.so

--expectbodyheaders

This is a boolean flag that indicates whether the ringFragmentSource is to expect BodyHeaders on all physics events. If the user has provided the --timestampextractor and --ids options as well and physics event data is observed that has no body header, then a warning is logged to the log file. However, if the same occurrence happens when the --timestampextractor library has not been specified, the program will exit with an error. This occurs because there is no way for the program to assign a legitimate timestamp value to the fragment header. Note that the --ids option is mandatory so that it will always be defined.

57.4.1. Writing and building timestamp extractors.

This section describes how to write timestamp extractors. The source code for the s800 timestamp extractor is also given as an annotated example.

Timestamp extractors must provide an entry point named timestamp. This function takes a single argument; the pointer to the Physics Ring Item which must be analzed. The function must return a 64 bit timestamp as its result (uint64_t).

If the timestamp extractor is written in C++, you must declare the timestamp function as extern "C". You do not need to do this if timestamp is writte in C.

Let's look at the timestamp extractor for the S800. We are not going to bother with the s800.h header file. The actual values of each symbol defined in that file are unimportant. We'll just explain the meaning of the symbols it defines that we need.

Example 57-7. S800 timestamp extractor (s800timestamp.c


#include "s800.h"                       (1)
#include <DataFormat.h>           (2)
#include <assert.h>
#include <stdint.h>


uint64_t
timestamp(pPhysicsEventItem item)      (3)
{
  pS800event pFullEvent;
  pS800timestamp pTimestamp;
  ppacket    pNextPacket;
  uint16_t*  pWords;
  int64_t   nRemaining;
  int        nPktSize;


  pFullEvent = (pS800event)(item->s_body);  (4)
  assert(pFullEvent->s_type == S800_PACKET);
  uint16_t versionWord = pFullEvent->s_version;  (5)
  versionWord = (versionWord & S800_OUTERVSNMASK) >> S800_OUTERVSNSHIFT;
  assert(versionWord == S800_OUTERVERSION);


  nRemaining = pFullEvent->s_size * sizeof(uint16_t); (6)
  pNextPacket= &(pFullEvent->s_firstPacket);
  nRemaining -= sizeof(S800event) - sizeof(packet);



  while(nRemaining > 0) {
    if (pNextPacket->s_type == S800_TIMESTAMP_PACKET) {
      pTimestamp = (pS800timestamp)pNextPacket;        (7)
      return (pTimestamp->s_timestamp);
    }

    nPktSize = pNextPacket->s_size;                 (8)
    pWords   = (uint16_t*)pNextPacket;
    pNextPacket = (ppacket)(pWords + nPktSize);
    nRemaining -= nPktSize*sizeof(uint16_t);
  }

  assert(0);                                        (9)


}
                

Before annotating the example, a few words about the event format. The S800 ring item body leads off with a 32 bit event size. The entire S800 event is encapsyualted in an S800 packet consisting of a word count and a 16 bit constant: S800_PACKET. The s800 packet includes a 16bit version word. The most significant four bits of that word are the overall event format while the bottom 12 bits are the experiment format version.

Following that, a set of 'packets' appear. Each packet has a 16 bit word size a 16 bit type and a payload whose size, when added to the size of the packet header equals the packet size word. One of these packets of type, S800_TIMESTAMP_PACKET constains the timestamp.

The numbers in the list of comments below refer to the corresponding numers in the program listing.

(1)
The s800.h header defines the structure of the minimal set of packet definitions required to pull the timestamp out of an S800 event. It is based on information provided by the operations technical group.
(2)
The DataFormat.h header defines all of the ring item data structures. We will only be using physics event items in this code.
(3)
This code declares the function that will be called by the event fragment data source for each physics ring item. If this were written in C++ (or even compiled with the C++ compiler), the entire function would need to be declared extern "C":


extern "C" {
uint64_t
timestamp(pPhysicsEventItem item) 
{
... // function body
}   // end function body
}   // end extern "C" section.
                            

(4)
The function is given a pointer to the entire ring item. In the case of the S800, we only care about the event body. This line of code produces a pointer to the event body.

The typecast makes that pointer (normally a void*) a pointer to a struct that represents the outermost layer of an S800 event.

(5)
The outermost event structure includes a format version level. The top four bits of this version indicate the revision level of the outer packet format. This is checked. If different than what was expected, the assert() call will cause the entire program to exit.

This drastic action is taken becaus if we cannot ensure that the version is one we know about, we can't ensure that the correct timestamp can be extracted from the events. Even worse than failing later on would be to send events with bad timestamp values.

(6)
This section sets up to iterate through the set of packets in the event.
(7)
If the next packet is a timestamp packet, the timestamp is extracted from it and returned to the caller.
(8)
If the current packet is not the timestamp packet, the pointer to the next packet is computed and the search continues.
(9)
If, after all packets in the event are examined, no timestamp packet is seen, this assert exits the program. The assumption is that something is very seriously wrong either with the event format or with the timestamp extractor.