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

Last change on this file since 10771 was 10758, checked in by lyard, 14 years ago
Fixed an issue with CCfits exceptions handling
File size: 10.4 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 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//! @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//
85void Fits::Open(const string& fileName, const string& tableName, FITS* file, int* fitsCounter, MessageImp* out)//ostream& out)
86{
87// if (fMess)
88// delete fMess;
89// fMess = new MessageImp(out);
90 fMess = out;
91 fFileName = fileName;
92 if (file == NULL)
93 {
94 try
95 {
96 fFile = new FITS(fileName, RWmode::Write);
97 }
98 catch (CCfits::FitsException e)
99 {
100 stringstream str;
101 str << "Could not open FITS file " << fileName << " reason: " << e.message();
102 fMess->Error(str);
103 fFile = NULL;
104 return;
105 }
106 fOwner = true;
107 fNumOpenFitsFiles = fitsCounter;
108 (*fNumOpenFitsFiles)++;
109 }
110 else
111 {
112 fFile = file;
113 fOwner = false;
114 }
115 //concatenate the standard and data columns
116 //do it the inneficient way first: its easier and faster to code.
117 vector<string> allNames;
118 vector<string> allDataTypes;
119 vector<string> allUnits;
120 fTotalNumBytes = 0;
121 for (unsigned int i=0;i<fStandardColDesc.size();i++)
122 {
123 allNames.push_back(fStandardColDesc[i].name);
124 allDataTypes.push_back(fStandardFormats[i]);
125 allUnits.push_back(fStandardColDesc[i].unit);
126 fTotalNumBytes += fStandardNumBytes[i];
127 }
128 //for (int i=static_cast<int>(fDataColDesc.size())-1;i>=0;i--)
129 for (int i=0; i< static_cast<int>(fDataColDesc.size()); i++)
130 {
131 if (fDataColDesc[i].name != "")
132 allNames.push_back(fDataColDesc[i].name);
133 else
134 {
135 stringstream stt;
136 stt << "Data" << i;
137 allNames.push_back(stt.str());
138 }
139 allDataTypes.push_back(fDataFormats[i]);
140 allUnits.push_back(fDataColDesc[i].unit);
141 }
142 fTotalNumBytes += fDataNumBytes;
143
144 bool updating = false;
145 try
146 {
147 string factTableName = "FACT-" + tableName;
148 fTable = fFile->addTable(factTableName, 0, allNames, allDataTypes, allUnits);
149 fTable->makeThisCurrent();
150 fCopyBuffer = new unsigned char[fTotalNumBytes];
151 fNumRows = fTable->rows();
152 if (fNumRows !=0)
153 {//If the file already existed, then we must load its data to memory before writing to it.
154 BinTable* bTable = dynamic_cast<BinTable*>(fTable);
155 if (!bTable)
156 {
157 fMess->Error("The table " + factTableName + " could not be converted to a binary table. skipping");
158 return;
159 }
160 //read the table binary data.
161 vector<string> colName;
162 bTable->readData(true, colName);
163
164 //double check that the data was indeed read from the disk. Go through the fTable instead as colName is empty (yes, it is !)
165 map<string, Column*> cMap = fTable->column();
166 map<string, Column*>::iterator cMapIt;
167
168 for (cMapIt = cMap.begin(); cMapIt != cMap.end(); cMapIt++)
169 {
170 if (!cMapIt->second->isRead())
171 {
172 fMess->Error("Column " + cMapIt->first + "Could not be read back from the disk");
173 return;
174 }
175 }
176 updating = true;
177 }
178 }
179 catch(CCfits::FitsException e)
180 {
181 stringstream str;
182 str << "Could not open or create FITS table " << tableName << " in file " << fileName << " reason: " << e.message();
183 fMess->Error(str);
184 fTable = NULL;
185 }
186
187 //As requested by Roland, the reference MjD is 0.0
188 fRefMjD = 0;//* static_cast<double*>(fStandardPointers[0]);
189 if (!updating)
190 WriteHeaderKeys();
191}
192// --------------------------------------------------------------------------
193//
194//!This writes a single header key in the currently open file.
195//!@param name the key
196//!@param value the key value
197//!@param a comment explaining the meaning of the key
198template <typename T>
199void Fits::WriteSingleHeaderKey(string name, T value, string comment)
200{
201 try
202 {
203 fTable->addKey(name, value, comment);
204 }
205 catch (CCfits::FitsException e)
206 {
207 stringstream str;
208 str << "Could not add header keys in file " << fFileName << " reason: " << e.message();
209 fMess->Error(str);
210 }
211}
212// --------------------------------------------------------------------------
213//
214//! This writes the standard header
215//
216void Fits::WriteHeaderKeys()
217{
218 if (!fTable)
219 return;
220 string name;
221 string comment;
222
223 string stringValue;
224
225 WriteSingleHeaderKey("EXTREL", 1.0f, "Release Number");
226 WriteSingleHeaderKey("TELESCOP", "FACT", "Telescope that acquired this data");
227 WriteSingleHeaderKey("ORIGIN", "ISDC", "Institution that wrote the file");
228 WriteSingleHeaderKey("CREATOR", "FACT++ DataLogger", "Program that wrote this file");
229 stringValue = Time().GetAsStr();
230 stringValue[10]= 'T';
231 WriteSingleHeaderKey("DATE", stringValue, "File creation data");
232 WriteSingleHeaderKey("TIMESYS", "TT", "Time frame system");
233 WriteSingleHeaderKey("TIMEUNIT", "d", "Time unit");
234 WriteSingleHeaderKey("TIMEREF", "UTC", "Time reference frame");
235 WriteSingleHeaderKey("MJDREF", fRefMjD, "Modified Julian Date of origin");
236 WriteSingleHeaderKey("TSTOP", fEndMjD, "Time of the last receied data");
237}
238// --------------------------------------------------------------------------
239//
240//! This writes one line of data to the file.
241//! @param conv the converter corresponding to the service being logged
242//
243void Fits::Write(Converter* conv)
244{
245
246 fTable->makeThisCurrent();
247 int status(0);
248 if (fits_insert_rows(fTable->fitsPointer(), fNumRows, 1, &status))
249 {
250 stringstream str;
251 str << "Could not insert row in file " << fFileName << ". cfitsio error code: " << status;
252 fMess->Error(str);
253 }
254 fNumRows++;
255
256 //the first standard variable is the current MjD
257 if (fEndMjD == 0)
258 {
259 double doubleValue = *static_cast<double*>(fStandardPointers[0]);// - fRefMjD;
260 WriteSingleHeaderKey("TSTART", doubleValue, "Time of the first received data");
261 }
262 fEndMjD = * static_cast<double*>(fStandardPointers[0]);
263
264 //first copy the standard variables to the copy buffer
265 int shift = 0;
266
267 for (unsigned int i=0;i<fStandardNumBytes.size();i++)
268 {
269 const char * charSrc = static_cast<char*>(fStandardPointers[i]);
270 reverse_copy(charSrc, charSrc+fStandardNumBytes[i], &fCopyBuffer[shift]);
271 shift+= fStandardNumBytes[i];
272 }
273
274 //now take care of the DIM data. The Converter is here for that purpose
275 conv->ToFits(static_cast<void*>(&fCopyBuffer[shift]), fDataPointer, fDataNumBytes);
276
277 //data copied to buffer, can write to fits
278 fits_write_tblbytes(fFile->fitsPointer(), fNumRows, 1, fTotalNumBytes, fCopyBuffer, &status);
279 if (status)
280 {
281 char text[30];//max length of cfitsio error strings (from doc)
282 fits_get_errstatus(status, text);
283 stringstream str;
284 str << "Error while writing FITS row in " << fFileName << ". Message: " << text << " [" << status << "]";
285 fMess->Error(str);
286 }
287}
288// --------------------------------------------------------------------------
289//
290//! This closes the currently openned FITS file.
291//! it also updates the header to reflect the time of the last logged row
292//
293void Fits::Close()
294{
295//WARNING: do NOT delete the table as it gets deleted by the fFile object
296// if (fTable != NULL)
297// delete fTable;
298
299 WriteSingleHeaderKey("TSTOP", fEndMjD, "Time of the last receied data");
300 if (fFile != NULL && fOwner)
301 delete fFile;
302 fFile = NULL;
303 if (fCopyBuffer != NULL)
304 delete [] fCopyBuffer;
305 fCopyBuffer = NULL;
306 if (fOwner && fNumOpenFitsFiles != NULL)
307 (*fNumOpenFitsFiles)--;
308//fMess is the MessageImp part of the dataLogger itself. Thus it should NOT be deleted by the Fits files destructor.
309// if (fMess)
310// delete fMess;
311 fMess = NULL;
312}
313
314// --------------------------------------------------------------------------
315//! Returns the size on the disk of the Fits file being written.
316int Fits::GetWrittenSize()
317{
318 if (!IsOpen())
319 return 0;
320
321 struct stat st;
322 if (stat(fFileName.c_str(), &st))
323 return 0;
324 else
325 return st.st_size;
326}
Note: See TracBrowser for help on using the repository browser.