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

Last change on this file since 17689 was 17661, checked in by ftemme, 10 years ago
Added the writing of MArrayF in MWriteFitsFile
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 for (std::map<TString, ofits*>::iterator it=fFitsTables.begin(); it!= fFitsTables.end();it++)
334 {
335 it->second->close();
336 delete it->second;
337 it->second = NULL;
338 }
339
340 return kTRUE;
341}
342
343void MWriteFitsFile::SetupHeaderKeys(MRawRunHeader &header)
344{
345 const MTime now(-1);
346 SetHeaderKey("ISMC",true,"Bool if File is Montecarlo File");
347 SetHeaderKey("TELESCOP", "FACT", "");
348 SetHeaderKey("PACKAGE", "MARS Cheobs", "");
349 SetHeaderKey("VERSION", "1.0", "");
350 SetHeaderKey("CREATOR", "Ceres", "");
351 SetHeaderKey("EXTREL", 1., "");
352 SetHeaderKey("COMPILED", __DATE__" " __TIME__, "");
353 //SetHeaderKey("REVISION", "0", "");
354 SetHeaderKey("ORIGIN", "FACT", "");
355 SetHeaderKey("DATE", now.GetStringFmt("%Y-%m-%dT%H:%M:%S").Data(), "");
356 SetHeaderKey("NIGHT", now.GetNightAsInt(), "");
357 SetHeaderKey("TIMESYS", "UTC", "");
358 SetHeaderKey("TIMEUNIT", "d", "");
359 SetHeaderKey("MJDREF", 40587, "");
360 //SetHeaderKey("BLDVER", 1, "");
361 SetHeaderKey("RUNID", header.GetRunNumber(), "");
362 SetHeaderKey("NBOARD", 40, "");
363 SetHeaderKey("NPIX", header.GetNumPixel(), "");
364 SetHeaderKey("NROI", header.GetNumSamplesHiGain(), "");
365 SetHeaderKey("NROITM", 0, "");
366 SetHeaderKey("TMSHIFT", 0, "");
367 SetHeaderKey("CAMERA", "MGeomCamFACT", "Montecarlo File");
368 SetHeaderKey("DAQ", "DRS4", "Montecarlo File");
369
370 // FTemme: ADCRANGE and ADC have to be calculated, using the values for
371 // the fadctype.
372// SetHeaderKey("ADCRANGE", 2000, "Dynamic range in mV");
373// SetHeaderKey("ADC", 12, "Resolution in bits");
374
375 switch(header.GetRunType())
376 {
377 case MRawRunHeader::kRTData|MRawRunHeader::kRTMonteCarlo:
378 SetHeaderKey("RUNTYPE", "data", "");
379 break;
380 case MRawRunHeader::kRTPedestal|MRawRunHeader::kRTMonteCarlo:
381 SetHeaderKey("RUNTYPE", "pedestal", "");
382 break;
383 case MRawRunHeader::kRTCalibration|MRawRunHeader::kRTMonteCarlo:
384 SetHeaderKey("RUNTYPE", "calibration", "");
385 break;
386 }
387// SetHeaderKey("ID", 777, "Board 0: Board ID");
388// SetHeaderKey("FMVER", 532, "Board 0: Firmware Version");
389// SetHeaderKey("DNA", "0", "");
390// SetHeaderKey("BOARD", 0, "");
391// SetHeaderKey("PRESC", 40, "");
392// SetHeaderKey("PHASE", 0, "");
393// SetHeaderKey("DAC0", 26500, "");
394// SetHeaderKey("DAC1", 0, "");
395// SetHeaderKey("DAC2", 0, "");
396// SetHeaderKey("DAC3", 0, "");
397// SetHeaderKey("DAC4", 28800, "");
398// SetHeaderKey("DAC5", 28800, "");
399// SetHeaderKey("DAC6", 28800, "");
400// SetHeaderKey("DAC7", 28800, "");
401 SetHeaderKey("REFCLK", header.GetFreqSampling(), "");
402 SetHeaderKey("DRSCALIB", false, "");
403// SetHeaderKey("TSTARTI", 0, "");
404// SetHeaderKey("TSTARTF", 0., "");
405// SetHeaderKey("TSTOPI", 0, "");
406// SetHeaderKey("TSTOPF", 0., "");
407// SetHeaderKey("DATE-OBS", "1970-01-01T00:00:00", "");
408// SetHeaderKey("DATE-END", "1970-01-01T00:00:00", "");
409// SetHeaderKey("NTRG", 0, "");
410// SetHeaderKey("NTRGPED", 0, "");
411// SetHeaderKey("NTRGLPE", 0, "");
412// SetHeaderKey("NTRGTIM", 0, "");
413// SetHeaderKey("NTRGLPI", 0, "");
414// SetHeaderKey("NTRGEXT1", 0, "");
415// SetHeaderKey("NTRGEXT2", 0, "");
416// SetHeaderKey("NTRGMISC", 0, "");
417}
418
419template<>
420std::string MWriteFitsFile::GetFitsString(const double& value)
421{
422 std::ostringstream returnVal;
423 returnVal << std::setprecision(value>1e-100 && value<1e100 ? 15 : 14) << value;
424 std::string temp = returnVal.str();
425 std::replace(temp.begin(), temp.end(), 'e', 'E');
426 if (temp.find_first_of('E')==std::string::npos && temp.find_first_of('.')==std::string::npos)
427 temp += ".";
428 return temp;
429}
430template<>
431std::string MWriteFitsFile::GetFitsString(const float& value)
432{
433 return GetFitsString((double)(value));
434}
435// --------------------------------------------------------------------------
436//
437// Opens all FITS files for all container, which were registered with the
438// AddContainer() - methods. The container is taken from the pList, if it
439// was registered just by its name.
440// Calling the AddContainer() after calling this method will not add new
441// containers to the list.
442//
443Bool_t MWriteFitsFile::GetContainer(MParList *pList)
444{
445 if (iTopFitsGroup == fTopFitsGroups.end())
446 // something went wrong while the top level group was created
447 return kFALSE;
448
449
450 // remove the extension from the filename
451 // this has been disabled for now
452 char fileNameNoExt[strlen(GetFileName()) + 1];
453 strcpy(fileNameNoExt, GetFileName());
454// char * pos = strrchr(fileNameNoExt, '.');
455// if (pos) *pos = 0;
456//*fLog << inf <<"Filename no ext: " << fileNameNoExt << endl;
457 // loop over all FITS tables which have to be created.
458 map<TString, map<TString, MFitsSubTable> >::iterator i_table =
459 fSubTables.begin();
460 while (i_table != fSubTables.end())
461 {
462
463 ofits* fitsTable = new ofits();
464 fitsTable->AllowCommentsTrimming(true);
465 TString dol = fileNameNoExt;
466 //get rid of the ".root" extension in the file name (if any)
467 if (dol(dol.Length()-5, dol.Length()) == ".root")
468 {
469 dol = dol(0, dol.Length()-5);
470 }
471 if (dol(dol.Length()-5, dol.Length()) == ".fits")
472 {
473 dol = dol(0, dol.Length()-5);
474 }
475 dol += "_";
476 dol += i_table->first;
477 dol += ".fits";
478 fitsTable->open(dol.Data());
479 *fLog << inf << "Opening FITS file: " << dol.Data() << endl;
480 fFitsTables[i_table->first] = fitsTable;
481 fTableObjectCreated[i_table->first] = true;
482 fTableHeaderWritten[i_table->first] = false;
483
484 // loop over all containers, which define a sub-table of the current table
485 Int_t num = 0;
486 map<TString, MFitsSubTable>::iterator i_subTable = i_table->second.begin();
487 while (i_subTable != i_table->second.end())
488 {
489
490 MFitsSubTable & subTable = i_subTable->second;
491 if (subTable.GetContainer() == NULL)
492 {
493 // container address is not yet known
494 const char * cname = i_subTable->first.Data();
495 MParContainer *cont = (MParContainer*)pList->FindObject(cname);
496 if (!cont)
497 {
498 // no corresponding container is available in pList
499 if (subTable.MustHave())
500 {
501 *fLog << err << "Cannot find parameter container '" << cname << "'." << endl;
502 return kFALSE;
503 }
504
505 // we can ignore this container, delete it from the map
506 *fLog << inf2 << "Unnecessary parameter container '" << cname << "' not found..." << endl;
507 map<TString, MFitsSubTable>::iterator i_tmp = i_subTable;
508 i_subTable++;
509 i_table->second.erase(i_tmp);
510 continue;
511 }
512
513 // we have our container address and can use it
514 subTable.SetContainer(cont);
515 }
516
517 // initialize all columns of the sub-table, defined by the current container
518 TClass * cl = i_subTable->second.GetContainer()->IsA();
519 if (!InitColumns(i_table->first, TString(cl->GetName()) + ".", fitsTable,
520 i_subTable->second.GetContainer(), cl) )
521 return kFALSE;
522
523 char attrName[10];
524 sprintf(attrName, "CLNAME%d", num);
525 num++;
526
527 InitAttr(attrName,
528 "A",
529 (void*)i_subTable->first.Data(),
530 NULL,
531 "MARS container name",
532 fitsTable);
533
534 i_subTable++;
535 }
536
537 // in case not all sub-tables were removed, we can now create the FITS table
538 if (i_table->second.size() > 0)
539 {
540 // create the DOL of the table. It will be something like:
541 // fileNameNoExt_dataName.fits[dataName]
542 TString dol = fileNameNoExt;
543 dol += "_";
544 dol += i_table->first;
545 dol += ".fits";
546
547 if (fOpenOption == "RECREATE")
548 // remove the file
549 unlink(dol.Data());
550
551 dol += "[";
552 dol += i_table->first;
553 dol += "]";
554// *fLog << err << "Reiner would have opened fits file: " << dol.Data() << endl;
555 //exit(0);
556 //fitsTable->open(dol.Data());
557
558 // attach the new table to the top level group
559 //TODO make sure that this attach below is not needed...
560 //iTopFitsGroup->second.Attach(fitsTable);
561 }
562
563 i_table++;
564 }
565
566 return kTRUE;
567}
568void MWriteFitsFile::InitAttr(const char* attrName,
569 const char* dataType,
570 void* var,
571 const char* unit,
572 const char* comment,
573 ofits* outFile)
574{
575 if (outFile == NULL)
576 return;
577 string dts(dataType);
578 string ans(attrName);
579 string cs;
580 if (comment != NULL)
581 cs = string(comment);
582 else
583 cs = "";
584 ostringstream val;
585 if ((dts == "bool") || (dts == "Bool_t") || (dts == "L"))
586 {
587 outFile->SetBool(ans, ((bool*)(var))[0], cs);
588 return;
589 }
590 if ((dts == "char") || (dts == "Char_t") || (dts == "S"))
591 {
592 val << ((char*)(var))[0];
593 outFile->SetStr(ans, val.str(), cs);
594 return;
595 }
596 if ((dts == "unsigned char") || (dts == "UChar_t") || (dts == "B"))
597 {
598 val << ((unsigned char*)(var))[0];
599 outFile->SetStr(ans, val.str(), cs);
600 return;
601 }
602 if ((dts == "short") || (dts == "Short_t") || (dts == "I"))
603 {
604 val << ((short*)(var))[0];
605 outFile->SetStr(ans, val.str(), cs);
606 return;
607 }
608 if ((dts == "unsigned short") || (dts == "UShort_t") || (dts == "U"))
609 {
610 val << ((unsigned short*)(var))[0];
611 outFile->SetStr(ans, val.str(), cs);
612 return;
613 }
614 if ((dts == "int") || (dts == "Int_t") || (dts == "V"))
615 {
616 outFile->SetInt(ans, ((int*)(var))[0], cs);
617 return;
618 }
619 if ((dts == "unsigned int") || (dts == "UInt_t") || (dts == "V"))
620 {
621 outFile->SetInt(ans, ((unsigned int*)(var))[0], cs);
622 return;
623 }
624 if ((dts == "long long") || (dts == "Long64_t") || (dts == "K"))
625 {
626 outFile->SetInt(ans, ((long long*)(var))[0], cs);
627 return;
628 }
629 if ((dts == "unsigned long long") || (dts == "ULong64_t") || (dts == "W"))
630 {
631 val << ((unsigned long long*)(var))[0];
632 outFile->SetStr(ans, val.str(), cs);
633 return;
634 }
635 if ((dts == "float") || (dts == "TFloat_t") || (dts == "E"))
636 {
637 outFile->SetFloat(ans, ((float*)(var))[0], cs);
638 return;
639 }
640 if ((dts == "double") || (dts == "TDouble_t") || (dts == "D"))
641 {
642 outFile->SetFloat(ans, ((double*)(var))[0], cs);
643 return;
644 }
645 if ((dts == "char*") || (dts == "A"))
646 {
647 outFile->SetStr(ans, string((char*)(var)), cs);
648 return;
649 }
650 //trigger error
651 *fLog << err << "Format string not recognized while adding header entry " << ans << " with type " << dts << endl;
652}
653// --------------------------------------------------------------------------
654//
655// This method is called when new data should be written to the FITS table.
656// In general only one row is written. (There are exceptions for Arrays).
657// A new row to a FITS table is written if at least for one container
658// (one sub-table) the ReadyToSave - flag is set.
659//
660Bool_t MWriteFitsFile::CheckAndWrite()
661{
662// try {
663
664 // loop over all tables
665 map<TString, map<TString, MFitsSubTable> >::iterator i_table =
666 fSubTables.begin();
667 while (i_table != fSubTables.end())
668 {
669 // is this table open?
670 if (fFitsTables.find(i_table->first) != fFitsTables.end())
671 {
672
673 // loop over all sub-tables
674 map<TString, MFitsSubTable>::iterator i_subTable = i_table->second.begin();
675 while (i_subTable != i_table->second.end())
676 {
677
678 if (i_subTable->second.GetContainer()->IsReadyToSave())
679 {
680 // first write the TClonesArray and set the size of the arrays
681 list<MArrayHelperBase*> & clHelper = fClHelper[i_table->first];
682 list<MArrayHelperBase*>::iterator i_clHelper = clHelper.begin();
683 while (i_clHelper != clHelper.end())
684 {
685 // write all entries in the TClonesArray in its FITS table
686 //removed this write because I do it elsewhere. is that alright ?
687 // (*i_clHelper)->Write();
688 i_clHelper++;
689 }
690
691 // write one line to this table
692 writeOneRow(i_table->first);
693// fFitsTables[i_table->first].Write();
694 break;
695 }
696 i_subTable++;
697 }
698 }
699
700 i_table++;
701 }
702// }
703// catch (exception &e)
704 // {
705 // *fLog << err << e.what() << endl;
706 // return kFALSE;
707 // }
708
709 return kTRUE;
710}
711
712string MWriteFitsFile::Trim(const string &str)
713{
714 // Trim Both leading and trailing spaces
715 const size_t first = str.find_first_not_of(' '); // Find the first character position after excluding leading blank spaces
716 const size_t last = str.find_last_not_of(' '); // Find the first character position from reverse af
717
718 // if all spaces or empty return an empty string
719 if (string::npos==first || string::npos==last)
720 return string();
721
722 return str.substr(first, last-first+1);
723}
724
725Bool_t MWriteFitsFile::VetoColumn(const std::string& colName)
726{
727 for (std::vector<string>::iterator it=fVetoedColumns.begin(); it != fVetoedColumns.end(); it++)
728 if (*it == colName)
729 {
730 *fLog << warn << "Warning: column " << colName << " is being vetoed twice" << endl;
731 return kFALSE;
732 }
733 fVetoedColumns.push_back(colName);
734 return kTRUE;
735}
736
737Bool_t MWriteFitsFile::SetBytesPerSample(const std::string& colName, uint32_t numBytes)
738{
739 for (map<string, uint32_t>::iterator it=fBytesPerSamples.begin(); it!=fBytesPerSamples.end(); it++)
740 if (it->first == colName)
741 {
742 *fLog << warn << "Warning: column " << colName << " bytes per sample is being redefined twice" << endl;
743 return kFALSE;
744 }
745 if (numBytes != 1 && numBytes != 2 && numBytes != 4 && numBytes != 8)
746 {
747 *fLog << warn << "Only powers of two are allowed for types re-mapping." << endl;
748 return kFALSE;
749 }
750 fBytesPerSamples[colName] = numBytes;
751 return kTRUE;
752}
753
754// --------------------------------------------------------------------------
755//
756// Initialize all columns in "fitsTable" of the class "classDef". The data
757// of this class are stored in a buffer, beginning at "baseAdr".
758//
759Bool_t MWriteFitsFile::InitColumns(const TString & tableName,
760 const TString & parentVarName,
761 ofits* fitsTable,
762 void * baseAdr,
763 TClass * classDef)
764{
765 // get all data members of the class
766 TList * dataMembers = classDef->GetListOfDataMembers();
767 TIter next(dataMembers);
768 TDataMember * dataMember;
769
770 // loop over all data members
771 while ((dataMember = (TDataMember*)next()) != NULL)
772 {
773 if (!dataMember->IsPersistent())
774 // don't store this variable
775 continue;
776
777 if (dataMember->Property() & ( G__BIT_ISENUM | G__BIT_ISSTATIC))
778 // we cannot store this
779 continue;
780
781 if (strcmp(dataMember->GetTrueTypeName(), "TClass*") == 0)
782 // we don't want to store this.
783 continue;
784
785 // is it an array of more than 1 dimension?
786 if (dataMember->GetArrayDim() > 1)
787 {
788 *fLog << err << "Two and more dimensional arrays of member variables"
789 " are not supported." << endl;
790 *fLog << "See variable " << dataMember->GetName() <<
791 " in container " << classDef->GetName() << endl;
792 return kFALSE;
793 }
794
795
796 // replace � by **2 in the comment field
797 string comment(dataMember->GetTitle());
798 string::size_type pos1, pos2;
799 if ((pos1 = comment.find(178)) != string::npos)
800 comment.replace(pos1, 1, "**2");
801
802 // get the optional mapping to the fits column names
803 string fitsOptions="";
804 string unit="unit";
805
806 pos1 = comment.find("{fits: ");
807 pos2 = comment.find('}');
808 std::map<string, string> fitsTokens;
809 if (pos1 != string::npos && pos2 != string::npos)
810 {
811 fitsOptions=comment.substr(pos1+7, (pos2-pos1)-7);
812 //do we have more than one option ?
813 string::size_type pos3= fitsOptions.find_first_of(';');
814 string::size_type pos4 = string::npos;
815 string key="";
816 string value="";
817 string keyValue="";
818 while (pos3 != string::npos)
819 {//we have at least 2 options left
820 // *fLog << err << "fitsOptions: " << fitsOptions << endl;
821 keyValue = fitsOptions.substr(0,pos3);
822 // *fLog << err << "keyValue: " << keyValue << endl;
823 pos4 = keyValue.find('=');
824 if (pos4 == string::npos)
825 {
826 *fLog << err << "Error while parsing comment \"" << comment << "\" from variable " << parentVarName + dataMember->GetName() << endl;
827 return kFALSE;
828 }
829 key = Trim(keyValue.substr(0, pos4));
830 value = Trim(keyValue.substr(pos4+1, pos3));
831 fitsTokens[key] = value;
832 // *fLog << err << "key: " << key << " value: " << value << endl;
833 fitsOptions = fitsOptions.substr(pos3+1, fitsOptions.size());
834 pos3 = fitsOptions.find_first_of(';');
835 }
836// *fLog << err << "fitsOptions: " << fitsOptions << endl;
837 keyValue = fitsOptions;
838 pos4 = keyValue.find('=');
839 if (pos4 == string::npos)
840 {
841 *fLog << err << "Error while parsing comment \"" << comment << "\" from variable " << parentVarName + dataMember->GetName() << endl;
842 return kFALSE;
843 }
844 key = Trim(keyValue.substr(0, pos4));
845 value = Trim(keyValue.substr(pos4+1, pos3));
846 fitsTokens[key] = value;
847// *fLog << err << "key: " << key << " value: " << value << endl;
848 }
849
850 TString colName = parentVarName + dataMember->GetName();
851
852 if (fitsTokens.find("unit") != fitsTokens.end())
853 unit = fitsTokens["unit"];
854 if (fitsTokens.find("name") != fitsTokens.end())
855 colName = fitsTokens["name"];
856
857 //check for column veto
858 if (std::find(fVetoedColumns.begin(), fVetoedColumns.end(), colName.Data())!=fVetoedColumns.end())
859 {
860 *fLog << inf << "Vetoing column " << colName.Data() << endl;
861 continue;
862 }
863
864 // set the array size
865 TString dataType = dataMember->GetTrueTypeName();
866
867 uint32_t dataLength = 0;
868 switch (dataMember->GetArrayDim())
869 {
870 case 0:
871 dataLength = 1;
872 break;
873 case 1:
874 dataLength = dataMember->GetMaxIndex(0);
875 break;
876 default:
877 *fLog << err << "n-dimensional array should have been discarded already " << colName.Data();
878 break;
879 };
880// if (dataLength == 0)
881// continue;
882
883 if (dataMember->Property() & G__BIT_ISCLASS)
884 {
885 // special treatment for classes
886 uint32_t arraySize = 0;
887 TString typeName;
888
889 if (strncmp(dataMember->GetTrueTypeName(), "MArray", 6) == 0)
890 {
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 if (strcmp(dataMember->GetTrueTypeName(), "MArrayB*") == 0)
904 {
905 typeName = "UChar_t";
906 arraySize = (*((MArrayB**)((char*)baseAdr + dataMember->GetOffset())))->GetSize();
907 InitSingleColumn(tableName,
908 arraySize,
909 typeName.Data(),
910 (char*)(*((MArrayB**)((char*)baseAdr + dataMember->GetOffset())))->GetArray(),
911 colName.Data(),
912 unit,
913 comment);
914 }
915 else if (strcmp(dataMember->GetTrueTypeName(), "MArrayF*") == 0)
916 {
917 typeName = "TFloat_t";
918 arraySize = (*((MArrayF**)((char*)baseAdr + dataMember->GetOffset())))->GetSize();
919 InitSingleColumn(tableName,
920 arraySize,
921 typeName.Data(),
922 (char*)(*((MArrayF**)((char*)baseAdr + dataMember->GetOffset())))->GetArray(),
923 colName.Data(),
924 unit,
925 comment);
926 }
927
928 else {
929 *fLog << err << dataMember->GetTrueTypeName() << " not yet implemented." << endl;
930 return kFALSE;
931 }
932
933
934 continue;
935 }
936 else if (strcmp(dataMember->GetTrueTypeName(), "TClonesArray") == 0)
937 {
938 *fLog << warn << "I'm skipping the TClonesArray for now" << endl;
939 continue;
940 // each TClonesArray requires a FITS table by itself.
941 MClonesArrayHelper * clHelper;
942
943 TClonesArray * cloneArray = (TClonesArray*)((char*)baseAdr + dataMember->GetOffset());
944 Bool_t status;
945 clHelper = new MClonesArrayHelper(cloneArray, fLog, status);
946 if (!status) return status;
947
948 fClHelper[tableName].push_back(clHelper);
949
950 // add one column in the parent table of the TClonesArray to store the
951 // number of entries in the TClonesArray.
952 InitSingleColumn(tableName,
953 1,
954 "UInt_t",
955 clHelper->GetArraySizePtr(),
956 colName.Data(),
957 unit,
958 comment);
959
960 // initialize the columns of the new FITS table, which will store the
961 // data entries of the TClonesArray
962 if (InitColumns(TString("noName"),
963 colName + "_",
964 fitsTable,
965 clHelper->GetDataBuffer(),
966 cloneArray->GetClass())
967 == kFALSE)
968 return kFALSE;
969
970 // the columns are initialized. We can create the FITS table
971 if (clHelper->OpenFitsTable(GetFileName(), dataMember->GetName(),
972 fOpenOption, fLog) == kFALSE)
973 return kFALSE;
974 }
975
976 else
977 {
978 // the current container has a variable of an other class. We create
979 // also columns of this other class in the same table
980 TClass * newClassDef = TClass::GetClass(dataMember->GetTrueTypeName(), kFALSE, kTRUE);
981 if (newClassDef)
982 {
983 if (InitColumns(tableName, colName + ".", fitsTable, (char*)baseAdr + dataMember->GetOffset(),
984 newClassDef) == kFALSE)
985 return kFALSE;
986 }
987 else
988 *fLog << warn << "Cannot write data of class " << colName + "." + dataMember->GetTrueTypeName() << endl;
989 }
990 continue;
991 }
992
993 InitSingleColumn(tableName,
994 dataLength,
995 dataType.Data(),
996 (char*)baseAdr + dataMember->GetOffset(),
997 colName.Data(),
998 unit,
999 comment);
1000
1001 }
1002 return kTRUE;
1003}
1004void MWriteFitsFile::InitSingleColumn(const TString& tableName,
1005 uint32_t count,
1006 const string& typeName,
1007 void* dataPointer,
1008 const string& columnName,
1009 const string& unit,
1010 const string& comment)
1011{
1012 if (!fTableObjectCreated[tableName])
1013 {
1014 *fLog << err << "ERROR: Cannot init column " << columnName << " before assigning object: " << tableName << endl;
1015 return;
1016 }
1017 int typeFound = 0;
1018 char typeChar = '0';
1019 if ((typeName == "bool") || (typeName == "Bool_t") || (typeName == "L"))
1020 {
1021 typeChar = 'L';
1022 typeFound++;
1023 }
1024 if ((typeName == "char") || (typeName == "Char_t") || (typeName == "S"))
1025 {
1026 typeChar = 'A';
1027 typeFound++;
1028 }
1029 if ((typeName == "unsigned char") || (typeName == "UChar_t") || (typeName == "B"))
1030 {
1031 typeChar = 'A';
1032// *fLog << warn << "Converting unsigned char to char in fits file" << endl;
1033 typeFound++;
1034 }
1035 if ((typeName == "short") || (typeName == "Short_t") || (typeName == "I"))
1036 {
1037 typeChar = 'I';
1038 typeFound++;
1039 }
1040 if ((typeName == "unsigned short") || (typeName == "UShort_t") || (typeName == "U"))
1041 {
1042 typeChar = 'I';
1043// *fLog << warn << "Converting unsigned short to short in fits file" << endl;
1044 typeFound++;
1045 }
1046 if ((typeName == "int") || (typeName == "Int_t") || (typeName == "V"))
1047 {
1048 typeChar = 'J';
1049 typeFound++;
1050 }
1051 if ((typeName == "unsigned int") || (typeName == "UInt_t") || (typeName == "V"))
1052 {
1053 typeChar = 'J';
1054// *fLog << warn << "Converting unsigned int to int in fits file" << endl;
1055 typeFound++;
1056 }
1057 if ((typeName == "long long") || (typeName == "Long64_t") || (typeName == "K"))
1058 {
1059 typeChar = 'K';
1060 typeFound++;
1061 }
1062 if ((typeName == "unsigned long long") || (typeName == "ULong64_t") || (typeName == "W"))
1063 {
1064 typeChar = 'K';
1065// *fLog << warn << "Converting unsigned long to long in fits file" << endl;
1066 typeFound++;
1067 }
1068 if ((typeName == "float") || (typeName=="TFloat_t") || (typeName == "E"))
1069 {
1070 typeChar = 'E';
1071 typeFound++;
1072 }
1073 if ((typeName == "double") || (typeName == "TDouble_t") || (typeName == "D"))
1074 {
1075 typeChar = 'D';
1076 typeFound++;
1077 }
1078// if ((typeName == "char*") || (typeName == "A"))
1079// {
1080// typeFound++;
1081// }
1082 if (typeFound != 1)
1083 {
1084 *fLog << err << "We have a problem with the data type: " << typeName << endl;
1085 return;
1086 }
1087 uint32_t colWidth = 0;
1088 switch (typeChar)
1089 {
1090 case 'L': colWidth = 1*count; break;
1091 case 'A': colWidth = 1*count; break;
1092 case 'B': colWidth = 1*count; break;
1093 case 'I': colWidth = 2*count; break;
1094 case 'J': colWidth = 4*count; break;
1095 case 'K': colWidth = 8*count; break;
1096 case 'E': colWidth = 4*count; break;
1097 case 'D': colWidth = 8*count; break;
1098 default:
1099 *fLog << err << "ERROR: typeChar could not be resolved to an actual type" << endl;
1100 };
1101 //check for type remapping here
1102 if (fBytesPerSamples.find(columnName) != fBytesPerSamples.end())
1103 {
1104 if (typeChar != 'A')
1105 {
1106 *fLog << err << "Attempt to remap type " << typeChar << " to " << fBytesPerSamples[columnName] << " is only allowed on bytes (variable name: " << columnName << "). Ignoring column" << endl;
1107 return;
1108 }
1109 uint32_t bytesPerSample = fBytesPerSamples.find(columnName)->second;
1110 if (colWidth%bytesPerSample != 0)
1111 {
1112 *fLog << err << "Type remapping cannot be done using " << bytesPerSample << " bytes per sample on an array of char of size " << colWidth << ". Ignoring column " << columnName << endl;
1113 return;
1114 }
1115 switch (bytesPerSample)
1116 {
1117 case 1: count = count/1; typeChar = 'A'; break;
1118 case 2: count = count/2; typeChar = 'I'; break;
1119 case 4: count = count/4; typeChar = 'J'; break;
1120 case 8: count = count/8; typeChar = 'K'; break;
1121 default:
1122 *fLog << err << "ERROR: num bytes per sample = " << bytesPerSample << " should have been forbidden already" << endl;
1123 }
1124
1125 }
1126
1127 fDataPointers[tableName].push_back(dataPointer);
1128 fTypeChars[tableName].push_back(typeChar);
1129 fColSizes[tableName].push_back(count);
1130 fColWidth[tableName].push_back(colWidth);
1131
1132 //FIXME ofits does not allow for much liberty regarding the size of the column names.
1133 //Truncating them badly here, will probably cause other problems -> Modify ofits.h instead
1134 string truncatedName=columnName.substr((columnName.size()>40)?columnName.size()-40:0,columnName.size());
1135 string truncatedComment = comment.substr((comment.size()>10)?comment.size()-10:0,comment.size());
1136// *fLog << warn << "In table " << tableName << " Adding column |" << truncatedName << "| |" << truncatedComment << "| |" << count << "| |" << typeChar;
1137// *fLog << warn << "| Real: "<< columnName << " comment: " << comment << endl;
1138 fFitsTables[tableName]->AddColumn(count, typeChar, truncatedName, unit, truncatedComment);
1139}
1140
1141void MWriteFitsFile::writeOneRow(const TString& tableName)
1142{
1143 if (!fTableHeaderWritten[tableName])
1144 {
1145 for (vector<ofits::Key>::const_iterator it = fHeaderKeys.begin(); it != fHeaderKeys.end(); it++)
1146 fFitsTables[tableName]->SetRaw(it->key, it->value, it->comment);
1147 fFitsTables[tableName]->WriteTableHeader(tableName.Data());
1148 fTableHeaderWritten[tableName] = true;
1149 }
1150 if (!fTableObjectCreated[tableName])
1151 {
1152 *fLog << err << "This is not good. Please initialize the fits table before writing to it: " << tableName << endl;
1153 return;
1154 }
1155 //first calculate the size of one row
1156 uint32_t rowWidth = 0;
1157 for (uint32_t i=0;i<fTypeChars[tableName].size();i++)
1158 rowWidth += fColWidth[tableName][i];
1159 unsigned char* tempBuffer = new unsigned char[rowWidth];
1160 //then copy the data to be written contiguously
1161 uint32_t bytesCounter = 0;
1162 for (uint32_t i=0;i<fDataPointers[tableName].size();i++)
1163 {
1164 memcpy(&tempBuffer[bytesCounter], fDataPointers[tableName][i], fColWidth[tableName][i]);
1165 bytesCounter+=fColWidth[tableName][i];
1166 }
1167 if (fFitsTables[tableName]->WriteRow(tempBuffer, bytesCounter) == false)
1168 *fLog << err << "Error while writing to FITS table " << tableName << endl;
1169 else
1170 fFitsTables[tableName]->FlushNumRows();
1171
1172 delete[] tempBuffer;
1173}
1174Bool_t MWriteFitsFile::ReInit(MParList *pList)
1175{
1176 if (fRule.Length() == 0)
1177 // there is not rule defined. We keep the old file
1178 return MWriteFile::ReInit(pList);
1179
1180 MRead *read = (MRead*)pList->FindTask("MRead");
1181 if (!read)
1182 {
1183 *fLog << err;
1184 *fLog << "ERROR: No Task 'MRead' found in the tasklist. This task is" << endl;
1185 *fLog << " necessary to get the filename. Without a read-filename" << endl;
1186 *fLog << " no output-filename can be created... abort." << endl;
1187 *fLog << endl;
1188 return kFALSE;
1189 }
1190
1191
1192 // close the current files
1193 CloseTopLevelGroup();
1194 for (std::map<TString,ofits*>::iterator it=fFitsTables.begin(); it!=fFitsTables.end(); it++)
1195 {
1196 (it->second)->close();
1197 delete it->second;
1198 }
1199 fFitsTables.clear();
1200 fDataPointers.clear();
1201 fTypeChars.clear();
1202 fColSizes.clear();
1203 fColWidth.clear();
1204 fTableObjectCreated.clear();
1205 fTableHeaderWritten.clear();
1206 DeleteArrayHelper();
1207 fClHelper.clear();
1208
1209 // get new filename
1210 const TString readFileName = read->GetFullFileName();
1211 const TString newname = MWriteRootFile::SubstituteName(fRule, readFileName);
1212
1213 // create new files
1214 OpenTopLevelGroup(newname.Data());
1215 if (!IsFileOpen())
1216 return kFALSE;
1217
1218
1219 MRawRunHeader* header = (MRawRunHeader*)pList->FindObject("MRawRunHeader");
1220 SetupHeaderKeys(*header);
1221
1222 if (GetContainer(pList) == kFALSE)
1223 return kFALSE;
1224
1225 // do, what has to be done in ReInit.
1226 return MWriteFile::ReInit(pList);
1227
1228}
1229
1230void MWriteFitsFile::DeleteArrayHelper()
1231{
1232 map<TString, list<MArrayHelperBase *> >::iterator i_helper1 = fClHelper.begin();
1233 while (i_helper1 != fClHelper.end())
1234 {
1235 list<MArrayHelperBase *>::iterator i_helper2 = i_helper1->second.begin();
1236 while(i_helper2 != i_helper1->second.end())
1237 {
1238 delete *i_helper2;
1239
1240 i_helper2++;
1241 }
1242
1243 i_helper1++;
1244 }
1245}
Note: See TracBrowser for help on using the repository browser.