source: trunk/Mars/mfileio/MWriteFitsFile.cc@ 17381

Last change on this file since 17381 was 17148, checked in by ftemme, 11 years ago
Merging changes from the MC branch in the trunk
File size: 45.4 KB
Line 
1#include <TDataMember.h>
2#include <TClonesArray.h>
3
4#include "MArrayS.h"
5#include "MArrayB.h"
6
7#include "MRead.h"
8#include "MParList.h"
9#include "MStatusDisplay.h"
10
11#include "MLog.h"
12#include "MLogManip.h"
13#include "MWriteRootFile.h"
14
15#include "MWriteFitsFile.h"
16#include "MFitsArray.h"
17
18#include "MTime.h"
19
20//for find
21#include <algorithm>
22
23ClassImp(MWriteFitsFile);
24
25using namespace std;
26
27map<MUniqueFileId, MTopFitsGroup> MWriteFitsFile::fTopFitsGroups;
28
29
30
31// ----------------------------------------------------------------------------
32//
33// if fileMode == kSingleFile
34// Opens the top level group file, if it was not already opened by an other
35// instance of MWriteFitsFile.
36// if fileMode == kMultiFiles
37// fname defines the rules to build the output filenames from the current
38// input filename. The files are opened in the ReInit() method.
39//
40// opt = RECREATE : an existing file will be deleted
41// = NEW : an already existing file will not be deleted and the new
42// file is not created. Check with IsFileOpen() if the new
43// top level group could be opened.
44// name will be the name of the group
45// title will be written as keyword TITLE into the header
46//
47MWriteFitsFile::MWriteFitsFile(const char *fname,
48 FILE_MODE fileMode,
49 const Option_t *opt,
50 const char *name,
51 const char *title)
52{
53 fOpenOption = opt;
54 if (name && name[0] != 0)
55 fGroupName = name;
56 else
57 fGroupName = "MARS";
58
59 if (title)
60 fTitle = title;
61
62 iTopFitsGroup = fTopFitsGroups.end();
63
64 if (fileMode == kMultiFiles)
65 fRule = fname;
66 else
67 *fLog << err << "The new version of MWriteFitsFile does not support multiple tables in one file. Please use kMultiFiles instead" << endl;
68 //ETIENNE DEPREC
69 // iTopFitsGroup will be set by OpenTopLevelGroup, if a top level
70 // group can be assigned to this MWriteFitsFile.
71 //OpenTopLevelGroup(fname);
72}
73
74MWriteFitsFile::MWriteFitsFile(const Int_t comp,
75 const char* rule,
76 const Option_t* option,
77 const char* ftitle,
78 const char* name,
79 const char* title)
80{
81 fOpenOption = option;
82 if (name && name[0] != 0)
83 fGroupName = name;
84 else
85 fGroupName = "MARS";
86 if (title) fTitle = ftitle;
87 iTopFitsGroup = fTopFitsGroups.end();
88 //always kMultiFiles
89 fRule = rule;
90}
91// ----------------------------------------------------------------------------
92//
93// Closes the top level group, it is is not used by an other instance of
94// MWriteFitsFile.
95// std::map<TString, std::ofits*>
96MWriteFitsFile::~MWriteFitsFile()
97{
98 for (std::map<TString, ofits*>::iterator it=fFitsTables.begin(); it!= fFitsTables.end();it++)
99 {
100 if (it->second != NULL)
101 {
102 it->second->close();
103 delete it->second;
104 }
105 }
106// CloseTopLevelGroup();
107 DeleteArrayHelper();
108}
109
110// ----------------------------------------------------------------------------
111//
112// If the same file is already used by an other instance of MWriteFitsFile,
113// the user counter is increasesd.
114// Else: If the file exist and option is "RECREATE", the file is deleted and
115// recreated.
116// Finally the file with an empty group file is created.
117//
118void MWriteFitsFile::OpenTopLevelGroup(const char * fname)
119{
120 // get rid of environment variables, ~, ... in filename
121 char * expandedFileName = gSystem->ExpandPathName(fname);
122
123 // get unique identifier of the new file
124 struct stat fileId;
125 if (stat(expandedFileName, &fileId) == 0)
126 {
127 // file exist already
128 iTopFitsGroup = fTopFitsGroups.find(MUniqueFileId(fileId.st_dev, fileId.st_ino));
129 if (iTopFitsGroup != fTopFitsGroups.end())
130 {
131 *fLog << err << "You are trying to use an already existing file. Please choose a different file name (" << fname << ")" << endl;
132 exit(0);
133 // this group is already open. we use it as well
134 *fLog << inf2 << "Found open file '" << GetFileName() << "'... re-using." << endl;
135 iTopFitsGroup->second.IncreaseUsage();
136 delete [] expandedFileName;
137 return;
138 }
139
140 // file exist, but we cannot use
141 if (fOpenOption == "RECREATE")
142 // remove the file
143 unlink(expandedFileName);
144
145 else if (fOpenOption == "NEW")
146 {
147 *fLog << err << "File '" << expandedFileName << "' exist already." << endl;
148 delete [] expandedFileName;
149 return;
150 }
151 else
152 {
153 *fLog << err << "Unsupported option (" << fOpenOption.Data() << ") to open file " <<
154 expandedFileName << endl;
155 delete [] expandedFileName;
156 return;
157 }
158 }
159
160 // file does not yet exist or was deleted
161
162// try {
163// AstroRootGroup topGroup;
164// char wrTitle[fTitle.Length() + 1];
165// if (fTitle.Length() > 0)
166// {
167// strcpy(wrTitle, fTitle.Data());
168// topGroup.InitAttr("TITLE", "A", wrTitle);
169// }
170
171 //ETIENNE do not open top level group as using one file per table
172 //I kept this code active anyway, because it is checked elsewhere in the code that iTopFitsGroup is set.
173 ofits* topGroup = NULL;
174// ofits* topGroup = new ofits();
175// TString dol = expandedFileName;
176// topGroup->open(dol.Data());
177
178
179 // store this new file in fTopFitsGroups and get the iterator to the
180 // new group
181 stat(expandedFileName, &fileId);
182 iTopFitsGroup = fTopFitsGroups.insert(pair<MUniqueFileId, MTopFitsGroup>
183 (MUniqueFileId(fileId.st_dev, fileId.st_ino),
184 MTopFitsGroup(expandedFileName, topGroup) )).first;
185
186 delete [] expandedFileName;
187
188}
189
190// --------------------------------------------------------------------------
191//
192// Reduces the use - counter of the top level group. Closes the group
193// table, if the counter is 0, i.e. no other MWriteFitsFile instance uses
194// the same top level group.
195//
196
197void MWriteFitsFile::CloseTopLevelGroup()
198{
199 if (iTopFitsGroup != fTopFitsGroups.end())
200 {
201 iTopFitsGroup->second.DecreaseUsage();
202 if (iTopFitsGroup->second.GetNumUsage() <= 0)
203 fTopFitsGroups.erase(iTopFitsGroup);
204
205 iTopFitsGroup = fTopFitsGroups.end();
206 }
207}
208
209// --------------------------------------------------------------------------
210//
211// returns the name of the top level group file. It may be expanded if the
212// name, given by the user, contained environment variables of the ~ sign.
213// If no top level group is open, "no_open_file" is returned.
214//
215const char * MWriteFitsFile::GetFileName() const
216{
217 if (IsFileOpen())
218 return iTopFitsGroup->second.GetFileName().Data();
219 else
220 return "no_open_file";
221}
222
223// --------------------------------------------------------------------------
224//
225// Add a new Container to list of containers which should be written to the
226// file. Give the name of the container which will identify the container
227// in the parameterlist. tname is the name of the FITS table to which the
228// container should be written (Remark: one table can hold more than one
229// container). The default is the same name as the container name.
230// If "mus"t is set to kTRUE (the default), then the container must exist
231// in the parameterlist. The container will be ignored, if "must" is set to
232// kFALSE and the container does not exist in the parameterlist.
233//
234void MWriteFitsFile::AddContainer(const char *cName,
235 const char *tName,
236 Bool_t must)
237{
238 if (cName == NULL || strlen(cName) == 0)
239 {
240 *fLog << warn << "Warning - container name in method MWriteFitsFile::"
241 "AddContainer not defined... ignored" << endl;
242 return;
243 }
244
245 // if tName is not defined, we use cName as the table name
246 TString tableName;
247 if (tName == NULL || strlen(tName) == 0)
248 tableName = cName;
249 else
250 tableName = tName;
251
252 // try to insert this container to the list of sub-tables. Write a
253 // warning, if the same container name was already defined and ignore
254 // this one.
255 if (fSubTables[tableName].insert( pair<TString, MFitsSubTable>
256 (cName, MFitsSubTable(must))).second == false)
257 {
258 *fLog << warn << "Warning - Container '"<< cName <<"' in table '" <<
259 tableName.Data() << "' already scheduled... ignored." << endl;
260 }
261}
262
263// --------------------------------------------------------------------------
264//
265// Add a new Container to list of containers which should be written to the
266// file. tname is the name of the FITS table to which the
267// container should be written (Remark: one table can hold more than one
268// container). The default is the same name as the container name.
269// "must" is ignored. It is just for compatibility with MWriteRootFiles.
270//
271void MWriteFitsFile::AddContainer(MParContainer *cont, const char *tName,
272 Bool_t must)
273{
274 if (cont == NULL)
275 {
276 *fLog << warn << "Warning - container in method MWriteFitsFile::"
277 "AddContainer not defined... ignored" << endl;
278 return;
279 }
280
281 TString tableName;
282 if (tName == NULL || strlen(tName) == 0)
283 tableName = cont->IsA()->GetName();
284 else
285 tableName = tName;
286
287 TString cName = cont->IsA()->GetName();
288 if (fSubTables[tableName].insert( pair<TString, MFitsSubTable>
289 (cName, MFitsSubTable(cont, must))).second == false)
290 {
291 *fLog << warn << "Warning - Container '"<< cName.Data() <<
292 "' in table '" << tableName.Data() <<
293 "' already scheduled... ignored." << endl;
294 }
295}
296
297// --------------------------------------------------------------------------
298//
299// Tries to open the given output file.
300//
301Int_t MWriteFitsFile::PreProcess(MParList *pList)
302{
303 if (fRule.Length() != 0)
304 // the file is not yet open
305 return kTRUE;
306 //
307 // test whether file is now open or not
308 //
309 if (!IsFileOpen())
310 {
311 *fLog << err << dbginf << "Cannot open file '" << GetFileName() << "'" << endl;
312 return kFALSE;
313 }
314
315 *fLog << inf << "File '" << GetFileName() << "' open for writing." << endl;
316
317 //
318 // Get the containers (pointers) from the parameter list you want to write
319 //
320 if (!GetContainer(pList))
321 return kFALSE;
322
323 //
324 // write the container if it is already in changed state
325 //
326 return CheckAndWrite();
327
328}
329
330Int_t MWriteFitsFile::PostProcess()
331{
332 for (std::map<TString, ofits*>::iterator it=fFitsTables.begin(); it!= fFitsTables.end();it++)
333 {
334 it->second->close();
335 delete it->second;
336 it->second = NULL;
337 }
338
339 return kTRUE;
340}
341
342void MWriteFitsFile::SetupHeaderKeys(MRawRunHeader &header)
343{
344 const MTime now(-1);
345 SetHeaderKey("ISMC",true,"Bool if File is Montecarlo File");
346 SetHeaderKey("TELESCOP", "FACT", "");
347 SetHeaderKey("PACKAGE", "MARS Cheobs", "");
348 SetHeaderKey("VERSION", "1.0", "");
349 SetHeaderKey("CREATOR", "Ceres", "");
350 SetHeaderKey("EXTREL", 1., "");
351 SetHeaderKey("COMPILED", __DATE__" "__TIME__, "");
352 //SetHeaderKey("REVISION", "0", "");
353 SetHeaderKey("ORIGIN", "FACT", "");
354 SetHeaderKey("DATE", now.GetStringFmt("%Y-%m-%dT%H:%M:%S").Data(), "");
355 SetHeaderKey("NIGHT", now.GetNightAsInt(), "");
356 SetHeaderKey("TIMESYS", "UTC", "");
357 SetHeaderKey("TIMEUNIT", "d", "");
358 SetHeaderKey("MJDREF", 40587, "");
359 //SetHeaderKey("BLDVER", 1, "");
360 SetHeaderKey("RUNID", header.GetRunNumber(), "");
361 SetHeaderKey("NBOARD", 40, "");
362 SetHeaderKey("NPIX", header.GetNumPixel(), "");
363 SetHeaderKey("NROI", header.GetNumSamplesHiGain(), "");
364 SetHeaderKey("NROITM", 0, "");
365 SetHeaderKey("TMSHIFT", 0, "");
366 SetHeaderKey("CAMERA", "MGeomCamFACT", "Montecarlo File");
367 SetHeaderKey("DAQ", "DRS4", "Montecarlo File");
368
369 // FTemme: ADCRANGE and ADC have to be calculated, using the values for
370 // the fadctype.
371// SetHeaderKey("ADCRANGE", 2000, "Dynamic range in mV");
372// SetHeaderKey("ADC", 12, "Resolution in bits");
373
374 switch(header.GetRunType())
375 {
376 case MRawRunHeader::kRTData|MRawRunHeader::kRTMonteCarlo:
377 SetHeaderKey("RUNTYPE", "data", "");
378 break;
379 case MRawRunHeader::kRTPedestal|MRawRunHeader::kRTMonteCarlo:
380 SetHeaderKey("RUNTYPE", "pedestal", "");
381 break;
382 case MRawRunHeader::kRTCalibration|MRawRunHeader::kRTMonteCarlo:
383 SetHeaderKey("RUNTYPE", "calibration", "");
384 break;
385 }
386// SetHeaderKey("ID", 777, "Board 0: Board ID");
387// SetHeaderKey("FMVER", 532, "Board 0: Firmware Version");
388// SetHeaderKey("DNA", "0", "");
389// SetHeaderKey("BOARD", 0, "");
390// SetHeaderKey("PRESC", 40, "");
391// SetHeaderKey("PHASE", 0, "");
392// SetHeaderKey("DAC0", 26500, "");
393// SetHeaderKey("DAC1", 0, "");
394// SetHeaderKey("DAC2", 0, "");
395// SetHeaderKey("DAC3", 0, "");
396// SetHeaderKey("DAC4", 28800, "");
397// SetHeaderKey("DAC5", 28800, "");
398// SetHeaderKey("DAC6", 28800, "");
399// SetHeaderKey("DAC7", 28800, "");
400 SetHeaderKey("REFCLK", header.GetFreqSampling(), "");
401 SetHeaderKey("DRSCALIB", false, "");
402// SetHeaderKey("TSTARTI", 0, "");
403// SetHeaderKey("TSTARTF", 0., "");
404// SetHeaderKey("TSTOPI", 0, "");
405// SetHeaderKey("TSTOPF", 0., "");
406// SetHeaderKey("DATE-OBS", "1970-01-01T00:00:00", "");
407// SetHeaderKey("DATE-END", "1970-01-01T00:00:00", "");
408// SetHeaderKey("NTRG", 0, "");
409// SetHeaderKey("NTRGPED", 0, "");
410// SetHeaderKey("NTRGLPE", 0, "");
411// SetHeaderKey("NTRGTIM", 0, "");
412// SetHeaderKey("NTRGLPI", 0, "");
413// SetHeaderKey("NTRGEXT1", 0, "");
414// SetHeaderKey("NTRGEXT2", 0, "");
415// SetHeaderKey("NTRGMISC", 0, "");
416}
417
418template<>
419std::string MWriteFitsFile::GetFitsString(const double& value)
420{
421 std::ostringstream returnVal;
422 returnVal << std::setprecision(value>1e-100 && value<1e100 ? 15 : 14) << value;
423 std::string temp = returnVal.str();
424 std::replace(temp.begin(), temp.end(), 'e', 'E');
425 if (temp.find_first_of('E')==std::string::npos && temp.find_first_of('.')==std::string::npos)
426 temp += ".";
427 return temp;
428}
429template<>
430std::string MWriteFitsFile::GetFitsString(const float& value)
431{
432 return GetFitsString((double)(value));
433}
434// --------------------------------------------------------------------------
435//
436// Opens all FITS files for all container, which were registered with the
437// AddContainer() - methods. The container is taken from the pList, if it
438// was registered just by its name.
439// Calling the AddContainer() after calling this method will not add new
440// containers to the list.
441//
442Bool_t MWriteFitsFile::GetContainer(MParList *pList)
443{
444 if (iTopFitsGroup == fTopFitsGroups.end())
445 // something went wrong while the top level group was created
446 return kFALSE;
447
448
449 // remove the extension from the filename
450 // this has been disabled for now
451 char fileNameNoExt[strlen(GetFileName()) + 1];
452 strcpy(fileNameNoExt, GetFileName());
453// char * pos = strrchr(fileNameNoExt, '.');
454// if (pos) *pos = 0;
455//*fLog << inf <<"Filename no ext: " << fileNameNoExt << endl;
456 // loop over all FITS tables which have to be created.
457 map<TString, map<TString, MFitsSubTable> >::iterator i_table =
458 fSubTables.begin();
459 while (i_table != fSubTables.end())
460 {
461
462 ofits* fitsTable = new ofits();
463 fitsTable->AllowCommentsTrimming(true);
464 TString dol = fileNameNoExt;
465 //get rid of the ".root" extension in the file name (if any)
466 if (dol(dol.Length()-5, dol.Length()) == ".root")
467 {
468 dol = dol(0, dol.Length()-5);
469 }
470 if (dol(dol.Length()-5, dol.Length()) == ".fits")
471 {
472 dol = dol(0, dol.Length()-5);
473 }
474 dol += "_";
475 dol += i_table->first;
476 dol += ".fits";
477 fitsTable->open(dol.Data());
478 *fLog << inf << "Opening FITS file: " << dol.Data() << endl;
479 fFitsTables[i_table->first] = fitsTable;
480 fTableObjectCreated[i_table->first] = true;
481 fTableHeaderWritten[i_table->first] = false;
482
483 // loop over all containers, which define a sub-table of the current table
484 Int_t num = 0;
485 map<TString, MFitsSubTable>::iterator i_subTable = i_table->second.begin();
486 while (i_subTable != i_table->second.end())
487 {
488
489 MFitsSubTable & subTable = i_subTable->second;
490 if (subTable.GetContainer() == NULL)
491 {
492 // container address is not yet known
493 const char * cname = i_subTable->first.Data();
494 MParContainer *cont = (MParContainer*)pList->FindObject(cname);
495 if (!cont)
496 {
497 // no corresponding container is available in pList
498 if (subTable.MustHave())
499 {
500 *fLog << err << "Cannot find parameter container '" << cname << "'." << endl;
501 return kFALSE;
502 }
503
504 // we can ignore this container, delete it from the map
505 *fLog << inf2 << "Unnecessary parameter container '" << cname << "' not found..." << endl;
506 map<TString, MFitsSubTable>::iterator i_tmp = i_subTable;
507 i_subTable++;
508 i_table->second.erase(i_tmp);
509 continue;
510 }
511
512 // we have our container address and can use it
513 subTable.SetContainer(cont);
514 }
515
516 // initialize all columns of the sub-table, defined by the current container
517 TClass * cl = i_subTable->second.GetContainer()->IsA();
518 if (!InitColumns(i_table->first, TString(cl->GetName()) + ".", fitsTable,
519 i_subTable->second.GetContainer(), cl) )
520 return kFALSE;
521
522 char attrName[10];
523 sprintf(attrName, "CLNAME%d", num);
524 num++;
525
526 InitAttr(attrName,
527 "A",
528 (void*)i_subTable->first.Data(),
529 NULL,
530 "MARS container name",
531 fitsTable);
532
533 i_subTable++;
534 }
535
536 // in case not all sub-tables were removed, we can now create the FITS table
537 if (i_table->second.size() > 0)
538 {
539 // create the DOL of the table. It will be something like:
540 // fileNameNoExt_dataName.fits[dataName]
541 TString dol = fileNameNoExt;
542 dol += "_";
543 dol += i_table->first;
544 dol += ".fits";
545
546 if (fOpenOption == "RECREATE")
547 // remove the file
548 unlink(dol.Data());
549
550 dol += "[";
551 dol += i_table->first;
552 dol += "]";
553// *fLog << err << "Reiner would have opened fits file: " << dol.Data() << endl;
554 //exit(0);
555 //fitsTable->open(dol.Data());
556
557 // attach the new table to the top level group
558 //TODO make sure that this attach below is not needed...
559 //iTopFitsGroup->second.Attach(fitsTable);
560 }
561
562 i_table++;
563 }
564
565 return kTRUE;
566}
567void MWriteFitsFile::InitAttr(const char* attrName,
568 const char* dataType,
569 void* var,
570 const char* unit,
571 const char* comment,
572 ofits* outFile)
573{
574 if (outFile == NULL)
575 return;
576 string dts(dataType);
577 string ans(attrName);
578 string cs;
579 if (comment != NULL)
580 cs = string(comment);
581 else
582 cs = "";
583 ostringstream val;
584 if ((dts == "bool") || (dts == "Bool_t") || (dts == "L"))
585 {
586 outFile->SetBool(ans, ((bool*)(var))[0], cs);
587 return;
588 }
589 if ((dts == "char") || (dts == "Char_t") || (dts == "S"))
590 {
591 val << ((char*)(var))[0];
592 outFile->SetStr(ans, val.str(), cs);
593 return;
594 }
595 if ((dts == "unsigned char") || (dts == "UChar_t") || (dts == "B"))
596 {
597 val << ((unsigned char*)(var))[0];
598 outFile->SetStr(ans, val.str(), cs);
599 return;
600 }
601 if ((dts == "short") || (dts == "Short_t") || (dts == "I"))
602 {
603 val << ((short*)(var))[0];
604 outFile->SetStr(ans, val.str(), cs);
605 return;
606 }
607 if ((dts == "unsigned short") || (dts == "UShort_t") || (dts == "U"))
608 {
609 val << ((unsigned short*)(var))[0];
610 outFile->SetStr(ans, val.str(), cs);
611 return;
612 }
613 if ((dts == "int") || (dts == "Int_t") || (dts == "V"))
614 {
615 outFile->SetInt(ans, ((int*)(var))[0], cs);
616 return;
617 }
618 if ((dts == "unsigned int") || (dts == "UInt_t") || (dts == "V"))
619 {
620 outFile->SetInt(ans, ((unsigned int*)(var))[0], cs);
621 return;
622 }
623 if ((dts == "long long") || (dts == "Long64_t") || (dts == "K"))
624 {
625 outFile->SetInt(ans, ((long long*)(var))[0], cs);
626 return;
627 }
628 if ((dts == "unsigned long long") || (dts == "ULong64_t") || (dts == "W"))
629 {
630 val << ((unsigned long long*)(var))[0];
631 outFile->SetStr(ans, val.str(), cs);
632 return;
633 }
634 if ((dts == "float") || (dts == "TFloat_t") || (dts == "E"))
635 {
636 outFile->SetFloat(ans, ((float*)(var))[0], cs);
637 return;
638 }
639 if ((dts == "double") || (dts == "TDouble_t") || (dts == "D"))
640 {
641 outFile->SetFloat(ans, ((double*)(var))[0], cs);
642 return;
643 }
644 if ((dts == "char*") || (dts == "A"))
645 {
646 outFile->SetStr(ans, string((char*)(var)), cs);
647 return;
648 }
649 //trigger error
650 *fLog << err << "Format string not recognized while adding header entry " << ans << " with type " << dts << endl;
651}
652// --------------------------------------------------------------------------
653//
654// This method is called when new data should be written to the FITS table.
655// In general only one row is written. (There are exceptions for Arrays).
656// A new row to a FITS table is written if at least for one container
657// (one sub-table) the ReadyToSave - flag is set.
658//
659Bool_t MWriteFitsFile::CheckAndWrite()
660{
661// try {
662
663 // loop over all tables
664 map<TString, map<TString, MFitsSubTable> >::iterator i_table =
665 fSubTables.begin();
666 while (i_table != fSubTables.end())
667 {
668 // is this table open?
669 if (fFitsTables.find(i_table->first) != fFitsTables.end())
670 {
671
672 // loop over all sub-tables
673 map<TString, MFitsSubTable>::iterator i_subTable = i_table->second.begin();
674 while (i_subTable != i_table->second.end())
675 {
676
677 if (i_subTable->second.GetContainer()->IsReadyToSave())
678 {
679 // first write the TClonesArray and set the size of the arrays
680 list<MArrayHelperBase*> & clHelper = fClHelper[i_table->first];
681 list<MArrayHelperBase*>::iterator i_clHelper = clHelper.begin();
682 while (i_clHelper != clHelper.end())
683 {
684 // write all entries in the TClonesArray in its FITS table
685 //removed this write because I do it elsewhere. is that alright ?
686 // (*i_clHelper)->Write();
687 i_clHelper++;
688 }
689
690 // write one line to this table
691 writeOneRow(i_table->first);
692// fFitsTables[i_table->first].Write();
693 break;
694 }
695 i_subTable++;
696 }
697 }
698
699 i_table++;
700 }
701// }
702// catch (exception &e)
703 // {
704 // *fLog << err << e.what() << endl;
705 // return kFALSE;
706 // }
707
708 return kTRUE;
709}
710
711string MWriteFitsFile::Trim(const string &str)
712{
713 // Trim Both leading and trailing spaces
714 const size_t first = str.find_first_not_of(' '); // Find the first character position after excluding leading blank spaces
715 const size_t last = str.find_last_not_of(' '); // Find the first character position from reverse af
716
717 // if all spaces or empty return an empty string
718 if (string::npos==first || string::npos==last)
719 return string();
720
721 return str.substr(first, last-first+1);
722}
723
724Bool_t MWriteFitsFile::VetoColumn(const std::string& colName)
725{
726 for (std::vector<string>::iterator it=fVetoedColumns.begin(); it != fVetoedColumns.end(); it++)
727 if (*it == colName)
728 {
729 *fLog << warn << "Warning: column " << colName << " is being vetoed twice" << endl;
730 return kFALSE;
731 }
732 fVetoedColumns.push_back(colName);
733 return kTRUE;
734}
735
736Bool_t MWriteFitsFile::SetBytesPerSample(const std::string& colName, uint32_t numBytes)
737{
738 for (map<string, uint32_t>::iterator it=fBytesPerSamples.begin(); it!=fBytesPerSamples.end(); it++)
739 if (it->first == colName)
740 {
741 *fLog << warn << "Warning: column " << colName << " bytes per sample is being redefined twice" << endl;
742 return kFALSE;
743 }
744 if (numBytes != 1 && numBytes != 2 && numBytes != 4 && numBytes != 8)
745 {
746 *fLog << warn << "Only powers of two are allowed for types re-mapping." << endl;
747 return kFALSE;
748 }
749 fBytesPerSamples[colName] = numBytes;
750 return kTRUE;
751}
752
753// --------------------------------------------------------------------------
754//
755// Initialize all columns in "fitsTable" of the class "classDef". The data
756// of this class are stored in a buffer, beginning at "baseAdr".
757//
758Bool_t MWriteFitsFile::InitColumns(const TString & tableName,
759 const TString & parentVarName,
760 ofits* fitsTable,
761 void * baseAdr,
762 TClass * classDef)
763{
764 // get all data members of the class
765 TList * dataMembers = classDef->GetListOfDataMembers();
766 TIter next(dataMembers);
767 TDataMember * dataMember;
768
769 // loop over all data members
770 while ((dataMember = (TDataMember*)next()) != NULL)
771 {
772 if (!dataMember->IsPersistent())
773 // don't store this variable
774 continue;
775
776 if (dataMember->Property() & ( G__BIT_ISENUM | G__BIT_ISSTATIC))
777 // we cannot store this
778 continue;
779
780 if (strcmp(dataMember->GetTrueTypeName(), "TClass*") == 0)
781 // we don't want to store this.
782 continue;
783
784 // is it an array of more than 1 dimension?
785 if (dataMember->GetArrayDim() > 1)
786 {
787 *fLog << err << "Two and more dimensional arrays of member variables"
788 " are not supported." << endl;
789 *fLog << "See variable " << dataMember->GetName() <<
790 " in container " << classDef->GetName() << endl;
791 return kFALSE;
792 }
793
794
795 // replace � by **2 in the comment field
796 string comment(dataMember->GetTitle());
797 string::size_type pos1, pos2;
798 if ((pos1 = comment.find(178)) != string::npos)
799 comment.replace(pos1, 1, "**2");
800
801 // get the optional mapping to the fits column names
802 string fitsOptions="";
803 string unit="unit";
804
805 pos1 = comment.find("{fits: ");
806 pos2 = comment.find('}');
807 std::map<string, string> fitsTokens;
808 if (pos1 != string::npos && pos2 != string::npos)
809 {
810 fitsOptions=comment.substr(pos1+7, (pos2-pos1)-7);
811 //do we have more than one option ?
812 string::size_type pos3= fitsOptions.find_first_of(';');
813 string::size_type pos4 = string::npos;
814 string key="";
815 string value="";
816 string keyValue="";
817 while (pos3 != string::npos)
818 {//we have at least 2 options left
819 // *fLog << err << "fitsOptions: " << fitsOptions << endl;
820 keyValue = fitsOptions.substr(0,pos3);
821 // *fLog << err << "keyValue: " << keyValue << endl;
822 pos4 = keyValue.find('=');
823 if (pos4 == string::npos)
824 {
825 *fLog << err << "Error while parsing comment \"" << comment << "\" from variable " << parentVarName + dataMember->GetName() << endl;
826 return kFALSE;
827 }
828 key = Trim(keyValue.substr(0, pos4));
829 value = Trim(keyValue.substr(pos4+1, pos3));
830 fitsTokens[key] = value;
831 // *fLog << err << "key: " << key << " value: " << value << endl;
832 fitsOptions = fitsOptions.substr(pos3+1, fitsOptions.size());
833 pos3 = fitsOptions.find_first_of(';');
834 }
835// *fLog << err << "fitsOptions: " << fitsOptions << endl;
836 keyValue = fitsOptions;
837 pos4 = keyValue.find('=');
838 if (pos4 == string::npos)
839 {
840 *fLog << err << "Error while parsing comment \"" << comment << "\" from variable " << parentVarName + dataMember->GetName() << endl;
841 return kFALSE;
842 }
843 key = Trim(keyValue.substr(0, pos4));
844 value = Trim(keyValue.substr(pos4+1, pos3));
845 fitsTokens[key] = value;
846// *fLog << err << "key: " << key << " value: " << value << endl;
847 }
848
849 TString colName = parentVarName + dataMember->GetName();
850
851 if (fitsTokens.find("unit") != fitsTokens.end())
852 unit = fitsTokens["unit"];
853 if (fitsTokens.find("name") != fitsTokens.end())
854 colName = fitsTokens["name"];
855
856 //check for column veto
857 if (std::find(fVetoedColumns.begin(), fVetoedColumns.end(), colName.Data())!=fVetoedColumns.end())
858 {
859 *fLog << inf << "Vetoing column " << colName.Data() << endl;
860 continue;
861 }
862
863 // set the array size
864 TString dataType = dataMember->GetTrueTypeName();
865
866 uint32_t dataLength = 0;
867 switch (dataMember->GetArrayDim())
868 {
869 case 0:
870 dataLength = 1;
871 break;
872 case 1:
873 dataLength = dataMember->GetMaxIndex(0);
874 break;
875 default:
876 *fLog << err << "n-dimensional array should have been discarded already " << colName.Data();
877 break;
878 };
879// if (dataLength == 0)
880// continue;
881
882 if (dataMember->Property() & G__BIT_ISCLASS)
883 {
884 // special treatment for classes
885 uint32_t arraySize = 0;
886 TString typeName;
887
888 if (strncmp(dataMember->GetTrueTypeName(), "MArray", 6) == 0)
889 {
890// MArrayHelperBase * clHelper;
891 if (strcmp(dataMember->GetTrueTypeName(), "MArrayS*") == 0)
892 {
893 typeName = "UShort_t";
894 arraySize = (*((MArrayS**)((char*)baseAdr + dataMember->GetOffset())))->GetSize();
895 InitSingleColumn(tableName,
896 arraySize,
897 typeName.Data(),
898 (char*)(*((MArrayS**)((char*)baseAdr + dataMember->GetOffset())))->GetArray(),
899 colName.Data(),
900 unit,
901 comment);
902 }
903 else
904 {
905 if (strcmp(dataMember->GetTrueTypeName(), "MArrayB*") == 0)
906 {
907 typeName = "UChar_t";
908 arraySize = (*((MArrayB**)((char*)baseAdr + dataMember->GetOffset())))->GetSize();
909 InitSingleColumn(tableName,
910 arraySize,
911 typeName.Data(),
912 (char*)(*((MArrayB**)((char*)baseAdr + dataMember->GetOffset())))->GetArray(),
913 colName.Data(),
914 unit,
915 comment);
916 }
917 else
918 {
919 *fLog << err << dataMember->GetTrueTypeName() << " not yet implemented." << endl;
920 return kFALSE;
921 }
922 }
923
924 continue;
925 }
926 else if (strcmp(dataMember->GetTrueTypeName(), "TClonesArray") == 0)
927 {
928 *fLog << warn << "I'm skipping the TClonesArray for now" << endl;
929 continue;
930 // each TClonesArray requires a FITS table by itself.
931 MClonesArrayHelper * clHelper;
932
933 TClonesArray * cloneArray = (TClonesArray*)((char*)baseAdr + dataMember->GetOffset());
934 Bool_t status;
935 clHelper = new MClonesArrayHelper(cloneArray, fLog, status);
936 if (!status) return status;
937
938 fClHelper[tableName].push_back(clHelper);
939
940 // add one column in the parent table of the TClonesArray to store the
941 // number of entries in the TClonesArray.
942 InitSingleColumn(tableName,
943 1,
944 "UInt_t",
945 clHelper->GetArraySizePtr(),
946 colName.Data(),
947 unit,
948 comment);
949
950 // initialize the columns of the new FITS table, which will store the
951 // data entries of the TClonesArray
952 if (InitColumns(TString("noName"),
953 colName + "_",
954 fitsTable,
955 clHelper->GetDataBuffer(),
956 cloneArray->GetClass())
957 == kFALSE)
958 return kFALSE;
959
960 // the columns are initialized. We can create the FITS table
961 if (clHelper->OpenFitsTable(GetFileName(), dataMember->GetName(),
962 fOpenOption, fLog) == kFALSE)
963 return kFALSE;
964 }
965
966 else
967 {
968 // the current container has a variable of an other class. We create
969 // also columns of this other class in the same table
970 TClass * newClassDef = TClass::GetClass(dataMember->GetTrueTypeName(), kFALSE, kTRUE);
971 if (newClassDef)
972 {
973 if (InitColumns(tableName, colName + ".", fitsTable, (char*)baseAdr + dataMember->GetOffset(),
974 newClassDef) == kFALSE)
975 return kFALSE;
976 }
977 else
978 *fLog << warn << "Cannot write data of class " << colName + "." + dataMember->GetTrueTypeName() << endl;
979 }
980 continue;
981 }
982
983 InitSingleColumn(tableName,
984 dataLength,
985 dataType.Data(),
986 (char*)baseAdr + dataMember->GetOffset(),
987 colName.Data(),
988 unit,
989 comment);
990
991 }
992 return kTRUE;
993}
994void MWriteFitsFile::InitSingleColumn(const TString& tableName,
995 uint32_t count,
996 const string& typeName,
997 void* dataPointer,
998 const string& columnName,
999 const string& unit,
1000 const string& comment)
1001{
1002 if (!fTableObjectCreated[tableName])
1003 {
1004 *fLog << err << "ERROR: Cannot init column " << columnName << " before assigning object: " << tableName << endl;
1005 return;
1006 }
1007 int typeFound = 0;
1008 char typeChar = '0';
1009 if ((typeName == "bool") || (typeName == "Bool_t") || (typeName == "L"))
1010 {
1011 typeChar = 'L';
1012 typeFound++;
1013 }
1014 if ((typeName == "char") || (typeName == "Char_t") || (typeName == "S"))
1015 {
1016 typeChar = 'A';
1017 typeFound++;
1018 }
1019 if ((typeName == "unsigned char") || (typeName == "UChar_t") || (typeName == "B"))
1020 {
1021 typeChar = 'A';
1022// *fLog << warn << "Converting unsigned char to char in fits file" << endl;
1023 typeFound++;
1024 }
1025 if ((typeName == "short") || (typeName == "Short_t") || (typeName == "I"))
1026 {
1027 typeChar = 'I';
1028 typeFound++;
1029 }
1030 if ((typeName == "unsigned short") || (typeName == "UShort_t") || (typeName == "U"))
1031 {
1032 typeChar = 'I';
1033// *fLog << warn << "Converting unsigned short to short in fits file" << endl;
1034 typeFound++;
1035 }
1036 if ((typeName == "int") || (typeName == "Int_t") || (typeName == "V"))
1037 {
1038 typeChar = 'J';
1039 typeFound++;
1040 }
1041 if ((typeName == "unsigned int") || (typeName == "UInt_t") || (typeName == "V"))
1042 {
1043 typeChar = 'J';
1044// *fLog << warn << "Converting unsigned int to int in fits file" << endl;
1045 typeFound++;
1046 }
1047 if ((typeName == "long long") || (typeName == "Long64_t") || (typeName == "K"))
1048 {
1049 typeChar = 'K';
1050 typeFound++;
1051 }
1052 if ((typeName == "unsigned long long") || (typeName == "ULong64_t") || (typeName == "W"))
1053 {
1054 typeChar = 'K';
1055// *fLog << warn << "Converting unsigned long to long in fits file" << endl;
1056 typeFound++;
1057 }
1058 if ((typeName == "float") || (typeName=="TFloat_t") || (typeName == "E"))
1059 {
1060 typeChar = 'E';
1061 typeFound++;
1062 }
1063 if ((typeName == "double") || (typeName == "TDouble_t") || (typeName == "D"))
1064 {
1065 typeChar = 'D';
1066 typeFound++;
1067 }
1068// if ((typeName == "char*") || (typeName == "A"))
1069// {
1070// typeFound++;
1071// }
1072 if (typeFound != 1)
1073 {
1074 *fLog << err << "We have a problem with the data type: " << typeName << endl;
1075 return;
1076 }
1077 uint32_t colWidth = 0;
1078 switch (typeChar)
1079 {
1080 case 'L': colWidth = 1*count; break;
1081 case 'A': colWidth = 1*count; break;
1082 case 'B': colWidth = 1*count; break;
1083 case 'I': colWidth = 2*count; break;
1084 case 'J': colWidth = 4*count; break;
1085 case 'K': colWidth = 8*count; break;
1086 case 'E': colWidth = 4*count; break;
1087 case 'D': colWidth = 8*count; break;
1088 default:
1089 *fLog << err << "ERROR: typeChar could not be resolved to an actual type" << endl;
1090 };
1091 //check for type remapping here
1092 if (fBytesPerSamples.find(columnName) != fBytesPerSamples.end())
1093 {
1094 if (typeChar != 'A')
1095 {
1096 *fLog << err << "Attempt to remap type " << typeChar << " to " << fBytesPerSamples[columnName] << " is only allowed on bytes (variable name: " << columnName << "). Ignoring column" << endl;
1097 return;
1098 }
1099 uint32_t bytesPerSample = fBytesPerSamples.find(columnName)->second;
1100 if (colWidth%bytesPerSample != 0)
1101 {
1102 *fLog << err << "Type remapping cannot be done using " << bytesPerSample << " bytes per sample on an array of char of size " << colWidth << ". Ignoring column " << columnName << endl;
1103 return;
1104 }
1105 switch (bytesPerSample)
1106 {
1107 case 1: count = count/1; typeChar = 'A'; break;
1108 case 2: count = count/2; typeChar = 'I'; break;
1109 case 4: count = count/4; typeChar = 'J'; break;
1110 case 8: count = count/8; typeChar = 'K'; break;
1111 default:
1112 *fLog << err << "ERROR: num bytes per sample = " << bytesPerSample << " should have been forbidden already" << endl;
1113 }
1114
1115 }
1116
1117 fDataPointers[tableName].push_back(dataPointer);
1118 fTypeChars[tableName].push_back(typeChar);
1119 fColSizes[tableName].push_back(count);
1120 fColWidth[tableName].push_back(colWidth);
1121
1122 //FIXME ofits does not allow for much liberty regarding the size of the column names.
1123 //Truncating them badly here, will probably cause other problems -> Modify ofits.h instead
1124 string truncatedName=columnName.substr((columnName.size()>40)?columnName.size()-40:0,columnName.size());
1125 string truncatedComment = comment.substr((comment.size()>10)?comment.size()-10:0,comment.size());
1126// *fLog << warn << "In table " << tableName << " Adding column |" << truncatedName << "| |" << truncatedComment << "| |" << count << "| |" << typeChar;
1127// *fLog << warn << "| Real: "<< columnName << " comment: " << comment << endl;
1128 fFitsTables[tableName]->AddColumn(count, typeChar, truncatedName, unit, truncatedComment);
1129}
1130
1131void MWriteFitsFile::writeOneRow(const TString& tableName)
1132{
1133 if (!fTableHeaderWritten[tableName])
1134 {
1135 for (vector<ofits::Key>::const_iterator it = fHeaderKeys.begin(); it != fHeaderKeys.end(); it++)
1136 fFitsTables[tableName]->SetRaw(it->key, it->value, it->comment);
1137 fFitsTables[tableName]->WriteTableHeader(tableName.Data());
1138 fTableHeaderWritten[tableName] = true;
1139 }
1140 if (!fTableObjectCreated[tableName])
1141 {
1142 *fLog << err << "This is not good. Please initialize the fits table before writing to it: " << tableName << endl;
1143 return;
1144 }
1145 //first calculate the size of one row
1146 uint32_t rowWidth = 0;
1147 for (uint32_t i=0;i<fTypeChars[tableName].size();i++)
1148 rowWidth += fColWidth[tableName][i];
1149 unsigned char* tempBuffer = new unsigned char[rowWidth];
1150 //then copy the data to be written contiguously
1151 uint32_t bytesCounter = 0;
1152 for (uint32_t i=0;i<fDataPointers[tableName].size();i++)
1153 {
1154 memcpy(&tempBuffer[bytesCounter], fDataPointers[tableName][i], fColWidth[tableName][i]);
1155 bytesCounter+=fColWidth[tableName][i];
1156 }
1157 if (fFitsTables[tableName]->WriteRow(tempBuffer, bytesCounter) == false)
1158 *fLog << err << "Error while writing to FITS table " << tableName << endl;
1159 else
1160 fFitsTables[tableName]->FlushNumRows();
1161
1162 delete[] tempBuffer;
1163}
1164Bool_t MWriteFitsFile::ReInit(MParList *pList)
1165{
1166 if (fRule.Length() == 0)
1167 // there is not rule defined. We keep the old file
1168 return MWriteFile::ReInit(pList);
1169
1170 MRead *read = (MRead*)pList->FindTask("MRead");
1171 if (!read)
1172 {
1173 *fLog << err;
1174 *fLog << "ERROR: No Task 'MRead' found in the tasklist. This task is" << endl;
1175 *fLog << " necessary to get the filename. Without a read-filename" << endl;
1176 *fLog << " no output-filename can be created... abort." << endl;
1177 *fLog << endl;
1178 return kFALSE;
1179 }
1180
1181
1182 // close the current files
1183 CloseTopLevelGroup();
1184 for (std::map<TString,ofits*>::iterator it=fFitsTables.begin(); it!=fFitsTables.end(); it++)
1185 {
1186 (it->second)->close();
1187 delete it->second;
1188 }
1189 fFitsTables.clear();
1190 fDataPointers.clear();
1191 fTypeChars.clear();
1192 fColSizes.clear();
1193 fColWidth.clear();
1194 fTableObjectCreated.clear();
1195 fTableHeaderWritten.clear();
1196 DeleteArrayHelper();
1197 fClHelper.clear();
1198
1199 // get new filename
1200 const TString readFileName = read->GetFullFileName();
1201 const TString newname = MWriteRootFile::SubstituteName(fRule, readFileName);
1202
1203 // create new files
1204 OpenTopLevelGroup(newname.Data());
1205 if (!IsFileOpen())
1206 return kFALSE;
1207
1208
1209 MRawRunHeader* header = (MRawRunHeader*)pList->FindObject("MRawRunHeader");
1210 SetupHeaderKeys(*header);
1211
1212 if (GetContainer(pList) == kFALSE)
1213 return kFALSE;
1214
1215 // do, what has to be done in ReInit.
1216 return MWriteFile::ReInit(pList);
1217
1218}
1219
1220void MWriteFitsFile::DeleteArrayHelper()
1221{
1222 map<TString, list<MArrayHelperBase *> >::iterator i_helper1 = fClHelper.begin();
1223 while (i_helper1 != fClHelper.end())
1224 {
1225 list<MArrayHelperBase *>::iterator i_helper2 = i_helper1->second.begin();
1226 while(i_helper2 != i_helper1->second.end())
1227 {
1228 delete *i_helper2;
1229
1230 i_helper2++;
1231 }
1232
1233 i_helper1++;
1234 }
1235}
Note: See TracBrowser for help on using the repository browser.