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

Last change on this file since 10699 was 10696, checked in by tbretz, 14 years ago
Removed an obviously wrong comment.
File size: 11.0 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, std::string dataFormat, void* dataPointer, long unsigned int numDataBytes)
38{
39 //check if entry already exist
40 for (std::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(std::vector<Description> desc, std::vector<std::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 std::stringstream 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//
83void Fits::Open(const std::string& fileName, const std::string& tableName, FITS* file, int* fitsCounter, std::ostream& out)
84{
85 if (fMess)
86 delete fMess;
87 fMess = new MessageImp(out);
88
89 fFileName = fileName;
90 if (file == NULL)
91 {
92 try
93 {
94 fFile = new FITS(fileName, RWmode::Write);
95 }
96 catch (CCfits::FitsError e)
97 {
98 std::stringstream str;
99 str << "Could not open FITS file " << fileName << " reason: " << e.message();
100 fMess->Error(str);
101 fFile = NULL;
102 return;
103 }
104 fOwner = true;
105 fNumOpenFitsFiles = fitsCounter;
106 (*fNumOpenFitsFiles)++;
107 }
108 else
109 {
110 fFile = file;
111 fOwner = false;
112 }
113 //concatenate the standard and data columns
114 //do it the inneficient way first: its easier and faster to code.
115 std::vector<std::string> allNames;
116 std::vector<std::string> allDataTypes;
117 std::vector<std::string> allUnits;
118 fTotalNumBytes = 0;
119 for (unsigned int i=0;i<fStandardColDesc.size();i++)
120 {
121 allNames.push_back(fStandardColDesc[i].name);
122 allDataTypes.push_back(fStandardFormats[i]);
123 allUnits.push_back(fStandardColDesc[i].unit);
124 fTotalNumBytes += fStandardNumBytes[i];
125 }
126 //for (int i=static_cast<int>(fDataColDesc.size())-1;i>=0;i--)
127 for (int i=0; i< static_cast<int>(fDataColDesc.size()); i++)
128 {
129 if (fDataColDesc[i].name != "")
130 allNames.push_back(fDataColDesc[i].name);
131 else
132 {
133 std::stringstream stt;
134 stt << "Data" << i;
135 allNames.push_back(stt.str());
136 }
137 allDataTypes.push_back(fDataFormats[i]);
138 allUnits.push_back(fDataColDesc[i].unit);
139 }
140 fTotalNumBytes += fDataNumBytes;
141
142 bool updating = false;
143 try
144 {
145 std::string factTableName = "FACT-" + tableName;
146 fTable = fFile->addTable(factTableName, 0, allNames, allDataTypes, allUnits);
147 fTable->makeThisCurrent();
148 fCopyBuffer = new unsigned char[fTotalNumBytes];
149 fNumRows = fTable->rows();
150 if (fNumRows !=0)
151 {//If the file already existed, then we must load its data to memory before writing to it.
152 BinTable* bTable = dynamic_cast<BinTable*>(fTable);
153 if (!bTable)
154 {
155 fMess->Error("The table " + factTableName + " could not be converted to a binary table. skipping");
156 return;
157 }
158 //read the table binary data.
159 std::vector<string> colName;
160 bTable->readData(true, colName);
161
162 //double check that the data was indeed read from the disk. Go through the fTable instead as colName is empty (yes, it is !)
163 std::map<std::string, Column*> cMap = fTable->column();
164 std::map<std::string, Column*>::iterator cMapIt;
165
166 for (cMapIt = cMap.begin(); cMapIt != cMap.end(); cMapIt++)
167 {
168 if (!cMapIt->second->isRead())
169 {
170 fMess->Error("Column " + cMapIt->first + "Could not be read back from the disk");
171 return;
172 }
173 }
174 updating = true;
175 }
176 }
177 catch(CCfits::FitsError e)
178 {
179 std::stringstream str;
180 str << "Could not open or create FITS table " << tableName << " in file " << fileName << " reason: " << e.message();
181 fMess->Error(str);
182 fTable = NULL;
183 }
184
185 fRefMjD = * static_cast<double*>(fStandardPointers[0]);
186 if (!updating)
187 WriteHeaderKeys();
188}
189// --------------------------------------------------------------------------
190//
191//! This writes the standard header
192//
193void Fits::WriteHeaderKeys()
194{
195 if (!fTable)
196 return;
197 std::string name;
198 std::string comment;
199
200 float floatValue;
201 double doubleValue;
202 std::string stringValue;
203 try
204 {
205 name = "EXTREL";
206 comment = "Release Number";
207 floatValue = 1.0f;
208 fTable->addKey(name, floatValue, comment);
209
210 name = "BASETYPE";
211 comment = "Base type of the data structure";
212 stringValue = "??????";
213 fTable->addKey(name, stringValue, comment);
214
215 name = "TELESCOP";
216 comment = "Telescope that acquired this data";
217 stringValue = "FACT";
218 fTable->addKey(name, stringValue, comment);
219
220 name = "ORIGIN";
221 comment = "Institution that wrote the file";
222 stringValue = "ISDC";
223 fTable->addKey(name, stringValue, comment);
224
225 name = "CREATOR";
226 comment = "Program that wrote this file";
227 stringValue = "FACT++_DataLogger";
228 fTable->addKey(name, stringValue, comment);
229
230 name = "DATE";
231 stringValue = Time().GetAsStr();
232 stringValue[10] = 'T';
233 comment = "File creation date";
234 fTable->addKey(name, stringValue, comment);
235
236 name = "TIMESYS";
237 stringValue = "TT";
238 comment = "Time frame system";
239 fTable->addKey(name, stringValue, comment);
240
241 name = "TIMEUNIT";
242 stringValue = "d";
243 comment = "Time unit";
244 fTable->addKey(name, stringValue, comment);
245
246 name = "TIMEREF";
247 stringValue = "UTC";
248 comment = "Time reference frame";
249 fTable->addKey(name, stringValue, comment);
250
251 name = "MJDREF";
252 doubleValue = fRefMjD;
253 comment = "Modified Julian Date of origin";
254 fTable->addKey(name, doubleValue, comment);
255 }
256 catch (CCfits::FitsError e)
257 {
258 std::stringstream str;
259 str << "Could not add header keys in file " << fFileName << " reason: " << e.message();
260 fMess->Error(str);
261 }
262 //More ?
263}
264// --------------------------------------------------------------------------
265//
266//! This writes one line of data to the file.
267//! @param conv the converter corresponding to the service being logged
268//
269void Fits::Write(Converter* conv)
270{
271
272 try
273 {
274 fTable->makeThisCurrent();
275 fTable->insertRows(fNumRows);
276 }
277 catch(CCfits::FitsError e)
278 {
279 std::stringstream str;
280 str << "Could not insert row in file " << fFileName << " reason: " << e.message();
281 fMess->Error(str);
282 }
283 fNumRows++;
284
285 //the first standard variable is the current MjD
286 if (fEndMjD == 0)
287 {
288 std::string name = "TSTART";
289 double doubleValue = *static_cast<double*>(fStandardPointers[0]) - fRefMjD;
290 std::string comment = "Time of the first received data";
291 fTable->addKey(name, doubleValue, comment);
292 }
293 fEndMjD = * static_cast<double*>(fStandardPointers[0]);
294
295 //first copy the standard variables to the copy buffer
296 int shift = 0;
297
298 for (unsigned int i=0;i<fStandardNumBytes.size();i++)
299 {
300 for (int j=0; j<fStandardNumBytes[i]; j++)
301 fCopyBuffer[shift+j] = static_cast<char*>(fStandardPointers[i])[fStandardNumBytes[i]-(j+1)];
302 shift+= fStandardNumBytes[i];
303 }
304
305 //now take care of the DIM data. The Converter is here for that purpose
306 conv->ToFits(static_cast<void*>(&fCopyBuffer[shift]), fDataPointer, fDataNumBytes);
307
308 //data copied to buffer, can write to fits
309 int status = 0;
310 //TODO check the status after the write operation
311 fits_write_tblbytes(fFile->fitsPointer(), fNumRows, 1, fTotalNumBytes, fCopyBuffer, &status);
312 if (status)
313 {
314 char text[30];//max length of cfitsio error strings (from doc)
315 fits_get_errstatus(status, text);
316 std::stringstream str;
317 str << "Error while writing FITS row in " << fFileName << ". Message: " << text << " [" << status << "]";
318 fMess->Error(str);
319 }
320 //This forces the writting of each row to the disk. Otherwise all rows are written when the file is closed.
321 ///TODO check whether this consumes too much resources or not. If it does, flush every N write operations instead
322 try
323 {
324 fFile->flush();
325 }
326 catch (CCfits::FitsError e)
327 {
328 std::stringstream str;
329 str << "Error while flushing bytes to disk. File: " << fFileName << " reason: " << e.message();
330 fMess->Error(str);
331 }
332}
333// --------------------------------------------------------------------------
334//
335//! This closes the currently openned FITS file.
336//! it also updates the header to reflect the time of the last logged row
337//
338void Fits::Close()
339{
340//WARNING: do NOT delete the table as it gets deleted by the fFile object
341// if (fTable != NULL)
342// delete fTable;
343 std::string name = "TEND";
344 double doubleValue = fEndMjD - fRefMjD;
345 std::string comment = "Time of the last received data";
346 fTable->addKey(name, doubleValue, comment);
347
348 if (fFile != NULL && fOwner)
349 delete fFile;
350 fFile = NULL;
351 if (fCopyBuffer != NULL)
352 delete [] fCopyBuffer;
353 fCopyBuffer = NULL;
354 if (fOwner && fNumOpenFitsFiles != NULL)
355 (*fNumOpenFitsFiles)--;
356 if (fMess)
357 delete fMess;
358 fMess = NULL;
359}
360
361// --------------------------------------------------------------------------
362//
363int Fits::GetWrittenSize()
364{
365 if (!IsOpen())
366 return 0;
367
368 struct stat st;
369 if (stat(fFileName.c_str(), &st))
370 return 0;
371 else
372 return st.st_size;
373}
Note: See TracBrowser for help on using the repository browser.