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-uriDefines 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-pathSpecifies 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.
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" #include <DataFormat.h> #include <assert.h> #include <stdint.h> uint64_t timestamp(pPhysicsEventItem item) { pS800event pFullEvent; pS800timestamp pTimestamp; ppacket pNextPacket; uint16_t* pWords; int64_t nRemaining; int nPktSize; pFullEvent = (pS800event)(item->s_body); assert(pFullEvent->s_type == S800_PACKET); uint16_t versionWord = pFullEvent->s_version; versionWord = (versionWord & S800_OUTERVSNMASK) >> S800_OUTERVSNSHIFT; assert(versionWord == S800_OUTERVERSION); nRemaining = pFullEvent->s_size * sizeof(uint16_t); pNextPacket= &(pFullEvent->s_firstPacket); nRemaining -= sizeof(S800event) - sizeof(packet); while(nRemaining > 0) { if (pNextPacket->s_type == S800_TIMESTAMP_PACKET) { pTimestamp = (pS800timestamp)pNextPacket; return (pTimestamp->s_timestamp); } nPktSize = pNextPacket->s_size; pWords = (uint16_t*)pNextPacket; pNextPacket = (ppacket)(pWords + nPktSize); nRemaining -= nPktSize*sizeof(uint16_t); } assert(0); }
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.
The typecast makes that pointer (normally a void*) a pointer to a struct that represents the outermost layer of an S800 event.
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.