source: branches/MarsMoreSimulationTruth/mfileio/MWriteFitsFile.cc@ 19082

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