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

Last change on this file since 10489 was 10489, checked in by lyard, 9 years ago
Added a number of subscriptions service and better error handling
File size: 10.8 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
21//for file stats
22#include <sys/stat.h>
23
24using namespace std;
25
26// --------------------------------------------------------------------------
27//
28//! This gives a standard variable to the file writter.
29//! This variable should not be related to the DIM service being logged.
30//! @param desc the description of the variable to add
31//! @param dataFormat the FITS data format corresponding to the variable to add.
32//! @param dataPointer the memory location where the variable is stored
33//! @param numDataBytes the number of bytes taken by the variable
34//
35void Fits::AddStandardColumn(Description& desc, std::string dataFormat, void* dataPointer, long unsigned int numDataBytes)
36{
37        //check if entry already exist
38        for (std::vector<Description>::iterator it=fStandardColDesc.begin(); it != fStandardColDesc.end(); it++)
39                if (it->name == desc.name)
40                        return;
41        fStandardColDesc.push_back(desc);
42        fStandardFormats.push_back(dataFormat);
43        fStandardPointers.push_back(dataPointer);
44        fStandardNumBytes.push_back(numDataBytes);
45}
46// --------------------------------------------------------------------------
47//
48//! This gives the file writter access to the DIM data
49//! @param desc a vector containing the description of all the columns to log
50//! @param dataFormat a vector containing the FITS data format of all the columsn to log
51//! @param dataPointer the memory location where the DIM data starts
52//! @param numDataBytes the number of bytes taken by the DIM data.
53//     
54void Fits::InitDataColumns(std::vector<Description> desc, std::vector<std::string>& dataFormat, void* dataPointer, int numDataBytes)
55{//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
56        if (desc.size() == dataFormat.size())
57        {
58                fDataColDesc = desc;
59        }
60        else
61        {
62                fDataColDesc.clear();
63                for (unsigned int i=0;i<dataFormat.size();i++)
64                {
65                        std::stringstream stt;
66                        stt << "Data" << i;
67                        fDataColDesc.push_back(Description(stt.str(), "comment", "unit"));
68                }
69        }               
70        fDataFormats = dataFormat;
71        fDataPointer = dataPointer;
72        fDataNumBytes = numDataBytes;   
73}
74// --------------------------------------------------------------------------
75//
76//! This opens the FITS file (after the columns have been passed)
77//! @param fileName the filename with complete or relative path of the file to open
78//! @param tableName the name of the table that will receive the logged data.
79//! @param file a pointer to an existing FITS file. If NULL, file will be opened and managed internally
80//
81void Fits::Open(const std::string& fileName, const std::string& tableName, FITS* file, int* fitsCounter, std::ostream& out)
82{               
83        if (fMess)
84                delete fMess;
85        fMess = new MessageImp(out);
86       
87        fFileName = fileName;
88        if (file == NULL)
89        {
90                try
91                {
92                        fFile = new FITS(fileName, RWmode::Write);
93                }
94                catch (CCfits::FitsError e)
95                {                       
96                        std::stringstream str;
97                        str << "Could not open FITS file " << fileName << " reason: " << e.message();
98                        fMess->Error(str);
99                        fFile = NULL;
100                        return;
101                }       
102                fOwner = true;
103                fNumOpenFitsFiles = fitsCounter;
104                (*fNumOpenFitsFiles)++;
105        }
106        else
107        {
108                fFile = file;
109                fOwner = false;
110        }
111        //concatenate the standard and data columns
112        //do it the inneficient way first: its easier and faster to code.
113        std::vector<std::string> allNames;
114        std::vector<std::string> allDataTypes;
115        std::vector<std::string> allUnits;
116        fTotalNumBytes = 0;
117        for (unsigned int i=0;i<fStandardColDesc.size();i++)
118        {
119                allNames.push_back(fStandardColDesc[i].name);
120                allDataTypes.push_back(fStandardFormats[i]);
121                allUnits.push_back(fStandardColDesc[i].unit);
122                fTotalNumBytes += fStandardNumBytes[i];
123        }
124        //for (int i=static_cast<int>(fDataColDesc.size())-1;i>=0;i--)
125        for (int i=0; i< static_cast<int>(fDataColDesc.size()); i++)
126        {
127                if (fDataColDesc[i].name != "")
128                        allNames.push_back(fDataColDesc[i].name);
129                else 
130                {
131                        std::stringstream stt;
132                        stt << "Data" << i;
133                        allNames.push_back(stt.str());
134                }
135                allDataTypes.push_back(fDataFormats[i]);
136                allUnits.push_back(fDataColDesc[i].unit);       
137        }
138        fTotalNumBytes += fDataNumBytes;
139       
140        bool updating = false;
141        try
142        {
143                std::string factTableName = "FACT-" + tableName;
144                fTable = fFile->addTable(factTableName, 0, allNames, allDataTypes, allUnits);
145                fCopyBuffer = new unsigned char[fTotalNumBytes]; 
146                fNumRows = fTable->rows();
147                if (fNumRows !=0)
148                {//If the file already existed, then we must load its data to memory before writing to it.
149                        std::vector<std::string> tableNameString;
150                        tableNameString.push_back(tableName);
151                        fFile->read(tableNameString);   
152                        std::map<std::string, Column*> cMap = fTable->column();
153                        std::map<std::string, Column*>::iterator cMapIt;
154                        for (cMapIt = cMap.begin(); cMapIt != cMap.end(); cMapIt++)
155                        {
156                                //TODO this only works for scalar columns I assume. upgrade it to fully read vector columns
157                                cMapIt->second->readData(1, fNumRows);         
158                        }       
159                        updating = true;
160                }
161        }
162        catch(CCfits::FitsError e)
163        {
164                std::stringstream str;
165                str << "Could not open or create FITS table " << tableName << " in  file " << fileName << " reason: " << e.message();
166                fMess->Error(str);
167                fTable = NULL;
168        }
169                       
170        fRefMjD = * static_cast<double*>(fStandardPointers[0]);
171        if (!updating)
172                WriteHeaderKeys();
173}
174// --------------------------------------------------------------------------
175//
176//! This writes the standard header
177//
178void Fits::WriteHeaderKeys()
179{
180        if (!fTable)
181                return;
182        std::string name;
183        std::string comment;
184       
185        float floatValue;
186        double doubleValue;
187        std::string stringValue;
188        try
189        {       
190                name = "EXTREL";
191                comment = "Release Number";
192                floatValue = 1.0f;
193                fTable->addKey(name, floatValue, comment);
194                               
195                name = "BASETYPE";
196                comment = "Base type of the data structure";
197                stringValue = "??????";
198                fTable->addKey(name, stringValue, comment);
199                               
200                name = "TELESCOP";
201                comment = "Telescope that acquired this data";
202                stringValue = "FACT";
203                fTable->addKey(name, stringValue, comment);
204                               
205                name = "ORIGIN";
206                comment = "Institution that wrote the file";
207                stringValue = "ISDC";
208                fTable->addKey(name, stringValue, comment);
209                               
210                name = "CREATOR";
211                comment = "Program that wrote this file";
212                stringValue = "FACT++_DataLogger";
213                fTable->addKey(name, stringValue, comment);
214                               
215                name = "DATE";
216                stringValue = Time().GetAsStr();
217                stringValue[10] = 'T';
218                comment = "File creation date";
219                fTable->addKey(name, stringValue, comment);
220               
221                name = "TIMESYS";
222                stringValue = "TT";
223                comment = "Time frame system";
224                fTable->addKey(name, stringValue, comment);
225               
226                name = "TIMEUNIT";
227                stringValue = "d";
228                comment = "Time unit";
229                fTable->addKey(name, stringValue, comment);
230               
231                name = "TIMEREF";
232                stringValue = "UTC";
233                comment = "Time reference frame";
234                fTable->addKey(name, stringValue, comment);
235                       
236                name = "MJDREF";
237                doubleValue = fRefMjD;
238                comment = "Modified Julian Date of origin";
239                fTable->addKey(name, doubleValue, comment);
240        }
241        catch (CCfits::FitsError e)
242        {
243                std::stringstream str;
244                str << "Could not add header keys in file " << fFileName << " reason: " << e.message();
245                fMess->Error(str);
246        }                       
247        //More ?
248}
249// --------------------------------------------------------------------------
250//
251//! This writes one line of data to the file.
252//! @param conv the converter corresponding to the service being logged
253//
254void Fits::Write(Converter* conv)
255{
256
257        fTable->makeThisCurrent();
258        try
259        {
260                fTable->insertRows(fNumRows);
261        }
262        catch(CCfits::FitsError e)
263        {
264                std::stringstream str;
265                str << "Could not insert row in file " << fFileName << " reason: " << e.message();
266                fMess->Error(str);
267        }
268        fNumRows++;
269                       
270        //the first standard variable is the current MjD
271        if (fEndMjD == 0)
272        {
273                std::string name = "TSTART";
274                double doubleValue = *static_cast<double*>(fStandardPointers[0]) - fRefMjD;
275                std::string comment = "Time of the first received data";
276                fTable->addKey(name, doubleValue, comment);     
277        }
278        fEndMjD = * static_cast<double*>(fStandardPointers[0]);
279                       
280        //first copy the standard variables to the copy buffer
281        int shift = 0;
282
283        for (unsigned int i=0;i<fStandardNumBytes.size();i++)
284        {
285                for (int j=0; j<fStandardNumBytes[i]; j++)
286                        fCopyBuffer[shift+j] = static_cast<char*>(fStandardPointers[i])[fStandardNumBytes[i]-(j+1)];
287                shift+= fStandardNumBytes[i];   
288        }
289
290        //now take care of the DIM data. The Converter is here for that purpose
291        conv->ToFits(static_cast<void*>(&fCopyBuffer[shift]), fDataPointer, fDataNumBytes);
292                         
293        //data copied to buffer, can write to fits
294        int status = 0;
295        //TODO check the status after the write operation
296        fits_write_tblbytes(fFile->fitsPointer(), fNumRows, 1, fTotalNumBytes, fCopyBuffer, &status);
297        if (status)
298        {
299                char text[30];//max length of cfitsio error strings (from doc)
300                fits_get_errstatus(status, text);
301                std::stringstream str;
302                str << "Error while writing FITS row in " << fFileName << ". Message: " << text << " [" << status << "]";
303                fMess->Error(str);     
304        }
305        //This forces the writting of each row to the disk. Otherwise all rows are written when the file is closed.
306        ///TODO check whether this consumes too much resources or not. If it does, flush every N write operations instead
307        try
308        {
309                fFile->flush();
310        }
311        catch (CCfits::FitsError e)
312        {
313                std::stringstream str;
314                str << "Error while flushing bytes to disk. File: " << fFileName << " reason: " << e.message();
315                fMess->Error(str);     
316        }
317}
318// --------------------------------------------------------------------------
319//
320//! This closes the currently openned FITS file.
321//! it also updates the header to reflect the time of the last logged row
322//     
323void Fits::Close() 
324{
325//WARNING: do NOT delete the table as it gets deleted by the fFile object
326//                      if (fTable != NULL)
327//                              delete fTable;
328        std::string name = "TEND";
329        double doubleValue = fEndMjD - fRefMjD;
330        std::string comment = "Time of the last received data";
331        fTable->addKey(name, doubleValue, comment);
332       
333        if (fFile != NULL && fOwner)
334                delete fFile;
335        fFile = NULL;
336        if (fCopyBuffer != NULL)
337                delete [] fCopyBuffer;
338        fCopyBuffer = NULL;
339        if (fOwner && fNumOpenFitsFiles != NULL)
340                (*fNumOpenFitsFiles)--;
341        if (fMess)
342                delete fMess;
343        fMess = NULL;
344}
345
346// --------------------------------------------------------------------------
347//
348//! This closes the currently openned FITS file.
349//! it also updates the header to reflect the time of the last logged row
350//     
351int Fits::GetWrittenSize()
352{
353        if (!IsOpen())
354                return 0;
355               
356        struct stat st;
357        if (stat(fFileName.c_str(), &st))
358                return 0;
359        else
360                return st.st_size;
361}
Note: See TracBrowser for help on using the repository browser.