Index: trunk/FACT++/src/smartfact.cc
===================================================================
--- trunk/FACT++/src/smartfact.cc	(revision 13759)
+++ trunk/FACT++/src/smartfact.cc	(revision 13765)
@@ -75,11 +75,11 @@
     deque<float> fFscControlTemperatureHist;
 
-    float fFscControlTemperatureAvg;
     float fFscControlHumidityAvg;
 
     float  fDriveControlPointingZd;
     string fDriveControlPointingAz;
-    float  fDriveControlTrackingDev;
     string fDriveControlSourceName;
+
+    deque<float> fDriveControlTrackingDevHist;
 
     int64_t fFadControlNumEvents;
@@ -163,8 +163,53 @@
     };
 
+    class DimControl : public DimState
+    {
+        typedef function<void(const DimData &)> callback;
+        map<string, callback> fCallbacks;
+    public:
+        DimControl() : DimState("DIM_CONTROL") { }
+
+        void AddCallback(const string &script, const callback &cb)
+        {
+            fCallbacks[script] = cb;
+        }
+
+        void infoHandler()
+        {
+            DimInfo *curr = getInfo(); // get current DimInfo address
+            if (!curr || curr != &dim)
+                return;
+
+            DimState::infoHandler();
+
+            // Evaluate msg
+            const size_t p0 = msg.find_first_of(':');
+            if (p0==string::npos)
+                return;
+
+            const size_t p1 = msg.find_last_of('[');
+            if (p1==string::npos)
+                return;
+
+            const size_t p2 = msg.find_first_of(':', p0+1);
+
+            const size_t p3 = p2==string::npos || p2>p1 ? p1-1 : p2;
+
+            const string file = msg.substr(p0+2, p3-p0-2);
+
+            const auto func = fCallbacks.find(file);
+            if (func==fCallbacks.end())
+                return;
+
+            // Call callback
+            func->second(DimData(curr));
+        }
+    };
+
+
 
     DimVersion fDim;
+    DimControl fDimControl;
     DimState   fDimMcp;
-    DimState   fDimControl;
     DimState   fDimDataLogger;
     DimState   fDimDriveControl;
@@ -258,9 +303,11 @@
         const char *ptr = reinterpret_cast<char*>(val.data());
 
-        ofstream fout(fPath+"/"+fname+".bin");
-        fout << d.time.JavaDate() << '\n';
-        fout << offset << '\n';
-        fout << offset+scale << '\n';
-        fout.write(ptr, val.size()*sizeof(uint8_t));
+        ostringstream out;
+        out << d.time.JavaDate() << '\n';
+        out << offset << '\n';
+        out << offset+scale << '\n';
+        out.write(ptr, val.size()*sizeof(uint8_t));
+
+        ofstream(fPath+"/"+fname+".bin") << out.str();
     }
 
@@ -407,5 +454,10 @@
             return;
 
-        const double zd  = d.get<double>(3*8) * M_PI / 180;
+        const double Ra  = d.get<double>(0*8);
+        const double Dec = d.get<double>(1*8);
+        const double Zd  = d.get<double>(3*8);
+        const double Az  = d.get<double>(4*8);
+
+        const double zd  = Zd                 * M_PI / 180;
         const double dzd = d.get<double>(5*8) * M_PI / 180;
         const double daz = d.get<double>(6*8) * M_PI / 180;
@@ -415,9 +467,26 @@
 
         // Simplified:
-        const double dev = cos(dzd) - sin(zd+dzd)*sin(zd)*(1.-cos(daz));
-        fDriveControlTrackingDev = acos(dev) * 180 / M_PI * 3600;
-
-        if (fDriveControlTrackingDev<0.01)
-            fDriveControlTrackingDev=0;
+        double dev = cos(dzd) - sin(zd+dzd)*sin(zd)*(1.-cos(daz));
+        dev = acos(dev) * 180 / M_PI * 3600;
+
+        fDriveControlTrackingDevHist.push_back(dev);
+        if (fDriveControlTrackingDevHist.size()>300)
+            fDriveControlTrackingDevHist.pop_front();
+
+        WriteBinary(d, "control-deviation-hist", fDriveControlTrackingDevHist, 120);
+
+        ostringstream out;
+        out << d.time.JavaDate() << '\n';
+
+        out << "#ffffff\t" << fDriveControlSourceName << '\n';
+        out << setprecision(5);
+        out << "#ffffff\t" << Ra  << '\n';
+        out << "#ffffff\t" << Dec << '\n';
+        out << setprecision(3);
+        out << "#ffffff\t" << Zd  << '\n';
+        out << "#ffffff\t" << Az  << '\n';
+        out << "#ffffff\t" << dev << '\n';
+
+        ofstream(fPath+"/tracking.txt") << out.str();
     }
 
@@ -447,5 +516,5 @@
         out << "#ffffff\t" << wang << '\n';
 
-        ofstream(fPath+"/drive.txt") << out.str();
+        ofstream(fPath+"/source.txt") << out.str();
     }
 
@@ -842,6 +911,4 @@
         rms = sqrt(rms/num-avg*avg);
 
-        fFscControlTemperatureAvg = avg;
-
         fFscControlTemperatureHist.push_back(avg);
         if (fFscControlTemperatureHist.size()>300)
@@ -853,4 +920,5 @@
         out << setprecision(3);
         out << d.time.JavaDate() << '\n';
+        out << "#ffffff\t" << fFscControlHumidityAvg << '\n';
         out << "#ffffff\t" << min      << '\n';
         out << "#ffffff\t" << avg      << '\n';
@@ -902,4 +970,21 @@
 
         WriteBinary(d, "ratescan-hist", fRateScanDataHist, 10, -2);
+    }
+
+    // -------------------------------------------------------------------
+
+    void HandleDoTest(const DimData &d)
+    {
+        ostringstream out;
+        out << d.time.JavaDate() << '\t' << fDimControl.online() << '\n';
+        switch (d.qos)
+        {
+        case -3: out << kHtmlWhite << "\tNot running\n";               break;
+        case -2: out << kHtmlBlue  << "\tLoading\n";                   break;
+        case -1: out << kHtmlBlue  << "\tStarted\n";                   break;
+        default: out << kHtmlGreen << "\tRunning [" << d.qos << "]\n"; break;
+        }
+
+        ofstream(fPath+"/dotest.txt") << out.str();
     }
 
@@ -1067,16 +1152,26 @@
 
             out << col << '\t';
-
-            if (fMcpConfigurationState!=5 &&
-                fMcpConfigurationState!=11 &&
-                fMcpConfigurationState!=12)
-                out << "Configuring ";
-            out << fMcpConfigurationName;
-
-            if (fDimRateControl.state()==5/*kStateSettingGlobalThreshold*/)
-                out << "Calibrating threshold";
-
-            if (fDimRateScan.state()==5/*kStateSettingGlobalThreshold*/)
-                out << "Rate scan in progress";
+            /*
+             out << fDimRateControl.state() << "/";
+             out << fDimRateScan.state() << "/";
+             out << fMcpConfigurationState << "/";
+             */
+
+            if (fDimRateControl.state()!=5 &&
+                fDimRateScan.state()!=5)
+            {
+                if (fMcpConfigurationState!=5 &&
+                    fMcpConfigurationState!=11 &&
+                    fMcpConfigurationState!=12)
+                    out << "Configuring ";
+                out << fMcpConfigurationName;
+            }
+            else
+                if (fDimRateControl.state()==5/*kStateSettingGlobalThreshold*/)
+                    out << "Calibrating threshold";
+                else
+
+                    if (fDimRateScan.state()==5/*kStateSettingGlobalThreshold*/)
+                        out << "Rate scan in progress";
 
             if (fDimMcp.state()>5 && fDimRateControl.state()!=5)
@@ -1128,4 +1223,5 @@
         if (fDimDriveControl.state()>=5)   // Armed, Moving, Tracking
         {
+            const double dev = fDriveControlTrackingDevHist.size()>0 ? fDriveControlTrackingDevHist.back() : 0;
             const State rc = GetState(fDimDriveControl);
             string col = kHtmlGreen;
@@ -1136,7 +1232,7 @@
             if (rc.index==7) // Tracking
             {
-                if (fDriveControlTrackingDev>60)   // ~1.5mm
+                if (dev>60)   // ~1.5mm
                     col = kHtmlYellow;
-                if (fDriveControlTrackingDev>100)  // ~1/4 of a pixel ~ 2.5mm
+                if (dev>100)  // ~1/4 of a pixel ~ 2.5mm
                     col = kHtmlRed;
             }
@@ -1150,5 +1246,5 @@
                 out << fDriveControlSourceName  << '\t';
                 out << setprecision(2);
-                out << fDriveControlTrackingDev << '\n';
+                out << dev << '\n';
                 out << setprecision(3);
             }
@@ -1160,7 +1256,11 @@
 
         // ------------------- FSC ------------------
-        if (fDimFscControl.state()>1)
-        {
-            out << kHtmlGreen << '\t' << fFscControlTemperatureAvg << '\n';
+        if (fDimFscControl.state()>1 && fFscControlTemperatureHist.size()>0)
+        {
+            if (fDimMagicWeather.state()==3 && fMagicWeatherHist[kTemp].size()>0)
+                out << kHtmlGreen << '\t' << fFscControlTemperatureHist.back()-fMagicWeatherHist[kTemp].back() << '\n';
+            else
+                out << kHtmlGreen << '\t' << fFscControlTemperatureHist.back() << " [abs]";
+
         }
         else
@@ -1311,5 +1411,4 @@
         //---
         fDimMcp                   ("MCP"),
-        fDimControl               ("DIM_CONTROL"),
         fDimDataLogger            ("DATA_LOGGER"),
         fDimDriveControl          ("DRIVE_CONTROL"),
@@ -1357,13 +1456,9 @@
         AddStateName(kStateRunning, "Running", "");
 
-        // Verbosity commands
-//        AddEvent("SET_VERBOSE", "B:1")
-//            (bind(&StateMachineMCP::SetVerbosity, this, placeholders::_1))
-//            ("set verbosity state"
-//             "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
-
         AddEvent("PRINT")
             (bind(&StateMachineSmartFACT::Print, this))
             ("");
+
+        fDimControl.AddCallback("dotest.dim", bind(&StateMachineSmartFACT::HandleDoTest, this, placeholders::_1));
     }
     ~StateMachineSmartFACT()
