Index: /trunk/FACT++/src/timecheck.cc
===================================================================
--- /trunk/FACT++/src/timecheck.cc	(revision 13981)
+++ /trunk/FACT++/src/timecheck.cc	(revision 13981)
@@ -0,0 +1,235 @@
+#include "StateMachineDim.h"
+
+#include "tools.h"
+#include "Time.h"
+#include "Configuration.h"
+#include "LocalControl.h"
+
+using namespace std;
+
+// ------------------------------------------------------------------------
+
+#include "DimDescriptionService.h"
+
+// ========================================================================
+// ========================================================================
+// ========================================================================
+
+class StateMachineTimeCheck : public StateMachineDim
+{
+private:
+    Time fLastUpdate;
+
+    string   fServer;
+    uint16_t fInterval;
+
+    DimDescribedService fService;
+
+    enum
+    {
+        kStateRunning    = 1,
+        kStateOutOfRange = 2
+    };
+
+    // ------------- Initialize variables before the Dim stuff ------------
+
+    int Execute()
+    {
+        Time now;
+        if (now-fLastUpdate<boost::posix_time::minutes(fInterval))
+            return kStateRunning;
+
+        fLastUpdate=now;
+
+        const string cmd = "ntpdate -q "+fServer;
+
+        Info("Calling '"+cmd+"'");
+
+        FILE *pipe = popen(cmd.c_str(), "r");
+        if (!pipe)
+        {
+            const string err = strerror(errno);
+            Error("Could not create pipe '"+cmd+"': "+err);
+            return 0x100;
+        }
+
+        vector<string> args;
+
+        string line;
+        while (1)
+        {
+            const int rc = fgetc(pipe);
+            if (rc==EOF || rc=='\n')
+            {
+                args.push_back(Tools::Trim(line));
+                break;
+            }
+
+            if (rc==',')
+            {
+                args.push_back(Tools::Trim(line));
+                line = "";
+                continue;
+            }
+
+            line += static_cast<unsigned char>(rc);
+        }
+        pclose(pipe);
+
+        if (args.size()!=4)
+        {
+            Error("First returned line contains other than four arguments (separated by commas)");
+            return 0x100;
+        }
+
+        if (args[2].substr(0, 7)!="offset ")
+        {
+            Error("Argument 3 '"+args[2]+"' is not what it ought to be.");
+            return 0x100;
+        }
+
+        try
+        {
+            const float offset = stof(args[2].substr(7));
+            fService.Update(offset);
+
+            const string msg = "NTP: "+fServer+" returned: "+args[2]+" ms";
+
+            if (offset>=1000)
+            {
+                Warn(msg);
+                return kStateOutOfRange;
+            }
+
+            Message(msg);
+
+        }
+        catch (const exception &e)
+        {
+            Error("Converting offset '"+args[2]+"' to float failed: "+e.what());
+            return 0x100;
+        }
+
+        return kStateRunning;
+    }
+
+    int Trigger()
+    {
+        fLastUpdate = Time()-boost::posix_time::minutes(fInterval);
+        return GetCurrentState();
+    }
+
+public:
+    StateMachineTimeCheck(ostream &out=cout) : StateMachineDim(out, "TIME_CHECK"),
+        fService("OFFSET", "F:1", "Time offset measured with ntp|offset[ms]:Time offset in milliseconds")
+    {
+        // State names
+        AddStateName(kStateRunning,    "Valid",      "Last check was valid.");
+        AddStateName(kStateOutOfRange, "OutOfRange", "Last time check exceeded 1s.");
+
+        AddEvent("TRIGGER");
+            (bind(&StateMachineTimeCheck::Trigger, this))
+            ("Trigger update");
+
+    }
+    int EvalOptions(Configuration &conf)
+    {
+        fServer   = conf.Get<string>("ntp-server");
+        fInterval = conf.Get<uint16_t>("interval");
+        if (fInterval==0)
+            fInterval=1;
+
+        Trigger();
+
+        return -1;
+    }
+};
+
+// ------------------------------------------------------------------------
+
+#include "Main.h"
+
+template<class T>
+int RunShell(Configuration &conf)
+{
+    return Main::execute<T, StateMachineTimeCheck>(conf);
+}
+
+void SetupConfiguration(Configuration &conf)
+{
+    po::options_description control("Time check");
+    control.add_options()
+        ("ntp-server", var<string>("hora.roa.es"),  "The ntp server to be queried")
+        ("interval",   var<uint16_t>(15),           "Interval in minutes the ntp server should be queried")
+        ;
+
+    conf.AddOptions(control);
+}
+
+/*
+ Extract usage clause(s) [if any] for SYNOPSIS.
+ Translators: "Usage" and "or" here are patterns (regular expressions) which
+ are used to match the usage synopsis in program output.  An example from cp
+ (GNU coreutils) which contains both strings:
+  Usage: cp [OPTION]... [-T] SOURCE DEST
+    or:  cp [OPTION]... SOURCE... DIRECTORY
+    or:  cp [OPTION]... -t DIRECTORY SOURCE...
+ */
+void PrintUsage()
+{
+    /*
+    cout <<
+        "SmartFACT is a tool writing the files needed for the SmartFACT web interface.\n"
+        "\n"
+        "The default is that the program is started without user intercation. "
+        "All actions are supposed to arrive as DimCommands. Using the -c "
+        "option, a local shell can be initialized. With h or help a short "
+        "help message about the usuage can be brought to the screen.\n"
+        "\n"
+        "Usage: smartfact [-c type] [OPTIONS]\n"
+        "  or:  smartfact [OPTIONS]\n";
+    cout << endl;*/
+}
+
+void PrintHelp()
+{
+    Main::PrintHelp<StateMachineTimeCheck>();
+
+    /* Additional help text which is printed after the configuration
+     options goes here */
+
+    /*
+     cout << "bla bla bla" << endl << endl;
+     cout << endl;
+     cout << "Environment:" << endl;
+     cout << "environment" << endl;
+     cout << endl;
+     cout << "Examples:" << endl;
+     cout << "test exam" << endl;
+     cout << endl;
+     cout << "Files:" << endl;
+     cout << "files" << endl;
+     cout << endl;
+     */
+}
+
+int main(int argc, const char* argv[])
+{
+    Configuration conf(argv[0]);
+    conf.SetPrintUsage(PrintUsage);
+    Main::SetupConfiguration(conf);
+    SetupConfiguration(conf);
+
+    if (!conf.DoParse(argc, argv, PrintHelp))
+        return -1;
+
+    if (!conf.Has("console"))
+        return RunShell<LocalStream>(conf);
+
+    if (conf.Get<int>("console")==0)
+        return RunShell<LocalShell>(conf);
+    else
+        return RunShell<LocalConsole>(conf);
+
+    return 0;
+}
