source: trunk/FACT++/src/Fits.cc@ 10954

Last change on this file since 10954 was 10947, checked in by tbretz, 13 years ago
Fixed a typo.
File size: 11.7 KB
Line 
1// **************************************************************************
2/** @class FactFits
3
4@brief FITS writter for the FACT project.
5
6The FactFits class is able to open, manage and update FITS files.
7
8The file columns should be given to the class before the file is openned. Once
9a file has been created, the structure of its columns cannot be changed. Only
10row can be added.
11
12This class relies on the CCfits and CFitsIO packages.
13
14*/
15// **************************************************************************
16#include "Fits.h"
17
18#include "Time.h"
19#include "Converter.h"
20#include "MessageImp.h"
21
22//for file stats
23#include <sys/stat.h>
24
25using namespace std;
26using namespace CCfits;
27
28// --------------------------------------------------------------------------
29//
30//! This gives a standard variable to the file writter.
31//! This variable should not be related to the DIM service being logged.
32//! @param desc the description of the variable to add
33//! @param dataFormat the FITS data format corresponding to the variable to add.
34//! @param dataPointer the memory location where the variable is stored
35//! @param numDataBytes the number of bytes taken by the variable
36//
37void Fits::AddStandardColumn(Description& desc, string dataFormat, void* dataPointer, long unsigned int numDataBytes)
38{
39 //check if entry already exist
40 for (vector<Description>::iterator it=fStandardColDesc.begin(); it != fStandardColDesc.end(); it++)
41 if (it->name == desc.name)
42 return;
43 fStandardColDesc.push_back(desc);
44 fStandardFormats.push_back(dataFormat);
45 fStandardPointers.push_back(dataPointer);
46 fStandardNumBytes.push_back(numDataBytes);
47}
48// --------------------------------------------------------------------------
49//
50//! This gives the file writter access to the DIM data
51//! @param desc a vector containing the description of all the columns to log
52//! @param dataFormat a vector containing the FITS data format of all the columsn to log
53//! @param dataPointer the memory location where the DIM data starts
54//! @param numDataBytes the number of bytes taken by the DIM data.
55//
56void Fits::InitDataColumns(vector<Description> desc, vector<string>& dataFormat, void* dataPointer, int numDataBytes)
57{//we will copy this information here. It duplicates the data, which is not great, but it is the easiest way of doing it right now
58 if (desc.size() == dataFormat.size())
59 {
60 fDataColDesc = desc;
61 }
62 else
63 {
64 fDataColDesc.clear();
65 for (unsigned int i=0;i<dataFormat.size();i++)
66 {
67 ostringstream stt;
68 stt << "Data" << i;
69 fDataColDesc.push_back(Description(stt.str(), "comment", "unit"));
70 }
71 }
72 fDataFormats = dataFormat;
73 fDataPointer = dataPointer;
74 fDataNumBytes = numDataBytes;
75}
76// --------------------------------------------------------------------------
77//
78//! This opens the FITS file (after the columns have been passed)
79//! @param fileName the filename with complete or relative path of the file to open
80//! @param tableName the name of the table that will receive the logged data.
81//! @param file a pointer to an existing FITS file. If NULL, file will be opened and managed internally
82//! @param fitsCounter a pointer to the integer keeping track of the opened FITS files
83//! @param out a pointer to the MessageImp that should be used to log errors
84//! @param runNumber the runNumber for which this file is opened. 0 means nightly file.
85//
86void Fits::Open(const string& fileName, const string& tableName, FITS* file, int* fitsCounter, MessageImp* out, int runNumber)
87{
88// if (fMess)
89// delete fMess;
90// fMess = new MessageImp(out);
91 fRunNumber = runNumber;
92 fMess = out;
93 fFileName = fileName;
94 if (file == NULL)
95 {
96 try
97 {
98 fFile = new FITS(fileName, RWmode::Write);
99 }
100 catch (CCfits::FitsException e)
101 {
102 ostringstream str;
103 str << "Could not open FITS file " << fileName << " reason: " << e.message();
104 fMess->Error(str);
105 fFile = NULL;
106 return;
107 }
108 fOwner = true;
109 fNumOpenFitsFiles = fitsCounter;
110 (*fNumOpenFitsFiles)++;
111 }
112 else
113 {
114 fFile = file;
115 fOwner = false;
116 }
117 //concatenate the standard and data columns
118 //do it the inneficient way first: its easier and faster to code.
119 vector<string> allNames;
120 vector<string> allDataTypes;
121 vector<string> allUnits;
122 fTotalNumBytes = 0;
123 for (unsigned int i=0;i<fStandardColDesc.size();i++)
124 {
125 allNames.push_back(fStandardColDesc[i].name);
126 allDataTypes.push_back(fStandardFormats[i]);
127 allUnits.push_back(fStandardColDesc[i].unit);
128 fTotalNumBytes += fStandardNumBytes[i];
129 }
130 //for (int i=static_cast<int>(fDataColDesc.size())-1;i>=0;i--)
131 for (int i=0; i< static_cast<int>(fDataColDesc.size()); i++)
132 {
133 if (fDataColDesc[i].name != "")
134 allNames.push_back(fDataColDesc[i].name);
135 else
136 {
137 ostringstream stt;
138 stt << "Data" << i;
139 allNames.push_back(stt.str());
140 }
141 allDataTypes.push_back(fDataFormats[i]);
142 allUnits.push_back(fDataColDesc[i].unit);
143 }
144 fTotalNumBytes += fDataNumBytes;
145
146 bool updating = false;
147 try
148 {
149 string factTableName = tableName;
150 //first, let's check if the table already exist in the file
151 vector<string> tryToLoadName;
152 tryToLoadName.push_back(factTableName);
153 fFile->read(tryToLoadName);
154 const multimap< string, CCfits::ExtHDU * >& extMap = fFile->extension();
155 if (extMap.find(factTableName) == extMap.end())
156 {
157 for (multimap<string, CCfits::ExtHDU*>::const_iterator it=extMap.begin(); it!= extMap.end(); it++)
158 fMess->Debug(it->first);
159 fTable = fFile->addTable(factTableName, 0, allNames, allDataTypes, allUnits);
160 }
161 else
162 {
163 fTable = dynamic_cast<CCfits::Table*>(extMap.find(factTableName)->second);
164 }
165 if (!fTable)
166 {
167 fMess->Error("The table " + factTableName + " could not be created nor loaded. skipping it");
168 return;
169 }
170 fTable->makeThisCurrent();
171 fCopyBuffer = new unsigned char[fTotalNumBytes];
172 fNumRows = fTable->rows();
173 if (fNumRows !=0)
174 {//If the file already existed, then we must load its data to memory before writing to it.
175 BinTable* bTable = dynamic_cast<BinTable*>(fTable);
176 if (!bTable)
177 {
178 fMess->Error("The table " + factTableName + " could not be converted to a binary table. skipping");
179 return;
180 }
181 //read the table binary data.
182 vector<string> colName;
183 bTable->readData(true, colName);
184
185 //double check that the data was indeed read from the disk. Go through the fTable instead as colName is empty (yes, it is !)
186 map<string, Column*> cMap = fTable->column();
187 map<string, Column*>::iterator cMapIt;
188
189 for (cMapIt = cMap.begin(); cMapIt != cMap.end(); cMapIt++)
190 {
191 if (!cMapIt->second->isRead())
192 {
193 fMess->Error("Column " + cMapIt->first + "Could not be read back from the disk");
194 return;
195 }
196 }
197 updating = true;
198 }
199
200 }
201 catch(CCfits::FitsException e)
202 {
203 ostringstream str;
204 str << "Could not open or create FITS table " << tableName << " in file " << fileName << " reason: " << e.message();
205 fMess->Error(str);
206 fTable = NULL;
207 }
208
209 //As requested by Roland, the reference MjD is 0.0
210 fRefMjD = 0;//* static_cast<double*>(fStandardPointers[0]);
211 if (!updating)
212 WriteHeaderKeys();
213}
214// --------------------------------------------------------------------------
215//
216//!This writes a single header key in the currently open file.
217//!@param name the key
218//!@param value the key value
219//!@param a comment explaining the meaning of the key
220template <typename T>
221void Fits::WriteSingleHeaderKey(string name, T value, string comment)
222{
223 try
224 {
225 fTable->addKey(name, value, comment);
226 }
227 catch (CCfits::FitsException e)
228 {
229 ostringstream str;
230 str << "Could not add header keys in file " << fFileName << " reason: " << e.message();
231 fMess->Error(str);
232 }
233}
234// --------------------------------------------------------------------------
235//
236//! This writes the standard header
237//
238void Fits::WriteHeaderKeys()
239{
240 if (!fTable)
241 return;
242 string name;
243 string comment;
244
245 string stringValue;
246
247 WriteSingleHeaderKey("EXTREL", 1.0f, "Release Number");
248 WriteSingleHeaderKey("TELESCOP", "FACT", "Telescope that acquired this data");
249 WriteSingleHeaderKey("ORIGIN", "ISDC", "Institution that wrote the file");
250 WriteSingleHeaderKey("CREATOR", "FACT++ DataLogger", "Program that wrote this file");
251 stringValue = Time().GetAsStr();
252 stringValue[10]= 'T';
253 WriteSingleHeaderKey("DATE", stringValue, "File creation data");
254 WriteSingleHeaderKey("TIMESYS", "TT", "Time frame system");
255 WriteSingleHeaderKey("TIMEUNIT", "d", "Time unit");
256 WriteSingleHeaderKey("TIMEREF", "UTC", "Time reference frame");
257 WriteSingleHeaderKey("MJDREF", fRefMjD, "Modified Julian Date of origin");
258 WriteSingleHeaderKey("TSTOP", fEndMjD, "Time of the last receied data");
259}
260// --------------------------------------------------------------------------
261//
262//! This writes one line of data to the file.
263//! @param conv the converter corresponding to the service being logged
264//
265void Fits::Write(Converter* conv)
266{
267 //first copy the standard variables to the copy buffer
268 int shift = 0;
269 for (unsigned int i=0;i<fStandardNumBytes.size();i++)
270 {
271 const char *charSrc = reinterpret_cast<char*>(fStandardPointers[i]);
272 reverse_copy(charSrc, charSrc+fStandardNumBytes[i], &fCopyBuffer[shift]);
273 shift += fStandardNumBytes[i];
274 }
275
276 try
277 {
278 //now take care of the DIM data. The Converter is here for that purpose
279 conv->ToFits(&fCopyBuffer[shift], fDataPointer, fDataNumBytes);
280 }
281 catch (const runtime_error &e)
282 {
283 ostringstream str;
284 str << fFileName << ": " << e.what();
285 fMess->Error(str);
286 return;
287 }
288
289 fTable->makeThisCurrent();
290
291 int status(0);
292 if (fits_insert_rows(fTable->fitsPointer(), fNumRows, 1, &status))
293 {
294 ostringstream str;
295 str << "Inserting row into " << fFileName << " failed (fits_insert_rows, rc=" << status << ")";
296 fMess->Error(str);
297 // FIXME: What is a proper action here?
298 }
299
300 fNumRows++;
301
302 //the first standard variable is the current MjD
303 if (fEndMjD==0)
304 {
305 const double doubleValue = *reinterpret_cast<double*>(fStandardPointers[0]);
306 WriteSingleHeaderKey("TSTART", doubleValue,
307 "Time of the first received data");
308 }
309 fEndMjD = *reinterpret_cast<double*>(fStandardPointers[0]);
310
311 //data copied to buffer, can write to fits
312 fits_write_tblbytes(fFile->fitsPointer(), fNumRows, 1, fTotalNumBytes, fCopyBuffer, &status);
313 if (status)
314 {
315 char text[30];//max length of cfitsio error strings (from doc)
316 fits_get_errstatus(status, text);
317 ostringstream str;
318 str << "Writing FITS row " << fNumRows << " in " << fFileName << ": " << text << " (file_write_tblbytes, rc=" << status << ")";
319 fMess->Error(str);
320 // FIXME: What is a proper action here?
321 }
322}
323
324// --------------------------------------------------------------------------
325//
326//! This closes the currently openned FITS file.
327//! it also updates the header to reflect the time of the last logged row
328//
329void Fits::Close()
330{
331//WARNING: do NOT delete the table as it gets deleted by the fFile object
332// if (fTable != NULL)
333// delete fTable;
334
335 WriteSingleHeaderKey("TSTOP", fEndMjD, "Time of the last receied data");
336 if (fFile != NULL && fOwner)
337 {
338// fFile->flush();
339 delete fFile;
340 }
341 fFile = NULL;
342 if (fCopyBuffer != NULL)
343 delete [] fCopyBuffer;
344 fCopyBuffer = NULL;
345 if (fOwner && fNumOpenFitsFiles != NULL)
346 (*fNumOpenFitsFiles)--;
347//fMess is the MessageImp part of the dataLogger itself. Thus it should NOT be deleted by the Fits files destructor.
348// if (fMess)
349// delete fMess;
350 fMess = NULL;
351}
352
353// --------------------------------------------------------------------------
354//! Returns the size on the disk of the Fits file being written.
355int Fits::GetWrittenSize() const
356{
357 if (!IsOpen())
358 return 0;
359
360 struct stat st;
361 if (stat(fFileName.c_str(), &st))
362 return 0;
363 else
364 return st.st_size;
365}
Note: See TracBrowser for help on using the repository browser.