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

Last change on this file since 12108 was 12035, checked in by lyard, 13 years ago
added moving of corrupted files to corrupted naming
File size: 8.9 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//
59void Fits::InitDataColumns(const vector<Description> &desc, const vector<string>& dataFormat, void* dataPointer)
60{
61 fDataFormats = dataFormat;
62 fDataPointer = dataPointer;
63
64 //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
65 if (desc.size() == dataFormat.size())
66 {
67 fDataColDesc = desc;
68 return;
69 }
70
71 fDataColDesc.clear();
72 for (unsigned int i=0;i<dataFormat.size();i++)
73 {
74 ostringstream stt;
75 stt << "Data" << i;
76 fDataColDesc.push_back(Description(stt.str(), "comment", "unit"));
77 }
78}
79
80// --------------------------------------------------------------------------
81//
82//! This opens the FITS file (after the columns have been passed)
83//! @param fileName the filename with complete or relative path of the file to open
84//! @param tableName the name of the table that will receive the logged data.
85//! @param file a pointer to an existing FITS file. If NULL, file will be opened and managed internally
86//! @param fitsCounter a pointer to the integer keeping track of the opened FITS files
87//! @param out a pointer to the MessageImp that should be used to log errors
88//! @param runNumber the runNumber for which this file is opened. 0 means nightly file.
89//
90bool Fits::Open(const string& fileName, const string& tableName, uint32_t* fitsCounter, MessageImp* out, int runNumber, FITS* file)
91{
92 fRunNumber = runNumber;
93 fMess = out;
94 fFileName = fileName;
95
96 if (fFile)
97 {
98 fMess->Error("File already open...");
99 return false;
100 }
101
102 fFile = new FitsFile(*fMess);
103
104 if (file == NULL)
105 {
106 if (!fFile->OpenFile(fileName, true))
107 return false;
108
109 fNumOpenFitsFiles = fitsCounter;
110 (*fNumOpenFitsFiles)++;
111 }
112 else
113 {
114 if (!fFile->SetFile(file))
115 return false;
116 }
117
118 //concatenate the standard and data columns
119 //do it the inneficient way first: its easier and faster to code.
120 for (unsigned int i=0;i<fStandardColDesc.size();i++)
121 {
122 fFile->AddColumn(fStandardColDesc[i].name, fStandardFormats[i],
123 fStandardColDesc[i].unit);
124 }
125
126 for (unsigned int i=0; i<fDataColDesc.size(); i++)
127 {
128 string name = fDataColDesc[i].name;
129 if (name.empty())
130 {
131 ostringstream stt;
132 stt << "Data" << i;
133 name = stt.str();
134 }
135
136 fFile->AddColumn(name, fDataFormats[i], fDataColDesc[i].unit);
137 }
138
139 try
140 {
141 if (!fFile->OpenNewTable(tableName, 100))
142 {
143 Close();
144 return false;
145 }
146
147 fCopyBuffer.resize(fFile->GetDataSize());
148
149 return fFile->GetNumRows()==0 ? true : WriteHeaderKeys();
150 }
151 catch (const CCfits::FitsException &e)
152 {
153 fMess->Error("Opening or creating table '"+tableName+"' in '"+fileName+"': "+e.message());
154
155 fFile->fTable = NULL;
156 Close();
157 return false;
158 }
159}
160
161// --------------------------------------------------------------------------
162//
163//! This writes the standard header
164//
165bool Fits::WriteHeaderKeys()
166{
167 if (!fFile->fTable)
168 return false;
169
170 if (!fFile->WriteDefaultKeys("datalogger"))
171 return false;
172
173 if (!fFile->WriteKeyNT("TSTART", "", "Time of the first receied data"))
174 return false;
175
176 if (!fFile->WriteKeyNT("TSTOP", "", "Time of the last receied data"))
177 return false;
178
179 return true;
180}
181void Fits::MoveFileToCorruptedFile()
182{
183 ostringstream corruptName;
184 struct stat st;
185 int append = 0;
186 corruptName << fFileName << "corrupt" << append;
187 while (stat(corruptName.str().c_str(), &st))
188 {
189 append++;
190 corruptName.str("");
191 corruptName << fFileName << "corrupt" << append;
192 }
193 if (rename(fFileName.c_str(), corruptName.str().c_str()) != 0)
194 fMess->Error("Could not rename file " + fFileName);
195 else
196 fMess->Message("Renamed file " + fFileName + " to " + corruptName.str());
197
198}
199// --------------------------------------------------------------------------
200//
201//! This writes one line of data to the file.
202//! @param conv the converter corresponding to the service being logged
203//
204bool Fits::Write(const Converter &conv)
205{
206 //first copy the standard variables to the copy buffer
207 int shift = 0;
208 for (unsigned int i=0;i<fStandardNumBytes.size();i++)
209 {
210 const char *charSrc = reinterpret_cast<char*>(fStandardPointers[i]);
211 reverse_copy(charSrc, charSrc+fStandardNumBytes[i], fCopyBuffer.data()+shift);
212 shift += fStandardNumBytes[i];
213 }
214
215 try
216 {
217 //now take care of the DIM data. The Converter is here for that purpose
218 conv.ToFits(fCopyBuffer.data()+shift, fDataPointer, fCopyBuffer.size()-shift);
219 }
220 catch (const runtime_error &e)
221 {
222 ostringstream str;
223 str << fFile->GetName() << ": " << e.what();
224 fMess->Error(str);
225 return false;
226 }
227
228 // This is not necessary, is it?
229 // fFile->fTable->makeThisCurrent();
230
231 if (!fFile->AddRow())
232 {
233 Close();
234 MoveFileToCorruptedFile();
235 return false;
236 }
237
238 if (!fFile->WriteData(fCopyBuffer))
239 {
240 Close();
241 return false;
242 }
243
244 //the first standard variable is the current MjD
245 if (fEndMjD==0)
246 {
247 // FIXME: Check error?
248 const double doubleValue = *reinterpret_cast<double*>(fStandardPointers[0]);
249 fFile->WriteKeyNT("TSTART", Time(doubleValue).Iso(),
250 "Time of the first received data");
251 }
252 fEndMjD = *reinterpret_cast<double*>(fStandardPointers[0]);
253
254 return true;
255}
256
257// --------------------------------------------------------------------------
258//
259//! This closes the currently openned FITS file.
260//! it also updates the header to reflect the time of the last logged row
261//
262void Fits::Close()
263{
264 if (!fFile)
265 return;
266
267 if (fFile->IsOpen() && fFile->IsOwner())
268 {
269 // FIMXE: Check for error? (It is allowed that fFile is NULL)
270 fFile->WriteKeyNT("TSTOP", Time(fEndMjD).Iso(),
271 "Time of the last receied data");
272 }
273
274 if (fFile->IsOwner())
275 {
276 if (fNumOpenFitsFiles != NULL)
277 (*fNumOpenFitsFiles)--;
278 }
279
280 const string name = fFile->GetName();
281
282 delete fFile;
283 fFile = NULL;
284
285 fMess->Info("Closed: "+name);
286
287 fMess = NULL;
288}
289
290// --------------------------------------------------------------------------
291//! Returns the size on the disk of the Fits file being written.
292int Fits::GetWrittenSize() const
293{
294 if (!IsOpen())
295 return 0;
296
297 struct stat st;
298 if (stat(fFile->GetName().c_str(), &st))
299 return 0;
300
301 return st.st_size;
302}
303
304/*
305 To be done:
306 - Check the check for column names in opennewtable
307 - If Open return false we end in an infinite loop (at least if
308 the dynamic cats to Bintable fails.
309
310*/
Note: See TracBrowser for help on using the repository browser.