Index: trunk/FACT++/src/fitsloader.cc
===================================================================
--- trunk/FACT++/src/fitsloader.cc	(revision 10778)
+++ trunk/FACT++/src/fitsloader.cc	(revision 10778)
@@ -0,0 +1,712 @@
+//****************************************************************
+/** @class FitsLoader
+
+  @brief Load a given Fits file and table, and dump selected columns if requested.
+
+  It derives from StateMachineDim. the first parent is here to enforce
+  a state machine behaviour
+  The possible states and transitions of the machine are:
+  \dot
+  digraph FitsLoader {
+          node [shape=record, fontname=Helvetica, fontsize=10];
+      e [label="Error" color="red"];
+   r [label="Ready"]
+   d [label="FileLoaded"]
+
+  e -> r
+  r -> e
+  r -> d
+  d -> r
+   }
+  \enddot
+ */
+ //****************************************************************
+#include "FACT.h"
+#include "Event.h"
+#include "StateMachineDim.h"
+#include "WindowLog.h"
+#include "Configuration.h"
+#include "LocalControl.h"
+#include "Description.h"
+
+
+#include <boost/bind.hpp>
+#if BOOST_VERSION < 104400
+#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 4))
+#undef BOOST_HAS_RVALUE_REFS
+#endif
+#endif
+#include <boost/thread.hpp>
+
+#include <iostream>
+
+#include <CCfits/CCfits>
+
+class FitsLoader : public StateMachineDim
+{
+
+public:
+    enum
+    {
+        kSM_FileLoaded = 20,
+    } localstates_t;
+
+    FitsLoader(ostream& out);
+    ~FitsLoader();
+
+    ///Define command names
+    static const char* fLoadFits;
+    static const char* fUnloadFits;
+    static const char* fListColumns;
+    static const char* fDumpColumns;
+    static const char* fClearDumpList;
+    static const char* fDoDump;
+    static const char* fConfigFileName;
+    static const char* fConfigTableName;
+    static const char* fConfigPrecName;
+    static const char* fConfigFileOutName;
+
+private:
+    ///Name of the fits file to load
+    string fFileName;
+    ///Name of the table to load from the  file
+    string fTableName;
+    ///FITS pointer
+    CCfits::FITS* fFile;
+    ///Table pointer
+    CCfits::Table* fTable;
+    ///Precision of the ofstream. Used to output a given number of significant digits for floats or doubles
+    int fStreamPrecision;
+    ///Name of the output file
+    string fFileOut;
+    ///map between the column names and their CCfits objects
+    map<string, CCfits::Column*> fColMap;
+    ///List of the column names to be dumped
+    vector<string> fDumpList;
+    ///Transition from ready to fileLoaded.
+    int LoadPlease();
+    ///Transition from fileLoaded to ready
+    int UnloadPlease();
+    ///Lists the loaded column names
+    int ListColumnsPlease(const Event&);
+    ///Add a column name to the dump list
+    int AddDumpColumnsPlease(const Event&);
+    ///Clear the dump list
+    int ClearDumpListPlease(const Event&);
+    ///Perform the dumping, based on the current dump list
+    int DoDumpPlease(const Event&);
+    ///Set the name of the Fits file to be loaded
+    int ConfigFileNamePlease(const Event&);
+    ///Set the name of the table to be loaded
+    int ConfigTableNamePlease(const Event&);
+    ///Set the ofstream precision
+    int SetOFStreamPrecisionPlease(const Event&);
+    ///Set the name of the output file
+    int SetFileOutPlease(const Event&);
+    ///Calculate the buffer size required to read a row of the fits table, as well as the offsets to each column
+    vector<int> CalculateBufferSize();
+    ///Write a single row of the selected data
+    void writeValuesFromFits(vector<int>& offsets,ofstream& targetFile, unsigned char* fitsBuffer);
+
+public:
+    ///Configures the fitsLoader from the config file and/or command arguments.
+    void SetupConfig(Configuration& conf);
+};
+
+const char* FitsLoader::fLoadFits = "load";
+const char* FitsLoader::fUnloadFits = "unload";
+const char* FitsLoader::fListColumns = "list_columns";
+const char* FitsLoader::fDumpColumns = "add_dump";
+const char* FitsLoader::fClearDumpList = "clear_dump";
+const char* FitsLoader::fDoDump = "dump";
+const char* FitsLoader::fConfigFileName = "set_file";
+const char* FitsLoader::fConfigTableName = "set_table";
+const char* FitsLoader::fConfigPrecName = "set_prec";
+const char* FitsLoader::fConfigFileOutName = "set_outfile";
+
+// --------------------------------------------------------------------------
+//
+//! Set the name of the output file
+//! @param evt
+//!        the event transporting the file name
+//
+int FitsLoader::SetFileOutPlease(const Event& evt)
+{
+    fFileOut = string(evt.GetText());
+    stringstream str;
+    str << "Output file is now " << fFileOut;
+    Message(str.str());
+    return 0;
+}
+// --------------------------------------------------------------------------
+//
+//! Set the precision of the ofstream. So that an appropriate number of significant digits are outputted.
+//! @param evt
+//!        the event transporting the precision
+//
+int FitsLoader::SetOFStreamPrecisionPlease(const Event& evt)
+{
+    fStreamPrecision = evt.GetInt();
+    stringstream str;
+    str << "OFStream precision is now " << fStreamPrecision;
+    Message(str.str());
+    return 0;
+}
+// --------------------------------------------------------------------------
+//
+//! Writes a single row of the selected FITS data to the output file.
+//! @param offsets
+//!         a vector containing the offsets to the columns (in bytes)
+//! @param targetFile
+//!         the ofstream where to write to
+//! @param fitsBuffer
+//!         the memory were the row has been loaded by cfitsio
+//
+void FitsLoader::writeValuesFromFits(vector<int>& offsets,ofstream& targetFile, unsigned char* fitsBuffer)
+{
+    targetFile.precision(fStreamPrecision);
+    map<string, CCfits::Column*>::iterator it;
+   for (it=fColMap.begin(); it != fColMap.end(); it++)
+    {
+        bool found = false;
+        for (vector<string>::iterator jt=fDumpList.begin(); jt != fDumpList.end(); jt++)
+        {
+            if (it->first == *jt)
+            {
+                found = true;
+                break;
+            }
+        }
+        if (!found)
+            continue;
+       int offset = offsets[it->second->index()-1];
+       const char* charSrc = reinterpret_cast<char*>(&fitsBuffer[offset]);
+        unsigned char copyBuffer[30];//max size of a single variable
+        for (int width = 0; width<it->second->width(); width++)
+        {
+            switch (it->second->type())
+            {
+            case CCfits::Tbyte:
+                targetFile << *charSrc;
+                charSrc += sizeof(char);
+            break;
+            case CCfits::Tushort:
+                targetFile << *reinterpret_cast<const unsigned short*>(charSrc);
+                charSrc += sizeof(char);
+            break;
+            case CCfits::Tshort:
+                targetFile << *reinterpret_cast<const short*>(charSrc);
+                charSrc += sizeof(char);
+            break;
+            case CCfits::Tuint:
+                reverse_copy(charSrc, charSrc+sizeof(unsigned int), copyBuffer);
+                //warning suppressed in gcc4.0.2
+                targetFile << *reinterpret_cast<unsigned int*>(copyBuffer);
+                charSrc += sizeof(int);
+            break;
+            case CCfits::Tint:
+                reverse_copy(charSrc, charSrc+sizeof(int), copyBuffer);
+                targetFile << *reinterpret_cast<int*>(copyBuffer);
+                charSrc += sizeof(int);
+            break;
+            case CCfits::Tulong:
+                reverse_copy(charSrc, charSrc+sizeof(unsigned long), copyBuffer);
+                targetFile << *reinterpret_cast<unsigned long*>(copyBuffer);
+                charSrc += sizeof(int);
+            break;
+            case CCfits::Tlong:
+                reverse_copy(charSrc, charSrc+sizeof(long), copyBuffer);
+                targetFile << *reinterpret_cast<long*>(copyBuffer);
+                charSrc += sizeof(int);
+            break;
+            case CCfits::Tlonglong:
+                reverse_copy(charSrc, charSrc+sizeof(long long), copyBuffer);
+                targetFile << *reinterpret_cast<long long*>(copyBuffer);
+                charSrc += sizeof(long long);
+            break;
+            case CCfits::Tfloat:
+                reverse_copy(charSrc, charSrc+sizeof(float), copyBuffer);
+                targetFile << *reinterpret_cast<float*>(copyBuffer);
+                charSrc += sizeof(float);
+            break;
+            case CCfits::Tdouble:
+                reverse_copy(charSrc, charSrc+sizeof(double), copyBuffer);
+                targetFile << *reinterpret_cast<double*>(copyBuffer);
+                charSrc += sizeof(double);
+            break;
+            case CCfits::Tnull:
+            case CCfits::Tbit:
+            case CCfits::Tlogical:
+            case CCfits::Tstring:
+            case CCfits::Tcomplex:
+            case CCfits::Tdblcomplex:
+            case CCfits::VTbit:
+            case CCfits::VTbyte:
+            case CCfits::VTlogical:
+            case CCfits::VTushort:
+            case CCfits::VTshort:
+            case CCfits::VTuint:
+            case CCfits::VTint:
+            case CCfits::VTulong:
+            case CCfits::VTlong:
+            case CCfits::VTlonglong:
+            case CCfits::VTfloat:
+            case CCfits::VTdouble:
+            case CCfits::VTcomplex:
+            case CCfits::VTdblcomplex:
+                Error("Data type not implemented yet.");
+                return;
+            break;
+            default:
+                Error("THIS SHOULD NEVER BE REACHED");
+                return;
+            }//switch
+            targetFile << " ";
+        }//width loop
+    }//iterator over the columns
+    targetFile << endl;
+}
+
+// --------------------------------------------------------------------------
+//
+//! Calculates the required buffer size for reading one row of the current table.
+//! Also calculates the offsets to all the columns
+//
+vector<int> FitsLoader::CalculateBufferSize()
+{
+    vector<int> result;
+    map<int,int> sizes;
+    int size = 0;
+
+    for (map<string, CCfits::Column*>::iterator it=fColMap.begin(); it != fColMap.end(); it++)
+    {
+        int width = it->second->width();
+        switch (it->second->type())
+        {
+        case CCfits::Tbyte:
+        case CCfits::Tushort:
+        case CCfits::Tshort:
+            Message("short");
+            sizes[it->second->index()] =  sizeof(char)*width;
+        break;
+        case CCfits::Tuint:
+        case CCfits::Tint:
+            Message("int");
+            sizes[it->second->index()] =  sizeof(int)*width;
+        break;
+        case CCfits::Tulong:
+        case CCfits::Tlong:
+            Message("long");
+            sizes[it->second->index()] = sizeof(int)*width;
+        break;
+        case CCfits::Tlonglong:
+            Message("longlong");
+            sizes[it->second->index()] =  sizeof(long long)*width;
+        break;
+        case CCfits::Tfloat:
+            Message("float");
+            sizes[it->second->index()] =  sizeof(float)*width;
+        break;
+        case CCfits::Tdouble:
+            Message("double");
+            sizes[it->second->index()] =  sizeof(double)*width;
+        break;
+        case CCfits::Tnull:
+        case CCfits::Tbit:
+        case CCfits::Tlogical:
+        case CCfits::Tstring:
+        case CCfits::Tcomplex:
+        case CCfits::Tdblcomplex:
+        case CCfits::VTbit:
+        case CCfits::VTbyte:
+        case CCfits::VTlogical:
+        case CCfits::VTushort:
+        case CCfits::VTshort:
+        case CCfits::VTuint:
+        case CCfits::VTint:
+        case CCfits::VTulong:
+        case CCfits::VTlong:
+        case CCfits::VTlonglong:
+        case CCfits::VTfloat:
+        case CCfits::VTdouble:
+        case CCfits::VTcomplex:
+        case CCfits::VTdblcomplex:
+            Error("Data type not implemented yet.");
+            return vector<int>();
+        break;
+        default:
+            Error("THIS SHOULD NEVER BE REACHED");
+            return vector<int>();
+        }
+    }
+    //calculate the offsets in the vector.
+    int checkIndex = 1;
+    for (map<int,int>::iterator it=sizes.begin(); it != sizes.end(); it++)
+    {
+        result.push_back(size);
+        size += it->second;
+        if (it->first != checkIndex)
+        {
+            stringstream str;
+            str << "Expected index " << checkIndex << " found " << it->first;
+            Error(str.str());
+        }
+        checkIndex++;
+    }
+    result.push_back(size);
+    return result;
+}
+// --------------------------------------------------------------------------
+//
+//! Constructor
+//! @param out
+//!        the ostream where to redirect the outputs
+//
+FitsLoader::FitsLoader(ostream& out) : StateMachineDim(out, "FITS_LOADER")
+{
+    //Add the existing states
+    AddStateName(kSM_FileLoaded,  "FileLoaded", "A Fits file has been loaded");
+
+    //Add the possible transitions
+    AddEvent(kSM_FileLoaded, fLoadFits, kSM_Ready)
+            (boost::bind(&FitsLoader::LoadPlease, this))
+            ("Loads the given Fits file");
+    AddEvent(kSM_Ready, fUnloadFits, kSM_FileLoaded)
+            (boost::bind(&FitsLoader::UnloadPlease, this))
+            ("Unloads the given Fits file");
+
+    //Add the possible configurations
+    AddEvent(fListColumns, "", kSM_FileLoaded)
+            (boost::bind(&FitsLoader::ListColumnsPlease, this, _1))
+            ("List the columns that were loaded from that file");
+    AddEvent(fDumpColumns, "C", kSM_FileLoaded)
+            (boost::bind(&FitsLoader::AddDumpColumnsPlease, this, _1))
+            ("Add a given column to the dumping list");
+    AddEvent(fClearDumpList, "", kSM_FileLoaded)
+            (boost::bind(&FitsLoader::ClearDumpListPlease, this, _1))
+            ("Clear the dumping list");
+    AddEvent(fDoDump, "", kSM_FileLoaded)
+            (boost::bind(&FitsLoader::DoDumpPlease, this, _1))
+            ("Perform the dump of columns data, based on the to dump list");
+    AddEvent(fConfigFileName, "C", kSM_Ready, kSM_FileLoaded)
+            (boost::bind(&FitsLoader::ConfigFileNamePlease, this, _1))
+            ("Gives the name of the Fits file to be loaded");
+    AddEvent(fConfigTableName, "C", kSM_Ready, kSM_FileLoaded)
+            (boost::bind(&FitsLoader::ConfigTableNamePlease, this, _1))
+            ("Gives the name of the Table to be loaded");
+    AddEvent(fConfigPrecName, "I", kSM_Ready, kSM_FileLoaded)
+            (boost::bind(&FitsLoader::SetOFStreamPrecisionPlease, this, _1))
+            ("Set the precision of the ofstream, i.e. the number of significant digits being outputted");
+    AddEvent(fConfigFileOutName, "C", kSM_Ready, kSM_FileLoaded)
+            (boost::bind(&FitsLoader::SetFileOutPlease, this, _1))
+            ("Set the name of the outputted file.");
+
+    fFile = NULL;
+    fStreamPrecision = 20;
+
+}
+// --------------------------------------------------------------------------
+//
+//! Destructor
+//
+FitsLoader::~FitsLoader()
+{
+    if (fFile)
+        delete fFile;
+    fFile = NULL;
+}
+// --------------------------------------------------------------------------
+//
+//! Loads the fits file based on the current parameters
+//
+int FitsLoader::LoadPlease()
+{
+    stringstream str;
+    try
+    {
+        fFile = new CCfits::FITS(fFileName);
+    }
+    catch (CCfits::FitsException e)
+     {
+         str << "Could not open FITS file " << fFileName << " reason: " << e.message();
+         Error(str);
+         return kSM_Ready;
+     }
+    str.str("");
+    const multimap< string, CCfits::ExtHDU * > extMap = fFile->extension();
+    if (extMap.find(fTableName) == extMap.end())
+    {
+        str.str("");
+        str << "Could not open table " << fTableName << ". Tables in file are: ";
+        for (std::multimap<string, CCfits::ExtHDU*>::const_iterator it=extMap.begin(); it != extMap.end(); it++)
+            str << it->first << " ";
+        Error(str.str());
+        return kSM_Ready;
+    }
+    else
+        fTable = dynamic_cast<CCfits::Table*>(extMap.find(fTableName)->second);
+    int numRows = fTable->rows();
+    str.str("");
+    str << "Loaded table has " << numRows << " rows";
+    Message(str.str());
+
+    fColMap = fTable->column();
+    if (fDumpList.size() != 0)
+    {
+        bool should_clear = false;
+        for (vector<string>::iterator it=fDumpList.begin(); it!= fDumpList.end(); it++)
+        {
+            if (fColMap.find(*it) == fColMap.end())
+            {
+                should_clear = true;
+                Error("Config-given dump list contains invalid entry " + *it + " clearing the list");
+            }
+        }
+        if (should_clear)
+            fDumpList.clear();
+    }
+    return kSM_FileLoaded;
+}
+// --------------------------------------------------------------------------
+//
+//! Unloads the Fits file
+//
+int FitsLoader::UnloadPlease()
+{
+    if (fFile)
+        delete fFile;
+    else
+        Error("Error: Fits file is  NULL while it should not have been");
+    fFile = NULL;
+    return kSM_Ready;
+}
+// --------------------------------------------------------------------------
+//
+//! List the columns that are in the loaded Fits table
+//
+int FitsLoader::ListColumnsPlease(const Event&)
+{
+    Message("Columns in the loaded table are:");
+    map<string, CCfits::Column*>::iterator it;
+    for (it=fColMap.begin(); it != fColMap.end(); it++)
+        Message(it->first);
+    return GetCurrentState();
+}
+// --------------------------------------------------------------------------
+//
+//! Add a given column name  to the list of columns to dump
+//! @param evt
+//!        the event transporting the column name
+//
+int FitsLoader::AddDumpColumnsPlease(const Event& evt)
+{
+    string evtText(evt.GetText());
+    //TODO check that this column indeed exist in the file
+    if (fColMap.find(evtText) != fColMap.end())
+        fDumpList.push_back(evtText);
+    else
+        Error("Could not find column " + evtText + " int table");
+    Message("New dump list:");
+    for (vector<string>::iterator it=fDumpList.begin(); it != fDumpList.end(); it++)
+        Message(*it);
+    return GetCurrentState();
+}
+// --------------------------------------------------------------------------
+//
+//! Clear the list of columns to dump
+//
+int FitsLoader::ClearDumpListPlease(const Event&)
+{
+    fDumpList.clear();
+    Message("Dump list is now empty");
+    return GetCurrentState();
+}
+// --------------------------------------------------------------------------
+//
+//! Perform the actual dump, based on the current parameters
+//
+int FitsLoader::DoDumpPlease(const Event&)
+{
+    fTable->makeThisCurrent();
+    vector<int> offsets = CalculateBufferSize();
+    int size = offsets[offsets.size()-1];
+    offsets.pop_back();
+    unsigned char* fitsBuffer = new unsigned char[size];
+
+    ofstream targetFile(fFileOut);
+    int status = 0;
+
+    for (int i=1;i<=fTable->rows(); i++)
+    {
+        fits_read_tblbytes(fFile->fitsPointer(), i, 1, size, fitsBuffer, &status);
+        if (status)
+        {
+            stringstream str;
+            str << "An error occurred while reading fits row #" << i << " error code: " << status;
+            Error(str.str());
+            str.str("");
+            for (unsigned int j=0;j<offsets.size(); j++)
+                str << offsets[j] << " ";
+            Error(str.str());
+        }
+        writeValuesFromFits(offsets, targetFile, fitsBuffer);
+    }
+    delete[] fitsBuffer;
+    return GetCurrentState();
+}
+// --------------------------------------------------------------------------
+//
+//! Set the name of the intput Fits file
+//! @param evt
+//!        the event transporting the file name
+//
+int FitsLoader::ConfigFileNamePlease(const Event& evt)
+{
+    fFileName = string(evt.GetText());
+    Message("New Fits file: " + fFileName);
+    return GetCurrentState();
+}
+// --------------------------------------------------------------------------
+//
+//! Set the name of the input table
+//! @param evt
+//!        the event transporting the table name
+//
+int FitsLoader::ConfigTableNamePlease(const Event& evt)
+{
+    fTableName = string(evt.GetText());
+    Message("New Fits table: " + fTableName);
+    return GetCurrentState();
+}
+// --------------------------------------------------------------------------
+//
+//! Retrieves the configuration parameters
+//! @param conf
+//!             the configuration object
+//
+void FitsLoader::SetupConfig(Configuration& conf)
+{
+    if (conf.Has("outfile"))
+    {
+        this->fFileOut = conf.Get<string>("outfile");
+        Message("Output file is: " + fFileOut);
+    }
+    if (conf.Has("fitsfile"))
+    {
+        this->fFileName = conf.Get<string>("fitsfile");
+        Message("Input fits is: " + fFileName);
+    }
+    if (conf.Has("tablename"))
+    {
+        this->fTableName = conf.Get<string>("tablename");
+        Message("Input Table is: " + fTableName);
+    }
+    if (conf.Has("dump"))
+    {
+        this->fDumpList = conf.Get<vector<string>>("dump");
+        Message("Dump list is:");
+        for (vector<string>::iterator it=fDumpList.begin(); it != fDumpList.end(); it++)
+            Message(*it);
+    }
+    if (conf.Has("precision"))
+    {
+        this->fStreamPrecision = conf.Get<int>("precision");
+        stringstream str;
+        str << "OFStream precision is: " << fStreamPrecision;
+        Message(str.str());
+    }
+}
+void RunThread(FitsLoader* loader)
+{
+    loader->Run(true);
+    Readline::Stop();
+}
+template<class T>
+int RunShell(Configuration& conf)
+{
+    static T shell(conf.GetName().c_str(), conf.Get<int>("console")!=1);
+
+    WindowLog& wout = shell.GetStreamOut();
+
+    FitsLoader loader(wout);
+    loader.SetupConfig(conf);
+    shell.SetReceiver(loader);
+
+    boost::thread t(boost::bind(RunThread, &loader));
+
+    shell.Run();
+
+    loader.Stop();
+
+    t.join();
+
+    return 0;
+}
+void PrintUsage()
+{
+    cout << "This is a usage. to be completed" << endl;
+}
+void PrintHelp()
+{
+    cout << "This is the help. I know, not so helpfull at the moment..." << endl;
+}
+void SetupConfiguration(Configuration& conf)
+{
+    po::options_description configp("Programm options");
+    configp.add_options()
+            ("console,c", var<int>(), "Use console (0=shell, 1=simple buffered, X=simple unbuffered)");
+
+    po::options_description configs("Fits Loader options");
+    configs.add_options()
+            ("outfile,o", var<string>(), "Output file")
+            ("fitsfile,f", var<string>(), "Input Fits file")
+            ("tablename,t", var<string>(), "Input Table")
+            ("dump,d", vars<string>(), "List of columns to dump")
+            ("precision,p", var<int>(), "Precision of ofstream")
+            ;
+//    conf.AddEnv("dns", "DIM_DNS_NODE");
+
+    conf.AddOptions(configp);
+    conf.AddOptions(configs);
+}
+int main(int argc, const char** argv)
+{
+    Configuration conf(argv[0]);
+    conf.SetPrintUsage(PrintUsage);
+    SetupConfiguration(conf);
+
+    po::variables_map vm;
+    try
+    {
+        vm = conf.Parse(argc, argv);
+    }
+    catch (exception& e)
+    {
+        cout << "Sorry, could not properly parse the program's arguments. returning" << endl;
+        cout << e.what() << endl;
+        return -1;
+    }
+
+    if (conf.HasPrint())
+            return -1;
+    if (conf.HasVersion())
+    {
+        FACT::PrintVersion(argv[0]);
+        return -1;
+    }
+
+    if (conf.HasHelp())
+    {
+        PrintHelp();
+        return -1;
+    }
+
+//    if (!conf.Has("console"))
+//        return Run(conf);
+    if (conf.Get<int>("console")==0)
+        return RunShell<LocalShell>(conf);
+    else
+        return RunShell<LocalConsole>(conf);
+
+    return 0;
+}
