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

Last change on this file since 10997 was 10955, checked in by lyard, 13 years ago
added WriteError state
File size: 12.1 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//
86bool 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 false;
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 Close();
169 return false;
170 }
171 fTable->makeThisCurrent();
172 fCopyBuffer = new unsigned char[fTotalNumBytes];
173 fNumRows = fTable->rows();
174 if (fNumRows !=0)
175 {//If the file already existed, then we must load its data to memory before writing to it.
176 BinTable* bTable = dynamic_cast<BinTable*>(fTable);
177 if (!bTable)
178 {
179 fMess->Error("The table " + factTableName + " could not be converted to a binary table. skipping");
180 Close();
181 return false;
182 }
183 //read the table binary data.
184 vector<string> colName;
185 bTable->readData(true, colName);
186
187 //double check that the data was indeed read from the disk. Go through the fTable instead as colName is empty (yes, it is !)
188 map<string, Column*> cMap = fTable->column();
189 map<string, Column*>::iterator cMapIt;
190
191 for (cMapIt = cMap.begin(); cMapIt != cMap.end(); cMapIt++)
192 {
193 if (!cMapIt->second->isRead())
194 {
195 fMess->Error("Column " + cMapIt->first + "Could not be read back from the disk");
196 Close();
197 return false;
198 }
199 }
200 updating = true;
201 }
202
203 }
204 catch(CCfits::FitsException e)
205 {
206 ostringstream str;
207 str << "Could not open or create FITS table " << tableName << " in file " << fileName << " reason: " << e.message();
208 fMess->Error(str);
209 fTable = NULL;
210 Close();
211 return false;
212 }
213
214 //As requested by Roland, the reference MjD is 0.0
215 fRefMjD = 0;//* static_cast<double*>(fStandardPointers[0]);
216 if (!updating)
217 return WriteHeaderKeys();
218
219 return true;
220}
221// --------------------------------------------------------------------------
222//
223//!This writes a single header key in the currently open file.
224//!@param name the key
225//!@param value the key value
226//!@param a comment explaining the meaning of the key
227template <typename T>
228bool Fits::WriteSingleHeaderKey(string name, T value, string comment)
229{
230 try
231 {
232 fTable->addKey(name, value, comment);
233 }
234 catch (CCfits::FitsException e)
235 {
236 ostringstream str;
237 str << "Could not add header keys in file " << fFileName << " reason: " << e.message();
238 fMess->Error(str);
239 return false;
240 }
241 return true;
242}
243// --------------------------------------------------------------------------
244//
245//! This writes the standard header
246//
247bool Fits::WriteHeaderKeys()
248{
249 if (!fTable)
250 return false;
251 string name;
252 string comment;
253
254 string stringValue;
255
256 if (!WriteSingleHeaderKey("EXTREL", 1.0f, "Release Number")) return false;
257 if (!WriteSingleHeaderKey("TELESCOP", "FACT", "Telescope that acquired this data")) return false;
258 if (!WriteSingleHeaderKey("ORIGIN", "ISDC", "Institution that wrote the file")) return false;
259 if (!WriteSingleHeaderKey("CREATOR", "FACT++ DataLogger", "Program that wrote this file")) return false;
260 stringValue = Time().GetAsStr();
261 stringValue[10]= 'T';
262 if (!WriteSingleHeaderKey("DATE", stringValue, "File creation data")) return false;
263 if (!WriteSingleHeaderKey("TIMESYS", "TT", "Time frame system")) return false;
264 if (!WriteSingleHeaderKey("TIMEUNIT", "d", "Time unit")) return false;
265 if (!WriteSingleHeaderKey("TIMEREF", "UTC", "Time reference frame")) return false;
266 if (!WriteSingleHeaderKey("MJDREF", fRefMjD, "Modified Julian Date of origin")) return false;
267 if (!WriteSingleHeaderKey("TSTOP", fEndMjD, "Time of the last receied data")) return false;
268 return true;
269}
270// --------------------------------------------------------------------------
271//
272//! This writes one line of data to the file.
273//! @param conv the converter corresponding to the service being logged
274//
275bool Fits::Write(Converter* conv)
276{
277 //first copy the standard variables to the copy buffer
278 int shift = 0;
279 for (unsigned int i=0;i<fStandardNumBytes.size();i++)
280 {
281 const char *charSrc = reinterpret_cast<char*>(fStandardPointers[i]);
282 reverse_copy(charSrc, charSrc+fStandardNumBytes[i], &fCopyBuffer[shift]);
283 shift += fStandardNumBytes[i];
284 }
285
286 try
287 {
288 //now take care of the DIM data. The Converter is here for that purpose
289 conv->ToFits(&fCopyBuffer[shift], fDataPointer, fDataNumBytes);
290 }
291 catch (const runtime_error &e)
292 {
293 ostringstream str;
294 str << fFileName << ": " << e.what();
295 fMess->Error(str);
296 return false;
297 }
298
299 fTable->makeThisCurrent();
300
301 int status(0);
302 if (fits_insert_rows(fTable->fitsPointer(), fNumRows, 1, &status))
303 {
304 ostringstream str;
305 str << "Inserting row into " << fFileName << " failed (fits_insert_rows, rc=" << status << ")";
306 fMess->Error(str);
307 Close();
308 return false;
309 }
310
311 fNumRows++;
312
313 //the first standard variable is the current MjD
314 if (fEndMjD==0)
315 {
316 const double doubleValue = *reinterpret_cast<double*>(fStandardPointers[0]);
317 WriteSingleHeaderKey("TSTART", doubleValue,
318 "Time of the first received data");
319 }
320 fEndMjD = *reinterpret_cast<double*>(fStandardPointers[0]);
321
322 //data copied to buffer, can write to fits
323 if (fits_write_tblbytes(fFile->fitsPointer(), fNumRows, 1, fTotalNumBytes, fCopyBuffer, &status))
324 {
325 char text[30];//max length of cfitsio error strings (from doc)
326 fits_get_errstatus(status, text);
327 ostringstream str;
328 str << "Writing FITS row " << fNumRows << " in " << fFileName << ": " << text << " (file_write_tblbytes, rc=" << status << ")";
329 fMess->Error(str);
330 Close();
331 return false;
332 }
333 return true;
334}
335
336// --------------------------------------------------------------------------
337//
338//! This closes the currently openned FITS file.
339//! it also updates the header to reflect the time of the last logged row
340//
341void Fits::Close()
342{
343//WARNING: do NOT delete the table as it gets deleted by the fFile object
344// if (fTable != NULL)
345// delete fTable;
346
347 WriteSingleHeaderKey("TSTOP", fEndMjD, "Time of the last receied data");
348 if (fFile != NULL && fOwner)
349 {
350// fFile->flush();
351 delete fFile;
352 }
353 fFile = NULL;
354 if (fCopyBuffer != NULL)
355 delete [] fCopyBuffer;
356 fCopyBuffer = NULL;
357 if (fOwner && fNumOpenFitsFiles != NULL)
358 (*fNumOpenFitsFiles)--;
359//fMess is the MessageImp part of the dataLogger itself. Thus it should NOT be deleted by the Fits files destructor.
360// if (fMess)
361// delete fMess;
362 fMess = NULL;
363}
364
365// --------------------------------------------------------------------------
366//! Returns the size on the disk of the Fits file being written.
367int Fits::GetWrittenSize() const
368{
369 if (!IsOpen())
370 return 0;
371
372 struct stat st;
373 if (stat(fFileName.c_str(), &st))
374 return 0;
375 else
376 return st.st_size;
377}
Note: See TracBrowser for help on using the repository browser.