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

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