Index: /trunk/FACT++/src/smartfact.cc
===================================================================
--- /trunk/FACT++/src/smartfact.cc	(revision 14217)
+++ /trunk/FACT++/src/smartfact.cc	(revision 14218)
@@ -370,4 +370,55 @@
     };
 
+    // ------------------------- History classes -----------------------
+
+    struct EventElement
+    {
+        Time time;
+        string msg;
+
+        EventElement(const Time &t, const string &s) : time(t), msg(s) { }
+    };
+
+    class EventHist : public deque<EventElement>
+    {
+        const boost::posix_time::time_duration deltat; //boost::posix_time::pos_infin
+        const uint64_t max;
+
+    public:
+        EventHist(const boost::posix_time::time_duration &dt=boost::posix_time::hours(12), uint64_t mx=UINT64_MAX) : deltat(dt), max(mx) { }
+
+        void add(const string &s, const Time &t=Time())
+        {
+            while (size()>0 && (front().time+deltat<t || size()>max))
+                pop_front();
+
+            push_back(EventElement(t, s));
+        }
+
+        string get() const
+        {
+            ostringstream out;
+
+            string last = "";
+            for (auto it=begin(); it!=end(); it++)
+            {
+                const string tm = it->time.GetAsStr("%H:%M:%S ");
+                out << (tm!=last?tm:"--:--:-- ") << it->msg << "<br/>";
+                last = tm;
+            }
+
+            return out.str();
+        }
+        string rget() const
+        {
+            ostringstream out;
+
+            for (auto it=rbegin(); it!=rend(); it++)
+                out << it->time.GetAsStr("%H:%M:%S ") << it->msg << "<br/>";
+
+            return out.str();
+        }
+    };
+
     // ------------------------- Internal variables -----------------------
 
@@ -385,14 +436,13 @@
     // ----------------------------- Data storage -------------------------
 
-    deque<string> fControlMessageHist;
-    int32_t       fControlScriptDepth;
-
-    uint32_t fMcpConfigurationState;   // For consistency
-     int64_t fMcpConfigurationMaxTime;
-     int64_t fMcpConfigurationMaxEvents;
-    string   fMcpConfigurationName;
-    Time     fMcpConfigurationRunStart;
-    Time     fMcpConfigurationLastTime;
-    deque<string> fMcpConfigurationHist;
+    EventHist fControlMessageHist;
+    int32_t   fControlScriptDepth;
+
+    uint32_t  fMcpConfigurationState;   // For consistency
+     int64_t  fMcpConfigurationMaxTime;
+     int64_t  fMcpConfigurationMaxEvents;
+    string    fMcpConfigurationName;
+    Time      fMcpConfigurationRunStart;
+    EventHist fMcpConfigurationHist;
 
     bool fLastRunFinishedWithZeroEvents;
@@ -447,8 +497,7 @@
     deque<float> fRateScanDataHist[41];
 
-    set<string>   fErrorList;
-    deque<string> fErrorHist;
-    deque<string> fChatHist;
-    Time          fChatLastTime;
+    set<string> fErrorList;
+    EventHist   fErrorHist;
+    EventHist   fChatHist;
 
     Sun   fSun;
@@ -636,22 +685,5 @@
             return;
 
-        const string time = d.GetTimeAsStr("%H:%M:%S ");
-
-        string str = "         ";
-        for (auto it=fControlMessageHist.rbegin(); it!=fControlMessageHist.rend(); it++)
-        {
-            str = it->substr(0, time.length());
-            if (str!="--:--:-- ")
-                break;
-        }
-
-        ostringstream tst;
-        tst << d.GetQoS();
-
-        string msg;
-        msg += str==time ? "--:--:-- " : time;
-        msg += d.Ptr<char>();
-
-        fControlMessageHist.push_back(msg);
+        fControlMessageHist.add(d.GetText(), d.GetTime());
 
         ostringstream out;
@@ -659,10 +691,5 @@
         out << Header(d) << '\n';
         out << HTML::kWhite << '\t';
-
-        out << "<->";
-        for (auto it=fControlMessageHist.begin(); it!=fControlMessageHist.end(); it++)
-            out << *it << "<br/>";
-        out << "</->";
-
+        out << "<->" << fControlMessageHist.get() << "</->";
         out << '\n';
 
@@ -709,41 +736,36 @@
     }
 
-    void HandleFscControlStateChange(const EventImp &d)
-    {
-        const int32_t &last  = fDimFscControl.last.second;
-        const int32_t &state = fDimFscControl.state();
-
-        if (last==DimState::kOffline || state==DimState::kOffline)
-            return;
-
-        if (last<FSC::State::kConnected && state==FSC::State::kConnected)
-        {
-            AddMcpConfigurationHist(d, "<B>Camera swiched on</B>");
-            SetAudio("startup");
-        }
-
-        if (last==FSC::State::kConnected && state<FSC::State::kConnected)
-        {
-            AddMcpConfigurationHist(d, "<B>Camera swiched off</B>");
-            SetAudio("shutdown");
-        }
-    }
-
     void AddMcpConfigurationHist(const EventImp &d, const string &msg)
     {
-        if (d.GetTime()>fMcpConfigurationLastTime+boost::posix_time::hours(12))
-            fMcpConfigurationHist.clear();
-
-        fMcpConfigurationLastTime  = d.GetTime();
-        fMcpConfigurationHist.push_back(d.GetTimeAsStr("%H:%M:%S ")+msg+"<br/>");
+        fMcpConfigurationHist.add(msg, d.GetTime());
 
         ostringstream out;
         out << d.GetJavaDate() << '\n';
         out << HTML::kWhite << '\t';
-        for (auto it=fMcpConfigurationHist.rbegin(); it!=fMcpConfigurationHist.rend(); it++)
-            out << *it;
+        out << "<->" << fMcpConfigurationHist.rget() << "</->";
         out << '\n';
 
         ofstream(fPath+"/observations.data") << out.str();
+    }
+
+    void HandleFscControlStateChange(const EventImp &d)
+    {
+        const int32_t &last  = fDimFscControl.last.second;
+        const int32_t &state = fDimFscControl.state();
+
+        if (last==DimState::kOffline || state==DimState::kOffline)
+            return;
+
+        if (last<FSC::State::kConnected && state==FSC::State::kConnected)
+        {
+            AddMcpConfigurationHist(d, "<B>Camera swiched on</B>");
+            SetAudio("startup");
+        }
+
+        if (last==FSC::State::kConnected && state<FSC::State::kConnected)
+        {
+            AddMcpConfigurationHist(d, "<B>Camera swiched off</B>");
+            SetAudio("shutdown");
+        }
     }
 
@@ -894,4 +916,19 @@
     }
 
+    void HandleDriveControlStateChange(const EventImp &d)
+    {
+        const int32_t &last  = fDimFscControl.last.second;
+        const int32_t &state = fDimFscControl.state();
+
+        if (last==DimState::kOffline || state==DimState::kOffline)
+            return;
+
+        if (last<Drive::State::kArmed && state>=Drive::State::kArmed)
+            AddMcpConfigurationHist(d, "Drive connected");
+
+        if (last>=Drive::State::kArmed && state<Drive::State::kArmed)
+            AddMcpConfigurationHist(d, "Drive disconnected");
+    }
+
     int HandleDrivePointing(const EventImp &d)
     {
@@ -1320,5 +1357,5 @@
         // by the MCP. Hence, we get a warning. So we have to require
         // two consecutive low rates.
-        if (*crate<0.1)
+        if (*crate<1)
             fFtmControlTriggerRateTooLow++;
         else
@@ -1679,19 +1716,8 @@
             return GetCurrentState();
 
-        if (d.GetTime()>fChatLastTime+boost::posix_time::hours(12))
-            fChatHist.clear();
-
         if (Time()<d.GetTime()+boost::posix_time::minutes(1))
             SetAudio("message");
 
-        fChatLastTime = d.GetTime();
-
-        string msg;
-        msg += d.GetTimeAsStr("%H:%M:%S ");
-        msg += d.Ptr<char>();
-
-        fChatHist.push_front(msg);
-        if (fChatHist.size()>80)
-            fChatHist.pop_back();
+        fChatHist.add(d.GetText(), d.GetTime());
 
         ostringstream out;
@@ -1699,10 +1725,5 @@
         out << Header(d) << '\n';
         out << HTML::kWhite << '\t';
-
-        out << "<->";
-        for (auto it=fChatHist.begin(); it!=fChatHist.end(); it++)
-            out << *it << "<br/>";
-        out << "</->";
-
+        out << "<->" << fChatHist.rget() << "</->";
         out << '\n';
 
@@ -1810,16 +1831,6 @@
 
         const bool isnew = fErrorList.insert(err).second;
-
         if (isnew)
-        {
-            ostringstream msg;
-            msg << "<pre>" << Time().GetAsStr("%m-%d %H:%M") << "</pre> <->" << err << "</->";
-            if (find(fErrorHist.begin(), fErrorHist.end(), msg.str())==fErrorHist.end())
-            {
-                fErrorHist.push_front(msg.str());
-                if (fErrorHist.size()>80)
-                    fErrorHist.pop_back();
-            }
-        }
+            fErrorHist.add(err);
 
         return isnew;
@@ -2122,5 +2133,5 @@
 
         newerr |= SetError(fFtmControlTriggerRateTooLow>2 && fDimMcp.state()==MCP::State::kTakingData,
-                           "Trigger rate below 100mHz during data taking");
+                           "Trigger rate below 1Hz during data taking");
 
         newerr |= SetError(fDimTimeCheck.state()==1,
@@ -2170,6 +2181,5 @@
             out << now.JavaDate() << '\n';
             out << HTML::kWhite << '\t';
-            for (auto it=fErrorHist.begin(); it!=fErrorHist.end(); it++)
-                out << *it << "<br/>";
+            out << "<->" << fErrorHist.rget() << "<->";
             out << '\n';
 
@@ -2601,4 +2611,5 @@
 
         fDimFscControl.SetCallback(bind(&StateMachineSmartFACT::HandleFscControlStateChange, this, placeholders::_1));
+        fDimDriveControl.SetCallback(bind(&StateMachineSmartFACT::HandleDriveControlStateChange, this, placeholders::_1));
         fDimControl.SetCallback(bind(&StateMachineSmartFACT::HandleControlStateChange, this, placeholders::_1));
         fDimControl.AddCallback("dotest.dim", bind(&StateMachineSmartFACT::HandleDoTest, this, placeholders::_1));
