#ifdef HAVE_LIBNOVA #include #include #include #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 Astro { public: Time time; Time fSunRiseDayTime; Time fSunRiseCivil; Time fSunRiseAstronomical; Time fSunRiseDarkTime; Time fSunSetDayTime; Time fSunSetCivil; Time fSunSetAstronomical; Time fSunSetDarkTime; int state; string description; string color; bool isday; public: Astro(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_lunar_rst (JD-0.5, &observer, &moon); 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); fSunSetDayTime = Time(sun_day.set); fSunSetCivil = Time(sun_civil.set); fSunSetAstronomical = Time(sun_astronomical.set); fSunSetDarkTime = Time(sun_dark.set); fSunRiseDayTime = Time(sun_day.rise); fSunRiseCivil = Time(sun_civil.rise); fSunRiseAstronomical = Time(sun_astronomical.rise); fSunRiseDarkTime = Time(sun_dark.rise); const bool is_day = JD>sun_day.rise; const bool is_night = JD>sun_dark.set; //ln_get_lunar_rst (JD+0.5, &observer, &moon); 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) { fSunRiseDayTime = Time(sun_day.rise); fSunRiseCivil = Time(sun_civil.rise); fSunRiseAstronomical = Time(sun_astronomical.rise); fSunRiseDarkTime = Time(sun_dark.rise); } if (is_night) { fSunSetDayTime = Time(sun_day.set); fSunSetCivil = Time(sun_civil.set); fSunSetAstronomical = Time(sun_astronomical.set); fSunSetDarkTime = 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 /* if (is_day^is_night) { cout << "SunSet: " << fSunSetDayTime << endl; cout << "SunRise: " << fSunRiseDayTime << endl; } else { cout << "SunRise: " << fSunRiseDayTime << endl; cout << "SunSet: " << fSunSetDayTime << endl; }*/ isday = is_day^is_night; state = isday ? 4 : 0; if (time>fSunSetDayTime) state++; if (time>fSunSetCivil) state++; if (time>fSunSetAstronomical) state++; if (time>fSunSetDarkTime) state++; if (time>fSunRiseDarkTime) state++; if (time>fSunRiseAstronomical) state++; if (time>fSunRiseCivil) state++; if (time>fSunRiseDayTime) state++; string name[] = { "dark time", "astron. twilight", "civil twilight", "sunrise", "day time", "sunset", "civil twilight", "astron. twilight", "dark time" }; description = state[name]; string arr; ostringstream out; if (isday) { out << fSunSetDarkTime-time; arr = "↓"; } else { out << fSunRiseDayTime-time; arr = "↑"; } description += " ["+out.str().substr(0, 5)+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; } #endif } }; class Moon { public: double ra; double dec; double disk; bool visible; Time fMoonRise; Time fMoonTransit; Time fMoonSet; string description; string color; Time time; 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; ln_rst_time moon; ln_get_lunar_rst(JD-0.5, &observer, &moon); fMoonRise = Time(moon.rise); fMoonTransit = Time(moon.transit); fMoonSet = 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) fMoonRise = Time(moon.rise); if (is_sinking) fMoonTransit = Time(moon.transit); if (is_dn) fMoonSet = Time(moon.set); ln_equ_posn pos; ln_get_lunar_equ_coords(JD, &pos); ra = pos.ra/15; dec = pos.dec; disk = ln_get_lunar_disk(JD)*100; if (!visible || disk<25) color = HTML::kGreen; else color = disk>75 ? HTML::kRed : HTML::kYellow; string arr; ostringstream dt; if (fMoonSet 1.0) arg = 1.0; if(arg < -1.0) arg = -1.0; return acos(arg) * 180/M_PI; } }; // ======================================================================== // ======================================================================== // ======================================================================== class StateMachineSmartFACT : public StateMachineDim { private: const Time fRunTime; enum states_t { kStateDimNetworkNA = 1, kStateRunning, }; // ------------------------- Internal variables ----------------------- PixelMap fPixelMap; Time fLastUpdate; 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; uint32_t fFadControlDrsRuns[3]; float fFtmControlTriggerRateCam; deque fFtmControlTriggerRateHist; float fFtmPatchThresholdMed; float fFtmBoardThresholdMed; bool fFtmControlFtuOk; uint64_t fRateScanDataId; uint8_t fRateScanBoard; deque fRateScanDataHist[41]; int fHasError; // ------------- Initialize variables before the Dim stuff ------------ DimVersion fDimDNS; DimControl fDimControl; DimDescribedState fDimMcp; DimDescribedState fDimDataLogger; DimDescribedState fDimDriveControl; DimDescribedState fDimMagicWeather; DimDescribedState fDimTngWeather; DimDescribedState fDimFeedback; DimDescribedState fDimBiasControl; DimDescribedState fDimFtmControl; DimDescribedState fDimFadControl; DimDescribedState fDimFscControl; DimDescribedState fDimRateControl; DimDescribedState fDimRateScan; DimDescribedState fDimChatServer; // ------------------------------------------------------------------- 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 += "<->"+string(d.Ptr())+""; fControlMessageHist.push_back(msg); ostringstream out; out << setprecision(3); out << d.GetJavaDate() << '\n'; out << HTML::kWhite << '\t'; for (auto it=fControlMessageHist.begin(); it!=fControlMessageHist.end(); it++) out << *it << "
"; 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(); 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)); } int HandleMcpConfiguration(const EventImp &d) { if (!CheckDataSize(d, "Mcp:Configuration", 16, true)) { fMcpConfigurationState = 0; fMcpConfigurationMaxTime = 0; fMcpConfigurationMaxEvents = 0; fMcpConfigurationName = ""; fMcpConfigurationRunStart = Time(Time::none); return GetCurrentState(); } fMcpConfigurationState = d.GetQoS(); fMcpConfigurationMaxTime = d.Get(); fMcpConfigurationMaxEvents = d.Get(8); fMcpConfigurationName = d.Ptr(16); if (d.GetQoS()==12) fMcpConfigurationRunStart = Time(); 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, "magicweather-"+name+"-hist", 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(); } 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(fMagicWeatherHist[kDir].back()/22.5+16.5))%16; //const uint16_t idx = uint16_t(floor(fmod(fMagicWeatherHist[kDir].back()/22.5+360+11.25, 360)/22.5))%16; Astro astro(-(17.+53./60+26.525/3600), 28.+45./60+42.462/3600); Moon moon (-(17.+53./60+26.525/3600), 28.+45./60+42.462/3600); ostringstream out; out << d.GetJavaDate() << '\n'; out << astro.color << '\t' << astro.description << '\n'; out << setprecision(2); out << (astro.isday?HTML::kWhite:moon.color) << '\t' << moon.description << '\n'; out << setprecision(3); for (int i=0; i<6; i++) out << HTML::kWhite << '\t' << fMagicWeatherHist[i].back() << '\n'; out << HTML::kWhite << '\t' << dir[idx] << '\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(); out.str(""); out << astro.time.JavaDate() << '\n'; out << HTML::kWhite << '\t' << astro.fSunRiseDarkTime.GetAsStr("%H:%M") << '\n'; out << HTML::kWhite << '\t' << astro.fSunRiseAstronomical.GetAsStr("%H:%M") << '\n'; out << HTML::kWhite << '\t' << astro.fSunRiseCivil.GetAsStr("%H:%M") << '\n'; out << HTML::kWhite << '\t' << astro.fSunRiseDayTime.GetAsStr("%H:%M") << '\n'; out << HTML::kWhite << '\t' << astro.fSunSetDayTime.GetAsStr("%H:%M") << '\n'; out << HTML::kWhite << '\t' << astro.fSunSetCivil.GetAsStr("%H:%M") << '\n'; out << HTML::kWhite << '\t' << astro.fSunSetAstronomical.GetAsStr("%H:%M") << '\n'; out << HTML::kWhite << '\t' << astro.fSunSetDarkTime.GetAsStr("%H:%M") << '\n'; out << HTML::kWhite << '\t' << moon.fMoonRise.GetAsStr("%H:%M") << '\n'; out << HTML::kWhite << '\t' << moon.fMoonTransit.GetAsStr("%H:%M") << '\n'; out << HTML::kWhite << '\t' << moon.fMoonSet.GetAsStr("%H:%M") << '\n'; out << HTML::kWhite << '\t'; ofstream(fPath+"/astro.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, "tng-dust-hist", 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); 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(az/22.5+16.5))%16; fDriveControlPointingAz = dir[idx]; ostringstream out; out << d.GetJavaDate() << '\n'; out << setprecision(5); out << fDriveControlPointingZd << '\n'; out << az << '\n'; ofstream(fPath+"/drive-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, "control-deviation-hist", 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; Moon moon(-(17.+53./60+26.525/3600), 28.+45./60+42.462/3600); if (moon.visible) { const double angle = moon.Angle(Ra, Dec); string col = HTML::kGreen; if (angle<20 || angle>140) col = HTML::kYellow; if (angle<10 || angle>150) col = HTML::kRed; out << col << '\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, "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, "biascontrol-voltage", val, 10, 65); else WriteBinary(d, "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, "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, "biascontrol-current-hist", 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)<0) return GetCurrentState(); fFtmControlTriggerRateCam = d.Get(20); 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(fFtmControlTriggerRateCam); if (fFtmControlTriggerRateHist.size()>60) fFtmControlTriggerRateHist.pop_front(); // FIXME: Add statistics for all kind of rates WriteBinary(d, "ftmcontrol-triggerrate-hist", fFtmControlTriggerRateHist, 100); WriteBinary(d, "ftmcontrol-boardrates", vector(brates, brates+40), 10); WriteBinary(d, "ftmcontrol-patchrates", vector(prates, prates+160), 10); ostringstream out; out << setprecision(3); out << d.GetJavaDate() << '\n'; out << HTML::kWhite << '\t' << fFtmControlTriggerRateCam << '\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, "ftmcontrol-thresholds-patch", vecp, 1000); WriteBinary(d, "ftmcontrol-thresholds-board", vecb, 1000); 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.avg << '\n'; out << HTML::kWhite << '\t' << statb.max << '\n'; out << HTML::kWhite << '\t' << statp.min << '\n'; out << HTML::kWhite << '\t' << statp.med << '\n'; //out << HTML::kWhite << '\t' << statp.avg << '\n'; out << HTML::kWhite << '\t' << statp.max << '\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 (d.GetQoS()) { 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; } 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, "fsccontrol-temperature-hist", 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, "ratescan-hist", fRateScanDataHist[0], 10, -2); WriteBinary(d, "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(); } // ------------------------------------------------------------------- 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() << 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() << fDimChatServer << 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<1) return HTML::kWhite + "\t—\n"; return (rc.index"; if (fDimDriveControl.state()>0xff) msg << "Drive in ERROR state
"; if (fDimBiasControl.state()"; if (fDimBiasControl.state()==BIAS::State::kOverCurrent) msg << "BIAS channels in OverCurrent
"; if (fDimBiasControl.state()==BIAS::State::kNotReferenced) msg << "BIAS voltage not at reference
"; if (fFeedbackCalibration.size()>0) { if (fBiasControlCurrentMed>75) msg << "Median current exceeds 75µA/pix
"; if (fBiasControlCurrentMax>90) msg << "Maximum current exceeds 90µA/pix
"; } if (fMagicWeatherHist[kHum].size()>0 && fMagicWeatherHist[kHum].back()>98 && (fDimMcp.state()==MCP::State::kTriggerOn || fDimMcp.state()==MCP::State::kTakingData)) msg << "Outside humidity exceeds 98% during data-taking
"; if (fMagicWeatherHist[kGusts].size()>0 && fMagicWeatherHist[kGusts].back()>98 && fDimDriveControl.state()==Drive::State::kTracking) msg << "Wind gusts exceed 50km/h during tracking
"; if (fFscControlTemperatureHist.size()>0 && fFscControlTemperatureHist.back()>7) msg << "Sensor temperature exceeds outside temperature by more than 7°C
"; if (fFtmControlTriggerRateCam<0.01 && (fDimMcp.state()==MCP::State::kTriggerOn || fDimMcp.state()==MCP::State::kTakingData)) msg << "Trigger rate below 10mHz during data taking
"; if (fFscControlHumidityAvg>60) msg << "Average camera humidity exceed 60%
"; if (!fDimControl.online()) msg << "dimctrl offline
"; if (fDriveControlMoonDist>155) msg << "Moon within the field-of-view of the cones
"; if (fDriveControlMoonDist>=0 && fDriveControlMoonDist<3) msg << "Moon within the field-of-view of the camera
"; if (!fDimFeedback.state()==Feedback::State::kCalibrating && fDimBiasControl.state()==BIAS::State::kVoltageOn && fBiasControlVoltageMed>3 && fFeedbackCalibration.size()==0) msg << "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() << fDimChatServer << endl; */ // FTU in error // FAD lost // -------------------------------------------------------------- const bool haserror = msg.str().size()>0; ostringstream out; out << now.JavaDate() << '\t' << haserror << '\t' << (fDimControl.state()>-3) << '\n'; out << setprecision(3); out << HTML::kWhite << '\t' << msg.str() << '\n'; if (haserror || fHasError) ofstream(fPath+"/error.data") << out.str(); fHasError = haserror; if (!fDimDNS.online()) return kStateDimNetworkNA; // ============================================================== out.str(""); out << now.JavaDate() << '\t' << fHasError << '\t' << (fDimControl.state()>-3) << '\n'; out << setprecision(3); // -------------- System status -------------- if (fDimMcp.state()>=MCP::State::kIdle) // Idle { string col = HTML::kBlue; if (fMcpConfigurationState!=MCP::State::kIdle && // Idle fMcpConfigurationState!=MCP::State::kTriggerOn && // Trigger On fMcpConfigurationState!=MCP::State::kTakingData) // Taking Data col = HTML::kYellow; else if (fDimFadControl.state()==FAD::State::kWritingData) col = HTML::kGreen; out << col << '\t'; if (fDimRateControl.state()!=RateControl::State::kSettingGlobalThreshold && fDimRateScan.state()!=RateScan::State::kInProgress) { 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) { ostringstream d; d << Time()-fMcpConfigurationRunStart; tim << d.str().substr(3, 5); } } 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 (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==6) // Moving col = HTML::kBlue; if (rc.index==5) // Armed col = HTML::kWhite; if (rc.index==7) // Tracking { if (dev>60) // ~1.5mm col = HTML::kYellow; if (dev>100) // ~1/4 of a pixel ~ 2.5mm col = HTML::kRed; } out << col << '\t'; //out << rc.name << '\t'; out << fDriveControlPointingZd << '\t'; out << setprecision(2); out << fDriveControlPointingAz << '\t'; if (fDimDriveControl.state()==7) { out << fDriveControlSourceName << '\t'; out << dev << '\n'; } else out << "\t\n"; out << setprecision(3); } else out << HTML::kWhite << '\n'; // ------------------- FSC ------------------ if (fDimFscControl.state()>FSC::State::kDisconnected && fFscControlTemperatureHist.size()>0) { out << HTML::kGreen << '\t' << fFscControlTemperatureHist.back() << '\n'; } else out << HTML::kWhite << '\n'; // --------------- MagicWeather ------------- if (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 (fDimFtmControl.state()==FTM::State::kTriggerOn) { string col = HTML::kGreen; if (fFtmControlTriggerRateCam<15) col = HTML::kYellow; if (fFtmControlTriggerRateCam>100) col = HTML::kRed; out << col << '\t' << fFtmControlTriggerRateCam << '\t'; out << fFtmPatchThresholdMed << '\n'; } else out << HTML::kWhite << '\n'; // --------------- BiasControl ------------- if (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 (fBiasControlCurrentMax>65) col = HTML::kYellow; if (fBiasControlCurrentMax>80) 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()==13) { 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 << now.JavaDate() << '\t' << fHasError << '\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(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(fDimChatServer, 1); out << HTML::kGreen << '\t' << dt.str().substr(0, dt.str().length()-7) << '\n'; } ofstream(fPath+"/status.data") << out.str(); return kStateRunning; } public: StateMachineSmartFACT(ostream &out=cout) : StateMachineDim(out, "SMART_FACT"), fPath("www/smartfact/data"), fControlScriptDepth(0), fMcpConfigurationState(-256), fMcpConfigurationMaxTime(0), fMcpConfigurationMaxEvents(0), fTngWeatherDustTime(Time::none), fBiasControlVoltageMed(0), fBiasControlCurrentMed(0), fBiasControlCurrentMax(0), fFscControlHumidityAvg(0), fFtmControlTriggerRateCam(0), fRateScanDataId(0), fRateScanBoard(0), fHasError(2), // --- fDimMcp ("MCP"), fDimDataLogger ("DATA_LOGGER"), fDimDriveControl("DRIVE_CONTROL"), 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"), fDimChatServer ("CHAT_SERVER") { fDimDNS.Subscribe(*this); fDimControl.Subscribe(*this); fDimMcp.Subscribe(*this); fDimDataLogger.Subscribe(*this); fDimDriveControl.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); fDimChatServer.Subscribe(*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_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"); 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") ; 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 -1; if (!conf.Has("console")) return RunShell(conf); if (conf.Get("console")==0) return RunShell(conf); else return RunShell(conf); return 0; }