source: branches/Mars_MC/mfileio/MWriteFitsFile.cc@ 17357

Last change on this file since 17357 was 17046, checked in by ftemme, 11 years ago
Moved the setting of the header keys for the fitsoutput of ceres files from MJSimulation.cc to MWriteFitsFile::ReInit, cause several used header keys are only then known and can't be set in MJSimulation::Process()
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.