CFEL - ASG Software Suite  2.5.0
CASS
hdf5_file_input.cpp
Go to the documentation of this file.
1 // Copyright (C) 2015 Lutz Foucar
2 
3 /**
4  * @file hdf5_file_input.cpp contains class for reading hdf5 data files
5  *
6  * @author Lutz Foucar
7  */
8 
9 #include <iostream>
10 #include <iomanip>
11 #include <fstream>
12 #include <string>
13 #include <sstream>
14 #include <stdexcept>
15 
16 #include <QtCore/QFileInfo>
17 
18 #include "hdf5_file_input.h"
19 
20 #include "cass_event.h"
21 #include "cass_settings.h"
22 #include "log.h"
23 #include "hdf5_handle.hpp"
24 #include "machine_device.hpp"
25 #include "acqiris_device.hpp"
26 #include "pixeldetector.hpp"
27 
28 using namespace std;
29 using namespace cass;
30 
31 namespace cass
32 {
33 /** A machine vale
34  *
35  * allow the user to have a CASSName next to the hdf5 key as the key might be
36  * unreadable. Also allow the index in case the value is contained in it
37  *
38  * @author Lutz Foucar
39  */
40 struct machineVal
41 {
42  /** the hdf5 file key */
43  string h5key;
44 
45  /** the name of the key within the cass event */
46  string cassname;
47 
48  /** in case the machinval in question is contained within an array this is
49  * the inxed of the array where the machine value iswritten to
50  */
51  int idx;
52 };
53 }//end namespace cass
54 
55 void HDF5FileInput::instance(string filelistname,
56  RingBuffer<CASSEvent> &ringbuffer,
57  Ratemeter &ratemeter, Ratemeter &loadmeter,
58  bool quitWhenDone,
59  QObject *parent)
60 {
61  if(_instance)
62  throw logic_error("HDF5FileInput::instance(): The instance of the base class is already initialized");
63  _instance = shared_pointer(new HDF5FileInput(filelistname,ringbuffer,ratemeter,loadmeter,quitWhenDone,parent));
64 }
65 
66 HDF5FileInput::HDF5FileInput(string filelistname,
67  RingBuffer<CASSEvent> &ringbuffer,
68  Ratemeter &ratemeter, Ratemeter &loadmeter,
69  bool quitWhenDone,
70  QObject *parent)
71  : InputBase(ringbuffer,ratemeter,loadmeter,parent),
72  _quitWhenDone(quitWhenDone),
73  _filelistname(filelistname)
74 {
75  Log::add(Log::VERBOSEINFO, "HDF5FileInput::HDF5FileInput: constructed");
76 }
77 
79 {
80 }
81 
83 {
84  /** load the settings from the ini file */
85  CASSSettings s;
86  s.beginGroup("HDF5FileInput");
87 
88  /** the string for the event id of the current event */
89  string EventIDName = s.value("EventIDKey","EventID").toString().toStdString();
90 
91  /** list of machine variables to extract from the hdf5 files */
92  typedef vector<machineVal> machineVals_t;
93  machineVals_t machineVals;
94  int size = s.beginReadArray("MachineValues");
95  for (int i = 0; i < size; ++i)
96  {
97  s.setArrayIndex(i);
98  string machineValName(s.value("HDF5Key","Invalid").toString().toStdString());
99  string CASSName(s.value("CASSName","Invalid").toString().toStdString());
100  /** skip if the value name has not been set */
101  if (machineValName == "Invalid")
102  continue;
103  CASSName = CASSName == "Invalid" ? machineValName : CASSName;
104  machineVals.push_back(machineVals_t::value_type());
105  machineVals.back().h5key = machineValName;
106  machineVals.back().cassname = CASSName;
107  machineVals.back().idx = s.value("ArrayIndex",0).toUInt();
108  }
109  s.endArray();
110 
111  /** list of acqiris channels that should be extracted from the hdf5 files */
112  typedef vector<AcqirisParams> acqirisVals_t;
113  acqirisVals_t acqirisVals;
114  size = s.beginReadArray("AcqirisValues");
115  for (int i = 0; i < size; ++i)
116  {
117  s.setArrayIndex(i);
118  string datakey= s.value("HDF5DataKey","Invalid").toString().toStdString();
119  /** skip if the name has not been set */
120  if (datakey == "Invalid")
121  continue;
122  acqirisVals.push_back(AcqirisParams());
123  acqirisVals.back().Instrument = s.value("CASSInstrumentID",1).toUInt();
124  acqirisVals.back().ChannelNumber = s.value("CASSChannelNumber",0).toUInt();
125  acqirisVals.back().DataKey = datakey;
126  acqirisVals.back().HorposKey = s.value("HDF5HorposKey","Invalid").toString().toStdString();
127  acqirisVals.back().VertOffsetKey = s.value("HDF5VerticalOffsetKey","Invalid").toString().toStdString();
128  acqirisVals.back().GainKey = s.value("HDF5GainKey","Invalid").toString().toStdString();
129  acqirisVals.back().SampleIntervalKey = s.value("HDF5SampleIntervalKey","Invalid").toString().toStdString();
130  }
131  s.endArray();
132 
133  /** list of pixeldetectors that should be extracted from the hdf5 files */
134  typedef vector<PixeldetectorParams> pixdetVals_t;
135  pixdetVals_t pixdetVals;
136  size = s.beginReadArray("PixelDetectorValues");
137  for (int i = 0; i < size; ++i)
138  {
139  s.setArrayIndex(i);
140  string datakey= s.value("HDF5DataKey","Invalid").toString().toStdString();
141  /** skip if the name has not been set */
142  if (datakey == "Invalid")
143  continue;
144  pixdetVals.push_back(PixeldetectorParams());
145  pixdetVals.back().DataKey = datakey;
146  pixdetVals.back().CASSID = s.value("CASSID",0).toInt();
147  }
148  s.endArray();
149 
150  s.endGroup();
151 
153  Tokenizer tokenize;
154 
155  /** retrieve all files in a list from the file */
156  Log::add(Log::VERBOSEINFO,"HDF5FileInput::run(): try to open filelist '" +
157  _filelistname + "'");
158  ifstream filelistfile(_filelistname.c_str());
159  if (!filelistfile.is_open())
160  throw invalid_argument("HDF5FileInput::run(): filelist '" + _filelistname +
161  "' could not be opened");
162  vector<string> filelist(tokenize(filelistfile));
163  filelistfile.close();
164 
165  /** go through the list of files and create a processor for each file and
166  * add them to the list of processors
167  */
168  vector<string>::const_iterator filelistIt(filelist.begin());
169  vector<string>::const_iterator filelistEnd(filelist.end());
170  uint64_t eventcounter(0);
171  for (;(!shouldQuit()) && (filelistIt != filelistEnd); ++filelistIt)
172  {
173  /** check if file exists, then load it */
174  string filename(*filelistIt);
176  if (info.exists())
177  {
178  /** open the hdf5 file */
179  hdf5::Handler h5handle(filename,"r");
180 
181  /** get the list of groups of the root group ('/') as they should reflect
182  * the events
183  */
184  list<string> events(h5handle.rootGroups());
185 
186  /** iterate through the list and extract the requested informations */
187  list<string>::const_iterator it(events.begin());
188  list<string>::const_iterator EventsEnd(events.end());
189  for (; (!shouldQuit()) && (it != EventsEnd); ++it)
190  {
191 
192  /** retrieve a new element from the ringbuffer. If one didn't get a
193  * an element (when the end iterator of the buffer is returned).
194  * Continue to the next iteration, where it is checked if the thread
195  * should quit.
196  */
198  if (rbItem == _ringbuffer.end())
199  continue;
200  CASSEvent& evt(*rbItem->element);
201 
202  /** fill the cassevent object with the contents from the file */
203  bool isGood(true);
204 
205  /** set the event id from the retrieved id field */
206  if (h5handle.dimension(*it + "/" + EventIDName) != 0)
207  throw invalid_argument("HDF5FileInput:run(): EventID from '"+
208  *it + "/" + EventIDName + "' is not a scalar number");
209  evt.id() = h5handle.readScalar<int>(*it + "/" + EventIDName);
210 
211  /** check if the eventid is valid (non zero number) */
212  if (evt.id() == 0)
213  isGood = false;
214 
215 
216  /** get reference to all devices of the CASSEvent and an iterator*/
217  CASSEvent::devices_t &devices(evt.devices());
218  CASSEvent::devices_t::iterator devIt;
219 
220  /** try to retrieve the data */
221  try
222  {
223  /** #### machine data #### */
224 
225  /** check if the event contains the machine data container, if so get a
226  * reference to it. Otherwise throw an error.
227  */
228  devIt = devices.find(CASSEvent::MachineData);
229  if (devIt == devices.end())
230  throw runtime_error("HDF5FileInput():The CASSEvent does not contain a Machine Data Device");
231  MachineData::Device &md (dynamic_cast<MachineData::Device&>(*(devIt->second)));
232 
233  /** go through all requested machine data events and retrieve the corresponding
234  * values for the tag */
235  machineVals_t::const_iterator machineValsIter(machineVals.begin());
236  machineVals_t::const_iterator machineValsEnd(machineVals.end());
237  for (; machineValsIter != machineValsEnd; ++machineValsIter)
238  {
239  const string key(*it + "/" + machineValsIter->h5key);
240  /** check if dimension is correct */
241  if (h5handle.dimension(key) == 0)
242  {
243  float machineValue(h5handle.readScalar<float>(key));
244  md.BeamlineData()[machineValsIter->cassname] = machineValue;
245  }
246  else if (h5handle.dimension(key) == 1)
247  {
248  vector<double> array;
249  size_t length(0);
250  h5handle.readArray(array, length, key);
251  md.BeamlineData()[machineValsIter->cassname] = array[machineValsIter->idx];
252  }
253  else
254  {
255  throw runtime_error("HDF5FileInput(): file '"+ filename + "': key '" +
256  key + "' is neither a scalar nor an array value");
257  }
258  }
259 
260 
261  /** acqiris data */
262 
263  /** retrieve a pointer to the right acqiris instrument */
264  devIt = devices.find(CASSEvent::Acqiris);
265  if (devIt == devices.end())
266  throw runtime_error("HDF5FileInput():The CASSEvent does not contain a Acqiris Device");
267  ACQIRIS::Device &acq (dynamic_cast<ACQIRIS::Device&>(*(devIt->second)));
268  /** go through all requested acqiris data */
269  acqirisVals_t::const_iterator acqirisValsIter(acqirisVals.begin());
270  acqirisVals_t::const_iterator acqirisValsEnd(acqirisVals.end());
271  for (; acqirisValsIter != acqirisValsEnd; ++acqirisValsIter)
272  {
273  /** retrieve a reference to the right instrument */
274  ACQIRIS::Instrument &instr(acq.instruments()[acqirisValsIter->Instrument]);
275  /** retrieve a reference to the channel container of the instrument */
276  ACQIRIS::Instrument::channels_t &channels(instr.channels());
277  /** resize the channel vector to how many channels are in the device */
278  channels.resize(acqirisValsIter->ChannelNumber + 1);
279  /** retrieve a reference to the channel we are working on */
280  ACQIRIS::Channel &chan(channels[acqirisValsIter->ChannelNumber]);
281  /** retrieve a reference to waveform container */
282  ACQIRIS::Channel::waveform_t &waveform = chan.waveform();
283  /** extract the meta infos from the hdf5 file */
284  chan.channelNbr() = acqirisValsIter->ChannelNumber;
285  chan.horpos() = (acqirisValsIter->HorposKey == "Invalid") ? 0 : h5handle.readScalar<double>(*it + "/" + acqirisValsIter->HorposKey);
286  chan.offset() = (acqirisValsIter->VertOffsetKey == "Invalid") ? 0 : h5handle.readScalar<double>(*it + "/" + acqirisValsIter->VertOffsetKey);
287  chan.gain() = (acqirisValsIter->GainKey == "Invalid") ? 1 : h5handle.readScalar<double>(*it + "/" + acqirisValsIter->GainKey);
288  chan.sampleInterval() = (acqirisValsIter->SampleIntervalKey == "Invalid") ? 1e-9 : h5handle.readScalar<double>(*it + "/" + acqirisValsIter->SampleIntervalKey);
289  /** extract the wavefrom and copy it to the CASSEvent container */
290  const string dkey(*it + "/" + acqirisValsIter->DataKey);
291  vector<float> array;
292  if (h5handle.dimension(dkey) == 1)
293  {
294  size_t length(0);
295  h5handle.readArray(array, length, dkey);
296  waveform.resize(length);
297  }
298  else if (h5handle.dimension(dkey) == 2)
299  {
300  pair<size_t,size_t> shape;
301  h5handle.readMatrix(array, shape, dkey);
302  if (shape.second != 1)
303  throw invalid_argument("HDF5FileInput: file '" + filename +
304  "': acqiris data key '" + dkey +
305  "' is a matrix of '" + toString(shape.first) +
306  "x" + toString(shape.second) + "'");
307  waveform.resize(shape.first*shape.second);
308  }
309  else
310  {
311  throw invalid_argument("HDF5FileInput: file '" + filename +
312  "': acqiris data key '" + dkey +
313  "' is not an array. It has dimension '" +
314  toString(h5handle.dimension(dkey)) + "'");
315  }
316  copy(array.begin(),array.end(),waveform.begin());
317  instr.id() = evt.id();
318  }
319 
320 
321  /** image data */
322 
323  /** retrieve the pixel detector part of the cassevent */
324  devIt = devices.find(CASSEvent::PixelDetectors);
325  if(devIt == devices.end())
326  throw runtime_error("HDF5FileInput: CASSEvent does not contains a pixeldetector device");
327  pixeldetector::Device &pixdev (dynamic_cast<pixeldetector::Device&>(*(devIt->second)));
328  /** go through all requested detectors and retrieve the data */
329  pixdetVals_t::const_iterator pixdetValsIter(pixdetVals.begin());
330  pixdetVals_t::const_iterator pixdetValsEnd(pixdetVals.end());
331  for(; pixdetValsIter != pixdetValsEnd; ++pixdetValsIter)
332  {
333  /** retrieve the right detector from the cassevent and reset it*/
334  pixeldetector::Detector &det(pixdev.dets()[pixdetValsIter->CASSID]);
335  det.frame().clear();
336  /** get the data from the file */
337  string dkey(*it + "/" + pixdetValsIter->DataKey);
338  if (h5handle.dimension(dkey) != 2)
339  throw invalid_argument("HDF5FileInput: file '" + filename +
340  "': pixeldetector data key '" + dkey +
341  "' is not a matrix. It has dimension '" +
342  toString(h5handle.dimension(dkey)) + "'");
343  pair<size_t,size_t> shape;
344  h5handle.readMatrix(det.frame(), shape, dkey);
345  /** set the metadata of the frame */
346  det.columns() = shape.first;
347  det.rows() = shape.second;
348  det.id() = evt.id();
349  }
350 
351  }
352  catch(const hdf5::DatasetError &error)
353  {
354  Log::add(Log::ERROR,"HDF5FileInput: file '" + filename +
355  "' is missing data. Error is '" + error.what() + "'");
356  isGood = false;
357  }
358 
359  /** add the event back to the ringbuffer and let the world know if its
360  * a good event. Also do some output about the rate
361  */
362  if (!isGood)
363  Log::add(Log::WARNING,"HDF5FileInput:: Event with id '"+
364  toString(rbItem->element->id()) + "' is bad: skipping Event");
365  else
366  ++eventcounter;
367  rbItem->element->setFilename(filename.c_str());
368  newEventAdded(rbItem->element->datagrambuffer().size());
369  _ringbuffer.doneFilling(rbItem, isGood);
370  }
371  }
372  else
373  Log::add(Log::ERROR,"FileInput::run(): could not open '" + filename + "'");
374  }
375 
376  /** quit the program if requested otherwise wait until the program is signalled
377  * to quit
378  */
379  Log::add(Log::INFO,"HDF5FileInput::run(): Finished with all files.");
380  if(!_quitWhenDone)
381  while(!shouldQuit())
382  this->sleep(1);
383  Log::add(Log::VERBOSEINFO, "HDF5FileInput::run(): closing the input");
384  Log::add(Log::INFO,"HDF5FileInput::run(): Extracted '" + toString(eventcounter) +
385  "' events.");
386 }
std::vector< int16_t > waveform_t
define the waveform
Definition: channel.hpp:35
bool _quitWhenDone
flag to tell the thread to quit when its done with all files
setArrayIndex(int i)
Event to store all LCLS Data.
Definition: cass_event.h:32
void readArray(std::vector< type > &array, size_t &arrayLength, const std::string &valname)
read a array with a given name into a linearized array
string h5key
the hdf5 file key
waveform_t & waveform()
setter
Definition: channel.hpp:105
RingBuffer< CASSEvent >::iter_type rbItem_t
define an item in the ringbuffer
Definition: input_base.h:109
class calculating a rate in Hz.
Definition: ratemeter.h:28
sleep(unsigned long secs)
An Acqiris Instrument.
status_t _status
the internal status of the thread
virtual uint64_t eventcounter()
retrieve the number of events that have been input so far
Definition: input_base.cpp:62
file contains declaration of the CASSEvent
Settings for CASS.
Definition: cass_settings.h:30
A handler for h5 files.
detectors_t & dets()
instrument setter
type readScalar(const std::string &valname)
read an scalar value with a given name as part of a given group
Input base class.
Definition: input_base.h:31
A machine vale.
STL namespace.
double & sampleInterval()
setter
Definition: channel.hpp:103
things written only at end of run H5Dump ProcessorSummary size
contains a hdf5 file reader class
void readMatrix(std::vector< type > &matrix, std::pair< size_t, size_t > &shape, const std::string &valname)
read a matrix with a given name into a linearized array
additional info
The Acqiris device.
static void add(Level level, const std::string &line)
add a string to the log
Definition: log.cpp:31
fromStdString(const std::string &str)
Exception thrown when there is an error with the dataset.
Definition: hdf5_handle.hpp:28
beginReadArray(const QString &prefix)
Container for all Machine related Data.
A Ringbuffer, handles communication between Input and Worker Threads.
Definition: ringbuffer.hpp:52
parameters needed to retrieve the pixeldetector data
devices_t & devices()
setters
Definition: cass_event.h:66
double & horpos()
setter
Definition: channel.hpp:101
size_t dimension(const std::string &valname) const
get the dimension of a value with a given name
bool shouldQuit() const
query whether this thread is told to quit
file contains the declaration of the acqiris part of the CASSEvent
tokenize to return all lines of an ascii file in a vector
Definition: cass.h:111
void load()
load the parameters used for the multifile input
id_t & id()
setters
Definition: cass_event.h:64
std::string toString(const Type &t)
convert any type to a string
Definition: cass.h:63
double & offset()
setter
Definition: channel.hpp:102
value(const QString &key, const QVariant &defaultValue=QVariant()
RingBuffer< CASSEvent > & _ringbuffer
reference to the ringbuffer
Definition: input_base.h:140
std::map< Device, DeviceBackend::shared_pointer > devices_t
mapping from device type to handler instance
Definition: cass_event.h:46
A Channel of an Acqiris Instrument.
Definition: channel.hpp:31
dsetList_t rootGroups() const
get the list of groups of the root group in the file
std::string _filelistname
name of the file containing all files that we need to process
string cassname
the name of the key within the cass event
contains container for simple pixel detector data
definitions of a machine device
std::vector< Channel > channels_t
a vector of Channels
std::tr1::shared_ptr< InputBase > shared_pointer
shared pointer of this type
Definition: input_base.h:35
file contains specialized class that do the settings for cass
the device containing pixel detector data
double & gain()
setter
Definition: channel.hpp:104
HDF5 File Input for cass.
Detector containing a ccd camera image.
rbItem_t getNextFillable(unsigned timeout=500)
retrieve an iterator to the next fillable event
Definition: input_base.cpp:48
size_t & channelNbr()
setter
Definition: channel.hpp:106
contains a logger for cass
parameters needed to retrieve the Acqiris data
void newEventAdded(const size_t eventsize)
increment the numer of events received in the ratemeter
Definition: input_base.cpp:37
easier api for hdf5 file writing
void runthis()
function with the main loop
const bldMap_t & BeamlineData() const
getter
beginGroup(const QString &prefix)
instruments_t & instruments()
instrument setter
int idx
in case the machinval in question is contained within an array this is the inxed of the array where t...