source: branches/MarsWobble/mfileio/MWriteFitsFile.cc@ 18277

Last change on this file since 18277 was 18161, checked in by ftemme, 10 years ago
It is possible that two objects of the same class will be added to MWriteFitsFile. For these cases it is better that the column name is derived from the name of the object and not from the name of the class
File size: 46.1 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 char attrName[10];
528 sprintf(attrName, "CLNAME%d", num);
529 num++;
530
531 InitAttr(attrName,
532 "A",
533 (void*)i_subTable->first.Data(),
534 NULL,
535 "MARS container name",
536 fitsTable);
537
538 i_subTable++;
539 }
540
541 // in case not all sub-tables were removed, we can now create the FITS table
542 if (i_table->second.size() > 0)
543 {
544 // create the DOL of the table. It will be something like:
545 // fileNameNoExt_dataName.fits[dataName]
546 TString dol = fileNameNoExt;
547 dol += "_";
548 dol += i_table->first;
549 dol += ".fits";
550
551 if (fOpenOption == "RECREATE")
552 // remove the file
553 unlink(dol.Data());
554
555 dol += "[";
556 dol += i_table->first;
557 dol += "]";
558// *fLog << err << "Reiner would have opened fits file: " << dol.Data() << endl;
559 //exit(0);
560 //fitsTable->open(dol.Data());
561
562 // attach the new table to the top level group
563 //TODO make sure that this attach below is not needed...
564 //iTopFitsGroup->second.Attach(fitsTable);
565 }
566
567 i_table++;
568 }
569
570 return kTRUE;
571}
572void MWriteFitsFile::InitAttr(const char* attrName,
573 const char* dataType,
574 void* var,
575 const char* unit,
576 const char* comment,
577 ofits* outFile)
578{
579 if (outFile == NULL)
580 return;
581 string dts(dataType);
582 string ans(attrName);
583 string cs;
584 if (comment != NULL)
585 cs = string(comment);
586 else
587 cs = "";
588 ostringstream val;
589 if ((dts == "bool") || (dts == "Bool_t") || (dts == "L"))
590 {
591 outFile->SetBool(ans, ((bool*)(var))[0], cs);
592 return;
593 }
594 if ((dts == "char") || (dts == "Char_t") || (dts == "S"))
595 {
596 val << ((char*)(var))[0];
597 outFile->SetStr(ans, val.str(), cs);
598 return;
599 }
600 if ((dts == "unsigned char") || (dts == "UChar_t") || (dts == "B"))
601 {
602 val << ((unsigned char*)(var))[0];
603 outFile->SetStr(ans, val.str(), cs);
604 return;
605 }
606 if ((dts == "short") || (dts == "Short_t") || (dts == "I"))
607 {
608 val << ((short*)(var))[0];
609 outFile->SetStr(ans, val.str(), cs);
610 return;
611 }
612 if ((dts == "unsigned short") || (dts == "UShort_t") || (dts == "U"))
613 {
614 val << ((unsigned short*)(var))[0];
615 outFile->SetStr(ans, val.str(), cs);
616 return;
617 }
618 if ((dts == "int") || (dts == "Int_t") || (dts == "V"))
619 {
620 outFile->SetInt(ans, ((int*)(var))[0], cs);
621 return;
622 }
623 if ((dts == "unsigned int") || (dts == "UInt_t") || (dts == "V"))
624 {
625 outFile->SetInt(ans, ((unsigned int*)(var))[0], cs);
626 return;
627 }
628 if ((dts == "long long") || (dts == "Long64_t") || (dts == "K"))
629 {
630 outFile->SetInt(ans, ((long long*)(var))[0], cs);
631 return;
632 }
633 if ((dts == "unsigned long long") || (dts == "ULong64_t") || (dts == "W"))
634 {
635 val << ((unsigned long long*)(var))[0];
636 outFile->SetStr(ans, val.str(), cs);
637 return;
638 }
639 if ((dts == "float") || (dts == "TFloat_t") || (dts == "E"))
640 {
641 outFile->SetFloat(ans, ((float*)(var))[0], cs);
642 return;
643 }
644 if ((dts == "double") || (dts == "TDouble_t") || (dts == "D"))
645 {
646 outFile->SetFloat(ans, ((double*)(var))[0], cs);
647 return;
648 }
649 if ((dts == "char*") || (dts == "A"))
650 {
651 outFile->SetStr(ans, string((char*)(var)), cs);
652 return;
653 }
654 //trigger error
655 *fLog << err << "Format string not recognized while adding header entry " << ans << " with type " << dts << endl;
656}
657// --------------------------------------------------------------------------
658//
659// This method is called when new data should be written to the FITS table.
660// In general only one row is written. (There are exceptions for Arrays).
661// A new row to a FITS table is written if at least for one container
662// (one sub-table) the ReadyToSave - flag is set.
663//
664Bool_t MWriteFitsFile::CheckAndWrite()
665{
666// try {
667
668 // loop over all tables
669 map<TString, map<TString, MFitsSubTable> >::iterator i_table =
670 fSubTables.begin();
671 while (i_table != fSubTables.end())
672 {
673 // is this table open?
674 if (fFitsTables.find(i_table->first) != fFitsTables.end())
675 {
676
677 // loop over all sub-tables
678 map<TString, MFitsSubTable>::iterator i_subTable = i_table->second.begin();
679 while (i_subTable != i_table->second.end())
680 {
681
682 if (i_subTable->second.GetContainer()->IsReadyToSave())
683 {
684 // first write the TClonesArray and set the size of the arrays
685 list<MArrayHelperBase*> & clHelper = fClHelper[i_table->first];
686 list<MArrayHelperBase*>::iterator i_clHelper = clHelper.begin();
687 while (i_clHelper != clHelper.end())
688 {
689 // write all entries in the TClonesArray in its FITS table
690 //removed this write because I do it elsewhere. is that alright ?
691 // (*i_clHelper)->Write();
692 i_clHelper++;
693 }
694
695 // write one line to this table
696 writeOneRow(i_table->first);
697// fFitsTables[i_table->first].Write();
698 break;
699 }
700 i_subTable++;
701 }
702 }
703
704 i_table++;
705 }
706// }
707// catch (exception &e)
708 // {
709 // *fLog << err << e.what() << endl;
710 // return kFALSE;
711 // }
712
713 return kTRUE;
714}
715
716string MWriteFitsFile::Trim(const string &str)
717{
718 // Trim Both leading and trailing spaces
719 const size_t first = str.find_first_not_of(' '); // Find the first character position after excluding leading blank spaces
720 const size_t last = str.find_last_not_of(' '); // Find the first character position from reverse af
721
722 // if all spaces or empty return an empty string
723 if (string::npos==first || string::npos==last)
724 return string();
725
726 return str.substr(first, last-first+1);
727}
728
729Bool_t MWriteFitsFile::VetoColumn(const std::string& colName)
730{
731 for (std::vector<string>::iterator it=fVetoedColumns.begin(); it != fVetoedColumns.end(); it++)
732 if (*it == colName)
733 {
734 *fLog << warn << "Warning: column " << colName << " is being vetoed twice" << endl;
735 return kFALSE;
736 }
737 fVetoedColumns.push_back(colName);
738 return kTRUE;
739}
740
741Bool_t MWriteFitsFile::SetBytesPerSample(const std::string& colName, uint32_t numBytes)
742{
743 for (map<string, uint32_t>::iterator it=fBytesPerSamples.begin(); it!=fBytesPerSamples.end(); it++)
744 if (it->first == colName)
745 {
746 *fLog << warn << "Warning: column " << colName << " bytes per sample is being redefined twice" << endl;
747 return kFALSE;
748 }
749 if (numBytes != 1 && numBytes != 2 && numBytes != 4 && numBytes != 8)
750 {
751 *fLog << warn << "Only powers of two are allowed for types re-mapping." << endl;
752 return kFALSE;
753 }
754 fBytesPerSamples[colName] = numBytes;
755 return kTRUE;
756}
757
758// --------------------------------------------------------------------------
759//
760// Initialize all columns in "fitsTable" of the class "classDef". The data
761// of this class are stored in a buffer, beginning at "baseAdr".
762//
763Bool_t MWriteFitsFile::InitColumns(const TString & tableName,
764 const TString & parentVarName,
765 ofits* fitsTable,
766 void * baseAdr,
767 TClass * classDef)
768{
769 // get all data members of the class
770 TList * dataMembers = classDef->GetListOfDataMembers();
771 TIter next(dataMembers);
772 TDataMember * dataMember;
773
774 // loop over all data members
775 while ((dataMember = (TDataMember*)next()) != NULL)
776 {
777 if (!dataMember->IsPersistent())
778 // don't store this variable
779 continue;
780
781 if (dataMember->Property() & ( G__BIT_ISENUM | G__BIT_ISSTATIC))
782 // we cannot store this
783 continue;
784
785 if (strcmp(dataMember->GetTrueTypeName(), "TClass*") == 0)
786 // we don't want to store this.
787 continue;
788
789 // is it an array of more than 1 dimension?
790 if (dataMember->GetArrayDim() > 1)
791 {
792 *fLog << err << "Two and more dimensional arrays of member variables"
793 " are not supported." << endl;
794 *fLog << "See variable " << dataMember->GetName() <<
795 " in container " << classDef->GetName() << endl;
796 return kFALSE;
797 }
798
799
800 // replace � by **2 in the comment field
801 string comment(dataMember->GetTitle());
802 string::size_type pos1, pos2;
803 if ((pos1 = comment.find(178)) != string::npos)
804 comment.replace(pos1, 1, "**2");
805
806 // get the optional mapping to the fits column names
807 string fitsOptions="";
808 string unit="unit";
809
810 pos1 = comment.find("{fits: ");
811 pos2 = comment.find('}');
812 std::map<string, string> fitsTokens;
813 if (pos1 != string::npos && pos2 != string::npos)
814 {
815 fitsOptions=comment.substr(pos1+7, (pos2-pos1)-7);
816 //do we have more than one option ?
817 string::size_type pos3= fitsOptions.find_first_of(';');
818 string::size_type pos4 = string::npos;
819 string key="";
820 string value="";
821 string keyValue="";
822 while (pos3 != string::npos)
823 {//we have at least 2 options left
824 // *fLog << err << "fitsOptions: " << fitsOptions << endl;
825 keyValue = fitsOptions.substr(0,pos3);
826 // *fLog << err << "keyValue: " << keyValue << endl;
827 pos4 = keyValue.find('=');
828 if (pos4 == string::npos)
829 {
830 *fLog << err << "Error while parsing comment \"" << comment << "\" from variable " << parentVarName + dataMember->GetName() << endl;
831 return kFALSE;
832 }
833 key = Trim(keyValue.substr(0, pos4));
834 value = Trim(keyValue.substr(pos4+1, pos3));
835 fitsTokens[key] = value;
836 // *fLog << err << "key: " << key << " value: " << value << endl;
837 fitsOptions = fitsOptions.substr(pos3+1, fitsOptions.size());
838 pos3 = fitsOptions.find_first_of(';');
839 }
840// *fLog << err << "fitsOptions: " << fitsOptions << endl;
841 keyValue = fitsOptions;
842 pos4 = keyValue.find('=');
843 if (pos4 == string::npos)
844 {
845 *fLog << err << "Error while parsing comment \"" << comment << "\" from variable " << parentVarName + dataMember->GetName() << endl;
846 return kFALSE;
847 }
848 key = Trim(keyValue.substr(0, pos4));
849 value = Trim(keyValue.substr(pos4+1, pos3));
850 fitsTokens[key] = value;
851// *fLog << err << "key: " << key << " value: " << value << endl;
852 }
853
854 TString colName = parentVarName + dataMember->GetName();
855
856 if (fitsTokens.find("unit") != fitsTokens.end())
857 unit = fitsTokens["unit"];
858 if (fitsTokens.find("name") != fitsTokens.end())
859 colName = fitsTokens["name"];
860
861 //check for column veto
862 if (std::find(fVetoedColumns.begin(), fVetoedColumns.end(), colName.Data())!=fVetoedColumns.end())
863 {
864 *fLog << inf << "Vetoing column " << colName.Data() << endl;
865 continue;
866 }
867
868 // set the array size
869 TString dataType = dataMember->GetTrueTypeName();
870
871 uint32_t dataLength = 0;
872 switch (dataMember->GetArrayDim())
873 {
874 case 0:
875 dataLength = 1;
876 break;
877 case 1:
878 dataLength = dataMember->GetMaxIndex(0);
879 break;
880 default:
881 *fLog << err << "n-dimensional array should have been discarded already " << colName.Data();
882 break;
883 };
884// if (dataLength == 0)
885// continue;
886
887 if (dataMember->Property() & G__BIT_ISCLASS)
888 {
889 // special treatment for classes
890 uint32_t arraySize = 0;
891 TString typeName;
892
893 if (strncmp(dataMember->GetTrueTypeName(), "MArray", 6) == 0)
894 {
895 if (strcmp(dataMember->GetTrueTypeName(), "MArrayS*") == 0)
896 {
897 typeName = "UShort_t";
898 arraySize = (*((MArrayS**)((char*)baseAdr + dataMember->GetOffset())))->GetSize();
899 InitSingleColumn(tableName,
900 arraySize,
901 typeName.Data(),
902 (char*)(*((MArrayS**)((char*)baseAdr + dataMember->GetOffset())))->GetArray(),
903 colName.Data(),
904 unit,
905 comment);
906 }
907 else if (strcmp(dataMember->GetTrueTypeName(), "MArrayB*") == 0)
908 {
909 typeName = "UChar_t";
910 arraySize = (*((MArrayB**)((char*)baseAdr + dataMember->GetOffset())))->GetSize();
911 InitSingleColumn(tableName,
912 arraySize,
913 typeName.Data(),
914 (char*)(*((MArrayB**)((char*)baseAdr + dataMember->GetOffset())))->GetArray(),
915 colName.Data(),
916 unit,
917 comment);
918 }
919 else if (strcmp(dataMember->GetTrueTypeName(), "MArrayF*") == 0)
920 {
921 typeName = "TFloat_t";
922 arraySize = (*((MArrayF**)((char*)baseAdr + dataMember->GetOffset())))->GetSize();
923 InitSingleColumn(tableName,
924 arraySize,
925 typeName.Data(),
926 (char*)(*((MArrayF**)((char*)baseAdr + dataMember->GetOffset())))->GetArray(),
927 colName.Data(),
928 unit,
929 comment);
930 }
931
932 else {
933 *fLog << err << dataMember->GetTrueTypeName() << " not yet implemented." << endl;
934 return kFALSE;
935 }
936
937
938 continue;
939 }
940 else if (strcmp(dataMember->GetTrueTypeName(), "TClonesArray") == 0)
941 {
942 *fLog << warn << "I'm skipping the TClonesArray for now" << endl;
943 continue;
944 // each TClonesArray requires a FITS table by itself.
945 MClonesArrayHelper * clHelper;
946
947 TClonesArray * cloneArray = (TClonesArray*)((char*)baseAdr + dataMember->GetOffset());
948 Bool_t status;
949 clHelper = new MClonesArrayHelper(cloneArray, fLog, status);
950 if (!status) return status;
951
952 fClHelper[tableName].push_back(clHelper);
953
954 // add one column in the parent table of the TClonesArray to store the
955 // number of entries in the TClonesArray.
956 InitSingleColumn(tableName,
957 1,
958 "UInt_t",
959 clHelper->GetArraySizePtr(),
960 colName.Data(),
961 unit,
962 comment);
963
964 // initialize the columns of the new FITS table, which will store the
965 // data entries of the TClonesArray
966 if (InitColumns(TString("noName"),
967 colName + "_",
968 fitsTable,
969 clHelper->GetDataBuffer(),
970 cloneArray->GetClass())
971 == kFALSE)
972 return kFALSE;
973
974 // the columns are initialized. We can create the FITS table
975 if (clHelper->OpenFitsTable(GetFileName(), dataMember->GetName(),
976 fOpenOption, fLog) == kFALSE)
977 return kFALSE;
978 }
979
980 else
981 {
982 // the current container has a variable of an other class. We create
983 // also columns of this other class in the same table
984 TClass * newClassDef = TClass::GetClass(dataMember->GetTrueTypeName(), kFALSE, kTRUE);
985 if (newClassDef)
986 {
987 if (InitColumns(tableName, colName + ".", fitsTable, (char*)baseAdr + dataMember->GetOffset(),
988 newClassDef) == kFALSE)
989 return kFALSE;
990 }
991 else
992 *fLog << warn << "Cannot write data of class " << colName + "." + dataMember->GetTrueTypeName() << endl;
993 }
994 continue;
995 }
996
997 InitSingleColumn(tableName,
998 dataLength,
999 dataType.Data(),
1000 (char*)baseAdr + dataMember->GetOffset(),
1001 colName.Data(),
1002 unit,
1003 comment);
1004
1005 }
1006 return kTRUE;
1007}
1008void MWriteFitsFile::InitSingleColumn(const TString& tableName,
1009 uint32_t count,
1010 const string& typeName,
1011 void* dataPointer,
1012 const string& columnName,
1013 const string& unit,
1014 const string& comment)
1015{
1016 if (!fTableObjectCreated[tableName])
1017 {
1018 *fLog << err << "ERROR: Cannot init column " << columnName << " before assigning object: " << tableName << endl;
1019 return;
1020 }
1021 int typeFound = 0;
1022 char typeChar = '0';
1023 if ((typeName == "bool") || (typeName == "Bool_t") || (typeName == "L"))
1024 {
1025 typeChar = 'L';
1026 typeFound++;
1027 }
1028 if ((typeName == "char") || (typeName == "Char_t") || (typeName == "S"))
1029 {
1030 typeChar = 'A';
1031 typeFound++;
1032 }
1033 if ((typeName == "unsigned char") || (typeName == "UChar_t") || (typeName == "B"))
1034 {
1035 typeChar = 'A';
1036// *fLog << warn << "Converting unsigned char to char in fits file" << endl;
1037 typeFound++;
1038 }
1039 if ((typeName == "short") || (typeName == "Short_t") || (typeName == "I"))
1040 {
1041 typeChar = 'I';
1042 typeFound++;
1043 }
1044 if ((typeName == "unsigned short") || (typeName == "UShort_t") || (typeName == "U"))
1045 {
1046 typeChar = 'I';
1047// *fLog << warn << "Converting unsigned short to short in fits file" << endl;
1048 typeFound++;
1049 }
1050 if ((typeName == "int") || (typeName == "Int_t") || (typeName == "V"))
1051 {
1052 typeChar = 'J';
1053 typeFound++;
1054 }
1055 if ((typeName == "unsigned int") || (typeName == "UInt_t") || (typeName == "V"))
1056 {
1057 typeChar = 'J';
1058// *fLog << warn << "Converting unsigned int to int in fits file" << endl;
1059 typeFound++;
1060 }
1061 if ((typeName == "long long") || (typeName == "Long64_t") || (typeName == "K"))
1062 {
1063 typeChar = 'K';
1064 typeFound++;
1065 }
1066 if ((typeName == "unsigned long long") || (typeName == "ULong64_t") || (typeName == "W"))
1067 {
1068 typeChar = 'K';
1069// *fLog << warn << "Converting unsigned long to long in fits file" << endl;
1070 typeFound++;
1071 }
1072 if ((typeName == "float") || (typeName=="TFloat_t") || (typeName == "E"))
1073 {
1074 typeChar = 'E';
1075 typeFound++;
1076 }
1077 if ((typeName == "double") || (typeName == "TDouble_t") || (typeName == "D"))
1078 {
1079 typeChar = 'D';
1080 typeFound++;
1081 }
1082// if ((typeName == "char*") || (typeName == "A"))
1083// {
1084// typeFound++;
1085// }
1086 if (typeFound != 1)
1087 {
1088 *fLog << err << "We have a problem with the data type: " << typeName << endl;
1089 return;
1090 }
1091 uint32_t colWidth = 0;
1092 switch (typeChar)
1093 {
1094 case 'L': colWidth = 1*count; break;
1095 case 'A': colWidth = 1*count; break;
1096 case 'B': colWidth = 1*count; break;
1097 case 'I': colWidth = 2*count; break;
1098 case 'J': colWidth = 4*count; break;
1099 case 'K': colWidth = 8*count; break;
1100 case 'E': colWidth = 4*count; break;
1101 case 'D': colWidth = 8*count; break;
1102 default:
1103 *fLog << err << "ERROR: typeChar could not be resolved to an actual type" << endl;
1104 };
1105 //check for type remapping here
1106 if (fBytesPerSamples.find(columnName) != fBytesPerSamples.end())
1107 {
1108 if (typeChar != 'A')
1109 {
1110 *fLog << err << "Attempt to remap type " << typeChar << " to " << fBytesPerSamples[columnName] << " is only allowed on bytes (variable name: " << columnName << "). Ignoring column" << endl;
1111 return;
1112 }
1113 uint32_t bytesPerSample = fBytesPerSamples.find(columnName)->second;
1114 if (colWidth%bytesPerSample != 0)
1115 {
1116 *fLog << err << "Type remapping cannot be done using " << bytesPerSample << " bytes per sample on an array of char of size " << colWidth << ". Ignoring column " << columnName << endl;
1117 return;
1118 }
1119 switch (bytesPerSample)
1120 {
1121 case 1: count = count/1; typeChar = 'A'; break;
1122 case 2: count = count/2; typeChar = 'I'; break;
1123 case 4: count = count/4; typeChar = 'J'; break;
1124 case 8: count = count/8; typeChar = 'K'; break;
1125 default:
1126 *fLog << err << "ERROR: num bytes per sample = " << bytesPerSample << " should have been forbidden already" << endl;
1127 }
1128
1129 }
1130
1131 fDataPointers[tableName].push_back(dataPointer);
1132 fTypeChars[tableName].push_back(typeChar);
1133 fColSizes[tableName].push_back(count);
1134 fColWidth[tableName].push_back(colWidth);
1135
1136 //FIXME ofits does not allow for much liberty regarding the size of the column names.
1137 //Truncating them badly here, will probably cause other problems -> Modify ofits.h instead
1138 string truncatedName=columnName.substr((columnName.size()>40)?columnName.size()-40:0,columnName.size());
1139 string truncatedComment = comment.substr((comment.size()>10)?comment.size()-10:0,comment.size());
1140// *fLog << warn << "In table " << tableName << " Adding column |" << truncatedName << "| |" << truncatedComment << "| |" << count << "| |" << typeChar;
1141// *fLog << warn << "| Real: "<< columnName << " comment: " << comment << endl;
1142 fFitsTables[tableName]->AddColumn(count, typeChar, truncatedName, unit, truncatedComment);
1143}
1144
1145void MWriteFitsFile::writeOneRow(const TString& tableName)
1146{
1147 if (!fTableHeaderWritten[tableName])
1148 {
1149 for (vector<ofits::Key>::const_iterator it = fHeaderKeys.begin(); it != fHeaderKeys.end(); it++)
1150 fFitsTables[tableName]->SetRaw(it->key, it->value, it->comment);
1151 fFitsTables[tableName]->WriteTableHeader(tableName.Data());
1152 fTableHeaderWritten[tableName] = true;
1153 }
1154 if (!fTableObjectCreated[tableName])
1155 {
1156 *fLog << err << "This is not good. Please initialize the fits table before writing to it: " << tableName << endl;
1157 return;
1158 }
1159 //first calculate the size of one row
1160 uint32_t rowWidth = 0;
1161 for (uint32_t i=0;i<fTypeChars[tableName].size();i++)
1162 rowWidth += fColWidth[tableName][i];
1163 unsigned char* tempBuffer = new unsigned char[rowWidth];
1164 //then copy the data to be written contiguously
1165 uint32_t bytesCounter = 0;
1166 for (uint32_t i=0;i<fDataPointers[tableName].size();i++)
1167 {
1168 memcpy(&tempBuffer[bytesCounter], fDataPointers[tableName][i], fColWidth[tableName][i]);
1169 bytesCounter+=fColWidth[tableName][i];
1170 }
1171 if (fFitsTables[tableName]->WriteRow(tempBuffer, bytesCounter) == false)
1172 *fLog << err << "Error while writing to FITS table " << tableName << endl;
1173 else
1174 fFitsTables[tableName]->FlushNumRows();
1175
1176 delete[] tempBuffer;
1177}
1178Bool_t MWriteFitsFile::ReInit(MParList *pList)
1179{
1180 if (fRule.Length() == 0)
1181 // there is not rule defined. We keep the old file
1182 return MWriteFile::ReInit(pList);
1183
1184 MRead *read = (MRead*)pList->FindTask("MRead");
1185 if (!read)
1186 {
1187 *fLog << err;
1188 *fLog << "ERROR: No Task 'MRead' found in the tasklist. This task is" << endl;
1189 *fLog << " necessary to get the filename. Without a read-filename" << endl;
1190 *fLog << " no output-filename can be created... abort." << endl;
1191 *fLog << endl;
1192 return kFALSE;
1193 }
1194
1195
1196 // close the current files
1197 CloseTopLevelGroup();
1198 for (std::map<TString,ofits*>::iterator it=fFitsTables.begin(); it!=fFitsTables.end(); it++)
1199 {
1200 (it->second)->close();
1201 delete it->second;
1202 }
1203 fFitsTables.clear();
1204 fDataPointers.clear();
1205 fTypeChars.clear();
1206 fColSizes.clear();
1207 fColWidth.clear();
1208 fTableObjectCreated.clear();
1209 fTableHeaderWritten.clear();
1210 DeleteArrayHelper();
1211 fClHelper.clear();
1212
1213 // get new filename
1214 const TString readFileName = read->GetFullFileName();
1215 const TString newname = MWriteRootFile::SubstituteName(fRule, readFileName);
1216
1217 // create new files
1218 OpenTopLevelGroup(newname.Data());
1219 if (!IsFileOpen())
1220 return kFALSE;
1221
1222
1223 MRawRunHeader* header = (MRawRunHeader*)pList->FindObject("MRawRunHeader");
1224 SetupHeaderKeys(*header);
1225
1226 if (GetContainer(pList) == kFALSE)
1227 return kFALSE;
1228
1229 // do, what has to be done in ReInit.
1230 return MWriteFile::ReInit(pList);
1231
1232}
1233
1234void MWriteFitsFile::DeleteArrayHelper()
1235{
1236 map<TString, list<MArrayHelperBase *> >::iterator i_helper1 = fClHelper.begin();
1237 while (i_helper1 != fClHelper.end())
1238 {
1239 list<MArrayHelperBase *>::iterator i_helper2 = i_helper1->second.begin();
1240 while(i_helper2 != i_helper1->second.end())
1241 {
1242 delete *i_helper2;
1243
1244 i_helper2++;
1245 }
1246
1247 i_helper1++;
1248 }
1249}
Note: See TracBrowser for help on using the repository browser.