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

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