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

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