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

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