52.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 must 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 switches are are required.

--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)

--timestampextactor=file-path

Specifies the path to the shared library you have built that extracts timestamps from ring events. For the s800 pre-built extractor this should be $DAQHOST/lib/libS800TimeExtractor.so

52.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 52-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.