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

Last change on this file since 12420 was 12420, checked in by lyard, 14 years ago
fixed the fix
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
23#include <sys/stat.h> //for file stats
24#include <cstdio> // for file rename
25
26using namespace std;
27using namespace CCfits;
28
29// --------------------------------------------------------------------------
30//
31//! This gives a standard variable to the file writter.
32//! This variable should not be related to the DIM service being logged.
33//! @param desc the description of the variable to add
34//! @param dataFormat the FITS data format corresponding to the variable to add.
35//! @param dataPointer the memory location where the variable is stored
36//! @param numDataBytes the number of bytes taken by the variable
37//
38void Fits::AddStandardColumn(const Description& desc, const string &dataFormat, void* dataPointer, long unsigned int numDataBytes)
39{
40 //check if entry already exist
41 for (vector<Description>::const_iterator it=fStandardColDesc.begin(); it != fStandardColDesc.end(); it++)
42 if (it->name == desc.name)
43 return;
44
45 fStandardColDesc.push_back(desc);
46 fStandardFormats.push_back(dataFormat);
47 fStandardPointers.push_back(dataPointer);
48 fStandardNumBytes.push_back(numDataBytes);
49}
50
51// --------------------------------------------------------------------------
52//
53//! This gives the file writter access to the DIM data
54//! @param desc a vector containing the description of all the columns to log
55//! @param dataFormat a vector containing the FITS data format of all the columsn to log
56//! @param dataPointer the memory location where the DIM data starts
57//! @param numDataBytes the number of bytes taken by the DIM data.
58//! @param out Message object to use for propagating messages
59//
60void Fits::InitDataColumns(const vector<Description> &desc, const vector<string>& dataFormat, void* dataPointer, MessageImp* out)
61{
62 fDataFormats = dataFormat;
63 fDataPointer = dataPointer;
64
65 //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
66 if ((desc.size() == dataFormat.size()+1) || //regular service
67 (desc.size() == dataFormat.size()+2))//service with ending string. skipped in fits
68 {//services have one (or two) more description than columns. skip the first entry while copying as it describes the table itself.
69 fDataColDesc.clear();
70 for (unsigned int i=0;i<dataFormat.size(); i++)
71 {
72 string name = desc[i+1].name;
73 if (name.length() > 68) name = name.substr(0, 68);
74 string comment = desc[i+1].comment;
75 if (comment.length() > 68) comment = comment.substr(0,68);
76 string unit = desc[i+1].unit;
77 if (unit.length() > 68) unit = comment.substr(0,68);
78 fDataColDesc.push_back(Description(name, comment, unit));
79 }
80 return;
81 }
82
83 {//if we arrived here, this means that the columns descriptions could not be parsed
84 ostringstream str;
85 str << "Expected " << dataFormat.size() << " descriptions of columns, got " << desc.size()-1 << " for service: ";
86 if (desc.size() > 0)
87 str << desc[0].name;
88 else
89 str << "<unknown>";
90
91 out->Warn(str.str());
92 }
93
94 fDataColDesc.clear();
95 // fDataColDesc.push_back(Description("service", "comment", "unit"));
96 for (unsigned int i=0;i<dataFormat.size();i++)
97 {
98 ostringstream stt;
99 stt << "Data" << i;
100 fDataColDesc.push_back(Description(stt.str(), "comment", "unit"));
101 }
102}
103
104// --------------------------------------------------------------------------
105//
106//! This opens the FITS file (after the columns have been passed)
107//! @param fileName the filename with complete or relative path of the file to open
108//! @param tableName the name of the table that will receive the logged data.
109//! @param file a pointer to an existing FITS file. If NULL, file will be opened and managed internally
110//! @param fitsCounter a pointer to the integer keeping track of the opened FITS files
111//! @param out a pointer to the MessageImp that should be used to log errors
112//! @param runNumber the runNumber for which this file is opened. 0 means nightly file.
113//
114bool Fits::Open(const string& fileName, const string& tableName, uint32_t* fitsCounter, MessageImp* out, int runNumber, FITS* file)
115{
116 fRunNumber = runNumber;
117 fMess = out;
118 fFileName = fileName;
119
120 if (fFile)
121 {
122 fMess->Error("File already open...");
123 return false;
124 }
125
126 fFile = new FitsFile(*fMess);
127
128 if (file == NULL)
129 {
130 if (!fFile->OpenFile(fileName, true))
131 return false;
132
133 fNumOpenFitsFiles = fitsCounter;
134 (*fNumOpenFitsFiles)++;
135 }
136 else
137 {
138 if (!fFile->SetFile(file))
139 return false;
140 }
141
142 //concatenate the standard and data columns
143 //do it the inneficient way first: its easier and faster to code.
144 for (unsigned int i=0;i<fStandardColDesc.size();i++)
145 {
146 fFile->AddColumn(fStandardColDesc[i].name, fStandardFormats[i],
147 fStandardColDesc[i].unit);
148 }
149
150 for (unsigned int i=0; i<fDataColDesc.size(); i++)
151 {
152 string name = fDataColDesc[i].name;
153 if (name.empty())
154 {
155 ostringstream stt;
156 stt << "Data" << i;
157 name = stt.str();
158 }
159//cout << endl << "#####adding column: " << name << " " << fDataFormats[i] << " " << fDataColDesc[i].unit << endl << endl;
160 fFile->AddColumn(name, fDataFormats[i], fDataColDesc[i].unit);
161 }
162
163 try
164 {
165 if (!fFile->OpenNewTable(tableName, 100))
166 {
167 Close();
168 return false;
169 }
170
171 fCopyBuffer.resize(fFile->GetDataSize());
172
173 return fFile->GetNumRows()==0 ? true : WriteHeaderKeys();
174 }
175 catch (const CCfits::FitsException &e)
176 {
177 fMess->Error("Opening or creating table '"+tableName+"' in '"+fileName+"': "+e.message());
178
179 fFile->fTable = NULL;
180 Close();
181 return false;
182 }
183}
184
185// --------------------------------------------------------------------------
186//
187//! This writes the standard header
188//
189bool Fits::WriteHeaderKeys()
190{
191 if (!fFile->fTable)
192 return false;
193
194 if (!fFile->WriteDefaultKeys("datalogger"))
195 return false;
196
197 if (!fFile->WriteKeyNT("TSTART", "", "Time of the first receied data"))
198 return false;
199
200 if (!fFile->WriteKeyNT("TSTOP", "", "Time of the last receied data"))
201 return false;
202
203 return true;
204}
205void Fits::MoveFileToCorruptedFile()
206{
207 ostringstream corruptName;
208 struct stat st;
209 int append = 0;
210 corruptName << fFileName << "corrupt" << append;
211 while (!stat(corruptName.str().c_str(), &st))
212 {
213 append++;
214 corruptName.str("");
215 corruptName << fFileName << "corrupt" << append;
216 }
217 if (rename(fFileName.c_str(), corruptName.str().c_str()) != 0)
218 {
219 ostringstream str;
220 str << "rename() failed for '" << fFileName << "': " << strerror(errno) << " [errno=" << errno << "]";
221 fMess->Error(str);
222 return;
223 }
224
225 fMess->Message("Renamed file " + fFileName + " to " + corruptName.str());
226
227}
228// --------------------------------------------------------------------------
229//
230//! This writes one line of data to the file.
231//! @param conv the converter corresponding to the service being logged
232//
233bool Fits::Write(const Converter &conv)
234{
235 //first copy the standard variables to the copy buffer
236 int shift = 0;
237 for (unsigned int i=0;i<fStandardNumBytes.size();i++)
238 {
239 const char *charSrc = reinterpret_cast<char*>(fStandardPointers[i]);
240 reverse_copy(charSrc, charSrc+fStandardNumBytes[i], fCopyBuffer.data()+shift);
241 shift += fStandardNumBytes[i];
242 }
243
244 try
245 {
246 //now take care of the DIM data. The Converter is here for that purpose
247 conv.ToFits(fCopyBuffer.data()+shift, fDataPointer, fCopyBuffer.size()-shift);
248 }
249 catch (const runtime_error &e)
250 {
251 ostringstream str;
252 str << fFile->GetName() << ": " << e.what();
253 fMess->Error(str);
254 return false;
255 }
256
257 // This is not necessary, is it?
258 // fFile->fTable->makeThisCurrent();
259
260 if (!fFile->AddRow())
261 {
262 Close();
263 MoveFileToCorruptedFile();
264 return false;
265 }
266
267 if (!fFile->WriteData(fCopyBuffer))
268 {
269 Close();
270 return false;
271 }
272
273 //the first standard variable is the current MjD
274 if (fEndMjD==0)
275 {
276 // FIXME: Check error?
277 const double doubleValue = *reinterpret_cast<double*>(fStandardPointers[0]);
278 fFile->WriteKeyNT("TSTART", Time(doubleValue).Iso(),
279 "Time of the first received data");
280 fFile->WriteKeyNT("TSTOP", Time(fEndMjD).Iso(),
281 "Time of the last receied data");
282 }
283 fEndMjD = *reinterpret_cast<double*>(fStandardPointers[0]);
284
285 return true;
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 if (!fFile)
296 return;
297
298 if (fFile->IsOpen() && fFile->IsOwner())
299 {
300 // FIMXE: Check for error? (It is allowed that fFile is NULL)
301 fFile->WriteKeyNT("TSTOP", Time(fEndMjD).Iso(),
302 "Time of the last receied data");
303 }
304
305 if (fFile->IsOwner())
306 {
307 if (fNumOpenFitsFiles != NULL)
308 (*fNumOpenFitsFiles)--;
309 }
310
311 const string name = fFile->GetName();
312
313 delete fFile;
314 fFile = NULL;
315
316 fMess->Info("Closed: "+name);
317
318 fMess = NULL;
319}
320
321// --------------------------------------------------------------------------
322//! Returns the size on the disk of the Fits file being written.
323int Fits::GetWrittenSize() const
324{
325 if (!IsOpen())
326 return 0;
327
328 struct stat st;
329 if (stat(fFile->GetName().c_str(), &st))
330 return 0;
331
332 return st.st_size;
333}
334
335/*
336 To be done:
337 - Check the check for column names in opennewtable
338 - If Open return false we end in an infinite loop (at least if
339 the dynamic cats to Bintable fails.
340
341*/
Note: See TracBrowser for help on using the repository browser.