Index: /trunk/FACT++/src/smartfact.cc
===================================================================
--- /trunk/FACT++/src/smartfact.cc	(revision 13750)
+++ /trunk/FACT++/src/smartfact.cc	(revision 13751)
@@ -101,5 +101,5 @@
     public:
         DimState(const string &n, const string s="STATE") :
-            server(n), info(make_pair(Time(), -2)),
+            server(n), info(make_pair(Time(), -4)),
             dim((n+"/"+s).c_str(), (void*)NULL, 0, this) { }
 
@@ -123,5 +123,5 @@
 
             info = make_pair(Time(tsec, tms*1000),
-                             disconnected ? -2 : dim.getQuality());
+                             disconnected ? -4 : dim.getQuality());
 
             msg = disconnected ? "" : dim.getString();
@@ -130,4 +130,6 @@
         const Time &time() const { return info.first; }
         const int  &state() const { return info.second; }
+
+        bool online() const { return info.second>-4; }
 
         const string &name() const { return server; }
@@ -211,4 +213,6 @@
     }
 
+    // -------------------------------------------------------------------
+
     bool HandleService(DimInfo *curr, const DimInfo &service, void (StateMachineSmartFACT::*handle)(const DimData &))
     {
@@ -239,5 +243,5 @@
 
     template<class T>
-        void WriteBinary(const string &fname, const T &t, double scale, double offset=0)
+        void WriteBinary(const DimData &d, const string &fname, const T &t, double scale, double offset=0)
     {
         vector<uint8_t> val(t.size(), 0);
@@ -255,4 +259,5 @@
 
         ofstream fout(fPath+"/"+fname+".bin");
+        fout << d.time.JavaDate() << '\n';
         fout << offset << '\n';
         fout << offset+scale << '\n';
@@ -324,5 +329,5 @@
         ofstream(fPath+"/"+name+".txt") << out.str();
 
-        WriteBinary("magicweather-"+name+"-hist", fMagicWeatherHist[i], max-min, min);
+        WriteBinary(d, "magicweather-"+name+"-hist", fMagicWeatherHist[i], max-min, min);
     }
 
@@ -472,12 +477,12 @@
 
         // Write the 160 patch values to a file
-        WriteBinary("feedback-deviation", dev, 1);
+        WriteBinary(d, "feedback-deviation", dev, 1);
 
         const Statistics stat(dev, 3);
 
         ostringstream out;
-        out << setprecision(3);
         out << d.time.JavaDate() << '\n';
         out << kHtmlWhite << '\t' << fFeedbackUserOffset << '\n';
+        out << setprecision(3);
         out << kHtmlWhite << '\t' << fFeedbackTempOffset << '\n';
         out << kHtmlWhite << '\t' << stat.min << '\n';
@@ -510,7 +515,7 @@
 
         if (fDimBiasControl.state()==BIAS::kVoltageOn)
-            WriteBinary("biascontrol-voltage", val, 10, 65);
+            WriteBinary(d, "biascontrol-voltage", val, 10, 65);
         else
-            WriteBinary("biascontrol-voltage", val, 75);
+            WriteBinary(d, "biascontrol-voltage", val, 75);
 
         ostringstream out;
@@ -537,11 +542,40 @@
         const bool cal = fFeedbackCalibration.size()>0 && fBiasControlVoltageVec.size()>0;
 
+        double power_tot = 0;
+        double power_apd = 0;
+
+        // 3900 Ohm/n + 1000 Ohm + 1100 Ohm  (with n=4 or n=5)
+        const double R[2] = { 3075, 2870 };
+
         // Calibrate the data (subtract offset)
         if (cal)
             for (int i=0; i<320; i++)
             {
+                // Measued current minus leakage current (bias crate calibration)
                 v[i] -= fBiasControlVoltageVec[i]/fFeedbackCalibration[i]*1e6;
-                v[i] /= fPixelMap.hv(i).group() ? 5 : 4;
+
+                // Total power participated in the camera at the G-APD
+                // and the serial resistors (total voltage minus voltage
+                // drop at resistors in bias crate)
+                power_tot += v[i]*(fBiasControlVoltageVec[i] - 1100e-6*v[i])*1e-6;
+
+                // Group index (0 or 1) of the of the pixel (4 or 5 pixel patch)
+                const int g = fPixelMap.hv(i).group();
+
+                // Current per G-APD
+                v[i] /= g ? 5 : 4;
+
+                // Power consumption per G-APD
+                if (i!=66 && i!=191 && i!=193)
+                    power_apd += v[i]*(fBiasControlVoltageVec[i]-R[g]*v[i]*1e-6)*1e-6;
             }
+
+        // Divide by number of summed channels, concert to mW
+        power_apd /= 317e-3; // [mW]
+
+        if (power_tot<1e-3)
+            power_tot = 0;
+        if (power_apd<1e-3)
+            power_apd = 0;
 
         // Get the maximum of each patch
@@ -554,5 +588,5 @@
 
         // Write the 160 patch values to a file
-        WriteBinary("biascontrol-current", val, 100);
+        WriteBinary(d, "biascontrol-current", val, 100);
 
         const Statistics stat(v, 0, 3);
@@ -568,5 +602,5 @@
 
         // write the history to a file
-        WriteBinary("biascontrol-current-hist", fBiasControlCurrentHist, 100);
+        WriteBinary(d, "biascontrol-current-hist", fBiasControlCurrentHist, 100);
 
         const string col0 = cal ? kHtmlGreen : kHtmlWhite;
@@ -598,5 +632,5 @@
 
         ostringstream out;
-        out << setprecision(3);
+        out << setprecision(2);
         out << d.time.JavaDate() << '\n';
         out << col0 << '\t' << (cal?"yes":"no") << '\n';
@@ -605,4 +639,5 @@
         out << col3 << '\t' << stat.avg << '\n';
         out << col4 << '\t' << stat.max << '\n';
+        out << kHtmlWhite << '\t' << power_tot << "W [" << power_apd << "mW]\n";
         ofstream(fPath+"/current.txt") << out.str();
     }
@@ -689,9 +724,9 @@
         // FIXME: Add statistics for all kind of rates
 
-        WriteBinary("ftmcontrol-triggerrate-hist",
+        WriteBinary(d, "ftmcontrol-triggerrate-hist",
                     fFtmControlTriggerRateHist, 100);
-        WriteBinary("ftmcontrol-boardrates",
+        WriteBinary(d, "ftmcontrol-boardrates",
                     vector<float>(brates, brates+40), 10);
-        WriteBinary("ftmcontrol-patchrates",
+        WriteBinary(d, "ftmcontrol-patchrates",
                     vector<float>(prates, prates+160), 10);
 
@@ -731,5 +766,5 @@
         vector<uint16_t> vec(ptr, ptr+160);
 
-        WriteBinary("ftmcontrol-thresholds", vec, 1000);
+        WriteBinary(d, "ftmcontrol-thresholds", vec, 1000);
 
         const Statistics stat(vec);
@@ -768,7 +803,7 @@
         switch (d.qos)
         {
-        case 0:  WriteBinary("fadcontrol-eventdata", max, 2,   -1); break;
-        case 1:  WriteBinary("fadcontrol-eventdata", max, 2,    0); break;
-        default: WriteBinary("fadcontrol-eventdata", max, 0.25, 0); break;
+        case 0:  WriteBinary(d, "fadcontrol-eventdata", max, 2,   -1); break;
+        case 1:  WriteBinary(d, "fadcontrol-eventdata", max, 2,    0); break;
+        default: WriteBinary(d, "fadcontrol-eventdata", max, 0.25, 0); break;
         }
     }
@@ -827,5 +862,5 @@
         ofstream(fPath+"/fsc.txt") << out.str();
 
-        WriteBinary("fsccontrol-temperature-hist",
+        WriteBinary(d, "fsccontrol-temperature-hist",
                     fFscControlTemperatureHist, 30);
     }
@@ -853,5 +888,5 @@
     void HandleRateScanData(const DimData &d)
     {
-        if (!CheckDataSize(d, "RateScan:Data", 24+200*40))
+        if (!CheckDataSize(d, "RateScan:Data", 824))
             return;
 
@@ -866,5 +901,5 @@
         fRateScanDataHist.push_back(rate);
 
-        WriteBinary("ratescan-hist", fRateScanDataHist, 10, -1);
+        WriteBinary(d, "ratescan-hist", fRateScanDataHist, 10, -2);
     }
 
@@ -930,10 +965,13 @@
         Out() << state.time().GetAsStr("%H:%M:%S.%f").substr(0, 12) << " - ";
         Out() << kBold << state.name() << ": ";
+        if (rc.index==-3)
+        {
+            Out() << kReset << "Offline" << endl;
+            return;
+        }
         if (rc.index==-2)
-        {
-            Out() << kReset << "Offline" << endl;
-            return;
-        }
-        Out() << rc.name << "[" << rc.index << "]";
+            Out() << state.state();
+        else
+            Out() << rc.name << "[" << rc.index << "]";
         Out() << kReset << " - " << kBlue << rc.comment << endl;
     }
@@ -963,10 +1001,21 @@
     string GetStateHtml(const DimState &state, int green) const
     {
+        if (!state.online())
+            return kHtmlWhite+"\t&mdash;\n";
+
+        if (&state==&fDimControl)
+        {
+            ostringstream out;
+            out << kHtmlGreen << '\t';
+            return kHtmlGreen+'\t'+(state.state()==-3?"Idle":state.msg)+'\n';
+        }
+
         const State rc = GetState(state);
 
-        if (rc.index==-2 && state.state()>-2)
+        // Sate not found in list, server online (-3: offline; -2: not found)
+        if (rc.index==-2)
         {
             ostringstream out;
-            out << kWhite << '\t' << state.state() << '\n';
+            out << kHtmlWhite << '\t' << state.state() << '\n';
             return out.str();
         }
@@ -977,5 +1026,5 @@
 
         if (rc.index<1)
-            return kHtmlWhite + "\t---\n";
+            return kHtmlWhite + "\t&mdash;\n";
 
 
@@ -1002,5 +1051,5 @@
 
         ostringstream out;
-        out << now.JavaDate() << '\n';
+        out << now.JavaDate() << '\t' << fDimControl.online() << '\n';
         out << setprecision(3);
 
@@ -1009,7 +1058,7 @@
         {
             string col = kHtmlBlue;
-            if (fMcpConfigurationState!= 5 &&
-                fMcpConfigurationState!=11 &&
-                fMcpConfigurationState!=12) // 9 e.g. Configuring3
+            if (fMcpConfigurationState!= 5 &&  // Idle
+                fMcpConfigurationState!=11 &&  // Trigger On
+                fMcpConfigurationState!=12)    // Taking Data
                 col = kHtmlYellow;
             else
@@ -1017,43 +1066,57 @@
                     col = kHtmlGreen;
 
-            out << col << '\t' << fMcpConfigurationName;
-
-            if (fMcpConfigurationMaxEvents>0 || fMcpConfigurationMaxTime>0 || fMcpConfigurationState==12)
-                out << " [";
-            if (fMcpConfigurationMaxEvents>0)
+            out << col << '\t';
+
+            if (fDimRateControl.state()==5/*kStateSettingGlobalThreshold*/)
+                out << "Rate control in progress";
+            else
             {
-                if (fFadControlNumEvents>0 && fMcpConfigurationState==12)
-                    out << fMcpConfigurationMaxEvents-fFadControlNumEvents;
-                else
-                    out << fMcpConfigurationMaxEvents;
+                if (fMcpConfigurationState!=5 &&
+                    fMcpConfigurationState!=11 &&
+                    fMcpConfigurationState!=12)
+                    out << "Configuring ";
+                out << fMcpConfigurationName;
             }
-            if (fMcpConfigurationMaxEvents>0 && (fMcpConfigurationMaxTime>0 || fMcpConfigurationState==12))
-                out << '/';
-            if (fMcpConfigurationMaxTime>0)
+
+            if (fDimMcp.state()>5 && fDimRateControl.state()!=5)
             {
-                if (fMcpConfigurationState==12)
+                if (fMcpConfigurationMaxEvents>0 || fMcpConfigurationMaxTime>0 || fMcpConfigurationState==12)
+                    out << " [";
+                if (fMcpConfigurationMaxEvents>0)
                 {
-
-                    const uint32_t dt = (Time()-fMcpConfigurationRunStart).total_seconds();
-                    if (dt>fMcpConfigurationMaxTime)
-                        out << "---";
+                    if (fFadControlNumEvents>0 && fMcpConfigurationState==12)
+                        out << fMcpConfigurationMaxEvents-fFadControlNumEvents;
                     else
-                        out << fMcpConfigurationMaxTime-dt << 's';
+                        out << fMcpConfigurationMaxEvents;
+                }
+                if (fMcpConfigurationMaxEvents>0 && (fMcpConfigurationMaxTime>0 || fMcpConfigurationState==12))
+                    out << '/';
+                if (fMcpConfigurationMaxTime>0)
+                {
+                    if (fMcpConfigurationState==12)
+                    {
+
+                        const uint32_t dt = (Time()-fMcpConfigurationRunStart).total_seconds();
+                        if (dt>fMcpConfigurationMaxTime)
+                            out << "---";
+                        else
+                            out << fMcpConfigurationMaxTime-dt << 's';
+                    }
+                    else
+                        out << fMcpConfigurationMaxTime << 's';
                 }
                 else
-                    out << fMcpConfigurationMaxTime << 's';
+                {
+                    if (fMcpConfigurationState==12)
+                    {
+                        ostringstream d;
+                        d << Time()-fMcpConfigurationRunStart;
+                        out << d.str().substr(3, 5);
+                    }
+                }
+
+                if (fMcpConfigurationMaxEvents>0 || fMcpConfigurationMaxTime>0 || fMcpConfigurationState==12)
+                    out << ']';
             }
-            else
-            {
-                if (fMcpConfigurationState==12)
-                {
-                    ostringstream d;
-                    d << Time()-fMcpConfigurationRunStart;
-                    out << d.str().substr(3, 5);
-                }
-            }
-
-            if (fMcpConfigurationMaxEvents>0 || fMcpConfigurationMaxTime>0 || fMcpConfigurationState==12)
-                out << ']';
         }
         else
@@ -1070,14 +1133,22 @@
             if (rc.index==5) // Armed
                 col = kHtmlWhite;
+            if (rc.index==7) // Tracking
+            {
+                if (fDriveControlTrackingDev>60)   // ~1.5mm
+                    col = kHtmlYellow;
+                if (fDriveControlTrackingDev>100)  // ~1/4 of a pixel ~ 2.5mm
+                    col = kHtmlRed;
+            }
             out << col << '\t';
-            out << rc.name << '\t';
+
+            //out << rc.name << '\t';
             out << fDriveControlPointingZd  << '\t';
             out << fDriveControlPointingAz  << '\t';
             if (fDimDriveControl.state()==7)
             {
+                out << fDriveControlSourceName  << '\t';
                 out << setprecision(2);
-                out << fDriveControlTrackingDev << '\t';
+                out << fDriveControlTrackingDev << '\n';
                 out << setprecision(3);
-                out << fDriveControlSourceName  << '\n';
             }
             else
@@ -1117,5 +1188,7 @@
             out << col << '\t';
             out << fMagicWeatherHist[kHum].back()   << '\t';
+            out << setprecision(2);
             out << fMagicWeatherHist[kGusts].back() << '\n';
+            out << setprecision(3);
         }
         else
@@ -1123,5 +1196,5 @@
 
         // --------------- FtmControl -------------
-        if (fDimFtmControl.state()>=FTM::kIdle)
+        if (fDimFtmControl.state()==FTM::kTriggerOn)
         {
             string col = kHtmlGreen;
@@ -1142,4 +1215,7 @@
             fDimBiasControl.state()==BIAS::kVoltageOff)
         {
+            const bool off = fDimBiasControl.state()==BIAS::kVoltageOff;
+            const bool oc  = fDimBiasControl.state()==BIAS::kOverCurrent;
+
             string col = fBiasControlVoltageMed>3?kHtmlGreen:kHtmlWhite;
             if (fBiasControlCurrentMax>65)
@@ -1170,13 +1246,20 @@
             else
             {
+                out << setprecision(2);
                 out << col << '\t';
-                out << fBiasControlCurrentMed << '\t';
-                if (cal)
-                    out << fBiasControlCurrentMax;
+                out << (off ? 0 : fBiasControlCurrentMed) << '\t';
+                if (oc)
+                    out << "(OC) ";
                 else
-                    out << "&mdash; ";
+                {
+                    if (cal)
+                        out << (off ? 0 : fBiasControlCurrentMax);
+                    else
+                        out << "&mdash; ";
+                }
                 out << '\t';
+                out << setprecision(3);
             }
-            out << fBiasControlVoltageMed << '\n';
+            out << (off ? 0 : fBiasControlVoltageMed) << '\n';
         }
         else
@@ -1191,5 +1274,5 @@
 
         out.str("");
-        out << now.JavaDate() << '\n';
+        out << now.JavaDate() << '\t' << fDimControl.online() << '\n';
 
         if (fDim.state()==0)
@@ -1200,5 +1283,5 @@
 
             out << GetStateHtml(fDimMcp,          4);
-            out << kHtmlWhite << '\t' << (fDimControl.state()>-2?fDimControl.msg:"---") << "\n";
+            out << GetStateHtml(fDimControl,      0);
             out << GetStateHtml(fDimDataLogger,   1);
             out << GetStateHtml(fDimDriveControl, 2);
