#ifdef HAVE_LIBNOVA #include #include #include #include #endif #ifdef HAVE_SQL #include "Database.h" #endif #include "Dim.h" #include "Event.h" #include "Shell.h" #include "StateMachineDim.h" #include "Connection.h" #include "Configuration.h" #include "Console.h" #include "Converter.h" #include "PixelMap.h" #include "tools.h" #include "LocalControl.h" #include "HeadersFAD.h" #include "HeadersBIAS.h" #include "HeadersFTM.h" #include "HeadersFSC.h" #include "HeadersMCP.h" #include "HeadersDrive.h" #include "HeadersFeedback.h" #include "HeadersRateScan.h" #include "HeadersRateControl.h" #include "HeadersMagicWeather.h" #include using namespace std; // ------------------------------------------------------------------------ #include "DimDescriptionService.h" #include "DimState.h" // ------------------------------------------------------------------------ /* template class buffer : public deque { int32_t max_size; public: buffer(int32_t max=-1) : max_size(max) { } const T &operator=(const T &t) const { push_back(t); if (max_size>0 && deque::size()>max_size) deque::pop_front(); } operator T() const { return deque::size()>0 ? deque::back() : T(); } bool valid() const { return deque::size()>0; } }; */ // ------------------------------------------------------------------------ namespace HTML { const static string kWhite = "#ffffff"; const static string kYellow = "#fffff0"; const static string kRed = "#fff8f0"; const static string kGreen = "#f0fff0"; const static string kBlue = "#f0f0ff"; }; // ======================================================================== // ======================================================================== // ======================================================================== class Sun { public: Time time; Time fRiseDayTime; Time fRiseCivil; Time fRiseAstronomical; Time fRiseDarkTime; Time fSetDayTime; Time fSetCivil; Time fSetAstronomical; Time fSetDarkTime; int state; string description; string color; bool isday; bool visible; public: Sun() : time(Time::none) { } // Could be done more efficient: Only recalcuate if // the current time exceeds at least on of the stored times Sun(double lon, double lat, const Time &t=Time()) : time(t) { #ifdef HAVE_LIBNOVA ln_lnlat_posn observer; observer.lng = lon; observer.lat = lat; // get Julian day from local time const double JD = time.JD(); ln_rst_time sun_day; ln_rst_time sun_civil; ln_rst_time sun_astronomical; ln_rst_time sun_dark; // Warning: return code of 1 means circumpolar and is not checked! ln_get_solar_rst (JD-0.5, &observer, &sun_day); ln_get_solar_rst_horizon(JD-0.5, &observer, - 6, &sun_civil); ln_get_solar_rst_horizon(JD-0.5, &observer, -12, &sun_astronomical); ln_get_solar_rst_horizon(JD-0.5, &observer, -18, &sun_dark); fSetDayTime = Time(sun_day.set); fSetCivil = Time(sun_civil.set); fSetAstronomical = Time(sun_astronomical.set); fSetDarkTime = Time(sun_dark.set); fRiseDayTime = Time(sun_day.rise); fRiseCivil = Time(sun_civil.rise); fRiseAstronomical = Time(sun_astronomical.rise); fRiseDarkTime = Time(sun_dark.rise); const bool is_day = JD>sun_day.rise; const bool is_night = JD>sun_dark.set; ln_get_solar_rst (JD+0.5, &observer, &sun_day); ln_get_solar_rst_horizon(JD+0.5, &observer, - 6, &sun_civil); ln_get_solar_rst_horizon(JD+0.5, &observer, -12, &sun_astronomical); ln_get_solar_rst_horizon(JD+0.5, &observer, -18, &sun_dark); if (is_day) { fRiseDayTime = Time(sun_day.rise); fRiseCivil = Time(sun_civil.rise); fRiseAstronomical = Time(sun_astronomical.rise); fRiseDarkTime = Time(sun_dark.rise); } if (is_night) { fSetDayTime = Time(sun_day.set); fSetCivil = Time(sun_civil.set); fSetAstronomical = Time(sun_astronomical.set); fSetDarkTime = Time(sun_dark.set); } // case 0: midnight to sun-rise | !is_day && !is_night | rise/set // case 1: sun-rise to sun-set | is_day && !is_night | set /rise // case 2: sun-set to midnight | is_day && is_night | rise/set isday = is_day^is_night; state = isday ? 4 : 0; if (time>fSetDayTime) state++; if (time>fSetCivil) state++; if (time>fSetAstronomical) state++; if (time>fSetDarkTime) state++; if (time>fRiseDarkTime) state++; if (time>fRiseAstronomical) state++; if (time>fRiseCivil) state++; if (time>fRiseDayTime) state++; string name[] = { "dark time", "astron. twilight", "civil twilight", "sunrise", "day time", "sunset", "civil twilight", "astron. twilight", "dark time" }; description = state[name]; const string arr = isday ? fSetDarkTime.MinutesTo(time)+"↓" : fRiseDarkTime.MinutesTo(time)+"↑"; description += " ["+arr+"]"; switch (state) { case 0: case 1: color = HTML::kGreen; break; case 2: case 3: color = HTML::kYellow; break; case 4: color = HTML::kRed; break; case 5: case 6: color = HTML::kYellow; break; case 7: case 8: color = HTML::kGreen; break; } visible = state>=3 && state<=5; #endif } }; class Moon { public: Time time; double ra; double dec; double zd; double az; double disk; bool visible; Time fRise; Time fTransit; Time fSet; string description; string color; int state; Moon() : time(Time::none) { } // Could be done more efficient: Only recalcuate if // the current time exceeds at least on of the stored times Moon(double lon, double lat, const Time &t=Time()) : time(t) { #ifdef HAVE_LIBNOVA const double JD = time.JD(); ln_lnlat_posn observer; observer.lng = lon; observer.lat = lat; //observer.lng.degrees = -5; //observer.lng.minutes = 36; //observer.lng.seconds = 30; //observer.lat.degrees = 42; //observer.lat.minutes = 35; //observer.lat.seconds = 40; ln_rst_time moon; ln_get_lunar_rst(JD-0.5, &observer, &moon); fRise = Time(moon.rise); fTransit = Time(moon.transit); fSet = Time(moon.set); //visible = // ((JD>moon.rise && JDmoon.rise) && moon.rise>moon.set); const bool is_up = JD>moon.rise; const bool is_sinking = JD>moon.transit; const bool is_dn = JD>moon.set; ln_get_lunar_rst(JD+0.5, &observer, &moon); if (is_up) fRise = Time(moon.rise); if (is_sinking) fTransit = Time(moon.transit); if (is_dn) fSet = Time(moon.set); ln_equ_posn pos; ln_get_lunar_equ_coords(JD, &pos); ln_hrz_posn hrz; ln_get_hrz_from_equ (&pos, &observer, JD, &hrz); az = hrz.az; zd = 90-hrz.alt; ra = pos.ra/15; dec = pos.dec; disk = ln_get_lunar_disk(JD)*100; state = 0; if (fRise 75 ? HTML::kRed : HTML::kYellow; const string arr = fSet 1.0) arg = 1.0; if(arg < -1.0) arg = -1.0; return acos(arg) * 180/M_PI; } static string Color(double angle) { if (angle<10 || angle>150) return HTML::kRed; if (angle<20 || angle>140) return HTML::kYellow; return HTML::kGreen; } }; // ======================================================================== // ======================================================================== // ======================================================================== class StateMachineSmartFACT : public StateMachineDim { private: enum states_t { kStateDimNetworkNA = 1, kStateRunning, }; // ------------------------- Internal variables ----------------------- const Time fRunTime; PixelMap fPixelMap; string fDatabase; Time fLastUpdate; Time fLastAstroCalc; string fPath; // ----------------------------- Data storage ------------------------- deque fControlMessageHist; int32_t fControlScriptDepth; uint32_t fMcpConfigurationState; // For consistency int64_t fMcpConfigurationMaxTime; int64_t fMcpConfigurationMaxEvents; string fMcpConfigurationName; Time fMcpConfigurationRunStart; enum weather_t { kWeatherBegin=0, kTemp = kWeatherBegin, kDew, kHum, kPress, kWind, kGusts, kDir, kWeatherEnd = kDir+1 }; deque fMagicWeatherHist[kWeatherEnd]; deque fTngWeatherDustHist; Time fTngWeatherDustTime; vector fFeedbackCalibration; float fFeedbackTempOffset; float fFeedbackUserOffset; vector fBiasControlVoltageVec; float fBiasControlPowerTot; float fBiasControlVoltageMed; float fBiasControlCurrentMed; float fBiasControlCurrentMax; deque fBiasControlCurrentHist; deque fFscControlTemperatureHist; float fFscControlHumidityAvg; float fDriveControlPointingZd; string fDriveControlPointingAz; string fDriveControlSourceName; float fDriveControlMoonDist; deque fDriveControlTrackingDevHist; int64_t fFadControlNumEvents; int32_t fFadControlDrsStep; vector fFadControlDrsRuns; deque fFtmControlTriggerRateHist; bool fFtmControlNewRunStarted; float fFtmPatchThresholdMed; float fFtmBoardThresholdMed; bool fFtmControlFtuOk; deque fRateControlThreshold; uint64_t fRateScanDataId; uint8_t fRateScanBoard; deque fRateScanDataHist[41]; set fErrorList; deque fErrorHist; Sun fSun; Moon fMoon; // --------------------------- File header ---------------------------- Time fAudioTime; string fAudioName; string Header(const Time &d) { ostringstream msg; msg << d.JavaDate() << '\t' << fAudioTime.JavaDate() << '\t' << fAudioName; return msg.str(); } string Header(const EventImp &d) { return Header(d.GetTime()); } void SetAudio(const string &name) { fAudioName = name; fAudioTime = Time(); } // ------------- Initialize variables before the Dim stuff ------------ DimVersion fDimDNS; DimControl fDimControl; DimDescribedState fDimMcp; DimDescribedState fDimDataLogger; DimDescribedState fDimDriveControl; DimDescribedState fDimTimeCheck; DimDescribedState fDimMagicWeather; DimDescribedState fDimTngWeather; DimDescribedState fDimFeedback; DimDescribedState fDimBiasControl; DimDescribedState fDimFtmControl; DimDescribedState fDimFadControl; DimDescribedState fDimFscControl; DimDescribedState fDimRateControl; DimDescribedState fDimRateScan; DimDescribedState fDimChat; DimDescribedState fDimSkypeClient; // ------------------------------------------------------------------- string GetDir(const double angle) { static const char *dir[] = { "N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW" }; const uint16_t idx = uint16_t(floor(angle/22.5+16.5))%16; return dir[idx]; } // ------------------------------------------------------------------- bool CheckDataSize(const EventImp &d, const char *name, size_t size, bool min=false) { if (d.GetSize()==0) return false; if ((!min && d.GetSize()==size) || (min && d.GetSize()>size)) return true; ostringstream msg; msg << name << " - Received service has " << d.GetSize() << " bytes, but expected "; if (min) msg << "more than "; msg << size << "."; Warn(msg); return false; } // ------------------------------------------------------------------- template void WriteBinary(const EventImp &d, const string &fname, const T &t, double scale, double offset=0) { const Statistics stat(t); vector val(t.size(), 0); for (uint64_t i=0; i127) range=127; if (range<0) range=0; val[i] = (uint8_t)range; } const char *ptr = reinterpret_cast(val.data()); ostringstream out; out << d.GetJavaDate() << '\n'; out << offset << '\n'; out << offset+scale << '\n'; out << setprecision(3); out << stat.min << '\n'; out << stat.med << '\n'; out << stat.max << '\n'; out.write(ptr, val.size()*sizeof(uint8_t)); ofstream(fPath+"/"+fname+".bin") << out.str(); } // ------------------------------------------------------------------- struct Statistics { float min; float max; float med; float avg; //float rms; template Statistics(const T &t, size_t offset_min=0, size_t offset_max=0) : min(0), max(0), med(0), avg(0) { if (t.size()==0) return; T copy(t); sort(copy.begin(), copy.end()); if (offset_min>t.size()) offset_min = 0; if (offset_max>t.size()) offset_max = 0; min = copy[offset_min]; max = copy[copy.size()-1-offset_max]; avg = accumulate (t.begin(), t.end(), 0.)/t.size(); const size_t p = t.size()/2; med = copy[p]; } }; void HandleControlMessageImp(const EventImp &d) { if (d.GetSize()==0) 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(); fControlMessageHist.push_back(msg); ostringstream out; out << setprecision(3); out << Header(d) << '\n'; out << HTML::kWhite << '\t'; out << "<->"; for (auto it=fControlMessageHist.begin(); it!=fControlMessageHist.end(); it++) out << *it << "
"; out << ""; out << '\n'; ofstream(fPath+"/scriptlog.data") << out.str(); } int HandleDimControlMessage(const EventImp &d) { if (d.GetSize()==0) return GetCurrentState(); if (d.GetQoS()==90) HandleControlMessageImp(d); return GetCurrentState(); } void HandleControlStateChange(const EventImp &d) { if (d.GetSize()==0) return; if (d.GetQoS()==-2 && fDimControl.scriptdepth==0) fControlMessageHist.clear(); // Not that this will also "ding" just after program startup // if the dimctrl is still in state -3 if (fDimControl.last.second!=DimState::kOffline && d.GetQoS()==-3 && fDimControl.scriptdepth==0) SetAudio("ding"); if (d.GetQoS()>=0) return; #if BOOST_VERSION < 104600 const string file = boost::filesystem::path(fDimControl.file).filename(); #else const string file = boost::filesystem::path(fDimControl.file).filename().string(); #endif HandleControlMessageImp(Event(d, fDimControl.shortmsg.data(), fDimControl.shortmsg.length()+1)); if (!file.empty()) HandleControlMessageImp(Event(d, file.data(), file.length()+1)); } void HandleFscControlStateChange() { const int32_t &last = fDimFscControl.last.second; const int32_t &state = fDimFscControl.state(); if (last==DimState::kOffline || state==DimState::kOffline) return; if (last=-2) SetAudio("losticks"); if (d.GetQoS()==MCP::State::kTakingData) { fMcpConfigurationRunStart = Time(); SetAudio("losticks"); } fMcpConfigurationState = d.GetQoS(); fMcpConfigurationMaxTime = d.Get(); fMcpConfigurationMaxEvents = d.Get(8); fMcpConfigurationName = d.Ptr(16); return GetCurrentState(); } void WriteWeather(const EventImp &d, const string &name, int i, float min, float max) { const Statistics stat(fMagicWeatherHist[i]); ostringstream out; out << setprecision(3); out << d.GetJavaDate() << '\n'; out << HTML::kWhite << '\t' << fMagicWeatherHist[i].back() << '\n'; out << HTML::kWhite << '\t' << stat.min << '\n'; out << HTML::kWhite << '\t' << stat.avg << '\n'; out << HTML::kWhite << '\t' << stat.max << '\n'; ofstream(fPath+"/"+name+".data") << out.str(); WriteBinary(d, "hist-magicweather-"+name, fMagicWeatherHist[i], max-min, min); } int HandleMagicWeatherData(const EventImp &d) { if (!CheckDataSize(d, "MagicWeather:Data", 7*4+2)) return GetCurrentState(); // Store a history of the last 300 entries for (int i=kWeatherBegin; i(2)[i]); if (fMagicWeatherHist[i].size()>300) fMagicWeatherHist[i].pop_front(); } ostringstream out; out << d.GetJavaDate() << '\n'; if (fSun.time.IsValid() && fMoon.time.IsValid()) { out << fSun.color << '\t' << fSun.description << '\n'; out << setprecision(2); out << (fSun.isday?HTML::kWhite:fMoon.color) << '\t' << fMoon.description << '\n'; } else out << "\n\n"; out << setprecision(3); for (int i=0; i<6; i++) out << HTML::kWhite << '\t' << fMagicWeatherHist[i].back() << '\n'; out << HTML::kWhite << '\t' << GetDir(fMagicWeatherHist[kDir].back()) << '\n'; out << HTML::kWhite << '\t'; if (fTngWeatherDustHist.size()>0) out << fTngWeatherDustHist.back() << '\t' << fTngWeatherDustTime.GetAsStr("%H:%M") << '\n'; else out << "\t\n"; ofstream(fPath+"/weather.data") << out.str(); WriteWeather(d, "temp", kTemp, -5, 35); WriteWeather(d, "dew", kDew, -5, 35); WriteWeather(d, "hum", kHum, 0, 100); WriteWeather(d, "wind", kWind, 0, 100); WriteWeather(d, "gusts", kGusts, 0, 100); WriteWeather(d, "press", kPress, 700, 1000); return GetCurrentState(); } int HandleTngWeatherDust(const EventImp &d) { if (!CheckDataSize(d, "TngWeather:Dust", 4)) return GetCurrentState(); fTngWeatherDustTime = d.GetTime(); fTngWeatherDustHist.push_back(d.GetFloat()); if (fTngWeatherDustHist.size()>300) fTngWeatherDustHist.pop_front(); const Statistics stat(fTngWeatherDustHist); const double scale = stat.max>0 ? pow(10, ceil(log10(stat.max))) : 0; WriteBinary(d, "hist-tng-dust", fTngWeatherDustHist, scale); ostringstream out; out << d.GetJavaDate() << '\n'; ofstream(fPath+"/tngdust.data") << out.str(); return GetCurrentState(); } int HandleDrivePointing(const EventImp &d) { if (!CheckDataSize(d, "DriveControl:Pointing", 16)) return GetCurrentState(); fDriveControlPointingZd = d.Get(); const double az = d.Get(8); fDriveControlPointingAz = GetDir(az); ostringstream out; out << d.GetJavaDate() << '\n'; out << setprecision(0) << fixed; out << HTML::kWhite << '\t' << az << '\t' << fDriveControlPointingAz << '\n'; out << HTML::kWhite << '\t' << fDriveControlPointingZd << '\n'; ofstream(fPath+"/pointing.data") << out.str(); return GetCurrentState(); } int HandleDriveTracking(const EventImp &d) { if (!CheckDataSize(d, "DriveControl:Tracking", 56)) return GetCurrentState(); const double Ra = d.Get(0*8); const double Dec = d.Get(1*8); const double Zd = d.Get(3*8); const double Az = d.Get(4*8); const double zd = Zd * M_PI / 180; const double dzd = d.Get(5*8) * M_PI / 180; const double daz = d.Get(6*8) * M_PI / 180; // Correct: // const double d = cos(del) - sin(zd+dzd)*sin(zd)*(1.-cos(daz)); // Simplified: 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, "hist-control-deviation", fDriveControlTrackingDevHist, 120); ostringstream out; out << d.GetJavaDate() << '\n'; out << HTML::kWhite << '\t' << fDriveControlSourceName << '\n'; out << setprecision(5); out << HTML::kWhite << '\t' << Ra << '\n'; out << HTML::kWhite << '\t' << Dec << '\n'; out << setprecision(3); out << HTML::kWhite << '\t' << Zd << '\n'; out << HTML::kWhite << '\t' << Az << '\n'; out << HTML::kWhite << '\t' << dev << '\n'; fDriveControlMoonDist = -1; if (fMoon.visible) { const double angle = fMoon.Angle(Ra, Dec); out << Moon::Color(angle) << '\t' << setprecision(3) << angle << '\n'; fDriveControlMoonDist = angle; } else out << HTML::kWhite << "\t— \n"; ofstream(fPath+"/tracking.data") << out.str(); return GetCurrentState(); } int HandleDriveSource(const EventImp &d) { if (!CheckDataSize(d, "DriveControl:Source", 7*4+2, true)) return GetCurrentState(); const double *ptr = d.Ptr(); const double ra = ptr[0]; // Ra[h] const double dec = ptr[1]; // Dec[deg] const double woff = ptr[4]; // Wobble offset [deg] const double wang = ptr[5]; // Wobble angle [deg] fDriveControlSourceName = d.Ptr(6*8); ostringstream out; out << d.GetJavaDate() << '\n'; out << HTML::kWhite << '\t' << fDriveControlSourceName << '\n'; out << setprecision(5); out << HTML::kWhite << '\t' << ra << '\n'; out << HTML::kWhite << '\t' << dec << '\n'; out << setprecision(3); out << HTML::kWhite << '\t' << woff << '\n'; out << HTML::kWhite << '\t' << wang << '\n'; ofstream(fPath+"/source.data") << out.str(); return GetCurrentState(); } int HandleFeedbackCalibration(const EventImp &d) { if (!CheckDataSize(d, "Feedback:Calibration", 3*4*416)) { fFeedbackCalibration.clear(); return GetCurrentState(); } const float *ptr = d.Ptr(); fFeedbackCalibration.assign(ptr+2*416, ptr+3*416); return GetCurrentState(); } int HandleFeedbackDeviation(const EventImp &d) { if (!CheckDataSize(d, "Feedback:Deviation", (2*416+2)*4)) return GetCurrentState(); const float *ptr = d.Ptr(); vector dev(ptr+416, ptr+416+320); fFeedbackTempOffset = ptr[2*416]; fFeedbackUserOffset = ptr[2*416+1]; for (int i=0; i<320; i++) dev[i] -= fFeedbackTempOffset+fFeedbackUserOffset; // Write the 160 patch values to a file WriteBinary(d, "cam-feedback-deviation", dev, 1); const Statistics stat(dev, 3); ostringstream out; out << d.GetJavaDate() << '\n'; out << HTML::kWhite << '\t' << fFeedbackUserOffset << '\n'; out << setprecision(3); out << HTML::kWhite << '\t' << fFeedbackTempOffset << '\n'; out << HTML::kWhite << '\t' << stat.min << '\n'; out << HTML::kWhite << '\t' << stat.med << '\n'; out << HTML::kWhite << '\t' << stat.avg << '\n'; out << HTML::kWhite << '\t' << stat.max << '\n'; ofstream(fPath+"/feedback.data") << out.str(); return GetCurrentState(); } int HandleBiasVoltage(const EventImp &d) { if (!CheckDataSize(d, "BiasControl:Voltage", 1664)) { fBiasControlVoltageVec.clear(); return GetCurrentState(); } fBiasControlVoltageVec.assign(d.Ptr(), d.Ptr()+320); const Statistics stat(fBiasControlVoltageVec); fBiasControlVoltageMed = stat.med; vector val(320, 0); for (int i=0; i<320; i++) { const int idx = (fPixelMap.hv(i).hw()/9)*2+fPixelMap.hv(i).group(); val[idx] = fBiasControlVoltageVec[i]; } if (fDimBiasControl.state()==BIAS::State::kVoltageOn) WriteBinary(d, "cam-biascontrol-voltage", val, 10, 65); else WriteBinary(d, "cam-biascontrol-voltage", val, 75); ostringstream out; out << setprecision(3); out << d.GetJavaDate() << '\n'; out << HTML::kWhite << '\t' << stat.min << '\n'; out << HTML::kWhite << '\t' << stat.med << '\n'; out << HTML::kWhite << '\t' << stat.avg << '\n'; out << HTML::kWhite << '\t' << stat.max << '\n'; ofstream(fPath+"/voltage.data") << out.str(); return GetCurrentState(); } int HandleBiasCurrent(const EventImp &d) { if (!CheckDataSize(d, "BiasControl:Current", 832)) return GetCurrentState(); // Convert dac counts to uA vector v(320); for (int i=0; i<320; i++) v[i] = d.Ptr()[i] * 5000./4096; 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; // 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, convert to mW power_apd /= 317e-3; // [mW] if (power_tot<1e-3) power_tot = 0; if (power_apd<1e-3) power_apd = 0; fBiasControlPowerTot = power_tot; // Get the maximum of each patch vector val(320, 0); for (int i=0; i<320; i++) { const int idx = (fPixelMap.hv(i).hw()/9)*2+fPixelMap.hv(i).group(); val[idx] = v[i]; } // Write the 160 patch values to a file WriteBinary(d, "cam-biascontrol-current", val, 100); const Statistics stat(v, 0, 3); // Exclude the three crazy channels fBiasControlCurrentMed = stat.med; fBiasControlCurrentMax = stat.max; // Store a history of the last 60 entries fBiasControlCurrentHist.push_back(fBiasControlCurrentMed); if (fBiasControlCurrentHist.size()>360) fBiasControlCurrentHist.pop_front(); // write the history to a file WriteBinary(d, "hist-biascontrol-current", fBiasControlCurrentHist, 100); const string col0 = cal ? HTML::kGreen : HTML::kWhite; string col1 = col0; string col2 = col0; string col3 = col0; string col4 = col0; if (cal && stat.min>65) col1 = kYellow; if (cal && stat.min>80) col1 = kRed; if (cal && stat.med>65) col2 = kYellow; if (cal && stat.med>80) col2 = kRed; if (cal && stat.avg>65) col3 = kYellow; if (cal && stat.avg>80) col3 = kRed; if (cal && stat.max>65) col4 = kYellow; if (cal && stat.max>80) col4 = kRed; ostringstream out; out << setprecision(2); out << d.GetJavaDate() << '\n'; out << col0 << '\t' << (cal?"yes":"no") << '\n'; out << col1 << '\t' << stat.min << '\n'; out << col2 << '\t' << stat.med << '\n'; out << col3 << '\t' << stat.avg << '\n'; out << col4 << '\t' << stat.max << '\n'; out << HTML::kWhite << '\t' << power_tot << "W [" << power_apd << "mW]\n"; ofstream(fPath+"/current.data") << out.str(); return GetCurrentState(); } int HandleFadEvents(const EventImp &d) { if (!CheckDataSize(d, "FadControl:Events", 4*4)) { fFadControlNumEvents = -1; return GetCurrentState(); } fFadControlNumEvents = d.Get(); return GetCurrentState(); } int HandleFadDrsRuns(const EventImp &d) { if (!CheckDataSize(d, "FadControl:DrsRuns", 4*4)) { fFadControlDrsStep = -1; return GetCurrentState(); } const uint32_t *ptr = d.Ptr(); fFadControlDrsStep = ptr[0]; fFadControlDrsRuns[0] = ptr[1]; fFadControlDrsRuns[1] = ptr[2]; fFadControlDrsRuns[2] = ptr[3]; return GetCurrentState(); } int HandleFadConnections(const EventImp &d) { if (!CheckDataSize(d, "FadControl:Connections", 41)) { //fStatusEventBuilderLabel->setText("Offline"); return GetCurrentState(); } string rc(40, '-'); // orange/red [45] const uint8_t *ptr = d.Ptr(); int c[4] = { '.', '.', '.', '.' }; for (int i=0; i<40; i++) { const uint8_t stat1 = ptr[i]&3; const uint8_t stat2 = ptr[i]>>3; if (stat1==0 && stat2==0) rc[i] = '.'; // gray [46] else if (stat1>=2 && stat2==8) rc[i] = stat1==2?'+':'*'; // green [43] : check [42] if (rc[i](20); // Camera rate // New run started if (crate<0) { fFtmControlNewRunStarted = true; return GetCurrentState(); } fFtmControlNewRunStarted = false; const float *brates = d.Ptr(24); // Board rate const float *prates = d.Ptr(24+160); // Patch rate // Store a history of the last 60 entries fFtmControlTriggerRateHist.push_back(crate); if (fFtmControlTriggerRateHist.size()>60) fFtmControlTriggerRateHist.pop_front(); // FIXME: Add statistics for all kind of rates WriteBinary(d, "hist-ftmcontrol-triggerrate", fFtmControlTriggerRateHist, 100); WriteBinary(d, "cam-ftmcontrol-boardrates", vector(brates, brates+40), 10); WriteBinary(d, "cam-ftmcontrol-patchrates", vector(prates, prates+160), 10); ostringstream out; out << setprecision(3); out << d.GetJavaDate() << '\n'; out << HTML::kWhite << '\t' << crate << '\n'; ofstream(fPath+"/trigger.data") << out.str(); const Statistics bstat(vector(brates, brates+40)); const Statistics pstat(vector(prates, prates+160)); out.str(""); out << d.GetJavaDate() << '\n'; out << HTML::kWhite << '\t' << bstat.min << '\n'; out << HTML::kWhite << '\t' << bstat.med << '\n'; out << HTML::kWhite << '\t' << bstat.avg << '\n'; out << HTML::kWhite << '\t' << bstat.max << '\n'; ofstream(fPath+"/boardrates.data") << out.str(); out.str(""); out << d.GetJavaDate() << '\n'; out << HTML::kWhite << '\t' << pstat.min << '\n'; out << HTML::kWhite << '\t' << pstat.med << '\n'; out << HTML::kWhite << '\t' << pstat.avg << '\n'; out << HTML::kWhite << '\t' << pstat.max << '\n'; ofstream(fPath+"/patchrates.data") << out.str(); return GetCurrentState(); } int HandleFtmStaticData(const EventImp &d) { if (!CheckDataSize(d, "FtmControl:StaticData", sizeof(FTM::DimStaticData))) return GetCurrentState(); const FTM::DimStaticData &dat = d.Ref(); vector vecp(dat.fThreshold, dat.fThreshold+160); vector vecb(dat.fMultiplicity, dat.fMultiplicity+40); WriteBinary(d, "cam-ftmcontrol-thresholds-patch", vecp, 1000); WriteBinary(d, "cam-ftmcontrol-thresholds-board", vecb, 100); const Statistics statp(vecp); const Statistics statb(vecb); fFtmPatchThresholdMed = statp.med; fFtmBoardThresholdMed = statb.med; ostringstream out; out << d.GetJavaDate() << '\n'; out << HTML::kWhite << '\t' << statb.min << '\n'; out << HTML::kWhite << '\t' << statb.med << '\n'; out << HTML::kWhite << '\t' << statb.max << '\n'; ofstream(fPath+"/thresholds-board.data") << out.str(); out.str(""); out << d.GetJavaDate() << '\n'; out << HTML::kWhite << '\t' << statp.min << '\n'; out << HTML::kWhite << '\t' << statp.med << '\n'; out << HTML::kWhite << '\t' << statp.max << '\n'; ofstream(fPath+"/thresholds-patch.data") << out.str(); out.str(""); out << d.GetJavaDate() << '\n'; out << HTML::kWhite << '\t' << statb.med << '\n'; out << HTML::kWhite << '\t' << statp.med << '\n'; ofstream(fPath+"/thresholds.data") << out.str(); out.str(""); out << d.GetJavaDate() << '\n'; out << HTML::kWhite << '\t' << dat.fTriggerInterval << '\n'; out << HTML::kWhite << '\t'; if (dat.HasPedestal()) out << dat.fTriggerSeqPed; else out << "–"; out << ':'; if (dat.HasLPext()) out << dat.fTriggerSeqLPext; else out << "–"; out << ':'; if (dat.HasLPint()) out << dat.fTriggerSeqLPint; else out << "–"; out << '\n'; out << HTML::kWhite << '\t' << (dat.HasTrigger()?"on":"off") << " / " << (dat.HasExt1()?"on":"off") << " / " << (dat.HasExt2()?"on":"off") << '\n'; out << HTML::kWhite << '\t' << (dat.HasVeto()?"on":"off") << " / " << (dat.HasClockConditioner()?"time cal":"marker") << '\n'; out << HTML::kWhite << '\t' << dat.fMultiplicityPhysics << " / " << dat.fMultiplicityCalib << '\n'; out << HTML::kWhite << '\t' << dat.fWindowPhysics << '\t' << dat.fWindowCalib << '\n'; out << HTML::kWhite << '\t' << dat.fDelayTrigger << '\t' << dat.fDelayTimeMarker << '\n'; out << HTML::kWhite << '\t' << dat.fDeadTime << '\n'; int64_t vp = dat.fPrescaling[0]; for (int i=1; i<40; i++) if (vp!=dat.fPrescaling[i]) vp = -1; if (vp<0) out << HTML::kYellow << "\tdifferent\n"; else out << HTML::kWhite << '\t' << 0.5*vp << "\n"; ofstream(fPath+"/ftm.data") << out.str(); // Active FTUs: IsActive(i) // Enabled Pix: IsEnabled(i) return GetCurrentState(); } int HandleFtmFtuList(const EventImp &d) { if (!CheckDataSize(d, "FtmControl:FtuList", sizeof(FTM::DimFtuList))) return GetCurrentState(); const FTM::DimFtuList &sdata = d.Ref(); ostringstream out; out << d.GetJavaDate() << '\n'; int cnt = 0; for (int i=0; i<4; i++) { out << HTML::kWhite << '\t'; for (int j=0; j<10; j++) if (sdata.IsActive(i*10+j)) { if (sdata.fPing[i*10+j]==1) { out << '*'; cnt++; } else out << sdata.fPing[i*10+j]; } else out << '-'; out << '\n'; } fFtmControlFtuOk = cnt==40; ofstream(fPath+"/ftu.data") << out.str(); return GetCurrentState(); } int HandleFadEventData(const EventImp &d) { if (!CheckDataSize(d, "FadControl:EventData", 23040)) return GetCurrentState(); //const float *avg = d.Ptr(); //const float *rms = d.Ptr(1440*sizeof(float)); const float *dat = d.Ptr(1440*sizeof(float)*2); //const float *pos = d.Ptr(1440*sizeof(float)*3); vector max(320, -2); for (int i=0; i<1440; i++) { if (i%9==8) continue; const int idx = (fPixelMap.hw(i).hw()/9)*2+fPixelMap.hw(i).group(); const double v = dat[i]/1000; if (v>max[idx]) max[idx]=v; } switch (fFadControlDrsStep) { case 0: WriteBinary(d, "cam-fadcontrol-eventdata", max, 2, -1); break; case 1: WriteBinary(d, "cam-fadcontrol-eventdata", max, 2, 0); break; default: WriteBinary(d, "cam-fadcontrol-eventdata", max, 0.25, 0); break; } return GetCurrentState(); } int HandleFscTemperature(const EventImp &d) { if (!CheckDataSize(d, "FscControl:Temperature", 240)) return GetCurrentState(); const float *ptr = d.Ptr(4); double avg = 0; double rms = 0; double min = 99; double max = -99; int num = 0; for (const float *t=ptr; tmax) max = *t; if (*t0) { fFscControlTemperatureHist.push_back(avg-fMagicWeatherHist[kTemp].back()); if (fFscControlTemperatureHist.size()>300) fFscControlTemperatureHist.pop_front(); } const Statistics stat(fFscControlTemperatureHist); ostringstream out; out << setprecision(3); out << d.GetJavaDate() << '\n'; out << HTML::kWhite << '\t' << fFscControlHumidityAvg << '\n'; out << HTML::kWhite << '\t' << min << '\n'; out << HTML::kWhite << '\t' << avg << '\n'; out << HTML::kWhite << '\t' << max << '\n'; out << HTML::kWhite << '\t' << stat.min << '\n'; out << HTML::kWhite << '\t' << stat.avg << '\n'; out << HTML::kWhite << '\t' << stat.max << '\n'; ofstream(fPath+"/fsc.data") << out.str(); WriteBinary(d, "hist-fsccontrol-temperature", fFscControlTemperatureHist, 10); return GetCurrentState(); } int HandleFscHumidity(const EventImp &d) { if (!CheckDataSize(d, "FscControl:Humidity", 5*4)) return GetCurrentState(); const float *ptr = d.Ptr(4); double avg = 0; int num = 0; for (const float *t=ptr; t0) { avg += *t; num++; } fFscControlHumidityAvg = avg/num; return GetCurrentState(); } int HandleRateScanData(const EventImp &d) { if (!CheckDataSize(d, "RateScan:Data", 824)) return GetCurrentState(); const uint64_t id = d.Get(); const float *rate = d.Ptr(20); if (fRateScanDataId!=id) { for (int i=0; i<41; i++) fRateScanDataHist[i].clear(); fRateScanDataId = id; } fRateScanDataHist[0].push_back(log10(rate[0])); double max = 0; for (int i=1; i<41; i++) { fRateScanDataHist[i].push_back(log10(rate[i])); if (rate[i]>max) max = rate[i]; } // Cycle by time! fRateScanBoard ++; fRateScanBoard %= 40; WriteBinary(d, "hist-ratescan", fRateScanDataHist[0], 10, -2); WriteBinary(d, "cam-ratescan-board", fRateScanDataHist[fRateScanBoard+1], 10, -4); ostringstream out; out << setprecision(3); out << d.GetJavaDate() << '\n'; out << HTML::kWhite << '\t' << fFtmBoardThresholdMed << '\n'; out << HTML::kWhite << '\t' << fFtmPatchThresholdMed << '\n'; out << HTML::kWhite << '\t' << pow(10, fRateScanDataHist[0].back()) << '\n'; out << HTML::kWhite << '\t' << max << '\n'; ofstream(fPath+"/ratescan.data") << out.str(); out.str(""); out << d.GetJavaDate() << '\n'; out << HTML::kWhite << '\t' << int(fRateScanBoard) << '\n'; out << HTML::kWhite << '\t' << pow(10, fRateScanDataHist[fRateScanBoard+1].back()) << '\n'; ofstream(fPath+"/ratescan_board.data") << out.str(); return GetCurrentState(); } int HandleRateControlThreshold(const EventImp &d) { if (!CheckDataSize(d, "RateControl:Threshold", 2)) return GetCurrentState(); const uint16_t th = d.Get(); fRateControlThreshold.push_back(th); if (fRateControlThreshold.size()>300) fRateControlThreshold.pop_front(); WriteBinary(d, "hist-ratecontrol-threshold", fRateControlThreshold, 1000); return GetCurrentState(); } // ------------------------------------------------------------------- void HandleDoTest(const EventImp &d) { ostringstream out; out << d.GetJavaDate() << '\n'; switch (d.GetQoS()) { case -3: out << HTML::kWhite << "\tNot running\n"; break; case -2: out << HTML::kBlue << "\tLoading\n"; break; case -1: out << HTML::kBlue << "\tStarted\n"; break; default: out << HTML::kGreen << "\tRunning [" << d.GetQoS() << "]\n"; break; } ofstream(fPath+"/dotest.data") << out.str(); } // ------------------------------------------------------------------- /* bool CheckEventSize(size_t has, const char *name, size_t size) { if (has==size) return true; ostringstream msg; msg << name << " - Received event has " << has << " bytes, but expected " << size << "."; Fatal(msg); return false; }*/ int Print() const { Out() << fDimDNS << endl; Out() << fDimMcp << endl; Out() << fDimControl << endl; Out() << fDimDataLogger << endl; Out() << fDimDriveControl << endl; Out() << fDimTimeCheck << endl; Out() << fDimFadControl << endl; Out() << fDimFtmControl << endl; Out() << fDimBiasControl << endl; Out() << fDimFeedback << endl; Out() << fDimRateControl << endl; Out() << fDimFscControl << endl; Out() << fDimMagicWeather << endl; Out() << fDimTngWeather << endl; Out() << fDimRateScan << endl; Out() << fDimChat << endl; Out() << fDimSkypeClient << endl; return GetCurrentState(); } string GetStateHtml(const DimState &state, int green) const { if (!state.online()) return HTML::kWhite+"\t—\n"; if (&state==&fDimControl) return HTML::kGreen +'\t'+(state.state()<-2?"Idle":fDimControl.shortmsg)+'\n'; const State rc = state.description(); // Sate not found in list, server online (-3: offline; -2: not found) if (rc.index==-2) { ostringstream out; out << HTML::kWhite << '\t' << state.state() << '\n'; return out.str(); } //ostringstream msg; //msg << HTML::kWhite << '\t' << rc.name << " [" << rc.index << "]\n"; //return msg.str(); if (rc.index<0) return HTML::kWhite + "\t—\n"; string col = HTML::kGreen; if (rc.index0xff) col = HTML::kRed; return col + '\t' + rc.name + '\n'; } bool SetError(bool b, const string &err) { if (!b) { fErrorList.erase(err); return 0; } const bool isnew = fErrorList.insert(err).second; if (isnew) { ostringstream msg; msg << "
" << Time().GetAsStr("%m-%d %H:%M") << "
<->" << err << ""; if (find(fErrorHist.begin(), fErrorHist.end(), msg.str())==fErrorHist.end()) { fErrorHist.push_front(msg.str()); if (fErrorHist.size()>80) fErrorHist.pop_back(); } } return isnew; } void UpdateAstronomy() { const double lon = -(17.+53./60+26.525/3600); const double lat = 28.+45./60+42.462/3600; Time now; fSun = Sun (lon, lat, now); fMoon = Moon(lon, lat, now); vector color(8, HTML::kWhite); color[fSun.state%8] = HTML::kBlue; ostringstream out; out << setprecision(3); out << now.JavaDate() << '\n'; out << color[0] << '\t' << fSun.fRiseDarkTime.GetAsStr("%H:%M") << '\n'; out << color[1] << '\t' << fSun.fRiseAstronomical.GetAsStr("%H:%M") << '\n'; out << color[2] << '\t' << fSun.fRiseCivil.GetAsStr("%H:%M") << '\n'; out << color[3] << '\t' << fSun.fRiseDayTime.GetAsStr("%H:%M") << '\n'; out << color[4] << '\t' << fSun.fSetDayTime.GetAsStr("%H:%M") << '\n'; out << color[5] << '\t' << fSun.fSetCivil.GetAsStr("%H:%M") << '\n'; out << color[6] << '\t' << fSun.fSetAstronomical.GetAsStr("%H:%M") << '\n'; out << color[7] << '\t' << fSun.fSetDarkTime.GetAsStr("%H:%M") << '\n'; ofstream(fPath+"/sun.data") << out.str(); color.assign(3, HTML::kWhite); color[fMoon.state%3] = HTML::kBlue; out.str(""); out << now.JavaDate() << '\n'; out << color[0] << '\t' << fMoon.fRise.GetAsStr("%H:%M") << '\n'; out << color[1] << '\t' << fMoon.fTransit.GetAsStr("%H:%M") << '\n'; out << color[2] << '\t' << fMoon.fSet.GetAsStr("%H:%M") << '\n'; out << (fSun.isday?HTML::kWhite:fMoon.color) << '\t' << fMoon.description << '\n'; if (!fMoon.visible) out << HTML::kWhite << "\t—\n"; else { string col = HTML::kWhite; if (!fSun.isday) { col = HTML::kGreen; if (fMoon.zd>25) col = HTML::kYellow; if (fMoon.zd>45 && fMoon.zd<80) col = HTML::kRed; if (fMoon.zd>=80) col = HTML::kRed; } out << col << '\t' << fMoon.zd << '\t' << GetDir(fMoon.az) << '\n'; } ostringstream out2; out2 << setprecision(3); out2 << now.JavaDate() << '\n'; #ifdef HAVE_SQL try { const mysqlpp::StoreQueryResult res = Database(fDatabase).query("SELECT fSourceName, fRightAscension, fDeclination FROM source").store(); out << HTML::kWhite << '\t'; out2 << HTML::kWhite << '\t'; for (vector::const_iterator v=res.begin(); v5) col = HTML::kRed; if (hrz.alt>25) col = HTML::kYellow; if (hrz.alt>60) col = HTML::kGreen; out2 << ""; out2 << "" << name << ""; if (hrz.alt>5) { out2 << "" << 90-hrz.alt << "°"; out2 << "" << GetDir(hrz.az) << ""; } else out2 << ""; out2 << ""; #endif const int32_t angle = fMoon.Angle(ra, dec); out << ""; out << "" << name << ""; out << "" << round(angle) << "°"; out << ""; } out << '\n'; out2 << '\n'; out << HTML::kWhite << '\t' << Time()-now << '\n'; out2 << HTML::kWhite << '\t' << Time()-now << '\n'; } catch (const exception &e) { out << '\n'; out2 << '\n'; out << HTML::kWhite << '\t' << "ERROR - "+string(e.what()) << '\n'; out2 << HTML::kWhite << '\t' << "ERROR - "+string(e.what()) << '\n'; } #endif ofstream(fPath+"/moon.data") << out.str(); ofstream(fPath+"/source-list.data") << out2.str(); } int Execute() { // Dispatch (execute) at most one handler from the queue. In contrary // to run_one(), it doesn't wait until a handler is available // which can be dispatched, so poll_one() might return with 0 // handlers dispatched. The handlers are always dispatched/executed // synchronously, i.e. within the call to poll_one() //poll_one(); Time now; if (now-fLastUpdate0; bool newerr = false; newerr |= SetError(!fDimDNS.online(), "<#darkred>DIM network not available"); newerr |= SetError(!fDimControl.online(), "dimctrl offline"); //newerr |= SetError(fDimDriveControl.state()==Drive::State::kLocked, // "<#darkred>Drive in LOCKED state, drive was automatically parked"); newerr |= SetError(fDimDriveControl.state()>0xff && data_taking && data_run, "Drive in ERROR state during data-taking"); newerr |= SetError(fDriveControlMoonDist>155, "Moon within the field-of-view of the cones"); newerr |= SetError(fDriveControlMoonDist>=0 && fDriveControlMoonDist<3, "Moon within the field-of-view of the camera"); newerr |= SetError(fDimBiasControl.state()0 && fBiasControlCurrentMed>80, "Median current exceeds 80µA/pix"); newerr |= SetError(fFeedbackCalibration.size()>0 && fBiasControlCurrentMax>100, "Maximum current exceeds 100µA/pix"); newerr |= SetError(fFscControlHumidityAvg>60, "Average camera humidity exceed 60%"); newerr |= SetError(fMagicWeatherHist[kHum].size()>0 && fMagicWeatherHist[kHum].back()>98 && data_taking, "Outside humidity exceeds 98% during data-taking"); newerr |= SetError(fMagicWeatherHist[kGusts].size()>0 && fMagicWeatherHist[kGusts].back()>98 && fDimDriveControl.state()==Drive::State::kTracking, "Wind gusts exceed 50km/h during tracking"); newerr |= SetError(fFscControlTemperatureHist.size()>0 && fFscControlTemperatureHist.back()>8, "Sensor temperature exceeds outside temperature by more than 8°C"); if (fFtmControlTriggerRateHist.size()>0 && !fFtmControlNewRunStarted) { newerr |= SetError(fFtmControlTriggerRateHist.size()>0 && fFtmControlTriggerRateHist.back()<0.01 && fDimMcp.state()==MCP::State::kTakingData, "Trigger rate below 10mHz during data taking"); } newerr |= SetError(fDimTimeCheck.state()==1, "Warning NTP time difference of drive PC exceeds 1s"); newerr |= SetError(fDimTimeCheck.state()<1, "Warning timecheck not running"); newerr |= SetError(fDimFeedback.state()!=Feedback::State::kCalibrating && fDimBiasControl.state()==BIAS::State::kVoltageOn && fBiasControlVoltageMed>3 && fFeedbackCalibration.size()==0, "Bias voltage switched on, but bias crate not calibrated"); // FTM in Connected instead of Idle --> power cyclen /* // Check offline and disconnected status? Out() << fDimMcp << endl; Out() << fDimControl << endl; Out() << fDimDataLogger << endl; Out() << fDimDriveControl << endl; Out() << fDimFadControl << endl; Out() << fDimFtmControl << endl; Out() << fDimBiasControl << endl; Out() << fDimFeedback << endl; Out() << fDimRateControl << endl; Out() << fDimFscControl << endl; Out() << fDimMagicWeather << endl; Out() << fDimRateScan << endl; Out() << fDimChat << endl; */ // FTU in error // FAD lost // -------------------------------------------------------------- ostringstream out; if (newerr) { SetAudio("sound_7"); out << now.JavaDate() << '\n'; out << HTML::kWhite << '\t'; for (auto it=fErrorHist.begin(); it!=fErrorHist.end(); it++) out << *it << "
"; out << '\n'; ofstream(fPath+"/errorhist.data") << out.str(); } out.str(""); out << Header(now) << '\t' << (fErrorList.size()>0) << '\t' << (fDimControl.state()>-3) << '\n'; out << setprecision(3); out << HTML::kWhite << '\t'; for (auto it=fErrorList.begin(); it!=fErrorList.end(); it++) out << *it << "
"; out << '\n'; if (haderr || fErrorList.size()>0) ofstream(fPath+"/error.data") << out.str(); // ============================================================== out.str(""); out << Header(now) << '\t' << (fErrorList.size()>0) << '\t' << (fDimControl.state()>-3) << '\n'; out << setprecision(3); // -------------- System status -------------- if (fDimDNS.online() && fDimMcp.state()>=MCP::State::kIdle) // Idle { string col = HTML::kBlue; switch (fMcpConfigurationState) { case MCP::State::kIdle: col = HTML::kWhite; break; case MCP::State::kConfiguring1: case MCP::State::kConfiguring2: case MCP::State::kConfiguring3: case MCP::State::kConfigured: case MCP::State::kTriggerOn: col = HTML::kBlue; break; case MCP::State::kTakingData: col = HTML::kBlue; if (fDimFadControl.state()==FAD::State::kWritingData) col = HTML::kGreen; break; } const bool other = fDimRateControl.state()==RateControl::State::kSettingGlobalThreshold || fDimRateScan.state()==RateScan::State::kInProgress; if (other) col = HTML::kBlue; out << col << '\t'; if (!other) { switch (fMcpConfigurationState) { case MCP::State::kIdle: out << "Idle [" << fMcpConfigurationName << "]"; break; case MCP::State::kConfiguring1: case MCP::State::kConfiguring2: case MCP::State::kConfiguring3: out << "Configuring [" << fMcpConfigurationName << "]"; break; case MCP::State::kConfigured: out << "Configured [" << fMcpConfigurationName << "]"; break; case MCP::State::kTriggerOn: case MCP::State::kTakingData: out << fMcpConfigurationName; if (fFadControlDrsRuns[2]>0) out << "(" << fFadControlDrsRuns[2] << ")"; break; } } else if (fDimRateControl.state()==RateControl::State::kSettingGlobalThreshold) out << "Calibrating threshold"; else if (fDimRateScan.state()==RateScan::State::kInProgress) out << "Rate scan in progress"; if (fMcpConfigurationState>MCP::State::kConfigured && fDimRateControl.state()!=RateControl::State::kSettingGlobalThreshold) { ostringstream evt; if (fMcpConfigurationMaxEvents>0) { const int64_t de = int64_t(fMcpConfigurationMaxEvents) - int64_t(fFadControlNumEvents); if (de>=0 && fMcpConfigurationState==MCP::State::kTakingData) evt << de; else evt << fMcpConfigurationMaxEvents; } else { if (fMcpConfigurationState==MCP::State::kTakingData) { if (fFadControlNumEvents>2999) evt << floor(fFadControlNumEvents/1000) << 'k'; else evt << fFadControlNumEvents; } } ostringstream tim; if (fMcpConfigurationMaxTime>0) { const uint32_t dt = (Time()-fMcpConfigurationRunStart).total_seconds(); if (dt<=fMcpConfigurationMaxTime && fMcpConfigurationState==MCP::State::kTakingData) tim << fMcpConfigurationMaxTime-dt << 's'; else tim << fMcpConfigurationMaxTime << 's'; } else { if (fMcpConfigurationState==MCP::State::kTakingData) tim << fMcpConfigurationRunStart.SecondsTo(); } const bool has_evt = !evt.str().empty(); const bool has_tim = !tim.str().empty(); if (has_evt || has_tim) out << " ["; out << evt.str(); if (has_evt && has_tim) out << '/'; out << tim.str(); if (has_evt || has_tim) out << ']'; } } else out << HTML::kWhite; out << '\n'; // ------------------ Drive ----------------- if (fDimDNS.online() && fDimDriveControl.state()>=Drive::State::kArmed) // Armed, Moving, Tracking { const double dev = fDriveControlTrackingDevHist.size()>0 ? fDriveControlTrackingDevHist.back() : 0; const State rc = fDimDriveControl.description(); string col = HTML::kGreen; if (rc.index==Drive::State::kMoving) // Moving col = HTML::kBlue; if (rc.index==Drive::State::kArmed) // Armed col = HTML::kWhite; if (rc.index==Drive::State::kTracking) // Tracking { if (dev>60) // ~1.5mm col = HTML::kYellow; if (dev>100) // ~1/4 of a pixel ~ 2.5mm col = HTML::kRed; } if (rc.index==0x100) col = HTML::kRed; out << col << '\t'; //out << rc.name << '\t'; out << fDriveControlPointingAz << ' '; out << fDriveControlPointingZd << "°"; out << setprecision(2); if (fDimDriveControl.state()==Drive::State::kTracking) { out << " ± " << dev << '"'; if (!fDriveControlSourceName.empty()) out << " [" << fDriveControlSourceName << ']'; } if (fDimDriveControl.state()==Drive::State::kMoving) out << " ⟳"; out << setprecision(3); } else out << HTML::kWhite << '\t'; if (fSun.time.IsValid() && fMoon.time.IsValid()) { if (fSun.visible) { out << " ☼"; if (fDimDriveControl.state()FSC::State::kDisconnected && fFscControlTemperatureHist.size()>0) { out << HTML::kGreen << '\t' << fFscControlTemperatureHist.back() << '\n'; } else out << HTML::kWhite << '\n'; // --------------- MagicWeather ------------- if (fDimDNS.online() && fDimMagicWeather.state()==MagicWeather::State::kReceiving && fMagicWeatherHist[kWeatherBegin].size()>0) { /* const float diff = fMagicWeatherHist[kTemp].back()-fMagicWeatherHist[kDew].back(); string col1 = HTML::kRed; if (diff>0.3) col1 = HTML::kYellow; if (diff>0.7) col1 = HTML::kGreen; */ const float wind = fMagicWeatherHist[kGusts].back(); const float hum = fMagicWeatherHist[kHum].back(); string col = HTML::kGreen; if (wind>35 || hum>95) col = HTML::kYellow; if (wind>50 || hum>98) col = HTML::kRed; out << col << '\t'; out << fMagicWeatherHist[kHum].back() << '\t'; out << setprecision(2); out << fMagicWeatherHist[kGusts].back() << '\n'; out << setprecision(3); } else out << HTML::kWhite << "\n"; // --------------- FtmControl ------------- if (fDimDNS.online() && fDimFtmControl.state()==FTM::State::kTriggerOn) { string col = HTML::kGreen; if (fFtmControlTriggerRateHist.size()>0 && !fFtmControlNewRunStarted) { if (fFtmControlTriggerRateHist.back()<15) col = HTML::kYellow; if (fFtmControlTriggerRateHist.back()>100) col = HTML::kRed; out << col << '\t' << fFtmControlTriggerRateHist.back() << " Hz"; } if (fDimBiasControl.state()==BIAS::State::kVoltageOn) out << " (" << fFtmPatchThresholdMed << ')'; out << '\n'; } else out << HTML::kWhite << '\n'; // --------------- BiasControl ------------- if (fDimDNS.online() && (fDimBiasControl.state()==BIAS::State::kRamping || fDimBiasControl.state()==BIAS::State::kOverCurrent || fDimBiasControl.state()==BIAS::State::kVoltageOn || fDimBiasControl.state()==BIAS::State::kVoltageOff)) { const bool off = fDimBiasControl.state()==BIAS::State::kVoltageOff; const bool oc = fDimBiasControl.state()==BIAS::State::kOverCurrent; string col = fBiasControlVoltageMed>3?HTML::kGreen:HTML::kWhite; if (fBiasControlCurrentMed>60 || fBiasControlCurrentMax>80) col = HTML::kYellow; if (fBiasControlCurrentMed>70 || fBiasControlCurrentMax>90) col = HTML::kRed; // Bias in overcurrent => Red if (fDimBiasControl.state()==BIAS::State::kOverCurrent) col = HTML::kRed; // MCP in ReadyForDatataking/Configuring/Configured/TriggerOn/TakingData // and Bias not in "data-taking state' => Red if (fMcpConfigurationState>MCP::State::kIdle && fDimBiasControl.state()!=BIAS::State::kVoltageOn && fDimBiasControl.state()!=BIAS::State::kVoltageOff) col = HTML::kRed; const bool cal = fFeedbackCalibration.size(); // Feedback is currently calibrating => Blue if (fDimFeedback.state()==Feedback::State::kCalibrating) { out << HTML::kBlue << '\t'; out << "***\t"; out << "***\t"; } else { out << setprecision(2); out << col << '\t'; out << (off ? 0 : fBiasControlCurrentMed) << '\t'; if (oc) out << "(OC) "; else { if (cal) out << (off ? 0 : fBiasControlCurrentMax); else out << "— "; } out << '\t'; out << setprecision(3); } if (cal && fDimFeedback.state()!=Feedback::State::kCalibrating) out << setprecision(2) << fBiasControlPowerTot << " W" << setprecision(3); else out << (off ? 0 : fBiasControlVoltageMed); out << '\n'; } else out << HTML::kWhite << '\n'; ofstream(fPath+"/fact.data") << out.str(); // ============================================================== out.str(""); out << Header(now) << '\t' << (fErrorList.size()>0) << '\t' << (fDimControl.state()>-3) << '\n'; if (!fDimDNS.online()) out << HTML::kWhite << "\tOffline\n\n\n\n\n\n\n\n\n\n\n\n\n"; else { ostringstream dt; dt << (Time()-fRunTime); out << HTML::kGreen << '\t' << fDimDNS.version() << '\n'; out << GetStateHtml(fDimControl, 0); out << GetStateHtml(fDimMcp, 4); out << GetStateHtml(fDimDataLogger, 1); out << GetStateHtml(fDimDriveControl, 2); out << GetStateHtml(fDimTimeCheck, 1); out << GetStateHtml(fDimFadControl, FAD::State::kConnected); out << GetStateHtml(fDimFtmControl, FTM::State::kConnected); out << GetStateHtml(fDimBiasControl, BIAS::State::kConnected); out << GetStateHtml(fDimFeedback, 4); out << GetStateHtml(fDimRateControl, 4); out << GetStateHtml(fDimFscControl, 2); out << GetStateHtml(fDimMagicWeather, 2); out << GetStateHtml(fDimTngWeather, 2); out << GetStateHtml(fDimRateScan, 4); out << GetStateHtml(fDimChat, 0); out << GetStateHtml(fDimSkypeClient, 1); out << HTML::kGreen << '\t' << dt.str().substr(0, dt.str().length()-7) << '\n'; } ofstream(fPath+"/status.data") << out.str(); if (now-fLastAstroCalc>boost::posix_time::seconds(15)) { UpdateAstronomy(); fLastAstroCalc = now; } return fDimDNS.online() ? kStateRunning : kStateDimNetworkNA; } public: StateMachineSmartFACT(ostream &out=cout) : StateMachineDim(out, "SMART_FACT"), fLastAstroCalc(boost::date_time::neg_infin), fPath("www/smartfact/data"), fControlScriptDepth(0), fMcpConfigurationState(DimState::kOffline), fMcpConfigurationMaxTime(0), fMcpConfigurationMaxEvents(0), fTngWeatherDustTime(Time::none), fBiasControlVoltageMed(0), fBiasControlCurrentMed(0), fBiasControlCurrentMax(0), fFscControlHumidityAvg(0), fDriveControlMoonDist(-1), fFadControlNumEvents(0), fFadControlDrsRuns(3), fRateScanDataId(0), fRateScanBoard(0), // --- fDimMcp ("MCP"), fDimDataLogger ("DATA_LOGGER"), fDimDriveControl("DRIVE_CONTROL"), fDimTimeCheck ("TIME_CHECK"), fDimMagicWeather("MAGIC_WEATHER"), fDimTngWeather ("TNG_WEATHER"), fDimFeedback ("FEEDBACK"), fDimBiasControl ("BIAS_CONTROL"), fDimFtmControl ("FTM_CONTROL"), fDimFadControl ("FAD_CONTROL"), fDimFscControl ("FSC_CONTROL"), fDimRateControl ("RATE_CONTROL"), fDimRateScan ("RATE_SCAN"), fDimChat ("CHAT"), fDimSkypeClient ("SKYPE_CLIENT") { fDimDNS.Subscribe(*this); fDimControl.Subscribe(*this); fDimMcp.Subscribe(*this); fDimDataLogger.Subscribe(*this); fDimDriveControl.Subscribe(*this); fDimTimeCheck.Subscribe(*this); fDimMagicWeather.Subscribe(*this); fDimTngWeather.Subscribe(*this); fDimFeedback.Subscribe(*this); fDimBiasControl.Subscribe(*this); fDimFtmControl.Subscribe(*this); fDimFadControl.Subscribe(*this); fDimFscControl.Subscribe(*this); fDimRateControl.Subscribe(*this); fDimRateScan.Subscribe(*this); fDimChat.Subscribe(*this); fDimSkypeClient.Subscribe(*this); fDimFscControl.SetCallback(bind(&StateMachineSmartFACT::HandleFscControlStateChange, this)); fDimControl.SetCallback(bind(&StateMachineSmartFACT::HandleControlStateChange, this, placeholders::_1)); fDimControl.AddCallback("dotest.dim", bind(&StateMachineSmartFACT::HandleDoTest, this, placeholders::_1)); Subscribe("DIM_CONTROL/MESSAGE") (bind(&StateMachineSmartFACT::HandleDimControlMessage, this, placeholders::_1)); Subscribe("MCP/CONFIGURATION") (bind(&StateMachineSmartFACT::HandleMcpConfiguration, this, placeholders::_1)); Subscribe("DRIVE_CONTROL/POINTING_POSITION") (bind(&StateMachineSmartFACT::HandleDrivePointing, this, placeholders::_1)); Subscribe("DRIVE_CONTROL/TRACKING_POSITION") (bind(&StateMachineSmartFACT::HandleDriveTracking, this, placeholders::_1)); Subscribe("DRIVE_CONTROL/SOURCE_POSITION") (bind(&StateMachineSmartFACT::HandleDriveSource, this, placeholders::_1)); Subscribe("FSC_CONTROL/TEMPERATURE") (bind(&StateMachineSmartFACT::HandleFscTemperature, this, placeholders::_1)); Subscribe("FSC_CONTROL/HUMIDITY") (bind(&StateMachineSmartFACT::HandleFscHumidity, this, placeholders::_1)); Subscribe("MAGIC_WEATHER/DATA") (bind(&StateMachineSmartFACT::HandleMagicWeatherData, this, placeholders::_1)); Subscribe("TNG_WEATHER/DUST") (bind(&StateMachineSmartFACT::HandleTngWeatherDust, this, placeholders::_1)); Subscribe("FEEDBACK/DEVIATION") (bind(&StateMachineSmartFACT::HandleFeedbackDeviation, this, placeholders::_1)); Subscribe("FEEDBACK/CALIBRATION") (bind(&StateMachineSmartFACT::HandleFeedbackCalibration, this, placeholders::_1)); Subscribe("BIAS_CONTROL/VOLTAGE") (bind(&StateMachineSmartFACT::HandleBiasVoltage, this, placeholders::_1)); Subscribe("BIAS_CONTROL/CURRENT") (bind(&StateMachineSmartFACT::HandleBiasCurrent, this, placeholders::_1)); Subscribe("FAD_CONTROL/CONNECTIONS") (bind(&StateMachineSmartFACT::HandleFadConnections, this, placeholders::_1)); Subscribe("FAD_CONTROL/EVENTS") (bind(&StateMachineSmartFACT::HandleFadEvents, this, placeholders::_1)); Subscribe("FAD_CONTROL/DRS_RUNS") (bind(&StateMachineSmartFACT::HandleFadDrsRuns, this, placeholders::_1)); Subscribe("FTM_CONTROL/TRIGGER_RATES") (bind(&StateMachineSmartFACT::HandleFtmTriggerRates, this, placeholders::_1)); Subscribe("FTM_CONTROL/STATIC_DATA") (bind(&StateMachineSmartFACT::HandleFtmStaticData, this, placeholders::_1)); Subscribe("FTM_CONTROL/FTU_LIST") (bind(&StateMachineSmartFACT::HandleFtmFtuList, this, placeholders::_1)); Subscribe("RATE_CONTROL/THRESHOLD") (bind(&StateMachineSmartFACT::HandleRateControlThreshold,this, placeholders::_1)); Subscribe("RATE_SCAN/DATA") (bind(&StateMachineSmartFACT::HandleRateScanData, this, placeholders::_1)); Subscribe("FAD_CONTROL/EVENT_DATA") (bind(&StateMachineSmartFACT::HandleFadEventData, this, placeholders::_1)); // ================================================================= // State names AddStateName(kStateDimNetworkNA, "DimNetworkNotAvailable", "The Dim DNS is not reachable."); AddStateName(kStateRunning, "Running", ""); // ================================================================= AddEvent("PRINT") (bind(&StateMachineSmartFACT::Print, this)) ("Print a list of the states of all connected servers."); } int EvalOptions(Configuration &conf) { if (!fPixelMap.Read(conf.Get("pixel-map-file"))) { Error("Reading mapping table from "+conf.Get("pixel-map-file")+" failed."); return 1; } fPath = conf.Get("path"); fDatabase = conf.Get("source-database"); ostringstream out; out << Time().JavaDate() << '\n'; ofstream(fPath+"/errorhist.data") << out.str(); ofstream(fPath+"/error.data") << out.str(); return -1; } }; // ------------------------------------------------------------------------ #include "Main.h" template int RunShell(Configuration &conf) { return Main::execute(conf); } void SetupConfiguration(Configuration &conf) { po::options_description control("Smart FACT"); control.add_options() ("pixel-map-file", var("FACTmapV5a.txt"), "Pixel mapping file. Used here to get the default reference voltage") ("path", var("www/smartfact/data"), "Output path for the data-files") ("source-database", var(), "Database link as in\n\tuser:password@server[:port]/database.") ; 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(); /* 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 127; if (!conf.Has("console")) return RunShell(conf); if (conf.Get("console")==0) return RunShell(conf); else return RunShell(conf); return 0; }