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

Last change on this file since 11394 was 11373, checked in by tbretz, 13 years ago
Minor change to some log-output
File size: 12.2 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(const Description& desc, const string &dataFormat, void* dataPointer, long unsigned int numDataBytes)
38{
39 //check if entry already exist
40 for (vector<Description>::const_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(const 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 << "Opening FITS file " << fileName << ": " << 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 (unsigned int i=0; i<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 //first, let's check if the table already exist in the file
150 vector<string> tryToLoadName;
151 tryToLoadName.push_back(tableName);
152 fFile->read(tryToLoadName);
153
154 const multimap< string, CCfits::ExtHDU * >& extMap = fFile->extension();
155 if (extMap.find(tableName) == 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(tableName, 0, allNames, allDataTypes, allUnits);
160 }
161 else
162 {
163 fTable = dynamic_cast<CCfits::Table*>(extMap.find(tableName)->second);
164 }
165 if (!fTable)
166 {
167 fMess->Error("Table " + tableName + " could not be created nor loaded from "+fileName);
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("Table " + tableName + " in "+fileName+" could not be converted to a binary table.");
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 const map<string, Column*> cMap = fTable->column();
189
190 for (map<string, Column*>::const_iterator cMapIt = cMap.begin(); cMapIt != cMap.end(); cMapIt++)
191 {
192 if (!cMapIt->second->isRead())
193 {
194 fMess->Error("Reading column " + cMapIt->first + " back from "+fileName+" failed.");
195 Close();
196 return false;
197 }
198 }
199 updating = true;
200 }
201
202 }
203 catch(CCfits::FitsException e)
204 {
205 ostringstream str;
206 str << "Opening or creating table " << tableName << " in " << fileName << ": " << e.message();
207 fMess->Error(str);
208 fTable = NULL;
209 Close();
210 return false;
211 }
212
213 //As requested by Roland, the reference MjD is 0.0
214 fRefMjD = 0;//* static_cast<double*>(fStandardPointers[0]);
215 if (!updating)
216 return WriteHeaderKeys();
217
218 return true;
219}
220// --------------------------------------------------------------------------
221//
222//!This writes a single header key in the currently open file.
223//!@param name the key
224//!@param value the key value
225//!@param a comment explaining the meaning of the key
226template <typename T>
227bool Fits::WriteSingleHeaderKey(const string &name, const T &value, const string &comment)
228{
229 try
230 {
231 fTable->addKey(name, value, comment);
232 }
233 catch (CCfits::FitsException e)
234 {
235 ostringstream str;
236 str << "Could not add header keys in file " << fFileName << " reason: " << e.message();
237 fMess->Error(str);
238 return false;
239 }
240 return true;
241}
242// --------------------------------------------------------------------------
243//
244//! This writes the standard header
245//
246bool Fits::WriteHeaderKeys()
247{
248 if (!fTable)
249 return false;
250
251 string stringValue = Time().GetAsStr();
252 stringValue[10]= 'T';
253
254 if (!WriteSingleHeaderKey("EXTREL", 1.0f, "Release Number")) return false;
255 if (!WriteSingleHeaderKey("TELESCOP", "FACT", "Telescope that acquired this data")) return false;
256 if (!WriteSingleHeaderKey("ORIGIN", "ISDC", "Institution that wrote the file")) return false;
257 if (!WriteSingleHeaderKey("CREATOR", "FACT++ DataLogger", "Program that wrote this file")) return false;
258 if (!WriteSingleHeaderKey("DATE", stringValue, "File creation data")) return false;
259 if (!WriteSingleHeaderKey("TIMESYS", "TT", "Time frame system")) return false;
260 if (!WriteSingleHeaderKey("TIMEUNIT", "d", "Time unit")) return false;
261 if (!WriteSingleHeaderKey("TIMEREF", "UTC", "Time reference frame")) return false;
262 if (!WriteSingleHeaderKey("MJDREF", fRefMjD, "Modified Julian Date of origin")) return false;
263 if (!WriteSingleHeaderKey("TSTOP", fEndMjD, "Time of the last receied data")) return false;
264 return true;
265}
266// --------------------------------------------------------------------------
267//
268//! This writes one line of data to the file.
269//! @param conv the converter corresponding to the service being logged
270//
271bool Fits::Write(Converter* conv)
272{
273 //first copy the standard variables to the copy buffer
274 int shift = 0;
275 for (unsigned int i=0;i<fStandardNumBytes.size();i++)
276 {
277 const char *charSrc = reinterpret_cast<char*>(fStandardPointers[i]);
278 reverse_copy(charSrc, charSrc+fStandardNumBytes[i], &fCopyBuffer[shift]);
279 shift += fStandardNumBytes[i];
280 }
281
282 try
283 {
284 //now take care of the DIM data. The Converter is here for that purpose
285 conv->ToFits(&fCopyBuffer[shift], fDataPointer, fDataNumBytes);
286 }
287 catch (const runtime_error &e)
288 {
289 ostringstream str;
290 str << fFileName << ": " << e.what();
291 fMess->Error(str);
292 return false;
293 }
294
295 fTable->makeThisCurrent();
296
297 int status(0);
298 if (fits_insert_rows(fTable->fitsPointer(), fNumRows, 1, &status))
299 {
300 ostringstream str;
301 char text[30];
302 fits_get_errstatus(status, text);
303 str << "Inserting row into " << fFileName << ": " << text << " (fits_insert_rows, rc=" << status << ")";
304 fMess->Error(str);
305 Close();
306 return false;
307 }
308
309 fNumRows++;
310
311 //the first standard variable is the current MjD
312 if (fEndMjD==0)
313 {
314 const double doubleValue = *reinterpret_cast<double*>(fStandardPointers[0]);
315 WriteSingleHeaderKey("TSTART", doubleValue,
316 "Time of the first received data");
317 }
318 fEndMjD = *reinterpret_cast<double*>(fStandardPointers[0]);
319
320 //data copied to buffer, can write to fits
321 if (fits_write_tblbytes(fFile->fitsPointer(), fNumRows, 1, fTotalNumBytes, fCopyBuffer, &status))
322 {
323 char text[30];//max length of cfitsio error strings (from doc)
324 fits_get_errstatus(status, text);
325 ostringstream str;
326 str << "Writing FITS row " << fNumRows << " in " << fFileName << ": " << text << " (file_write_tblbytes, rc=" << status << ")";
327 fMess->Error(str);
328 Close();
329 return false;
330 }
331 return true;
332}
333
334// --------------------------------------------------------------------------
335//
336//! This closes the currently openned FITS file.
337//! it also updates the header to reflect the time of the last logged row
338//
339void Fits::Close()
340{
341//WARNING: do NOT delete the table as it gets deleted by the fFile object
342// if (fTable != NULL)
343// delete fTable;
344 if (fFile != NULL && fOwner)
345 {
346// CCfits::FITS* backupFits = fFile;
347// fFile = NULL;
348 WriteSingleHeaderKey("TSTOP", fEndMjD, "Time of the last receied data");
349 delete fFile;
350
351 fMess->Info("Closed: "+fFileName);
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
376 return st.st_size;
377}
Note: See TracBrowser for help on using the repository browser.