source: trunk/FACT++/src/smartfact.cc@ 20115

Last change on this file since 20115 was 19712, checked in by tbretz, 5 years ago
Added GTC dust measurements
File size: 121.6 KB
Line 
1#ifdef HAVE_NOVA
2#include "Prediction.h"
3#endif
4
5#ifdef HAVE_SQL
6#include "Database.h"
7#endif
8
9#include <sys/stat.h> //for file stats
10#include <sys/statvfs.h> //for file statvfs
11
12#include <numeric> // std::accumulate
13
14#include "Dim.h"
15#include "Event.h"
16#include "Shell.h"
17#include "StateMachineDim.h"
18#include "Connection.h"
19#include "Configuration.h"
20#include "Console.h"
21#include "DimWriteStatistics.h"
22#include "PixelMap.h"
23
24#include "tools.h"
25
26#include "LocalControl.h"
27
28#include "HeadersFAD.h"
29#include "HeadersBIAS.h"
30#include "HeadersFTM.h"
31#include "HeadersFSC.h"
32#include "HeadersGPS.h"
33#include "HeadersSQM.h"
34#include "HeadersMCP.h"
35#include "HeadersLid.h"
36#include "HeadersDrive.h"
37#include "HeadersPower.h"
38#include "HeadersPFmini.h"
39#include "HeadersBiasTemp.h"
40#include "HeadersAgilent.h"
41#include "HeadersFeedback.h"
42#include "HeadersRateScan.h"
43#include "HeadersRateControl.h"
44#include "HeadersTNGWeather.h"
45#include "HeadersGTC.h"
46#include "HeadersMagicLidar.h"
47#include "HeadersMagicWeather.h"
48#include "HeadersTemperature.h"
49#include "HeadersRainSensor.h"
50
51#include <boost/filesystem.hpp>
52
53using namespace std;
54
55// ------------------------------------------------------------------------
56
57#include "DimDescriptionService.h"
58#include "DimState.h"
59
60// ------------------------------------------------------------------------
61/*
62template<class T>
63 class buffer : public deque<T>
64 {
65 int32_t max_size;
66
67 public:
68 buffer(int32_t max=-1) : max_size(max) { }
69 const T &operator=(const T &t) const { push_back(t); if (max_size>0 && deque<T>::size()>max_size) deque<T>::pop_front(); }
70 operator T() const { return deque<T>::size()>0 ? deque<T>::back() : T(); }
71 bool valid() const { return deque<T>::size()>0; }
72 };
73*/
74
75// ------------------------------------------------------------------------
76
77namespace HTML
78{
79 const static string kWhite = "#ffffff";
80 const static string kYellow = "#fffff0";
81 const static string kRed = "#fff8f0";
82 const static string kGreen = "#f0fff0";
83 const static string kBlue = "#f0f0ff";
84};
85
86// ========================================================================
87// ========================================================================
88// ========================================================================
89
90class Sun
91{
92public:
93 Time time;
94
95 // This is always the time of the next...
96 Time fSunRise00;
97 Time fSunRise06;
98 Time fSunRise12;
99 Time fSunRise18;
100
101 Time fSunSet00;
102 Time fSunSet06;
103 Time fSunSet12;
104 Time fSunSet18;
105
106 int state;
107 string description;
108 string color;
109
110 bool isday;
111 bool visible;
112
113#ifdef HAVE_NOVA
114 Nova::RstTime Rst(double jd, double hrz=LN_SOLAR_STANDART_HORIZON)
115 {
116 Nova::RstTime rs = Nova::GetSolarRst(jd-0.5, hrz);
117 if (jd>rs.rise || jd>rs.set)
118 {
119 const Nova::RstTime rs2 = Nova::GetSolarRst(jd+0.5, hrz);
120 if (jd>rs.rise)
121 rs.rise = rs2.rise;
122 if (jd>rs.set)
123 rs.set = rs2.set;
124 }
125 return rs;
126 }
127#endif
128
129public:
130 Sun() : time(Time::none)
131 {
132 }
133
134 // Could be done more efficient: Only recalcuate if
135 // the current time exceeds at least on of the stored times
136 Sun(const Time &t) : time(t)
137 {
138#ifdef HAVE_NOVA
139 // get Julian day from local time
140 const double JD = time.JD();
141
142 // >0deg : day
143 // -6deg - 0deg : civil twilight
144 // -12deg - -6deg : nautical twilight
145 // -18deg - -12deg : astronomical twilight
146 // <-18deg : night
147
148 const Nova::RstTime sun00 = Rst(JD);
149 const Nova::RstTime sun06 = Rst(JD, -6);
150 const Nova::RstTime sun12 = Rst(JD, -12);
151 const Nova::RstTime sun18 = Rst(JD, -18);
152
153 fSunRise00 = sun00.rise;
154 fSunRise06 = sun06.rise;
155 fSunRise12 = sun12.rise;
156 fSunRise18 = sun18.rise;
157
158 fSunSet00 = sun00.set;
159 fSunSet06 = sun06.set;
160 fSunSet12 = sun12.set;
161 fSunSet18 = sun18.set;
162
163 array<double,8> arr =
164 {{
165 sun00.set,
166 sun06.set,
167 sun12.set,
168 sun18.set,
169 sun18.rise,
170 sun12.rise,
171 sun06.rise,
172 sun00.rise,
173 }};
174
175
176 state = std::min_element(arr.begin(), arr.end())-arr.begin();
177
178 string name[] =
179 {
180 "day time",
181 "civil twilight",
182 "nautical twilight",
183 "astron. twilight",
184 "dark time",
185 "astron. twilight",
186 "nautical twilight",
187 "civil twilight"
188 };
189
190 description = name[state];
191
192 const string txt = fSunRise18<fSunSet18 ?
193 time.MinutesTo(fSunRise18)+"&uarr;" :
194 time.MinutesTo(fSunSet18)+"&darr;";
195
196 description += " ["+txt+"]";
197
198 isday = state==0;
199
200 switch (state)
201 {
202 case 0: color = HTML::kRed; break;
203 case 1: case 2: color = HTML::kYellow; break;
204 case 3: case 4: case 5: color = HTML::kGreen; break;
205 case 6: case 7: color = HTML::kYellow; break;
206 }
207
208 visible = state==0;
209
210 /*
211 // Warning: return code of 1 means circumpolar and is not checked!
212 Nova::RstTime sun_day = Nova::GetSolarRst(JD-0.5);
213 Nova::RstTime sun_civil = Nova::GetSolarRst(JD-0.5, -6);
214 Nova::RstTime sun_astronomical = Nova::GetSolarRst(JD-0.5, -12);
215 Nova::RstTime sun_dark = Nova::GetSolarRst(JD-0.5, -18);
216
217 fSetDayTime = Time(sun_day.set);
218 fSetCivil = Time(sun_civil.set);
219 fSetAstronomical = Time(sun_astronomical.set);
220 fSetDarkTime = Time(sun_dark.set);
221
222 fRiseDayTime = Time(sun_day.rise);
223 fRiseCivil = Time(sun_civil.rise);
224 fRiseAstronomical = Time(sun_astronomical.rise);
225 fRiseDarkTime = Time(sun_dark.rise);
226
227 const bool is_day = JD>sun_day.rise;
228 const bool is_night = JD>sun_dark.set;
229
230 sun_day = Nova::GetSolarRst(JD+0.5);
231 sun_civil = Nova::GetSolarRst(JD+0.5, -6);
232 sun_astronomical = Nova::GetSolarRst(JD+0.5, -12);
233 sun_dark = Nova::GetSolarRst(JD+0.5, -18);
234
235 if (is_day)
236 {
237 fRiseDayTime = Time(sun_day.rise);
238 fRiseCivil = Time(sun_civil.rise);
239 fRiseAstronomical = Time(sun_astronomical.rise);
240 fRiseDarkTime = Time(sun_dark.rise);
241 }
242
243 if (is_night)
244 {
245 fSetDayTime = Time(sun_day.set);
246 fSetCivil = Time(sun_civil.set);
247 fSetAstronomical = Time(sun_astronomical.set);
248 fSetDarkTime = Time(sun_dark.set);
249 }
250
251 // case 0: midnight to sun-rise | !is_day && !is_night | rise/set | -> isday=0
252 // case 1: sun-rise to sun-set | is_day && !is_night | set /rise | -> isday=1
253 // case 2: sun-set to midnight | is_day && is_night | rise/set | -> isday=0
254
255 isday = is_day^is_night;
256
257 Time fRiseDayTime; // 0: Start of day time (=end of civil twilight)
258 Time fRiseCivil; // -6: End of nautical twilight
259 Time fRiseAstronomical; // -12: End of astron. twilight
260 Time fRiseDarkTime; // -18: End of dark time
261
262 Time fSetDayTime; // 0: End of day time (=start of civil twilight)
263 Time fSetCivil; // -6: Start of nautical twilight
264 Time fSetAstronomical; // -12: Start of astron. twilight
265 Time fSetDarkTime; // -18: Start of dark time
266
267 state = isday ? 4 : 0; // 0 [-> Day time ]
268 if (time>fSetDayTime) state++; // 1 [-> Civil twilight]
269 if (time>fSetCivil) state++; // 2 [-> Naut. twilight]
270 if (time>fSetAstronomical) state++; // 3 [-> Astro. twilight]
271 if (time>fSetDarkTime) state++; // 4 [-> Dark time ]
272
273 if (time>fRiseDarkTime) state++; // 5 [-> Astro. twilight]
274 if (time>fRiseAstronomical) state++; // 6 [-> Naut. twilight]
275 if (time>fRiseCivil) state++; // 7 [-> Civil twilight]
276 if (time>fRiseDayTime) state++; // 8 [-> Day time ]
277
278 string name[] =
279 {
280 "dark time", // 0
281 "astron. twilight", // 1
282 "civil twilight", // 2
283 "sunrise", // 3
284 "day time", // 4
285 "sunset", // 5
286 "civil twilight", // 6
287 "astron. twilight", // 7
288 "dark time" // 8
289 };
290
291 description = name[state];
292
293 const string arr = isday ?
294 fSetDarkTime.MinutesTo(time)+"&darr;" :
295 fRiseDarkTime.MinutesTo(time)+"&uarr;";
296
297 description += " ["+arr+"]";
298
299 switch (state)
300 {
301 case 0: case 1: color = HTML::kGreen; break;
302 case 2: case 3: color = HTML::kYellow; break;
303 case 4: color = HTML::kRed; break;
304 case 5: case 6: color = HTML::kYellow; break;
305 case 7: case 8: color = HTML::kGreen; break;
306 }
307
308 visible = state>=3 && state<=5;
309 */
310#endif
311 }
312};
313
314class Moon
315{
316public:
317 Time time;
318
319 double ra;
320 double dec;
321
322 double zd;
323 double az;
324
325 double disk;
326
327 bool visible;
328
329 Time fRise;
330 Time fTransit;
331 Time fSet;
332
333 string description;
334 string color;
335
336 int state;
337
338 Moon() : time(Time::none)
339 {
340 }
341
342 // Could be done more efficient: Only recalcuate if
343 // the current time exceeds at least on of the stored times
344 Moon(const Time &t) : time(t)
345 {
346#ifdef HAVE_NOVA
347 const double JD = time.JD();
348
349 Nova::RstTime moon = Nova::GetLunarRst(JD-0.5);
350
351 fRise = Time(moon.rise);
352 fTransit = Time(moon.transit);
353 fSet = Time(moon.set);
354
355 //visible =
356 // ((JD>moon.rise && JD<moon.set ) && moon.rise<moon.set) ||
357 // ((JD<moon.set || JD>moon.rise) && moon.rise>moon.set);
358
359 const bool is_up = JD>moon.rise;
360 const bool is_sinking = JD>moon.transit;
361 const bool is_dn = JD>moon.set;
362
363 moon = Nova::GetLunarRst(JD+0.5);
364 if (is_up)
365 fRise = Time(moon.rise);
366 if (is_sinking)
367 fTransit = Time(moon.transit);
368 if (is_dn)
369 fSet = Time(moon.set);
370
371 const Nova::EquPosn pos = Nova::GetLunarEquCoords(JD);
372 const Nova::ZdAzPosn hrz = Nova::GetHrzFromEqu(pos, JD);
373
374 az = hrz.az;
375 zd = hrz.zd;
376
377 ra = pos.ra/15;
378 dec = pos.dec;
379
380 disk = Nova::GetLunarDisk(JD)*100;
381 state = 0;
382 if (fRise <fTransit && fRise <fSet) state = 0; // not visible
383 if (fTransit<fSet && fTransit<fRise) state = 1; // before culm
384 if (fSet <fRise && fSet <fTransit) state = 2; // after culm
385
386 visible = state!=0;
387
388 // 0: not visible
389 // 1: visible before cul
390 // 2: visible after cul
391
392 if (!visible || disk<25)
393 color = HTML::kGreen;
394 else
395 color = disk>75 ? HTML::kRed : HTML::kYellow;
396
397 const string arr = fSet<fRise ?
398 fSet.MinutesTo(time) +"&darr;" :
399 fRise.MinutesTo(time)+"&uarr;";
400
401 ostringstream out;
402 out << setprecision(2);
403 out << (visible?"visible ":"") << (disk<0.1?0:disk) << "% [" << arr << "]";
404
405 description = out.str();
406#endif
407 }
408
409 double Angle(double r, double d) const
410 {
411 const double theta0 = M_PI/2-d*M_PI/180;
412 const double phi0 = r*M_PI/12;
413
414 const double theta1 = M_PI/2-dec*M_PI/180;
415 const double phi1 = ra*M_PI/12;
416
417 const double x0 = sin(theta0) * cos(phi0);
418 const double y0 = sin(theta0) * sin(phi0);
419 const double z0 = cos(theta0);
420
421 const double x1 = sin(theta1) * cos(phi1);
422 const double y1 = sin(theta1) * sin(phi1);
423 const double z1 = cos(theta1);
424
425 double arg = x0*x1 + y0*y1 + z0*z1;
426 if(arg > 1.0) arg = 1.0;
427 if(arg < -1.0) arg = -1.0;
428
429 return acos(arg) * 180/M_PI;
430 }
431
432 static string Color(double angle)
433 {
434 if (angle<10 || angle>150)
435 return HTML::kRed;
436 if (angle<20 || angle>140)
437 return HTML::kYellow;
438 return HTML::kGreen;
439 }
440};
441
442// ========================================================================
443// ========================================================================
444// ========================================================================
445
446class StateMachineSmartFACT : public StateMachineDim
447{
448public:
449 static bool fIsServer;
450
451private:
452 enum states_t
453 {
454 kStateDimNetworkNA = 1,
455 kStateRunning,
456 };
457
458 // ------------------------- History classes -----------------------
459
460 struct EventElement
461 {
462 Time time;
463 string msg;
464
465 EventElement(const Time &t, const string &s) : time(t), msg(s) { }
466 };
467
468 class EventHist : public list<EventElement>
469 {
470 const boost::posix_time::time_duration deltat; //boost::posix_time::pos_infin
471 const uint64_t max;
472
473 public:
474 EventHist(const boost::posix_time::time_duration &dt=boost::posix_time::hours(12), uint64_t mx=UINT64_MAX) : deltat(dt), max(mx) { }
475
476 void add(const string &s, const Time &t=Time())
477 {
478 while (!empty() && (front().time+deltat<t || size()>max))
479 pop_front();
480
481 emplace_back(t, s);
482 }
483
484 void clean()
485 {
486 for (auto it=begin(); it!=end();)
487 if (!it->time)
488 {
489 const auto is = it++;
490 erase(is);
491 }
492 }
493
494 string get() const
495 {
496 ostringstream out;
497
498 string last = "";
499 for (auto it=begin(); it!=end(); it++)
500 {
501 const string tm = it->time.GetAsStr("%H:%M:%S ");
502 out << (tm!=last?tm:"--:--:-- ") << it->msg << "<br/>";
503 last = tm;
504 }
505
506 return out.str();
507 }
508 string rget() const
509 {
510 ostringstream out;
511
512 for (auto it=rbegin(); it!=rend(); it++)
513 out << it->time.GetAsStr("%H:%M:%S ") << it->msg << "<br/>";
514
515 return out.str();
516 }
517 };
518
519 // ------------------------- Internal variables -----------------------
520
521 const Time fRunTime;
522
523 PixelMap fPixelMap;
524
525 string fDatabase;
526
527 Time fLastUpdate;
528 Time fLastAstroCalc;
529
530 string fPath;
531
532 // ----------------------------- Data storage -------------------------
533
534 EventHist fControlMessageHist;
535 EventHist fControlAlarmHist;
536
537 int32_t fMcpConfigurationState; // For consistency
538 int64_t fMcpConfigurationMaxTime;
539 int64_t fMcpConfigurationMaxEvents;
540 string fMcpConfigurationName;
541 Time fMcpConfigurationRunStart;
542 EventHist fMcpConfigurationHist;
543 bool fLastRunFinishedWithZeroEvents;
544
545 enum weather_t { kWeatherBegin=0, kTemp = kWeatherBegin, kDew, kHum, kPress, kWind, kGusts, kDir, kWeatherEnd = kDir+1 };
546 deque<float> fMagicWeatherHist[kWeatherEnd];
547
548 deque<float> fTngWeatherDustHist;
549 Time fTngWeatherDustTime;
550
551 deque<float> fGtcDustHist;
552 Time fGtcDustTime;
553
554 deque<float> fRainSensorDataHist;
555 Time fRainSensorDataTime;
556
557 vector<float> fBiasControlVoltageVec;
558
559 float fBiasControlPowerTot;
560 float fBiasControlVoltageMed;
561 float fBiasControlCurrentMed;
562 float fBiasControlCurrentMax;
563
564 deque<float> fBiasControlCurrentHist;
565 deque<float> fFscControlTemperatureHist;
566
567 float fFscControlHumidityAvg;
568
569 deque<float> fPfMiniHumidityHist;
570 deque<float> fPfMiniTemperatureHist;
571
572 deque<float> fTemperatureControlHist;
573
574 float fDriveControlPointingZd;
575 string fDriveControlPointingAz;
576 string fDriveControlSourceName;
577 float fDriveControlMoonDist;
578
579 deque<float> fDriveControlTrackingDevHist;
580
581 int64_t fFadControlNumEvents;
582 int64_t fFadControlStartRun;
583 int32_t fFadControlDrsStep;
584 vector<uint32_t> fFadControlDrsRuns;
585
586 deque<float> fFtmControlTriggerRateHist;
587 int32_t fFtmControlTriggerRateTooLow;
588 int fFtmControlState;
589
590 float fFtmPatchThresholdMed;
591 float fFtmBoardThresholdMed;
592
593 bool fFtmControlFtuOk;
594
595 deque<float> fRateControlThreshold;
596
597 uint64_t fRateScanDataId;
598 uint8_t fRateScanBoard;
599 deque<float> fRateScanDataHist[41];
600
601 set<string> fErrorList;
602 EventHist fErrorHist;
603 EventHist fChatHist;
604
605 uint64_t fFreeSpace;
606
607 Sun fSun;
608 Moon fMoon;
609
610 // --------------------------- File header ----------------------------
611
612 Time fAudioTime;
613 string fAudioName;
614
615 string Header(const Time &d)
616 {
617 ostringstream msg;
618 msg << d.JavaDate() << '\t' << fAudioTime.JavaDate() << '\t' << fAudioName;
619 return msg.str();
620 }
621
622 string Header(const EventImp &d)
623 {
624 return Header(d.GetTime());
625 }
626
627 void SetAudio(const string &name)
628 {
629 fAudioName = name;
630 fAudioTime = Time();
631 }
632
633 // ------------- Initialize variables before the Dim stuff ------------
634
635 DimVersion fDimDNS;
636 DimControl fDimControl;
637 DimDescribedState fDimMcp;
638 DimDescribedState fDimDataLogger;
639 DimDescribedState fDimDriveControl;
640 DimDescribedState fDimTimeCheck;
641 DimDescribedState fDimMagicWeather;
642 DimDescribedState fDimMagicLidar;
643 DimDescribedState fDimTngWeather;
644 DimDescribedState fDimGtcDust;
645 DimDescribedState fDimTemperature;
646 DimDescribedState fDimRainSensor;
647 DimDescribedState fDimFeedback;
648 DimDescribedState fDimBiasControl;
649 DimDescribedState fDimFtmControl;
650 DimDescribedState fDimFadControl;
651 DimDescribedState fDimFscControl;
652 DimDescribedState fDimPfMiniControl;
653 DimDescribedState fDimBiasTemp;
654 DimDescribedState fDimGpsControl;
655 DimDescribedState fDimSqmControl;
656 DimDescribedState fDimAgilentControl24;
657 DimDescribedState fDimAgilentControl50;
658 DimDescribedState fDimAgilentControl80;
659 DimDescribedState fDimPwrControl;
660 DimDescribedState fDimLidControl;
661 DimDescribedState fDimRateControl;
662 DimDescribedState fDimRateScan;
663 DimDescribedState fDimChat;
664 DimDescribedState fDimSkypeClient;
665
666 // -------------------------------------------------------------------
667
668 string GetDir(const double angle)
669 {
670 static const char *dir[] =
671 {
672 "N", "NNE", "NE", "ENE",
673 "E", "ESE", "SE", "SSE",
674 "S", "SSW", "SW", "WSW",
675 "W", "WNW", "NW", "NNW"
676 };
677
678 const uint16_t idx = uint16_t(floor(angle/22.5+16.5))%16;
679 return dir[idx];
680 }
681
682 // -------------------------------------------------------------------
683
684 bool CheckDataSize(const EventImp &d, const char *name, size_t size, bool min=false)
685 {
686 if (d.GetSize()==0)
687 return false;
688
689 if ((!min && d.GetSize()==size) || (min && d.GetSize()>size))
690 return true;
691
692 ostringstream msg;
693 msg << name << " - Received service has " << d.GetSize() << " bytes, but expected ";
694 if (min)
695 msg << "more than ";
696 msg << size << ".";
697 Warn(msg);
698 return false;
699 }
700
701 // -------------------------------------------------------------------
702
703 template<class T>
704 void WriteBinaryVec(const Time &tm, const string &fname, const vector<T> &vec, double scale, double offset=0, const string &title="", const string &col="")
705 {
706 if (vec.empty())
707 return;
708
709 ostringstream out;
710 out << tm.JavaDate() << '\n';
711 out << offset << '\n';
712 out << offset+scale << '\n';
713 out << setprecision(3);
714 if (!title.empty())
715 out << title << '\x7f';
716 else
717 {
718 const Statistics stat(vec[0]);
719 out << stat.min << '\n';
720 out << stat.med << '\n';
721 out << stat.max << '\x7f';
722 }
723 if (!col.empty())
724 out << col;
725 for (auto it=vec.cbegin(); it!=vec.cend(); it++)
726 {
727 // The valid range is from 1 to 127
728 // \0 is used to seperate different curves
729 vector<uint8_t> val(it->size());
730 for (uint64_t i=0; i<it->size(); i++)
731 {
732 float range = nearbyint(126*(double(it->at(i))-offset)/scale); // [-2V; 2V]
733 if (range>126)
734 range=126;
735 if (range<0)
736 range=0;
737 val[i] = (uint8_t)range;
738 }
739
740 const char *ptr = reinterpret_cast<char*>(val.data());
741 out.write(ptr, val.size()*sizeof(uint8_t));
742 out << '\x7f';
743 }
744
745 ofstream(fPath+"/"+fname+".bin") << out.str();
746 }
747 /*
748 template<class T>
749 void WriteBinaryVec(const EventImp &d, const string &fname, const vector<T> &vec, double scale, double offset=0, const string &title="")
750 {
751 WriteBinaryVec(d.GetTime(), fname, vec, scale, offset, title);
752 }
753
754 template<class T>
755 void WriteBinary(const Time &tm, const string &fname, const T &t, double scale, double offset=0)
756 {
757 WriteBinaryVec(tm, fname, vector<T>(&t, &t+1), scale, offset);
758 }
759
760 template<class T>
761 void WriteBinary(const EventImp &d, const string &fname, const T &t, double scale, double offset=0)
762 {
763 WriteBinaryVec(d.GetTime(), fname, vector<T>(&t, &t+1), scale, offset);
764 }*/
765
766 template<class T>
767 void WriteHist(const EventImp &d, const string &fname, const T &t, double scale, double offset=0)
768 {
769 WriteBinaryVec(d.GetTime(), fname, vector<T>(&t, &t+1), scale, offset, "", "000");
770 }
771
772 template<class T>
773 void WriteCam(const EventImp &d, const string &fname, const T &t, double scale, double offset=0)
774 {
775 WriteBinaryVec(d.GetTime(), fname, vector<T>(&t, &t+1), scale, offset, "", "");
776 }
777
778
779 // -------------------------------------------------------------------
780
781 struct Statistics
782 {
783 float min;
784 float max;
785 float med;
786 float avg;
787 //float rms;
788
789 template<class T>
790 Statistics(const T &t, size_t offset_min=0, size_t offset_max=0)
791 : min(0), max(0), med(0), avg(0)
792 {
793 if (t.empty())
794 return;
795
796 T copy(t);
797 sort(copy.begin(), copy.end());
798
799 if (offset_min>t.size())
800 offset_min = 0;
801 if (offset_max>t.size())
802 offset_max = 0;
803
804 min = copy[offset_min];
805 max = copy[copy.size()-1-offset_max];
806 avg = accumulate (t.begin(), t.end(), 0.)/t.size();
807
808 const size_t p = copy.size()/2;
809 med = copy.size()%2 ? copy[p] : (copy[p-1]+copy[p])/2.;
810 }
811 };
812
813 void HandleControlMessageImp(const EventImp &d)
814 {
815 if (d.GetSize()==0)
816 return;
817
818 fControlMessageHist.add(d.GetText(), d.GetTime());
819
820 ostringstream out;
821 out << setprecision(3);
822 out << Header(d) << '\n';
823 out << HTML::kWhite << '\t';
824 out << "<->" << fControlMessageHist.get() << "</->";
825 out << '\n';
826
827 ofstream(fPath+"/scriptlog.data") << out.str();
828 }
829
830 int HandleDimControlMessage(const EventImp &d)
831 {
832 if (d.GetSize()==0)
833 return GetCurrentState();
834
835 if (d.GetQoS()==MessageImp::kAlarm)
836 {
837 if (d.GetSize()<2)
838 for (auto it=fControlAlarmHist.begin(); it!=fControlAlarmHist.end(); it++)
839 it->time = Time(Time::none);
840 else
841 fControlAlarmHist.add(d.GetText(), d.GetTime());
842 }
843
844 if (d.GetQoS()==MessageImp::kComment && d.GetSize()>1)
845 HandleControlMessageImp(d);
846
847 return GetCurrentState();
848 }
849
850 int HandleControlStateChange(const EventImp &d)
851 {
852 if (d.GetSize()==0)
853 return StateMachineImp::kSM_KeepState;
854
855 if (fDimControl.scriptdepth>0)
856 return StateMachineImp::kSM_KeepState;
857
858 if (d.GetQoS()>=2)
859 return StateMachineImp::kSM_KeepState;
860
861#if BOOST_VERSION < 104600
862 const string file = boost::filesystem::path(fDimControl.file).filename();
863#else
864 const string file = boost::filesystem::path(fDimControl.file).filename().string();
865#endif
866
867 // [0] DimControl::kIdle
868 // [1] DimControl::kLoading
869 // [2] DimControl::kCompiling
870 // [3] DimControl::kRunning
871 if (d.GetQoS()==1)
872 {
873 fControlMessageHist.clear();
874 HandleControlMessageImp(Event(d, "========================================", 41));
875 }
876
877 HandleControlMessageImp(Event(d, ("----- "+fDimControl.shortmsg+" -----").data(), fDimControl.shortmsg.length()+13));
878 if (!file.empty() && d.GetQoS()<2)
879 HandleControlMessageImp(Event(d, file.data(), file.length()+1));
880
881 // Note that this will also "ding" just after program startup
882 // if the dimctrl is still in state -3
883 if (d.GetQoS()==0)
884 {
885 HandleControlMessageImp(Event(d, "========================================", 41));
886 if (fDimControl.last.second!=DimState::kOffline)
887 SetAudio("ding");
888 }
889
890 return StateMachineImp::kSM_KeepState;
891 }
892
893 void AddMcpConfigurationHist(const EventImp &d, const string &msg)
894 {
895 fMcpConfigurationHist.add(msg, d.GetTime());
896
897 ostringstream out;
898 out << d.GetJavaDate() << '\n';
899 out << HTML::kWhite << '\t';
900 out << "<->" << fMcpConfigurationHist.rget() << "</->";
901 out << '\n';
902
903 ofstream(fPath+"/observations.data") << out.str();
904 }
905
906 int HandleFscControlStateChange(const EventImp &d)
907 {
908 const int32_t &last = fDimFscControl.last.second;
909 const int32_t &state = fDimFscControl.state();
910
911 if (last==DimState::kOffline || state==DimState::kOffline)
912 return StateMachineImp::kSM_KeepState;
913
914 if (last<FSC::State::kConnected && state==FSC::State::kConnected)
915 {
916 AddMcpConfigurationHist(d, "<B>FSC swiched on</B>");
917 //SetAudio("startup");
918 }
919
920 if (last==FSC::State::kConnected && state<FSC::State::kConnected)
921 {
922 AddMcpConfigurationHist(d, "<B>FSC swiched off</B>");
923 //SetAudio("shutdown");
924 }
925
926 return StateMachineImp::kSM_KeepState;
927 }
928
929 int HandleMcpConfiguration(const EventImp &d)
930 {
931 if (!CheckDataSize(d, "Mcp:Configuration", 16, true))
932 {
933 fMcpConfigurationState = DimState::kOffline;
934 fMcpConfigurationMaxTime = 0;
935 fMcpConfigurationMaxEvents = 0;
936 fMcpConfigurationName = "";
937 fMcpConfigurationRunStart = Time(Time::none);
938 return GetCurrentState();
939 }
940
941 // If a run ends...
942 if (fMcpConfigurationState==MCP::State::kTakingData && d.GetQoS()==MCP::State::kIdle)
943 {
944 // ...and no script is running just play a simple 'tick'
945 // ...and a script is running just play a simple 'tick'
946 if (/*fDimControl.state()<-2 &&*/ fDimControl.scriptdepth==0)
947 SetAudio("dong");
948 else
949 SetAudio("losticks");
950
951 fLastRunFinishedWithZeroEvents = fFadControlNumEvents==0;
952
953 ostringstream out;
954 out << "<#darkred>" << d.Ptr<char>(16);
955 if (!fDriveControlSourceName.empty())
956 out << " [" << fDriveControlSourceName << ']';
957 out << " (N=" << fFadControlNumEvents << ')';
958 out << "</#>";
959
960 AddMcpConfigurationHist(d, out.str());
961 }
962
963 if (d.GetQoS()==MCP::State::kTakingData)
964 {
965 fMcpConfigurationRunStart = Time();
966 SetAudio("losticks");
967
968 ostringstream out;
969 out << "<#darkgreen>" << fMcpConfigurationName;
970 if (!fDriveControlSourceName.empty())
971 out << " [" << fDriveControlSourceName << ']';
972 if (fFadControlStartRun>0)
973 out << " (Run " << fFadControlStartRun << ')';
974 out << "</#>";
975
976 AddMcpConfigurationHist(d, out.str());
977 }
978
979 fMcpConfigurationState = d.GetQoS();
980 fMcpConfigurationMaxTime = d.Get<uint64_t>();
981 fMcpConfigurationMaxEvents = d.Get<uint64_t>(8);
982 fMcpConfigurationName = d.Ptr<char>(16);
983
984 return GetCurrentState();
985 }
986
987 void WriteWeather(const EventImp &d, const string &name, int i, float min, float max)
988 {
989 const Statistics stat(fMagicWeatherHist[i]);
990
991 ostringstream out;
992 out << setprecision(3);
993 out << d.GetJavaDate() << '\n';
994
995 out << HTML::kWhite << '\t' << fMagicWeatherHist[i].back() << '\n';
996 out << HTML::kWhite << '\t' << stat.min << '\n';
997 out << HTML::kWhite << '\t' << stat.avg << '\n';
998 out << HTML::kWhite << '\t' << stat.max << '\n';
999
1000 ofstream(fPath+"/"+name+".data") << out.str();
1001
1002 WriteHist(d, "hist-magicweather-"+name, fMagicWeatherHist[i], max-min, min);
1003 }
1004
1005 int HandleMagicWeatherData(const EventImp &d)
1006 {
1007 if (!CheckDataSize(d, "MagicWeather:Data", 7*4+2))
1008 return GetCurrentState();
1009
1010 // Store a history of the last 300 entries
1011 for (int i=kWeatherBegin; i<kWeatherEnd; i++)
1012 {
1013 fMagicWeatherHist[i].push_back(d.Ptr<float>(2)[i]);
1014 if (fMagicWeatherHist[i].size()>300)
1015 fMagicWeatherHist[i].pop_front();
1016 }
1017
1018 ostringstream out;
1019 out << d.GetJavaDate() << '\n';
1020 if (fSun.time.IsValid() && fMoon.time.IsValid())
1021 {
1022 out << fSun.color << '\t' << fSun.description << '\n';
1023 out << setprecision(2);
1024 out << (fSun.isday?HTML::kWhite:fMoon.color) << '\t' << fMoon.description << '\n';
1025 }
1026 else
1027 out << "\n\n";
1028 out << setprecision(3);
1029 for (int i=0; i<6; i++)
1030 out << HTML::kWhite << '\t' << fMagicWeatherHist[i].back() << '\n';
1031 out << HTML::kWhite << '\t' << GetDir(fMagicWeatherHist[kDir].back()) << '\n';
1032 out << HTML::kWhite << '\t';
1033 if (!fTngWeatherDustHist.empty())
1034 out << fTngWeatherDustHist.back() << '\t' << fTngWeatherDustTime.GetAsStr("%H:%M") << '\n';
1035 else
1036 out << "\t\n";
1037 out << HTML::kWhite << '\t';
1038 if (!fGtcDustHist.empty())
1039 out << fGtcDustHist.back() << '\t' << fGtcDustTime.GetAsStr("%H:%M") << '\n';
1040 else
1041 out << "\t\n";
1042
1043 ofstream(fPath+"/weather.data") << out.str();
1044
1045 WriteWeather(d, "temp", kTemp, -5, 35);
1046 WriteWeather(d, "dew", kDew, -5, 35);
1047 WriteWeather(d, "hum", kHum, 0, 100);
1048 WriteWeather(d, "wind", kWind, 0, 100);
1049 WriteWeather(d, "gusts", kGusts, 0, 100);
1050 WriteWeather(d, "press", kPress, 700, 1000);
1051
1052 return GetCurrentState();
1053 }
1054
1055 int HandleTngWeatherDust(const EventImp &d)
1056 {
1057 if (!CheckDataSize(d, "TngWeather:Dust", 4))
1058 return GetCurrentState();
1059
1060 fTngWeatherDustTime = d.GetTime();
1061
1062 fTngWeatherDustHist.push_back(d.GetFloat());
1063 if (fTngWeatherDustHist.size()>300)
1064 fTngWeatherDustHist.pop_front();
1065
1066 const Statistics stat(fTngWeatherDustHist);
1067
1068 const double scale = stat.max>0 ? pow(10, ceil(log10(stat.max))) : 0;
1069
1070 WriteHist(d, "hist-tng-dust", fTngWeatherDustHist, scale);
1071
1072 ostringstream out;
1073 out << d.GetJavaDate() << '\n';
1074
1075 ofstream(fPath+"/tngdust.data") << out.str();
1076
1077 return GetCurrentState();
1078 }
1079
1080 int HandleTngWeatherData(const EventImp &d)
1081 {
1082 if (!CheckDataSize(d, "TngWeather:Data", sizeof(TNGWeather::DimWeather)))
1083 return GetCurrentState();
1084
1085 const auto &data = d.Ref<TNGWeather::DimWeather>();
1086
1087 ostringstream out;
1088 out << d.GetJavaDate() << '\n';
1089 out << setprecision(3);
1090 out << HTML::kWhite << '\t' << data.fTemperature << '\n';
1091 out << HTML::kWhite << '\t' << data.fTempTrend << '\n';
1092 out << HTML::kWhite << '\t' << data.fDewPoint << '\n';
1093 out << HTML::kWhite << '\t' << data.fHumidity << '\n';
1094 out << HTML::kWhite << '\t' << data.fAirPressure << '\n';
1095 out << HTML::kWhite << '\t' << data.fWindSpeed << '\n';
1096 out << HTML::kWhite << '\t' << data.fWindDirection << '\n';
1097 out << HTML::kWhite << '\t' << data.fDustTotal << '\n';
1098 out << HTML::kWhite << '\t' << data.fSolarimeter << '\n';
1099
1100 ofstream(fPath+"/tngdata.data") << out.str();
1101
1102 return GetCurrentState();
1103 }
1104
1105 int HandleGtcDustData(const EventImp &d)
1106 {
1107 if (!CheckDataSize(d, "GtcDust:Data", 4))
1108 return GetCurrentState();
1109
1110 fGtcDustTime = d.GetTime();
1111
1112 fGtcDustHist.push_back(d.GetFloat());
1113 if (fGtcDustHist.size()>300)
1114 fGtcDustHist.pop_front();
1115
1116 const Statistics stat(fGtcDustHist);
1117
1118 const double scale = stat.max>0 ? pow(10, ceil(log10(stat.max))) : 0;
1119
1120 WriteHist(d, "hist-gtc-dust", fGtcDustHist, scale);
1121
1122 ostringstream out;
1123 out << d.GetJavaDate() << '\n';
1124
1125 ofstream(fPath+"/gtcdust.data") << out.str();
1126
1127 return GetCurrentState();
1128 }
1129
1130
1131 int HandleRainSensorData(const EventImp &d)
1132 {
1133 if (!CheckDataSize(d, "RainSensor:Data", 12)) // F:1;X:1
1134 return GetCurrentState();
1135
1136 fRainSensorDataTime = d.GetTime();
1137
1138 fRainSensorDataHist.push_back(d.GetFloat());
1139 if (fRainSensorDataHist.size()>300)
1140 fRainSensorDataHist.pop_front();
1141
1142 const Statistics stat(fRainSensorDataHist);
1143
1144 const double scale = stat.max>0 ? pow(10, ceil(log10(stat.max))) : 0;
1145
1146 WriteHist(d, "hist-rain-sensor-data", fRainSensorDataHist, scale);
1147
1148 ostringstream out;
1149 out << d.GetJavaDate() << '\n';
1150
1151 ofstream(fPath+"/rainsensor.data") << out.str();
1152
1153 return GetCurrentState();
1154 }
1155
1156 int HandleDriveControlStateChange(const EventImp &d)
1157 {
1158 const int32_t &last = fDimFscControl.last.second;
1159 const int32_t &state = fDimFscControl.state();
1160
1161 if (last==DimState::kOffline || state==DimState::kOffline)
1162 return StateMachineImp::kSM_KeepState;
1163
1164 if (last<Drive::State::kInitialized && state>=Drive::State::kInitialized)
1165 AddMcpConfigurationHist(d, "Drive ready");
1166
1167 if (last>=Drive::State::kInitialized && state<Drive::State::kInitialized)
1168 AddMcpConfigurationHist(d, "Drive not ready");
1169
1170 return StateMachineImp::kSM_KeepState;
1171 }
1172
1173 int HandleDrivePointing(const EventImp &d)
1174 {
1175 if (!CheckDataSize(d, "DriveControl:Pointing", 16))
1176 return GetCurrentState();
1177
1178 fDriveControlPointingZd = d.Get<double>();
1179
1180 const double az = d.Get<double>(8);
1181
1182 fDriveControlPointingAz = GetDir(az);
1183
1184 ostringstream out;
1185 out << d.GetJavaDate() << '\n';
1186
1187 out << setprecision(0) << fixed;
1188 out << HTML::kWhite << '\t' << az << '\t' << fDriveControlPointingAz << '\n';
1189 out << HTML::kWhite << '\t' << fDriveControlPointingZd << '\n';
1190
1191 ofstream(fPath+"/pointing.data") << out.str();
1192
1193 return GetCurrentState();
1194 }
1195
1196 int HandleDriveTracking(const EventImp &d)
1197 {
1198 if (!CheckDataSize(d, "DriveControl:Tracking", 96))
1199 return GetCurrentState();
1200
1201
1202
1203 const double Ra = d.Get<double>(0*8);
1204 const double Dec = d.Get<double>(1*8);
1205 const double Zd = d.Get<double>(6*8);
1206 const double Az = d.Get<double>(7*8);
1207
1208 const double dev = d.Get<double>(11*8);
1209
1210 fDriveControlTrackingDevHist.push_back(dev);
1211 if (fDriveControlTrackingDevHist.size()>300)
1212 fDriveControlTrackingDevHist.pop_front();
1213
1214 WriteHist(d, "hist-control-deviation", fDriveControlTrackingDevHist, 120);
1215
1216 ostringstream out;
1217 out << d.GetJavaDate() << '\n';
1218
1219 out << HTML::kWhite << '\t' << fDriveControlSourceName << '\n';
1220 out << setprecision(5);
1221 out << HTML::kWhite << '\t' << Ra << '\n';
1222 out << HTML::kWhite << '\t' << Dec << '\n';
1223 out << setprecision(3);
1224 out << HTML::kWhite << '\t' << Zd << '\n';
1225 out << HTML::kWhite << '\t' << Az << '\n';
1226 out << HTML::kWhite << '\t' << dev << '\n';
1227
1228 fDriveControlMoonDist = -1;
1229
1230 if (fMoon.visible)
1231 {
1232 const double angle = fMoon.Angle(Ra, Dec);
1233 out << Moon::Color(angle) << '\t' << setprecision(3) << angle << '\n';
1234
1235 fDriveControlMoonDist = angle;
1236 }
1237 else
1238 out << HTML::kWhite << "\t&mdash; \n";
1239
1240 ofstream(fPath+"/tracking.data") << out.str();
1241
1242 return GetCurrentState();
1243 }
1244
1245 int HandleDriveSource(const EventImp &d)
1246 {
1247 if (!CheckDataSize(d, "DriveControl:Source", 5*8+31))
1248 return GetCurrentState();
1249
1250 const double *ptr = d.Ptr<double>();
1251
1252 const double ra = ptr[0]; // Ra[h]
1253 const double dec = ptr[1]; // Dec[deg]
1254 const double woff = ptr[2]; // Wobble offset [deg]
1255 const double wang = ptr[3]; // Wobble angle [deg]
1256 const double period = ptr[4]; // Wobble angle [deg]
1257
1258 fDriveControlSourceName = d.Ptr<char>(5*8);
1259
1260 ostringstream out;
1261 out << d.GetJavaDate() << '\n';
1262
1263 out << HTML::kWhite << '\t' << fDriveControlSourceName << '\n';
1264 out << setprecision(5);
1265 out << HTML::kWhite << '\t' << ra << '\n';
1266 out << HTML::kWhite << '\t' << dec << '\n';
1267 out << setprecision(3);
1268 out << HTML::kWhite << '\t' << woff << '\n';
1269 out << HTML::kWhite << '\t' << wang << '\n';
1270 out << HTML::kWhite << '\t' << period << '\n';
1271
1272 ofstream(fPath+"/source.data") << out.str();
1273
1274 return GetCurrentState();
1275 }
1276
1277 int HandleFeedbackCalibratedCurrents(const EventImp &d)
1278 {
1279 if (!CheckDataSize(d, "Feedback:CalibratedCurrents", (416+1+1+1+1+1+416+1+1)*sizeof(float)+sizeof(uint32_t)))
1280 return GetCurrentState();
1281
1282 const float *ptr = d.Ptr<float>();
1283
1284 double power_tot = 0;
1285 double power_apd = 0;
1286
1287 if (fBiasControlVoltageVec.size()>0)
1288 {
1289 // Calibrate the data (subtract offset)
1290 for (int i=0; i<320; i++)
1291 {
1292 // Exclude crazy pixels
1293 if (i==66 || i==191 || i==193)
1294 continue;
1295
1296 // Group index (0 or 1) of the of the pixel (4 or 5 pixel patch)
1297 const int N = fPixelMap.hv(i).count();
1298
1299 // Serial resistor of the individual G-APDs
1300 double R5 = 3900/N;
1301
1302 // This is also valid for the patches with wrong resistors,
1303 // because Iapd is a factor f larger but R a factor f smaller
1304 double Iapd = ptr[i] * 1e-6; // [A]
1305 double Iout = Iapd*N; // [A]
1306
1307 double UdrpCam = 1000 *Iout; // Voltage seen by everything in Camera
1308 double UdrpApd = (R5+2000)*Iout; // Voltage seen by G-APD
1309
1310 const double pwrCam = Iout * (fBiasControlVoltageVec[i]-UdrpCam);
1311 const double pwrApd = Iout * (fBiasControlVoltageVec[i]-UdrpApd);
1312
1313 // Total power participated in the camera at the G-APD
1314 // and the serial resistors (total voltage minus voltage
1315 // drop at resistors in bias crate)
1316 power_tot += pwrCam;
1317
1318 // Power consumption per G-APD
1319 power_apd += pwrApd;
1320 }
1321 }
1322
1323 // Divide by number of summed channels, convert to mW
1324 power_apd /= (320-3)*1e-3; // [mW]
1325
1326 if (power_tot<1e-3)
1327 power_tot = 0;
1328 if (power_apd<1e-3)
1329 power_apd = 0;
1330
1331 fBiasControlPowerTot = power_tot;
1332
1333 // --------------------------------------------------------
1334
1335 // Get the maximum of each patch
1336 vector<float> val(320, 0);
1337 for (int i=0; i<320; i++)
1338 {
1339 const int idx = (fPixelMap.hv(i).hw()/9)*2+fPixelMap.hv(i).group();
1340 val[idx] = ptr[i];
1341 }
1342
1343 // Write the 160 patch values to a file
1344 WriteCam(d, "cam-biascontrol-current", val, 100);
1345
1346 // --------------------------------------------------------
1347
1348 // After being displayed, exclude the patches with
1349 // the crazy pixels from the statsitics
1350
1351 vector<float> cpy(ptr, ptr+320);
1352 cpy[66] = 0;
1353 cpy[191] = 0;
1354 cpy[193] = 0;
1355 const Statistics stat(cpy);
1356
1357 // Exclude the three crazy channels
1358 fBiasControlCurrentMed = stat.med;
1359 fBiasControlCurrentMax = stat.max;
1360
1361 // Store a history of the last 60 entries
1362 fBiasControlCurrentHist.push_back(fBiasControlCurrentMed);
1363 if (fBiasControlCurrentHist.size()>360)
1364 fBiasControlCurrentHist.pop_front();
1365
1366 // write the history to a file
1367 WriteHist(d, "hist-biascontrol-current", fBiasControlCurrentHist, 125);
1368
1369 // --------------------------------------------------------
1370
1371 string col1 = HTML::kGreen;
1372 string col2 = HTML::kGreen;
1373 string col3 = HTML::kGreen;
1374 string col4 = HTML::kGreen;
1375
1376 if (stat.min>90)
1377 col1 = HTML::kYellow;
1378 if (stat.min>110)
1379 col1 = HTML::kRed;
1380
1381 if (stat.med>90)
1382 col2 = HTML::kYellow;
1383 if (stat.med>110)
1384 col2 = HTML::kRed;
1385
1386 if (stat.avg>90)
1387 col3 = HTML::kYellow;
1388 if (stat.avg>110)
1389 col3 = HTML::kRed;
1390
1391 if (stat.max>90)
1392 col4 = HTML::kYellow;
1393 if (stat.max>110)
1394 col4 = HTML::kRed;
1395
1396 ostringstream out;
1397 out << setprecision(3);
1398 out << d.GetJavaDate() << '\n';
1399 out << HTML::kGreen << '\t' << "yes" << '\n';
1400 out << col1 << '\t' << stat.min << '\n';
1401 out << col2 << '\t' << stat.med << '\n';
1402 out << col3 << '\t' << stat.avg << '\n';
1403 out << col4 << '\t' << stat.max << '\n';
1404 out << HTML::kWhite << '\t' << power_tot << "W [" << power_apd << "mW]\n";
1405 ofstream(fPath+"/current.data") << out.str();
1406
1407 // --------------------------------------------------------
1408
1409 const float Unom = ptr[2*416+6];
1410 const float Utmp = ptr[2*416+7];
1411
1412 vector<float> Uov(ptr+416+6, ptr+416+6+320);
1413
1414 WriteCam(d, "cam-feedback-overvoltage", Uov, 0.2, -0.1);
1415
1416 const Statistics stat2(Uov);
1417
1418 out.str("");
1419 out << d.GetJavaDate() << '\n';
1420 out << setprecision(3);
1421 out << HTML::kWhite << '\t' << Utmp << '\n';
1422 out << HTML::kWhite << '\t' << Unom << '\n';
1423 out << HTML::kWhite << '\t' << stat2.min << '\n';
1424 out << HTML::kWhite << '\t' << stat2.med << '\n';
1425 out << HTML::kWhite << '\t' << stat2.avg << '\n';
1426 out << HTML::kWhite << '\t' << stat2.max << '\n';
1427 ofstream(fPath+"/feedback.data") << out.str();
1428
1429 return GetCurrentState();
1430 }
1431
1432 int HandleBiasCurrent(const EventImp &d)
1433 {
1434 if (fDimFeedback.state()>=Feedback::State::kCalibrated)
1435 return GetCurrentState();
1436
1437 if (!CheckDataSize(d, "BiasControl:Current", 832))
1438 return GetCurrentState();
1439
1440 // Convert dac counts to uA
1441 vector<float> v(320);
1442 for (int i=0; i<320; i++)
1443 v[i] = d.Ptr<uint16_t>()[i] * 5000./4096;
1444
1445 fBiasControlPowerTot = 0;
1446
1447 // Get the maximum of each patch
1448 vector<float> val(320, 0);
1449 for (int i=0; i<320; i++)
1450 {
1451 const PixelMapEntry &hv = fPixelMap.hv(i);
1452 if (!hv)
1453 continue;
1454
1455 const int idx = (hv.hw()/9)*2+hv.group();
1456 val[idx] = v[i];
1457 }
1458
1459 // Write the 160 patch values to a file
1460 WriteCam(d, "cam-biascontrol-current", val, 1000);
1461
1462 const Statistics stat(v, 0, 3);
1463
1464 // Exclude the three crazy channels
1465 fBiasControlCurrentMed = stat.med;
1466 fBiasControlCurrentMax = stat.max;
1467
1468 // Store a history of the last 60 entries
1469 fBiasControlCurrentHist.push_back(fBiasControlCurrentMed);
1470 if (fBiasControlCurrentHist.size()>360)
1471 fBiasControlCurrentHist.pop_front();
1472
1473 // write the history to a file
1474 WriteHist(d, "hist-biascontrol-current", fBiasControlCurrentHist, 1000);
1475
1476 ostringstream out;
1477 out << setprecision(3);
1478 out << d.GetJavaDate() << '\n';
1479 out << HTML::kWhite<< '\t' << "no" << '\n';
1480 out << HTML::kWhite << '\t' << stat.min << '\n';
1481 out << HTML::kWhite << '\t' << stat.med << '\n';
1482 out << HTML::kWhite << '\t' << stat.avg << '\n';
1483 out << HTML::kWhite << '\t' << stat.max << '\n';
1484 out << HTML::kWhite << '\t' << "---\n";
1485 ofstream(fPath+"/current.data") << out.str();
1486
1487 return GetCurrentState();
1488 }
1489
1490 int HandleBiasVoltage(const EventImp &d)
1491 {
1492 if (!CheckDataSize(d, "BiasControl:Voltage", 1664))
1493 {
1494 fBiasControlVoltageVec.clear();
1495 return GetCurrentState();
1496 }
1497
1498 fBiasControlVoltageVec.assign(d.Ptr<float>(), d.Ptr<float>()+320);
1499
1500 const Statistics stat(fBiasControlVoltageVec);
1501
1502 fBiasControlVoltageMed = stat.med;
1503
1504 vector<float> val(320, 0);
1505 for (int i=0; i<320; i++)
1506 {
1507 const int idx = (fPixelMap.hv(i).hw()/9)*2+fPixelMap.hv(i).group();
1508 val[idx] = fBiasControlVoltageVec[i];
1509 }
1510
1511 if (fDimBiasControl.state()==BIAS::State::kVoltageOn || fDimBiasControl.state()==BIAS::State::kRamping)
1512 WriteCam(d, "cam-biascontrol-voltage", val, 10, 65);
1513 else
1514 WriteCam(d, "cam-biascontrol-voltage", val, 75);
1515
1516 ostringstream out;
1517 out << setprecision(3);
1518 out << d.GetJavaDate() << '\n';
1519 out << HTML::kWhite << '\t' << stat.min << '\n';
1520 out << HTML::kWhite << '\t' << stat.med << '\n';
1521 out << HTML::kWhite << '\t' << stat.avg << '\n';
1522 out << HTML::kWhite << '\t' << stat.max << '\n';
1523 ofstream(fPath+"/voltage.data") << out.str();
1524
1525 return GetCurrentState();
1526 }
1527
1528 int HandleFadEvents(const EventImp &d)
1529 {
1530 if (!CheckDataSize(d, "FadControl:Events", 4*4))
1531 {
1532 fFadControlNumEvents = -1;
1533 return GetCurrentState();
1534 }
1535
1536 fFadControlNumEvents = d.Get<uint32_t>();
1537
1538 return GetCurrentState();
1539 }
1540
1541 int HandleFadStartRun(const EventImp &d)
1542 {
1543 if (!CheckDataSize(d, "FadControl:StartRun", 16))
1544 {
1545 fFadControlStartRun = -1;
1546 return GetCurrentState();
1547 }
1548
1549 fFadControlStartRun = d.Get<int64_t>();
1550
1551 return GetCurrentState();
1552 }
1553
1554 int HandleFadDrsRuns(const EventImp &d)
1555 {
1556 if (!CheckDataSize(d, "FadControl:DrsRuns", 5*4))
1557 {
1558 fFadControlDrsStep = -1;
1559 return GetCurrentState();
1560 }
1561
1562 const uint32_t *ptr = d.Ptr<uint32_t>();
1563 fFadControlDrsStep = ptr[0];
1564 fFadControlDrsRuns[0] = ptr[1];
1565 fFadControlDrsRuns[1] = ptr[2];
1566 fFadControlDrsRuns[2] = ptr[3];
1567
1568 return GetCurrentState();
1569 }
1570
1571 int HandleFadConnections(const EventImp &d)
1572 {
1573 if (!CheckDataSize(d, "FadControl:Connections", 41))
1574 {
1575 //fStatusEventBuilderLabel->setText("Offline");
1576 return GetCurrentState();
1577 }
1578
1579 string rc(40, '-'); // orange/red [45]
1580
1581 const uint8_t *ptr = d.Ptr<uint8_t>();
1582
1583 int c[4] = { '.', '.', '.', '.' };
1584
1585 for (int i=0; i<40; i++)
1586 {
1587 const uint8_t stat1 = ptr[i]&3;
1588 const uint8_t stat2 = ptr[i]>>3;
1589
1590 if (stat1==0 && stat2==0)
1591 rc[i] = '.'; // gray [46]
1592 else
1593 if (stat1>=2 && stat2==8)
1594 rc[i] = stat1==2?'+':'*'; // green [43] : check [42]
1595
1596 if (rc[i]<c[i/10])
1597 c[i/10] = rc[i];
1598 }
1599
1600 string col[4];
1601 for (int i=0; i<4; i++)
1602 switch (c[i])
1603 {
1604 case '.': col[i]=HTML::kWhite; break;
1605 case '-': col[i]=HTML::kRed; break;
1606 case '+': col[i]=HTML::kYellow; break;
1607 case '*': col[i]=HTML::kGreen; break;
1608 }
1609
1610 ostringstream out;
1611 out << setprecision(3);
1612 out << d.GetJavaDate() << '\n';
1613 out << col[0] << '\t' << rc.substr( 0, 10) << '\n';
1614 out << col[1] << '\t' << rc.substr(10, 10) << '\n';
1615 out << col[2] << '\t' << rc.substr(20, 10) << '\n';
1616 out << col[3] << '\t' << rc.substr(30, 10) << '\n';
1617 ofstream(fPath+"/fad.data") << out.str();
1618
1619 return GetCurrentState();
1620 }
1621
1622 /*
1623 int HandleFtmControlStateChange()
1624 {
1625 const int32_t &last = fDimFtmControl.last.second;
1626 const int32_t &state = fDimFtmControl.state();
1627
1628 // If a new run has been started ensure that the counter
1629 // is reset. The reset in HandleFtmTriggerRates might
1630 // arrive only after the run was started.
1631 if (last!=FTM::State::kTriggerOn && state==MCP::State::kTriggerOn)
1632 fFtmControlTriggerRateTooLow = -1;
1633
1634 return StateMachineImp::kSM_KeepState;
1635 }*/
1636
1637
1638 int HandleFtmTriggerRates(const EventImp &d)
1639 {
1640 if (!CheckDataSize(d, "FtmControl:TriggerRates", 24+160+640+8))
1641 {
1642 fFtmControlTriggerRateTooLow = 0;
1643 return GetCurrentState();
1644 }
1645
1646 const FTM::DimTriggerRates &dim = d.Ref<FTM::DimTriggerRates>();
1647
1648 // If the trigger rate is too low...
1649 // ... and the run was not just started (can lead to very small elapsed times)
1650 // ... and the trigger is switched on
1651 // ... and there was no state change (then the trigger was started or stopped)
1652 fFtmControlTriggerRateTooLow =
1653 dim.fTriggerRate<1 && dim.fElapsedTime>0.45 &&
1654 (fFtmControlState&FTM::kFtmStates)==FTM::kFtmRunning &&
1655 (fFtmControlState&FTM::kFtmStates)==(d.GetQoS()&FTM::kFtmStates);
1656
1657 fFtmControlState = d.GetQoS();
1658
1659 const float *brates = dim.fBoardRate; // Board rate
1660 const float *prates = dim.fPatchRate; // Patch rate
1661
1662 // Store a history of the last 60 entries
1663 fFtmControlTriggerRateHist.push_back(dim.fTriggerRate);
1664 if (fFtmControlTriggerRateHist.size()>300)
1665 fFtmControlTriggerRateHist.pop_front();
1666
1667 // FIXME: Add statistics for all kind of rates
1668
1669 WriteHist(d, "hist-ftmcontrol-triggerrate",
1670 fFtmControlTriggerRateHist, 100);
1671 WriteCam(d, "cam-ftmcontrol-boardrates",
1672 vector<float>(brates, brates+40), 10);
1673 WriteCam(d, "cam-ftmcontrol-patchrates",
1674 vector<float>(prates, prates+160), 10);
1675
1676 ostringstream out;
1677 out << setprecision(3);
1678 out << d.GetJavaDate() << '\n';
1679 out << HTML::kWhite << '\t' << dim.fTriggerRate << '\n';
1680
1681 ofstream(fPath+"/trigger.data") << out.str();
1682
1683 const Statistics bstat(vector<float>(brates, brates+ 40));
1684 const Statistics pstat(vector<float>(prates, prates+160));
1685
1686 out.str("");
1687 out << d.GetJavaDate() << '\n';
1688 out << HTML::kWhite << '\t' << bstat.min << '\n';
1689 out << HTML::kWhite << '\t' << bstat.med << '\n';
1690 out << HTML::kWhite << '\t' << bstat.avg << '\n';
1691 out << HTML::kWhite << '\t' << bstat.max << '\n';
1692 ofstream(fPath+"/boardrates.data") << out.str();
1693
1694 out.str("");
1695 out << d.GetJavaDate() << '\n';
1696 out << HTML::kWhite << '\t' << pstat.min << '\n';
1697 out << HTML::kWhite << '\t' << pstat.med << '\n';
1698 out << HTML::kWhite << '\t' << pstat.avg << '\n';
1699 out << HTML::kWhite << '\t' << pstat.max << '\n';
1700 ofstream(fPath+"/patchrates.data") << out.str();
1701
1702 return GetCurrentState();
1703 }
1704
1705 int HandleFtmStaticData(const EventImp &d)
1706 {
1707 if (!CheckDataSize(d, "FtmControl:StaticData", sizeof(FTM::DimStaticData)))
1708 return GetCurrentState();
1709
1710 // If the FTM is in state Configuring, the clock conditioner
1711 // is always reported to be unlocked
1712 fFtmControlState = d.GetQoS();
1713
1714 const FTM::DimStaticData &dat = d.Ref<FTM::DimStaticData>();
1715
1716 vector<uint16_t> vecp(dat.fThreshold, dat.fThreshold+160);
1717 vector<uint16_t> vecb(dat.fMultiplicity, dat.fMultiplicity+40);
1718
1719 WriteCam(d, "cam-ftmcontrol-thresholds-patch", vecp, 1000);
1720 WriteCam(d, "cam-ftmcontrol-thresholds-board", vecb, 100);
1721
1722 const Statistics statp(vecp);
1723 const Statistics statb(vecb);
1724
1725 fFtmPatchThresholdMed = statp.med;
1726 fFtmBoardThresholdMed = statb.med;
1727
1728 ostringstream out;
1729 out << d.GetJavaDate() << '\n';
1730 out << HTML::kWhite << '\t' << statb.min << '\n';
1731 out << HTML::kWhite << '\t' << statb.med << '\n';
1732 out << HTML::kWhite << '\t' << statb.max << '\n';
1733 ofstream(fPath+"/thresholds-board.data") << out.str();
1734
1735 out.str("");
1736 out << d.GetJavaDate() << '\n';
1737 out << HTML::kWhite << '\t' << statp.min << '\n';
1738 out << HTML::kWhite << '\t' << statp.med << '\n';
1739 out << HTML::kWhite << '\t' << statp.max << '\n';
1740 ofstream(fPath+"/thresholds-patch.data") << out.str();
1741
1742 out.str("");
1743 out << d.GetJavaDate() << '\n';
1744 out << HTML::kWhite << '\t' << statb.med << '\n';
1745 out << HTML::kWhite << '\t' << statp.med << '\n';
1746 ofstream(fPath+"/thresholds.data") << out.str();
1747
1748 out.str("");
1749 out << d.GetJavaDate() << '\n';
1750 out << HTML::kWhite << '\t' << dat.fTriggerInterval << '\n';
1751 out << HTML::kWhite << '\t';
1752 if (dat.HasPedestal())
1753 out << dat.fTriggerSeqPed;
1754 else
1755 out << "&ndash;";
1756 out << ':';
1757 if (dat.HasLPext())
1758 out << dat.fTriggerSeqLPext;
1759 else
1760 out << "&ndash;";
1761 out << ':';
1762 if (dat.HasLPint())
1763 out << dat.fTriggerSeqLPint;
1764 else
1765 out << "&ndash;";
1766 out << '\n';
1767
1768 out << HTML::kWhite << '\t' << (dat.HasTrigger()?"on":"off") << " / " << (dat.HasExt1()?"on":"off") << " / " << (dat.HasExt2()?"on":"off") << '\n';
1769 out << HTML::kWhite << '\t' << (dat.HasVeto()?"on":"off") << " / " << (dat.HasClockConditioner()?"time cal":"marker") << '\n';
1770 out << HTML::kWhite << '\t' << dat.fMultiplicityPhysics << " / " << dat.fMultiplicityCalib << '\n';
1771 out << HTML::kWhite << '\t' << dat.fWindowPhysics << '\t' << dat.fWindowCalib << '\n';
1772 out << HTML::kWhite << '\t' << dat.fDelayTrigger << '\t' << dat.fDelayTimeMarker << '\n';
1773 out << HTML::kWhite << '\t' << dat.fDeadTime << '\n';
1774
1775 int64_t vp = dat.fPrescaling[0];
1776 for (int i=1; i<40; i++)
1777 if (vp!=dat.fPrescaling[i])
1778 vp = -1;
1779
1780 if (vp<0)
1781 out << HTML::kYellow << "\tdifferent\n";
1782 else
1783 out << HTML::kWhite << '\t' << 0.5*vp << "\n";
1784
1785 ofstream(fPath+"/ftm.data") << out.str();
1786
1787 // Active FTUs: IsActive(i)
1788 // Enabled Pix: IsEnabled(i)
1789
1790 return GetCurrentState();
1791 }
1792
1793 int HandleFtmFtuList(const EventImp &d)
1794 {
1795 if (!CheckDataSize(d, "FtmControl:FtuList", sizeof(FTM::DimFtuList)))
1796 return GetCurrentState();
1797
1798 const FTM::DimFtuList &sdata = d.Ref<FTM::DimFtuList>();
1799
1800 ostringstream out;
1801 out << d.GetJavaDate() << '\n';
1802
1803 int cnt = 0;
1804 for (int i=0; i<4; i++)
1805 {
1806 out << HTML::kWhite << '\t';
1807 for (int j=0; j<10; j++)
1808 if (sdata.IsActive(i*10+j))
1809 {
1810 if (sdata.fPing[i*10+j]==1)
1811 {
1812 out << '*';
1813 cnt++;
1814 }
1815 else
1816 out << sdata.fPing[i*10+j];
1817 }
1818 else
1819 out << '-';
1820 out << '\n';
1821 }
1822
1823 fFtmControlFtuOk = cnt==40;
1824
1825 ofstream(fPath+"/ftu.data") << out.str();
1826
1827 return GetCurrentState();
1828 }
1829
1830 int HandleFadEventData(const EventImp &d)
1831 {
1832 if (!CheckDataSize(d, "FadControl:EventData", 23048))
1833 return GetCurrentState();
1834
1835 //const uint32_t run = d.GetUint();
1836 //const uint32_t evt = d.GetUint(4);
1837
1838 const float *dat = d.Ptr<float>(8+1440*sizeof(float)*2);
1839
1840 /*
1841 vector<float> max(320, 0);
1842 for (int i=0; i<1440; i++)
1843 {
1844 if (i%9==8)
1845 continue;
1846
1847 const int idx = (fPixelMap.hw(i).hw()/9)*2+fPixelMap.hw(i).group();
1848 const double v = dat[i]/1000;
1849 //if (v>max[idx])
1850 // max[idx]=v;
1851
1852 max[idx] += v/4;
1853 } */
1854
1855 vector<float> val(1440);
1856 for (int i=0; i<1440; i++)
1857 val[i] = dat[i]/1000;
1858
1859 vector<float> sorted(val);
1860 nth_element(sorted.begin(), sorted.begin()+3, sorted.end(),
1861 std::greater<float>());
1862
1863 const uint32_t trig = d.GetQoS() & FAD::EventHeader::kLPext;
1864
1865 const float min = fFadControlDrsRuns[0]==0 ? -1 : 0;
1866
1867 float scale = 2;
1868 if (trig&FAD::EventHeader::kLPext)
1869 scale = 1;
1870 if (trig&FAD::EventHeader::kPedestal)
1871 scale = 0.25;
1872 if (trig==0)
1873 scale = max(0.25f, sorted[3]);
1874
1875 // assume it is drs-gain
1876 //if ((trig&FAD::EventHeader::kPedestal) && fFadControlDrsRuns[0]>0 && fFadControlDrsRuns[1]==0)
1877 // min = 0.75;
1878
1879 WriteCam(d, "cam-fadcontrol-eventdata", val, scale, min);
1880
1881 return GetCurrentState();
1882 }
1883
1884 int HandleStats(const EventImp &d)
1885 {
1886 if (!CheckDataSize(d, "Stats", 4*8))
1887 {
1888 fFreeSpace = UINT64_MAX;
1889 return GetCurrentState();
1890 }
1891
1892 const DimWriteStatistics::Stats &s = d.Ref<DimWriteStatistics::Stats>();
1893 fFreeSpace = s.freeSpace;
1894
1895 return GetCurrentState();
1896 }
1897
1898 int HandleFscTemperature(const EventImp &d)
1899 {
1900 if (!CheckDataSize(d, "FscControl:Temperature", 240))
1901 return GetCurrentState();
1902
1903 const float *ptr = d.Ptr<float>(4);
1904
1905 double avg = 0;
1906 double rms = 0;
1907 double min = 99;
1908 double max = -99;
1909
1910 int num = 0;
1911 for (const float *t=ptr; t<ptr+31; t++)
1912 {
1913 if (*t==0)
1914 continue;
1915
1916 if (*t>max)
1917 max = *t;
1918
1919 if (*t<min)
1920 min = *t;
1921
1922 avg += *t;
1923 rms += *t * *t;
1924
1925 num++;
1926 }
1927
1928 avg /= num;
1929 rms /= num;
1930 rms += avg*avg;
1931 rms = rms<0 ? 0 : sqrt(rms);
1932
1933 // Clean broken reports
1934 static double pre_rms1 = 1.5;
1935 static double pre_rms2 = 0;
1936
1937 const double cut = pre_rms1 + 0.1;
1938
1939 const bool reject = rms>cut && pre_rms2<cut;
1940
1941 pre_rms2 = pre_rms1;
1942 pre_rms1 = rms;
1943
1944 if (reject)
1945 return GetCurrentState();
1946
1947
1948 if (!fMagicWeatherHist[kTemp].empty())
1949 {
1950 fFscControlTemperatureHist.push_back(avg-fMagicWeatherHist[kTemp].back());
1951 if (fFscControlTemperatureHist.size()>300)
1952 fFscControlTemperatureHist.pop_front();
1953 }
1954
1955 const Statistics stat(fFscControlTemperatureHist);
1956
1957 ostringstream out;
1958 out << setprecision(3);
1959 out << d.GetJavaDate() << '\n';
1960 out << HTML::kWhite << '\t' << fFscControlHumidityAvg << '\n';
1961 out << HTML::kWhite << '\t' << stat.min << '\n';
1962 out << HTML::kWhite << '\t' << stat.avg << '\n';
1963 out << HTML::kWhite << '\t' << stat.max << '\n';
1964
1965 ofstream(fPath+"/fsc.data") << out.str();
1966
1967 WriteHist(d, "hist-fsccontrol-temperature",
1968 fFscControlTemperatureHist, 10);
1969
1970 out.str("");
1971 out << setprecision(3);
1972 out << d.GetJavaDate() << '\n';
1973 out << HTML::kWhite << '\t' << max << '\n';
1974 out << HTML::kWhite << '\t' << avg << '\n';
1975 out << HTML::kWhite << '\t' << min << '\n';
1976
1977 ofstream(fPath+"/camtemp.data") << out.str();
1978
1979 return GetCurrentState();
1980 }
1981
1982 int HandleFscBiasTemp(const EventImp &d)
1983 {
1984 if (!CheckDataSize(d, "FscControl:BiasTemp", 323*4))
1985 return GetCurrentState();
1986
1987 const float *ptr = d.Ptr<float>(4);
1988 const float avg = d.Get<float>(321*4);
1989 //const float rms = d.Get<float>(322*4);
1990
1991 vector<double> tout(320);
1992 for (int i=0; i<320; i++)
1993 {
1994 const int idx = (fPixelMap.hv(i).hw()/9)*2+fPixelMap.hv(i).group();
1995 tout[idx] = ptr[i];
1996 }
1997
1998 WriteCam(d, "cam-fsccontrol-temperature", tout, 3, avg-1.75);
1999
2000 return GetCurrentState();
2001 }
2002
2003 int HandleFscHumidity(const EventImp &d)
2004 {
2005 if (!CheckDataSize(d, "FscControl:Humidity", 5*4))
2006 return GetCurrentState();
2007
2008 const float *ptr = d.Ptr<float>(4);
2009
2010 double avg =0;
2011 int num = 0;
2012
2013 for (const float *t=ptr; t<ptr+4; t++)
2014 if (*t>0 && *t<=100 && t!=ptr+2 /*excl broken sensor*/)
2015 {
2016 avg += *t;
2017 num++;
2018 }
2019
2020 fFscControlHumidityAvg = num>0 ? avg/num : 0;
2021
2022 return GetCurrentState();
2023 }
2024
2025 int HandlePfMiniData(const EventImp &d)
2026 {
2027 if (!CheckDataSize(d, "PfMini:Data", sizeof(PFmini::Data)))
2028 return GetCurrentState();
2029
2030 const PFmini::Data &data = d.Ref<PFmini::Data>();
2031
2032 ostringstream out;
2033
2034 out << fixed << setprecision(1);
2035 out << d.GetJavaDate() << '\n';
2036
2037 out << HTML::kGreen << '\t' << data.temp << '\n';
2038 out << HTML::kGreen << '\t' << data.hum << '\n';
2039
2040 ofstream(fPath+"/pfmini.data") << out.str();
2041
2042 fPfMiniTemperatureHist.push_back(data.temp);
2043 if (fPfMiniTemperatureHist.size()>60*4) // 1h
2044 fPfMiniTemperatureHist.pop_front();
2045
2046 fPfMiniHumidityHist.push_back(data.hum);
2047 if (fPfMiniHumidityHist.size()>60*4) // 1h
2048 fPfMiniHumidityHist.pop_front();
2049
2050 WriteHist(d, "hist-pfmini-temp",
2051 fPfMiniTemperatureHist, 45, 0);
2052
2053 WriteHist(d, "hist-pfmini-hum",
2054 fPfMiniHumidityHist, 100, 0);
2055
2056 return GetCurrentState();
2057 }
2058
2059 int HandleBiasTemp(const EventImp &d)
2060 {
2061 if (!CheckDataSize(d, "BiasTemp:Data", sizeof(BiasTemp::Data)))
2062 return GetCurrentState();
2063
2064 const BiasTemp::Data &data = d.Ref<BiasTemp::Data>();
2065
2066 ostringstream out;
2067
2068 out << fixed << setprecision(1);
2069 out << d.GetJavaDate() << '\n';
2070
2071 out << HTML::kGreen << '\t' << data.time << '\n';
2072 out << HTML::kGreen << '\t' << data.avg << '\n';
2073 out << HTML::kGreen << '\t' << data.rms << '\n';
2074
2075 ofstream(fPath+"/biastemp.data") << out.str();
2076
2077 return GetCurrentState();
2078 }
2079
2080 int HandleGpsNema(const EventImp &d)
2081 {
2082 if (!CheckDataSize(d, "GpsControl:Nema", sizeof(GPS::NEMA)))
2083 return GetCurrentState();
2084
2085 const GPS::NEMA &nema = d.Ref<GPS::NEMA>();
2086
2087 ostringstream out;
2088
2089 out << fixed;
2090 out << d.GetJavaDate() << '\n';
2091
2092 switch (nema.qos)
2093 {
2094 case 1: out << HTML::kGreen << "\tGPS fix [1]\n"; break;
2095 case 2: out << HTML::kGreen << "\tDifferential fix [2]\n"; break;
2096 default: out << HTML::kRed << "\tinvalid [" << nema.qos << "]\n"; break;
2097 }
2098
2099 out << HTML::kWhite << '\t' << nema.count << '\n';
2100 out << HTML::kWhite << '\t' << Time(floor(Time().Mjd())+nema.time).GetAsStr("%H:%M:%S") << '\n';
2101 out << HTML::kWhite << '\t' << setprecision(4) << nema.lat << '\n';
2102 out << HTML::kWhite << '\t' << setprecision(4) << nema.lng << '\n';
2103 out << HTML::kWhite << '\t' << setprecision(1) << nema.height << "\n";
2104 out << HTML::kWhite << '\t' << setprecision(1) << nema.hdop << "\n";
2105 out << HTML::kWhite << '\t' << setprecision(1) << nema.geosep << "\n";
2106
2107 ofstream(fPath+"/gps.data") << out.str();
2108
2109 return GetCurrentState();
2110 }
2111
2112 int HandleSqmData(const EventImp &d)
2113 {
2114 if (!CheckDataSize(d, "SqmControl:Data", sizeof(SQM::Data)))
2115 return GetCurrentState();
2116
2117 const SQM::Data &data = d.Ref<SQM::Data>();
2118
2119 ostringstream out;
2120
2121 out << fixed;
2122 out << d.GetJavaDate() << '\n';
2123 out << HTML::kWhite << '\t' << setprecision(2) << data.mag << '\n';
2124 out << HTML::kWhite << '\t' << data.freq << '\n';
2125 out << HTML::kWhite << '\t' << data.counts << '\n';
2126 out << HTML::kWhite << '\t' << setprecision(3) << data.period << '\n';
2127 out << HTML::kWhite << '\t' << setprecision(1) << data.temp << "\n";
2128
2129 ofstream(fPath+"/sqm.data") << out.str();
2130
2131 return GetCurrentState();
2132 }
2133
2134 string GetTempColor(float t)
2135 {
2136 if (t>25 && t<30)
2137 return HTML::kGreen;
2138
2139 if (t<20 || t>35)
2140 return HTML::kRed;
2141
2142 return HTML::kYellow;
2143 }
2144
2145 int HandleTemperatureData(const EventImp &d)
2146 {
2147 if (!CheckDataSize(d, "Temperature:Data", 3*sizeof(float)))
2148 return GetCurrentState();
2149
2150 const float *temp = d.Ptr<float>();
2151
2152 ostringstream out;
2153
2154 out << fixed << setprecision(1);
2155 out << d.GetJavaDate() << '\n';
2156
2157 out << GetTempColor(temp[1]) << '\t' << temp[1] << '\n';
2158 out << GetTempColor(temp[0]) << '\t' << temp[0] << '\n';
2159 out << GetTempColor(temp[2]) << '\t' << temp[2] << '\n';
2160
2161 ofstream(fPath+"/temperature.data") << out.str();
2162
2163 fTemperatureControlHist.push_back(temp[0]);
2164 if (fTemperatureControlHist.size()>60) // 1h
2165 fTemperatureControlHist.pop_front();
2166
2167 WriteHist(d, "hist-temperaturecontrol",
2168 fTemperatureControlHist, 45, 0);
2169
2170 return GetCurrentState();
2171 }
2172
2173 int HandleAgilentData(const EventImp &d, const string &ext)
2174 {
2175 if (!CheckDataSize(d, ("Agilent"+ext+":Data").c_str(), 4*sizeof(float)))
2176 return GetCurrentState();
2177
2178 const float *data = d.Ptr<float>();
2179
2180 ostringstream out;
2181
2182 out << fixed << setprecision(1);
2183 out << d.GetJavaDate() << '\n';
2184
2185 out << HTML::kWhite << '\t' << data[0] << '\n';
2186 out << HTML::kWhite << '\t' << data[1] << '\n';
2187 out << HTML::kWhite << '\t' << data[2] << '\n';
2188 out << HTML::kWhite << '\t' << data[3] << '\n';
2189
2190 ofstream(fPath+"/agilent"+ext+".data") << out.str();
2191
2192 return GetCurrentState();
2193 }
2194
2195 int HandleRateScanData(const EventImp &d)
2196 {
2197 if (!CheckDataSize(d, "RateScan:Data", 824))
2198 return GetCurrentState();
2199
2200 const uint64_t id = d.Get<uint64_t>();
2201 const float *rate = d.Ptr<float>(20);
2202
2203 if (fRateScanDataId!=id)
2204 {
2205 for (int i=0; i<41; i++)
2206 fRateScanDataHist[i].clear();
2207 fRateScanDataId = id;
2208 }
2209 fRateScanDataHist[0].push_back(log10(rate[0]));
2210
2211 double max = 0;
2212 for (int i=1; i<41; i++)
2213 {
2214 fRateScanDataHist[i].push_back(log10(rate[i]));
2215 if (rate[i]>max)
2216 max = rate[i];
2217 }
2218
2219 // Cycle by time!
2220 fRateScanBoard ++;
2221 fRateScanBoard %= 40;
2222
2223 WriteHist(d, "hist-ratescan", fRateScanDataHist[0], 10, -2);
2224 WriteCam(d, "cam-ratescan-board", fRateScanDataHist[fRateScanBoard+1], 10, -4);
2225
2226 ostringstream out;
2227 out << setprecision(3);
2228 out << d.GetJavaDate() << '\n';
2229 out << HTML::kWhite << '\t' << fFtmBoardThresholdMed << '\n';
2230 out << HTML::kWhite << '\t' << fFtmPatchThresholdMed << '\n';
2231 out << HTML::kWhite << '\t' << floor(pow(10, fRateScanDataHist[0].back())+.5) << '\n';
2232 out << HTML::kWhite << '\t' << floor(max+.5) << '\n';
2233
2234 ofstream(fPath+"/ratescan.data") << out.str();
2235
2236 out.str("");
2237 out << d.GetJavaDate() << '\n';
2238 out << HTML::kWhite << '\t' << int(fRateScanBoard) << '\n';
2239 out << HTML::kWhite << '\t' << pow(10, fRateScanDataHist[fRateScanBoard+1].back()) << '\n';
2240
2241 ofstream(fPath+"/ratescan_board.data") << out.str();
2242
2243 return GetCurrentState();
2244 }
2245
2246 int HandleRateControlThreshold(const EventImp &d)
2247 {
2248 if (!CheckDataSize(d, "RateControl:Threshold", 18))
2249 return GetCurrentState();
2250
2251 const uint16_t th = d.Get<uint16_t>();
2252
2253 fRateControlThreshold.push_back(th);
2254 if (fRateControlThreshold.size()>300)
2255 fRateControlThreshold.pop_front();
2256
2257 WriteHist(d, "hist-ratecontrol-threshold", fRateControlThreshold, 1000);
2258
2259 return GetCurrentState();
2260 }
2261
2262 int HandleChatMsg(const EventImp &d)
2263 {
2264 if (d.GetSize()==0 || d.GetQoS()!=MessageImp::kComment)
2265 return GetCurrentState();
2266
2267 if (Time()<d.GetTime()+boost::posix_time::minutes(1))
2268 SetAudio("message");
2269
2270 fChatHist.add(d.GetText(), d.GetTime());
2271
2272 ostringstream out;
2273 out << setprecision(3);
2274 out << Header(d) << '\n';
2275 out << HTML::kWhite << '\t';
2276 out << "<->" << fChatHist.rget() << "</->";
2277 out << '\n';
2278
2279 ofstream(fPath+"/chat.data") << out.str();
2280
2281 return GetCurrentState();
2282 }
2283
2284 // -------------------------------------------------------------------
2285
2286 int HandleDoTest(const EventImp &d)
2287 {
2288 ostringstream out;
2289 out << d.GetJavaDate() << '\n';
2290
2291 switch (d.GetQoS())
2292 {
2293 case -3: out << HTML::kWhite << "\tNot running\n"; break;
2294 case -2: out << HTML::kBlue << "\tLoading\n"; break;
2295 case -1: out << HTML::kBlue << "\tStarted\n"; break;
2296 default: out << HTML::kGreen << "\tRunning [" << d.GetQoS() << "]\n"; break;
2297 }
2298
2299 ofstream(fPath+"/dotest.data") << out.str();
2300
2301 return StateMachineImp::kSM_KeepState;
2302 }
2303
2304 // -------------------------------------------------------------------
2305
2306 /*
2307 bool CheckEventSize(size_t has, const char *name, size_t size)
2308 {
2309 if (has==size)
2310 return true;
2311
2312 ostringstream msg;
2313 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
2314 Fatal(msg);
2315 return false;
2316 }*/
2317
2318 int Print() const
2319 {
2320 Out() << fDimDNS << endl;
2321 Out() << fDimMcp << endl;
2322 Out() << fDimControl << endl;
2323 Out() << fDimDataLogger << endl;
2324 Out() << fDimDriveControl << endl;
2325 Out() << fDimTimeCheck << endl;
2326 Out() << fDimFadControl << endl;
2327 Out() << fDimFtmControl << endl;
2328 Out() << fDimBiasControl << endl;
2329 Out() << fDimFeedback << endl;
2330 Out() << fDimRateControl << endl;
2331 Out() << fDimFscControl << endl;
2332 Out() << fDimAgilentControl24 << endl;
2333 Out() << fDimAgilentControl50 << endl;
2334 Out() << fDimAgilentControl80 << endl;
2335 Out() << fDimPwrControl << endl;
2336 Out() << fDimLidControl << endl;
2337 Out() << fDimMagicWeather << endl;
2338 Out() << fDimTngWeather << endl;
2339 Out() << fDimGtcDust << endl;
2340 Out() << fDimMagicLidar << endl;
2341 Out() << fDimTemperature << endl;
2342 Out() << fDimRainSensor << endl;
2343 Out() << fDimRateScan << endl;
2344 Out() << fDimChat << endl;
2345 Out() << fDimSkypeClient << endl;
2346
2347 return GetCurrentState();
2348 }
2349
2350 string GetStateHtml(const DimState &state, int green) const
2351 {
2352 if (!state.online())
2353 return HTML::kWhite+"\t&mdash;\n";
2354
2355 if (&state==&fDimControl)
2356 return HTML::kGreen +'\t'+(state.state()==0?"Idle":fDimControl.shortmsg)+'\n';
2357
2358 const State rc = state.description();
2359
2360 // Sate not found in list, server online (-3: offline; -2: not found)
2361 if (rc.index==-2)
2362 {
2363 ostringstream out;
2364 out << HTML::kWhite << '\t' << state.state() << '\n';
2365 return out.str();
2366 }
2367
2368 //ostringstream msg;
2369 //msg << HTML::kWhite << '\t' << rc.name << " [" << rc.index << "]\n";
2370 //return msg.str();
2371
2372 if (rc.index<0)
2373 return HTML::kWhite + "\t&mdash;\n";
2374
2375 string col = HTML::kGreen;
2376 if (rc.index<green)
2377 col = HTML::kYellow;
2378 if (rc.index>0xff)
2379 col = HTML::kRed;
2380
2381 return col + '\t' + rc.name + '\n';
2382 }
2383
2384 bool SetError(bool b, const string &err)
2385 {
2386 if (!b)
2387 {
2388 fErrorList.erase(err);
2389 return 0;
2390 }
2391
2392 const bool isnew = fErrorList.insert(err).second;
2393 if (isnew)
2394 fErrorHist.add(err);
2395
2396 return isnew;
2397 }
2398
2399#ifdef HAVE_NOVA
2400
2401 //vector<pair<Nova::EquPosn, double>> fMoonCoords;
2402
2403 vector<Nova::SolarObjects> fCoordinates;
2404
2405 void CalcCoordinates(double jd)
2406 {
2407 jd = floor(jd);
2408
2409 fCoordinates.clear();
2410 for (double h=0; h<1; h+=1./(24*12))
2411 fCoordinates.emplace_back(jd+h);
2412 }
2413
2414 pair<vector<float>, pair<Time, float>> GetVisibility(Nova::EquPosn *src=0)
2415 {
2416 const double sunset = fSun.fSunSet12.JD()-1;
2417 const double sunrise = fSun.fSunRise12.JD();
2418
2419 Nova::EquPosn moon;
2420 Nova::EquPosn *pos = src ? src : &moon;
2421
2422 double max = 0;
2423 double maxjd = 0;
2424
2425 int cnt = 0;
2426
2427 vector<float> alt;
2428 for (auto it=fCoordinates.begin(); it!=fCoordinates.end(); it++)
2429 {
2430 if (src==0)
2431 moon = it->fMoonEqu;
2432
2433 const Nova::HrzPosn hrz = Nova::GetHrzFromEqu(*pos, it->fJD);
2434
2435 if (it->fJD>sunset && it->fJD<sunrise)
2436 alt.push_back(hrz.alt);
2437
2438 if (hrz.alt>max)
2439 {
2440 max = hrz.alt;
2441 maxjd = it->fJD;
2442 }
2443
2444 if (it->fJD>sunset && it->fJD<sunrise && hrz.alt>15)
2445 cnt++;
2446 }
2447
2448 if (max<=15 || cnt==0)
2449 return make_pair(vector<float>(), make_pair(Time(), 0));
2450
2451 return make_pair(alt, make_pair(maxjd, maxjd>sunset&&maxjd<sunrise?max:0));
2452 }
2453
2454 pair<vector<float>, pair<Time, float>> GetLightCondition(const Nova::EquPosn &src_pos)
2455 {
2456 const double sunset = fSun.fSunSet12.JD()-1;
2457 const double sunrise = fSun.fSunRise12.JD();
2458
2459 double max = -1;
2460 double maxjd = 0;
2461
2462 int cnt = 0;
2463
2464 vector<float> vec;
2465 for (auto it=fCoordinates.begin(); it!=fCoordinates.end(); it++)
2466 {
2467 double cur = -1;
2468
2469 if (it->fJD>sunset && it->fJD<sunrise)
2470 {
2471 cur = FACT::PredictI(*it, src_pos);
2472 vec.push_back(cur);
2473 }
2474
2475 if (cur>max)
2476 {
2477 max = cur;
2478 maxjd = it->fJD;
2479 }
2480
2481 if (it->fJD>sunset && it->fJD<sunrise && cur>0)
2482 cnt++;
2483 }
2484
2485 if (max<=0 || cnt==0)
2486 return make_pair(vector<float>(), make_pair(Time(), 0));
2487
2488 return make_pair(vec, make_pair(maxjd, maxjd>sunset&&maxjd<sunrise?max:-1));
2489 }
2490#endif
2491
2492 void UpdateAstronomy()
2493 {
2494 Time now;
2495
2496#ifdef HAVE_NOVA
2497 CalcCoordinates(now.JD());
2498#endif
2499
2500 fSun = Sun (now);
2501 fMoon = Moon(now);
2502
2503 vector<string> color(8, HTML::kWhite);
2504 color[fSun.state] = HTML::kBlue;
2505
2506 ostringstream out;
2507 out << setprecision(3);
2508 out << now.JavaDate() << '\n';
2509 out << color[4] << '\t' << fSun.fSunRise18.GetAsStr("%H:%M") << '\n';
2510 out << color[5] << '\t' << fSun.fSunRise12.GetAsStr("%H:%M") << '\n';
2511 out << color[6] << '\t' << fSun.fSunRise06.GetAsStr("%H:%M") << '\n';
2512 out << color[7] << '\t' << fSun.fSunRise00.GetAsStr("%H:%M") << '\n';
2513
2514 out << color[0] << '\t' << fSun.fSunSet00.GetAsStr("%H:%M") << '\n';
2515 out << color[1] << '\t' << fSun.fSunSet06.GetAsStr("%H:%M") << '\n';
2516 out << color[2] << '\t' << fSun.fSunSet12.GetAsStr("%H:%M") << '\n';
2517 out << color[3] << '\t' << fSun.fSunSet18.GetAsStr("%H:%M") << '\n';
2518
2519 ofstream(fPath+"/sun.data") << out.str();
2520
2521 color.assign(3, HTML::kWhite);
2522 color[fMoon.state%3] = HTML::kBlue;
2523
2524 out.str("");
2525 out << now.JavaDate() << '\n';
2526
2527 out << color[0] << '\t' << fMoon.fRise.GetAsStr("%H:%M") << '\n';
2528 out << color[1] << '\t' << fMoon.fTransit.GetAsStr("%H:%M") << '\n';
2529 out << color[2] << '\t' << fMoon.fSet.GetAsStr("%H:%M") << '\n';
2530
2531 out << (fSun.isday?HTML::kWhite:fMoon.color) << '\t' << fMoon.description << '\n';
2532
2533 if (!fMoon.visible)
2534 out << HTML::kWhite << "\t&mdash;\t\n";
2535 else
2536 {
2537 string col = HTML::kWhite;
2538 if (!fSun.isday)
2539 {
2540 col = HTML::kGreen;
2541 if (fMoon.zd>25)
2542 col = HTML::kYellow;
2543 if (fMoon.zd>45 && fMoon.zd<80)
2544 col = HTML::kRed;
2545 if (fMoon.zd>=80)
2546 col = HTML::kRed;
2547 }
2548 out << col << '\t' << fMoon.zd << '\t' << GetDir(fMoon.az) << '\n';
2549 }
2550
2551 ostringstream out2, out3, out4;
2552 out2 << setprecision(3);
2553 out2 << now.JavaDate() << '\n';
2554 out3 << now.JavaDate() << '\n';
2555 out4 << now.JavaDate() << '\n';
2556
2557 struct Entry
2558 {
2559 string name;
2560 float value;
2561 int color;
2562 Entry(const string &n, float v, int c) : name(n), value(v), color(c%8) { }
2563
2564 const string &Col() const
2565 {
2566 // If this list is updatd the number count in the constructor needs
2567 // to be updated, too
2568 static const string hcol[] = { "888", "8cf", "c8f", "bbb", "8fc", "cf8", "f8c", "fc8" };
2569 return hcol[color];
2570 }
2571
2572 vector<float> GetColor(double scale, double offset=0) const
2573 {
2574 vector<float> rc(3);
2575 rc[0] = double(Col()[0])*scale/126+offset;
2576 rc[1] = double(Col()[1])*scale/126+offset;
2577 rc[2] = double(Col()[2])*scale/126+offset;
2578 return rc;
2579 }
2580 };
2581
2582 multimap<Time, Entry> culmination;
2583 multimap<Time, Entry> lightcond;
2584 vector<vector<float>> alt;
2585 vector<vector<float>> cur;
2586
2587#ifdef HAVE_NOVA
2588 int ccol = 0;
2589 int lcol = 0;
2590
2591 /*const*/ pair<vector<float>, pair<Time, float>> vism = GetVisibility();
2592 if (!vism.first.empty())
2593 {
2594 const Entry entry("Moon", vism.second.second, ccol);
2595 culmination.insert(make_pair(vism.second.first, entry));
2596 const vector<float> col = entry.GetColor(75, 15);
2597 vism.first.insert(vism.first.begin(), col.begin(), col.end());
2598 alt.push_back(vism.first);
2599
2600 ccol++;
2601 }
2602#endif
2603
2604#ifdef HAVE_SQL
2605 try
2606 {
2607 const mysqlpp::StoreQueryResult res =
2608 Database(fDatabase).query("SELECT fSourceName, fRightAscension, fDeclination FROM Source WHERE fSourceTypeKEY=1").store();
2609
2610 out << HTML::kWhite << '\t';
2611 out2 << HTML::kWhite << '\t';
2612 out3 << HTML::kWhite << '\t';
2613 out4 << HTML::kWhite << '\t';
2614
2615 for (vector<mysqlpp::Row>::const_iterator v=res.begin(); v<res.end(); v++)
2616 {
2617 const string name = (*v)[0].c_str();
2618 const double ra = (*v)[1];
2619 const double dec = (*v)[2];
2620#ifdef HAVE_NOVA
2621 Nova::EquPosn pos;
2622 pos.ra = ra*15;
2623 pos.dec = dec;
2624
2625 const Nova::ZdAzPosn hrz = Nova::GetHrzFromEqu(pos, now.JD());
2626
2627 /*const*/ pair<vector<float>, pair<Time, float>> vis = GetVisibility(&pos);
2628 if (!vis.first.empty())
2629 {
2630 const Entry entry(name, vis.second.second, ccol);
2631 culmination.insert(make_pair(vis.second.first, entry));
2632 const vector<float> col = entry.GetColor(75, 15);
2633 vis.first.insert(vis.first.begin(), col.begin(), col.end());
2634 alt.push_back(vis.first);
2635
2636 ccol++;
2637
2638 /*const*/ pair<vector<float>, pair<Time, float>> lc = GetLightCondition(pos);
2639 if (!lc.first.empty())
2640 {
2641 const Entry entry2(name, lc.second.second, lcol);
2642 lightcond.insert(make_pair(lc.second.first, entry2));
2643 const vector<float> col2 = entry2.GetColor(100);
2644 lc.first.insert(lc.first.begin(), col2.begin(), col2.end());
2645 cur.push_back(lc.first);
2646
2647 lcol++;
2648 }
2649 }
2650
2651 string col = HTML::kWhite;
2652 if (hrz.zd<85)
2653 col = HTML::kRed;
2654 if (hrz.zd<65)
2655 col = HTML::kYellow;
2656 if (hrz.zd<30)
2657 col = HTML::kGreen;
2658
2659 out2 << "<tr bgcolor='" << col << "'>";
2660 out2 << "<td>" << name << "</td>";
2661 if (hrz.zd<85)
2662 {
2663 out2 << "<td>" << hrz.zd << "&deg;</td>";
2664 out2 << "<td>" << GetDir(hrz.az) << "</td>";
2665 }
2666 else
2667 out2 << "<td/><td/>";
2668 out2 << "</tr>";
2669#endif
2670 const int32_t angle = fMoon.Angle(ra, dec);
2671
2672 out << "<tr bgcolor='" << Moon::Color(angle) << "'>";
2673 out << "<td>" << name << "</td>";
2674 out << "<td>" << round(angle) << "&deg;</td>";
2675 out << "</tr>";
2676 }
2677
2678 for (auto it=culmination.begin(); it!=culmination.end(); it++)
2679 {
2680 const Entry &e = it->second;
2681 if (it!=culmination.begin())
2682 out3 << ", ";
2683 out3 << "<B#" << e.Col() << ">" << e.name << "</B>";
2684 if (e.value>0)
2685 out3 << " [" << nearbyint(90-e.value) << "&deg;]";
2686 }
2687
2688 out4 << setprecision(3);
2689
2690 for (auto it=lightcond.begin(); it!=lightcond.end(); it++)
2691 {
2692 const Entry &e = it->second;
2693 if (it!=lightcond.begin())
2694 out4 << ", ";
2695 out4 << "<B#" << e.Col() << ">" << e.name << "</B>";
2696 if (e.value>0)
2697 out4 << " [" << nearbyint(e.value) << "]";
2698 }
2699
2700 const Time st = fSun.fSunSet12;;
2701 const Time rs = fSun.fSunRise12;
2702
2703 ostringstream title;
2704 title << st.GetAsStr("%H:%M");
2705 title << " / ";
2706 title << ((rs>st?rs-st:st-rs)/20).minutes();
2707 title << "' / ";
2708 title << rs.GetAsStr("%H:%M");
2709
2710 out << '\n';
2711 out2 << '\n';
2712 out3 << '\n';
2713 out4 << '\n';
2714 out << HTML::kWhite << '\t' << Time()-now << '\n';
2715 out2 << HTML::kWhite << '\t' << Time()-now << '\n';
2716
2717 WriteBinaryVec(now, "hist-visibility", alt, 75, 15, "Alt "+title.str());
2718 WriteBinaryVec(now, "hist-current-prediction", cur, 100, 0, "I " +title.str());
2719 }
2720 catch (const exception &e)
2721 {
2722 out << '\n';
2723 out2 << '\n';
2724 out << HTML::kWhite << '\t' << "ERROR - "+string(e.what()) << '\n';
2725 out2 << HTML::kWhite << '\t' << "ERROR - "+string(e.what()) << '\n';
2726 out3 << HTML::kWhite << '\t' << "ERROR - "+string(e.what()) << '\n';
2727 out4 << HTML::kWhite << '\t' << "ERROR - "+string(e.what()) << '\n';
2728 }
2729#endif
2730
2731 ofstream(fPath+"/moon.data") << out.str();
2732 ofstream(fPath+"/source-list.data") << out2.str();
2733 ofstream(fPath+"/visibility.data") << out3.str();
2734 ofstream(fPath+"/current-prediction.data") << out4.str();
2735 }
2736
2737 int Execute()
2738 {
2739 Time now;
2740 if (now-fLastUpdate<boost::posix_time::seconds(1))
2741 return fDimDNS.online() ? kStateRunning : kStateDimNetworkNA;
2742 fLastUpdate=now;
2743
2744 // ==============================================================
2745
2746 bool reqscript = false;
2747
2748#ifdef HAVE_SQL
2749 try
2750 {
2751 const string query = Tools::Form("SELECT COUNT(*) FROM calendar.Data WHERE NOT u LIKE 'moon' AND y=%d AND m=%d AND d=%d",
2752 now.NightAsInt()/10000, (now.NightAsInt()/100)%100-1, now.NightAsInt()%100);
2753
2754 const mysqlpp::StoreQueryResult res = Database(fDatabase).query(query).store();
2755
2756 const uint32_t cnt = res[0][0];
2757
2758 reqscript = cnt>0 && (fSun.state==3 || fSun.state==4);
2759 }
2760 catch (const exception &e)
2761 {
2762 Out() << e.what() << endl;
2763 }
2764#endif
2765 // ==============================================================
2766
2767 struct statvfs vfs;
2768 statvfs("/newdata", &vfs);
2769
2770 const uint64_t free_newdata = vfs.f_bsize*vfs.f_bavail;
2771
2772 // ==============================================================
2773
2774 const bool data_taking =
2775 fDimMcp.state()==MCP::State::kTriggerOn ||
2776 fDimMcp.state()==MCP::State::kTakingData;
2777
2778 const bool data_run =
2779 fMcpConfigurationName=="data" ||
2780 fMcpConfigurationName=="data-rt";
2781
2782 const bool bias_on =
2783 fDimBiasControl.state()==BIAS::State::kRamping ||
2784 fDimBiasControl.state()==BIAS::State::kOverCurrent ||
2785 fDimBiasControl.state()==BIAS::State::kVoltageOn;
2786
2787 const bool calibrated =
2788 fDimFeedback.state()>=Feedback::State::kCalibrated;
2789
2790 const bool haderr = !fErrorList.empty();
2791
2792 bool newerr = false;
2793
2794 newerr |= SetError(!fDimDNS.online(),
2795 "<b><#darkred>DIM network not available</#></b>");
2796 newerr |= SetError(!fDimControl.online(),
2797 "<b>no dimctrl server available</b>");
2798 newerr |= SetError(fDimDataLogger.state()<20 || fDimDataLogger.state()>40,
2799 "<b>datalogger not ready</b>");
2800
2801 newerr |= SetError(fDimControl.state()!=3 && reqscript,
2802 "<b>No script running during datataking time.</b>");
2803
2804 //newerr |= SetError(fDimDriveControl.state()==Drive::State::kLocked,
2805 // "<b><#darkred>Drive in LOCKED state, drive was automatically parked</#></b>");
2806
2807 newerr |= SetError(fDimDriveControl.state()>0xff && data_taking && data_run,
2808 "Drive in ERROR state during data-run");
2809 newerr |= SetError(fDriveControlMoonDist>155,
2810 "Moon within the field-of-view of the cones");
2811 newerr |= SetError(fDriveControlMoonDist>=0 && fDriveControlMoonDist<3,
2812 "Moon within the field-of-view of the camera");
2813
2814 newerr |= SetError(fDimBiasControl.state()<BIAS::State::kRamping && data_taking && data_run,
2815 "BIAS not operating during data-run");
2816 newerr |= SetError(fDimBiasControl.state()==BIAS::State::kOverCurrent,
2817 "BIAS channels in OverCurrent");
2818 newerr |= SetError(fDimBiasControl.state()==BIAS::State::kNotReferenced,
2819 "BIAS voltage not at reference");
2820
2821 newerr |= SetError(fDimFeedback.state()==Feedback::State::kOnStandby,
2822 "Feedback in standby due to high currents");
2823
2824
2825 newerr |= SetError(bias_on && calibrated && fBiasControlCurrentMed>115,
2826 "Median current (excl. crazy) exceeds 115&micro;A/pix");
2827 newerr |= SetError(bias_on && calibrated && fBiasControlCurrentMax>160,
2828 "Maximum current (excl. crazy) exceeds 160&micro;A/pix");
2829
2830 newerr |= SetError(fFscControlHumidityAvg>60,
2831 "Average camera humidity exceed 60%");
2832
2833 newerr |= SetError(!fPfMiniHumidityHist.empty() && fPfMiniHumidityHist.back()>50,
2834 "Camera humidity inside camera exceeds 50% (PFmini)");
2835 newerr |= SetError(!fTemperatureControlHist.empty() && (fTemperatureControlHist.back()<18.0 || fTemperatureControlHist.back()>22.0),
2836 "Container temperature outside [18.0;22.0]&deg;C");
2837
2838 newerr |= SetError(!fMagicWeatherHist[kHum].empty() && fMagicWeatherHist[kHum].back()>98 && fDimLidControl.state()==Lid::State::kOpen,
2839 "Outside humidity exceeds 98% while lid is open");
2840 newerr |= SetError(!fMagicWeatherHist[kGusts].empty() && fMagicWeatherHist[kGusts].back()>50 && (fDimDriveControl.state()==Drive::State::kTracking||fDimDriveControl.state()==Drive::State::kOnTrack),
2841 "Wind gusts exceed 50km/h during tracking");
2842
2843 newerr |= SetError(fDimFscControl.state()>=FSC::State::kConnected && !fFscControlTemperatureHist.empty() && fFscControlTemperatureHist.back()>15,
2844 "Sensor temperature exceeds outside temperature by more than 15&deg;C");
2845
2846 newerr |= SetError(fFtmControlTriggerRateTooLow>0,
2847 "Trigger rate below 1Hz while trigger switched on");
2848
2849 newerr |= SetError(fFtmControlState!=FTM::kFtmConfig && (fFtmControlState&FTM::kFtmLocked)==0,
2850 "FTM - clock conditioner not locked!");
2851
2852 newerr |= SetError(fDimTimeCheck.state()==1,
2853 "Warning NTP time difference of drive PC exceeds 1s");
2854 newerr |= SetError(fDimTimeCheck.state()<1,
2855 "Warning timecheck not running");
2856
2857 newerr |= SetError(fDimBiasControl.state()==BIAS::State::kVoltageOn &&
2858 fDimFeedback.state()<Feedback::State::kCalibrating &&
2859 fBiasControlVoltageMed>3,
2860 "Bias voltage switched on, but bias crate not calibrated");
2861
2862 newerr |= SetError(fLastRunFinishedWithZeroEvents,
2863 "Last run finshed, but contained zero events.");
2864
2865 newerr |= SetError(fFreeSpace<uint64_t(50000000000),
2866 "Less than 50GB disk space left on newdaq.");
2867
2868 newerr |= SetError(free_newdata<uint64_t(800000000000),
2869 "Less than 800GB disk space left on /newdata.");
2870
2871 newerr |= SetError(fDimPwrControl.state()==Power::State::kCoolingFailure,
2872 "Cooling unit reports failure!");
2873
2874 for (auto it=fControlAlarmHist.begin(); it!=fControlAlarmHist.end(); it++)
2875 newerr |= SetError(it->time.IsValid(), it->msg);
2876 fControlAlarmHist.clean();;
2877
2878 fLastRunFinishedWithZeroEvents = false;
2879
2880 // FTM in Connected instead of Idle --> power cyclen
2881
2882 /* // Check offline and disconnected status?
2883 Out() << fDimMcp << endl;
2884 Out() << fDimControl << endl;
2885 Out() << fDimDataLogger << endl;
2886 Out() << fDimDriveControl << endl;
2887 Out() << fDimFadControl << endl;
2888 Out() << fDimFtmControl << endl;
2889 Out() << fDimBiasControl << endl;
2890 Out() << fDimFeedback << endl;
2891 Out() << fDimRateControl << endl;
2892 Out() << fDimFscControl << endl;
2893 Out() << fDimMagicWeather << endl;
2894 Out() << fDimRateScan << endl;
2895 Out() << fDimChat << endl;
2896 */
2897
2898 // FTU in error
2899 // FAD lost
2900
2901 // --------------------------------------------------------------
2902 ostringstream out;
2903
2904 if (newerr)
2905 {
2906 SetAudio("error");
2907
2908 out << now.JavaDate() << '\n';
2909 out << HTML::kWhite << '\t';
2910 out << "<->" << fErrorHist.rget() << "<->";
2911 out << '\n';
2912
2913 ofstream(fPath+"/errorhist.data") << out.str();
2914 }
2915
2916 out.str("");
2917 out << Header(now) << '\t' << (!fErrorList.empty()) << '\t' << (fDimControl.state()>0) << '\n';
2918 out << setprecision(3);
2919 out << HTML::kWhite << '\t';
2920 for (auto it=fErrorList.begin(); it!=fErrorList.end(); it++)
2921 out << *it << "<br/>";
2922 out << '\n';
2923
2924 if (haderr || !fErrorList.empty())
2925 ofstream(fPath+"/error.data") << out.str();
2926
2927 // ==============================================================
2928
2929 out.str("");
2930 out << Header(now) << '\t' << (!fErrorList.empty()) << '\t' << (fDimControl.state()>0) << '\n';
2931 out << setprecision(3);
2932
2933 // -------------- System status --------------
2934 if (fDimDNS.online() && fDimMcp.state()>=MCP::State::kIdle) // Idle
2935 {
2936 string col = HTML::kBlue;
2937 switch (fMcpConfigurationState)
2938 {
2939 case MCP::State::kIdle:
2940 case DimState::kOffline:
2941 col = HTML::kWhite;
2942 break;
2943 case MCP::State::kConfiguring1:
2944 case MCP::State::kConfiguring2:
2945 case MCP::State::kConfiguring3:
2946 case MCP::State::kConfigured:
2947 case MCP::State::kTriggerOn:
2948 col = HTML::kBlue;
2949 break;
2950 case MCP::State::kTakingData:
2951 col = HTML::kBlue;
2952 if (fDimFadControl.state()==FAD::State::kRunInProgress)
2953 col = HTML::kGreen;
2954 break;
2955 }
2956
2957 const bool other =
2958 fDimRateControl.state()==RateControl::State::kSettingGlobalThreshold ||
2959 fDimLidControl.state()==Lid::State::kMoving ||
2960 fDimRateScan.state()==RateScan::State::kInProgress;
2961
2962 if (other)
2963 col = HTML::kBlue;
2964
2965 out << col << '\t';
2966
2967 if (!other)
2968 {
2969 const string conf = fMcpConfigurationName.length()>0?" ["+fMcpConfigurationName+"]":"";
2970 switch (fMcpConfigurationState)
2971 {
2972 case MCP::State::kIdle:
2973 out << "Idle" << conf;
2974 break;
2975 case MCP::State::kConfiguring1:
2976 case MCP::State::kConfiguring2:
2977 case MCP::State::kConfiguring3:
2978 out << "Configuring" << conf;
2979 break;
2980 case MCP::State::kConfigured:
2981 out << "Configured" << conf;
2982 break;
2983 case MCP::State::kTriggerOn:
2984 case MCP::State::kTakingData:
2985 out << fMcpConfigurationName;
2986 if (fFadControlDrsRuns[2]>0)
2987 out << "(" << fFadControlDrsRuns[2] << ")";
2988 break;
2989
2990 case MCP::State::kCrateReset0:
2991 out << "Crate reset phase 0";
2992 break;
2993 case MCP::State::kCrateReset1:
2994 out << "Crate reset phase 1";
2995 break;
2996 case MCP::State::kCrateReset2:
2997 out << "Crate reset phase 2";
2998 break;
2999 case MCP::State::kCrateReset3:
3000 out << "Crate reset phase 3";
3001 break;
3002 }
3003 }
3004 else
3005 if (fDimRateControl.state()==RateControl::State::kSettingGlobalThreshold)
3006 out << "Calibrating threshold";
3007 else
3008 if (fDimRateScan.state()==RateScan::State::kInProgress)
3009 out << "Rate scan in progress";
3010 else
3011 if (fDimLidControl.state()==Lid::State::kMoving)
3012 out << "Lid moving";
3013
3014
3015 if (fMcpConfigurationState>MCP::State::kConfigured &&
3016 fDimRateControl.state()!=RateControl::State::kSettingGlobalThreshold)
3017 {
3018 ostringstream evt;
3019 if (fMcpConfigurationMaxEvents>0)
3020 {
3021 const int64_t de = int64_t(fMcpConfigurationMaxEvents) - int64_t(fFadControlNumEvents);
3022 if (de>=0 && fMcpConfigurationState==MCP::State::kTakingData)
3023 evt << de;
3024 else
3025 evt << fMcpConfigurationMaxEvents;
3026 }
3027 else
3028 {
3029 if (fMcpConfigurationState==MCP::State::kTakingData)
3030 {
3031 if (fFadControlNumEvents>2999)
3032 evt << floor(fFadControlNumEvents/1000) << 'k';
3033 else
3034 evt << fFadControlNumEvents;
3035 }
3036 }
3037
3038 ostringstream tim;
3039 if (fMcpConfigurationMaxTime>0)
3040 {
3041 const uint32_t dt = (Time()-fMcpConfigurationRunStart).total_seconds();
3042 if (dt<=fMcpConfigurationMaxTime && fMcpConfigurationState==MCP::State::kTakingData)
3043 tim << fMcpConfigurationMaxTime-dt << 's';
3044 else
3045 tim << fMcpConfigurationMaxTime << 's';
3046 }
3047 else
3048 {
3049 if (fMcpConfigurationState==MCP::State::kTakingData)
3050 tim << fMcpConfigurationRunStart.SecondsTo();
3051 }
3052
3053 const bool has_evt = !evt.str().empty();
3054 const bool has_tim = !tim.str().empty();
3055
3056 if (has_evt || has_tim)
3057 out << " [";
3058 out << evt.str();
3059 if (has_evt && has_tim)
3060 out << '/';
3061 out << tim.str();
3062 if (has_evt || has_tim)
3063 out << ']';
3064 }
3065 }
3066 else
3067 out << HTML::kWhite;
3068 out << '\n';
3069
3070 // ------------------ Drive -----------------
3071 if (fDimDNS.online() && fDimDriveControl.state()>=Drive::State::kInitialized) // Armed, Moving, Tracking, OnTrack, Error
3072 {
3073 const uint32_t dev = !fDriveControlTrackingDevHist.empty() ? round(fDriveControlTrackingDevHist.back()) : 0;
3074 const State rc = fDimDriveControl.description();
3075 string col = HTML::kGreen;
3076 if (fDimDriveControl.state()==Drive::State::kInitialized) // Armed
3077 col = HTML::kWhite;
3078 if (fDimDriveControl.state()>Drive::State::kInitialized && // Moving
3079 fDimDriveControl.state()<Drive::State::kTracking)
3080 col = HTML::kBlue;
3081 if (fDimDriveControl.state()==Drive::State::kTracking || // Tracking
3082 fDimDriveControl.state()==Drive::State::kOnTrack)
3083 {
3084 if (dev>60) // ~1.5mm
3085 col = HTML::kYellow;
3086 if (dev>120) // ~1/4 of a pixel ~ 2.5mm
3087 col = HTML::kRed;
3088 }
3089 if (fDimDriveControl.state()>0xff)
3090 col = HTML::kRed;
3091 out << col << '\t';
3092
3093 //out << rc.name << '\t';
3094 out << fDriveControlPointingAz << ' ';
3095 out << fDriveControlPointingZd << "&deg;";
3096 out << setprecision(2);
3097 if (fDimDriveControl.state()==Drive::State::kTracking ||
3098 fDimDriveControl.state()==Drive::State::kOnTrack) // Tracking
3099 {
3100 out << " &plusmn; " << dev << '"';
3101 if (!fDriveControlSourceName.empty())
3102 out << " [" << fDriveControlSourceName << ']';
3103 }
3104 if (fDimDriveControl.state()>Drive::State::kInitialized && // Moving
3105 fDimDriveControl.state()<Drive::State::kTracking)
3106 out << " &#10227;";
3107 out << setprecision(3);
3108 }
3109 else
3110 out << HTML::kWhite << '\t';
3111
3112 if (fSun.time.IsValid() && fMoon.time.IsValid())
3113 {
3114 if (fSun.visible)
3115 {
3116 out << " &#9788;";
3117 if (fDimDriveControl.state()<Drive::State::kInitialized)
3118 out << " [" << fSun.fSunSet12.MinutesTo() << "&darr;]";
3119 }
3120 else
3121 if (!fSun.visible && fMoon.visible)
3122 {
3123 out << " &#9790;";
3124 if (fDimDriveControl.state()<Drive::State::kInitialized)
3125 out << " [" << fMoon.disk << "%]";
3126 }
3127 }
3128 if (fDimDNS.online() && fDimDriveControl.state()>0xff)
3129 out << " <ERR>";
3130 if (fDimDNS.online() && fDimDriveControl.state()==Drive::State::kLocked)
3131 out << " &otimes;";
3132 out << '\n';
3133
3134 // ------------------- FSC ------------------
3135 if (fDimDNS.online() && fDimFscControl.state()>FSC::State::kDisconnected && !fFscControlTemperatureHist.empty())
3136 {
3137 string col = HTML::kGreen;
3138 if (fFscControlTemperatureHist.back()>9)
3139 col = HTML::kYellow;
3140 if (fFscControlTemperatureHist.back()>15)
3141 col = HTML::kRed;
3142
3143 out << col << '\t' << fFscControlTemperatureHist.back() << '\n';
3144 }
3145 else
3146 out << HTML::kWhite << '\n';
3147
3148 // --------------- MagicWeather -------------
3149 if (fDimDNS.online() && fDimMagicWeather.state()==MagicWeather::State::kReceiving && !fMagicWeatherHist[kWeatherBegin].empty())
3150 {
3151 /*
3152 const float diff = fMagicWeatherHist[kTemp].back()-fMagicWeatherHist[kDew].back();
3153 string col1 = HTML::kRed;
3154 if (diff>0.3)
3155 col1 = HTML::kYellow;
3156 if (diff>0.7)
3157 col1 = HTML::kGreen;
3158 */
3159
3160 const float wind = fMagicWeatherHist[kGusts].back();
3161 const float hum = fMagicWeatherHist[kHum].back();
3162 string col = HTML::kGreen;
3163 if (wind>35 || hum>95)
3164 col = HTML::kYellow;
3165 if (wind>45 || hum>98)
3166 col = HTML::kRed;
3167
3168 if (fDimRainSensor.state()==RainSensor::State::kValid && !fRainSensorDataHist.empty() && fRainSensorDataHist.back()>0)
3169 {
3170 out << HTML::kRed << '\t';
3171 out << "RAIN" << '\t';
3172 }
3173 else
3174 {
3175 out << col << '\t';
3176 out << fMagicWeatherHist[kHum].back() << '\t';
3177 }
3178 out << setprecision(2);
3179 out << fMagicWeatherHist[kGusts].back() << '\n';
3180 out << setprecision(3);
3181 }
3182 else
3183 out << HTML::kWhite << "\n";
3184
3185 // --------------- FtmControl -------------
3186 if (fDimDNS.online() && fDimFtmControl.state()==FTM::State::kTriggerOn)
3187 {
3188 string col = HTML::kGreen;
3189 if (!fFtmControlTriggerRateHist.empty())
3190 {
3191 if (fFtmControlTriggerRateHist.back()<15)
3192 col = HTML::kYellow;
3193 if (fFtmControlTriggerRateHist.back()>100)
3194 col = HTML::kRed;
3195
3196 out << col << '\t' << fFtmControlTriggerRateHist.back() << " Hz";
3197 }
3198
3199 if (bias_on)
3200 out << " (" << setprecision(4) << fFtmPatchThresholdMed << ')';
3201 out << '\n';
3202 }
3203 else
3204 out << HTML::kWhite << '\n';
3205
3206 // --------------- BiasControl -------------
3207 const bool bias_off = fDimBiasControl.state()==BIAS::State::kVoltageOff;
3208 const bool bias_oc = fDimBiasControl.state()==BIAS::State::kOverCurrent;
3209
3210 if (fDimDNS.online() && (bias_on || bias_off))
3211 {
3212
3213 string col = fBiasControlVoltageMed>3?HTML::kGreen:HTML::kWhite;
3214 if (bias_on)
3215 {
3216 if (fBiasControlCurrentMed>95 || fBiasControlCurrentMax>135)
3217 col = HTML::kYellow;
3218 if (fBiasControlCurrentMed>100 || fBiasControlCurrentMax>140)
3219 col = HTML::kRed;
3220 }
3221
3222 // Bias in overcurrent => Red
3223 if (bias_oc)
3224 col = HTML::kRed;
3225
3226 // MCP in ReadyForDatataking/Configuring/Configured/TriggerOn/TakingData
3227 // and Bias not in "data-taking state' => Red
3228 if (fMcpConfigurationState>MCP::State::kIdle && !bias_on)
3229 col = HTML::kWhite;
3230
3231 const bool cal = fDimFeedback.state()>=Feedback::State::kCalibrated;
3232
3233 // Feedback is currently calibrating => Blue
3234 if (fDimFeedback.state()==Feedback::State::kCalibrating)
3235 {
3236 out << HTML::kBlue << '\t';
3237 out << "***\t";
3238 out << "***\t";
3239 }
3240 else
3241 {
3242 out << col << '\t';
3243 out << setprecision(fBiasControlCurrentMed<100?2:3);
3244 out << (bias_off ? 0 : (fBiasControlCurrentMed<10?fBiasControlCurrentMed:floor(fBiasControlCurrentMed))) << '\t';
3245 if (bias_oc)
3246 out << "(OC) ";
3247 else
3248 {
3249 if (cal)
3250 {
3251 out << setprecision(fBiasControlCurrentMax<100?2:3);
3252 out << (bias_off ? 0 : (fBiasControlCurrentMax<10?fBiasControlCurrentMax:floor(fBiasControlCurrentMax)));
3253 }
3254 else
3255 out << "&mdash; ";
3256 }
3257 out << '\t';
3258 }
3259 if (cal && fDimFeedback.state()!=Feedback::State::kCalibrating)
3260 out << setprecision(2) << fBiasControlPowerTot << " W";
3261 else
3262 out << setprecision(3) << (bias_off ? 0 : fBiasControlVoltageMed) << " V";
3263 out << '\n';
3264 }
3265 else
3266 out << HTML::kWhite << '\n';
3267
3268 ofstream(fPath+"/fact.data") << out.str();
3269
3270 // ==============================================================
3271
3272 out.str("");
3273 out << Header(now) << '\t' << (!fErrorList.empty()) << '\t' << (fDimControl.state()>0) << '\n';
3274
3275 if (!fDimDNS.online())
3276 out << HTML::kWhite << "\tOffline\n\n\n\n\n\n\n\n\n\n\n\n\n";
3277 else
3278 {
3279 ostringstream dt;
3280 dt << (Time()-fRunTime);
3281
3282 out << HTML::kGreen << '\t' << fDimDNS.version() << '\n';
3283
3284 out << GetStateHtml(fDimControl, 0);
3285 out << GetStateHtml(fDimMcp, MCP::State::kConnected);
3286 out << GetStateHtml(fDimDataLogger, 1);
3287 out << GetStateHtml(fDimDriveControl, Drive::State::kConnected);
3288 out << GetStateHtml(fDimTimeCheck, 1);
3289 out << GetStateHtml(fDimFadControl, FAD::State::kConnected);
3290 out << GetStateHtml(fDimFtmControl, FTM::State::kConnected);
3291 out << GetStateHtml(fDimBiasControl, BIAS::State::kConnected);
3292 out << GetStateHtml(fDimFeedback, Feedback::State::kConnected);
3293 out << GetStateHtml(fDimRateControl, RateControl::State::kConnected);
3294 out << GetStateHtml(fDimFscControl, FSC::State::kConnected);
3295 out << GetStateHtml(fDimPfMiniControl, PFmini::State::kConnected);
3296 out << GetStateHtml(fDimBiasTemp, BiasTemp::State::kConnected);
3297 out << GetStateHtml(fDimGpsControl, GPS::State::kConnected);
3298 out << GetStateHtml(fDimSqmControl, SQM::State::kConnected);
3299 out << GetStateHtml(fDimAgilentControl24, Agilent::State::kVoltageOff);
3300 out << GetStateHtml(fDimAgilentControl50, Agilent::State::kVoltageOff);
3301 out << GetStateHtml(fDimAgilentControl80, Agilent::State::kVoltageOff);
3302 out << GetStateHtml(fDimPwrControl, Power::State::kSystemOff);
3303 out << GetStateHtml(fDimLidControl, Lid::State::kConnected);
3304 out << GetStateHtml(fDimRateScan, RateScan::State::kConnected);
3305 out << GetStateHtml(fDimMagicWeather, MagicWeather::State::kConnected);
3306 out << GetStateHtml(fDimTngWeather, TNGWeather::State::kConnected);
3307 out << GetStateHtml(fDimGtcDust, GTC::State::kReceiving);
3308 out << GetStateHtml(fDimMagicLidar, MagicLidar::State::kConnected);
3309 out << GetStateHtml(fDimTemperature, Temperature::State::kValid);
3310 out << GetStateHtml(fDimRainSensor, RainSensor::State::kValid);
3311 out << GetStateHtml(fDimChat, 0);
3312 out << GetStateHtml(fDimSkypeClient, 1);
3313
3314 string col = HTML::kRed;
3315 if (fFreeSpace>uint64_t(199999999999))
3316 col = HTML::kYellow;
3317 if (fFreeSpace>uint64_t(999999999999))
3318 col = HTML::kGreen;
3319 if (fFreeSpace==UINT64_MAX)
3320 col = HTML::kWhite;
3321
3322 out << col << '\t' << Tools::Scientific(fFreeSpace) << "B\n";
3323
3324 col = HTML::kRed;
3325 if (free_newdata > uint64_t(999999999999))
3326 col = HTML::kYellow;
3327 if (free_newdata > uint64_t(149999999999))
3328 col = HTML::kGreen;
3329 if (free_newdata == UINT64_MAX)
3330 col = HTML::kWhite;
3331
3332 out << col << '\t' << Tools::Scientific(free_newdata) << "B\n";
3333
3334 out << HTML::kGreen << '\t' << dt.str().substr(0, dt.str().length()-7) << '\n';
3335 }
3336
3337 ofstream(fPath+"/status.data") << out.str();
3338
3339 if (now-fLastAstroCalc>boost::posix_time::seconds(15))
3340 {
3341 UpdateAstronomy();
3342 fLastAstroCalc = now;
3343 }
3344
3345 return fDimDNS.online() ? kStateRunning : kStateDimNetworkNA;
3346 }
3347
3348
3349public:
3350 StateMachineSmartFACT(ostream &out=cout) : StateMachineDim(out, fIsServer?"SMART_FACT":""),
3351 fLastAstroCalc(boost::date_time::neg_infin),
3352 fPath("www/smartfact/data"),
3353 fMcpConfigurationState(DimState::kOffline),
3354 fMcpConfigurationMaxTime(0),
3355 fMcpConfigurationMaxEvents(0),
3356 fLastRunFinishedWithZeroEvents(false),
3357 fTngWeatherDustTime(Time::none),
3358 fGtcDustTime(Time::none),
3359 fBiasControlVoltageMed(0),
3360 fBiasControlCurrentMed(0),
3361 fBiasControlCurrentMax(0),
3362 fFscControlHumidityAvg(0),
3363 fDriveControlMoonDist(-1),
3364 fFadControlNumEvents(0),
3365 fFadControlDrsRuns(3),
3366 fFtmControlState(FTM::kFtmLocked),
3367 fRateScanDataId(0),
3368 fRateScanBoard(0),
3369 fFreeSpace(UINT64_MAX),
3370 // ---
3371 fDimMcp ("MCP"),
3372 fDimDataLogger ("DATA_LOGGER"),
3373 fDimDriveControl ("DRIVE_CONTROL"),
3374 fDimTimeCheck ("TIME_CHECK"),
3375 fDimMagicWeather ("MAGIC_WEATHER"),
3376 fDimMagicLidar ("MAGIC_LIDAR"),
3377 fDimTngWeather ("TNG_WEATHER"),
3378 fDimGtcDust ("GTC_DUST"),
3379 fDimTemperature ("TEMPERATURE"),
3380 fDimRainSensor ("RAIN_SENSOR"),
3381 fDimFeedback ("FEEDBACK"),
3382 fDimBiasControl ("BIAS_CONTROL"),
3383 fDimFtmControl ("FTM_CONTROL"),
3384 fDimFadControl ("FAD_CONTROL"),
3385 fDimFscControl ("FSC_CONTROL"),
3386 fDimPfMiniControl ("PFMINI_CONTROL"),
3387 fDimBiasTemp ("BIAS_TEMP"),
3388 fDimGpsControl ("GPS_CONTROL"),
3389 fDimSqmControl ("SQM_CONTROL"),
3390 fDimAgilentControl24("AGILENT_CONTROL_24V"),
3391 fDimAgilentControl50("AGILENT_CONTROL_50V"),
3392 fDimAgilentControl80("AGILENT_CONTROL_80V"),
3393 fDimPwrControl ("PWR_CONTROL"),
3394 fDimLidControl ("LID_CONTROL"),
3395 fDimRateControl ("RATE_CONTROL"),
3396 fDimRateScan ("RATE_SCAN"),
3397 fDimChat ("CHAT"),
3398 fDimSkypeClient ("SKYPE_CLIENT")
3399 {
3400 fDimDNS.Subscribe(*this);
3401 fDimControl.Subscribe(*this);
3402 fDimMcp.Subscribe(*this);
3403 fDimDataLogger.Subscribe(*this);
3404 fDimDriveControl.Subscribe(*this);
3405 fDimTimeCheck.Subscribe(*this);
3406 fDimMagicWeather.Subscribe(*this);
3407 fDimMagicLidar.Subscribe(*this);
3408 fDimTngWeather.Subscribe(*this);
3409 fDimGtcDust.Subscribe(*this);
3410 fDimTemperature.Subscribe(*this);
3411 fDimRainSensor.Subscribe(*this);
3412 fDimFeedback.Subscribe(*this);
3413 fDimBiasControl.Subscribe(*this);
3414 fDimFtmControl.Subscribe(*this);
3415 fDimFadControl.Subscribe(*this);
3416 fDimFscControl.Subscribe(*this);
3417 fDimPfMiniControl.Subscribe(*this);
3418 fDimBiasTemp.Subscribe(*this);
3419 fDimGpsControl.Subscribe(*this);
3420 fDimSqmControl.Subscribe(*this);
3421 fDimAgilentControl24.Subscribe(*this);
3422 fDimAgilentControl50.Subscribe(*this);
3423 fDimAgilentControl80.Subscribe(*this);
3424 fDimPwrControl.Subscribe(*this);
3425 fDimLidControl.Subscribe(*this);
3426 fDimRateControl.Subscribe(*this);
3427 fDimRateScan.Subscribe(*this);
3428 fDimChat.Subscribe(*this);
3429 fDimSkypeClient.Subscribe(*this);
3430
3431 fDimFscControl.SetCallback(bind(&StateMachineSmartFACT::HandleFscControlStateChange, this, placeholders::_1));
3432 //fDimFtmControl.SetCallback(bind(&StateMachineSmartFACT::HandleFtmControlStateChange, this));
3433 fDimDriveControl.SetCallback(bind(&StateMachineSmartFACT::HandleDriveControlStateChange, this, placeholders::_1));
3434 fDimControl.SetCallback(bind(&StateMachineSmartFACT::HandleControlStateChange, this, placeholders::_1));
3435 fDimControl.AddCallback("dotest.dim", bind(&StateMachineSmartFACT::HandleDoTest, this, placeholders::_1));
3436
3437 Subscribe("DIM_CONTROL/MESSAGE")
3438 (bind(&StateMachineSmartFACT::HandleDimControlMessage, this, placeholders::_1));
3439
3440 Subscribe("MCP/CONFIGURATION")
3441 (bind(&StateMachineSmartFACT::HandleMcpConfiguration, this, placeholders::_1));
3442
3443 Subscribe("DRIVE_CONTROL/POINTING_POSITION")
3444 (bind(&StateMachineSmartFACT::HandleDrivePointing, this, placeholders::_1));
3445 Subscribe("DRIVE_CONTROL/TRACKING_POSITION")
3446 (bind(&StateMachineSmartFACT::HandleDriveTracking, this, placeholders::_1));
3447 Subscribe("DRIVE_CONTROL/SOURCE_POSITION")
3448 (bind(&StateMachineSmartFACT::HandleDriveSource, this, placeholders::_1));
3449
3450 Subscribe("FSC_CONTROL/TEMPERATURE")
3451 (bind(&StateMachineSmartFACT::HandleFscTemperature, this, placeholders::_1));
3452 Subscribe("FSC_CONTROL/HUMIDITY")
3453 (bind(&StateMachineSmartFACT::HandleFscHumidity, this, placeholders::_1));
3454 Subscribe("FSC_CONTROL/BIAS_TEMP")
3455 (bind(&StateMachineSmartFACT::HandleFscBiasTemp, this, placeholders::_1));
3456
3457 Subscribe("PFMINI_CONTROL/DATA")
3458 (bind(&StateMachineSmartFACT::HandlePfMiniData, this, placeholders::_1));
3459
3460 Subscribe("BIAS_TEMP/DATA")
3461 (bind(&StateMachineSmartFACT::HandleBiasTemp, this, placeholders::_1));
3462
3463 Subscribe("GPS_CONTROL/NEMA")
3464 (bind(&StateMachineSmartFACT::HandleGpsNema, this, placeholders::_1));
3465
3466 Subscribe("SQM_CONTROL/DATA")
3467 (bind(&StateMachineSmartFACT::HandleSqmData, this, placeholders::_1));
3468
3469 Subscribe("TEMPERATURE/DATA")
3470 (bind(&StateMachineSmartFACT::HandleTemperatureData, this, placeholders::_1));
3471
3472 Subscribe("AGILENT_CONTROL_24V/DATA")
3473 (bind(&StateMachineSmartFACT::HandleAgilentData, this, placeholders::_1, "24"));
3474 Subscribe("AGILENT_CONTROL_50V/DATA")
3475 (bind(&StateMachineSmartFACT::HandleAgilentData, this, placeholders::_1, "50"));
3476 Subscribe("AGILENT_CONTROL_80V/DATA")
3477 (bind(&StateMachineSmartFACT::HandleAgilentData, this, placeholders::_1, "80"));
3478
3479 Subscribe("MAGIC_WEATHER/DATA")
3480 (bind(&StateMachineSmartFACT::HandleMagicWeatherData, this, placeholders::_1));
3481 Subscribe("TNG_WEATHER/DUST")
3482 (bind(&StateMachineSmartFACT::HandleTngWeatherDust, this, placeholders::_1));
3483 Subscribe("TNG_WEATHER/DATA")
3484 (bind(&StateMachineSmartFACT::HandleTngWeatherData, this, placeholders::_1));
3485 Subscribe("GTC_DUST/DATA")
3486 (bind(&StateMachineSmartFACT::HandleGtcDustData, this, placeholders::_1));
3487 Subscribe("RAIN_SENSOR/DATA")
3488 (bind(&StateMachineSmartFACT::HandleRainSensorData, this, placeholders::_1));
3489
3490 Subscribe("FEEDBACK/CALIBRATED_CURRENTS")
3491 (bind(&StateMachineSmartFACT::HandleFeedbackCalibratedCurrents, this, placeholders::_1));
3492
3493 Subscribe("BIAS_CONTROL/VOLTAGE")
3494 (bind(&StateMachineSmartFACT::HandleBiasVoltage, this, placeholders::_1));
3495 Subscribe("BIAS_CONTROL/CURRENT")
3496 (bind(&StateMachineSmartFACT::HandleBiasCurrent, this, placeholders::_1));
3497
3498 Subscribe("FAD_CONTROL/CONNECTIONS")
3499 (bind(&StateMachineSmartFACT::HandleFadConnections, this, placeholders::_1));
3500 Subscribe("FAD_CONTROL/EVENTS")
3501 (bind(&StateMachineSmartFACT::HandleFadEvents, this, placeholders::_1));
3502 Subscribe("FAD_CONTROL/START_RUN")
3503 (bind(&StateMachineSmartFACT::HandleFadStartRun, this, placeholders::_1));
3504 Subscribe("FAD_CONTROL/DRS_RUNS")
3505 (bind(&StateMachineSmartFACT::HandleFadDrsRuns, this, placeholders::_1));
3506 Subscribe("FAD_CONTROL/EVENT_DATA")
3507 (bind(&StateMachineSmartFACT::HandleFadEventData, this, placeholders::_1));
3508 Subscribe("FAD_CONTROL/STATS")
3509 (bind(&StateMachineSmartFACT::HandleStats, this, placeholders::_1));
3510
3511 Subscribe("DATA_LOGGER/STATS")
3512 (bind(&StateMachineSmartFACT::HandleStats, this, placeholders::_1));
3513
3514 Subscribe("FTM_CONTROL/TRIGGER_RATES")
3515 (bind(&StateMachineSmartFACT::HandleFtmTriggerRates, this, placeholders::_1));
3516 Subscribe("FTM_CONTROL/STATIC_DATA")
3517 (bind(&StateMachineSmartFACT::HandleFtmStaticData, this, placeholders::_1));
3518 Subscribe("FTM_CONTROL/FTU_LIST")
3519 (bind(&StateMachineSmartFACT::HandleFtmFtuList, this, placeholders::_1));
3520
3521 Subscribe("RATE_CONTROL/THRESHOLD")
3522 (bind(&StateMachineSmartFACT::HandleRateControlThreshold,this, placeholders::_1));
3523
3524 Subscribe("RATE_SCAN/DATA")
3525 (bind(&StateMachineSmartFACT::HandleRateScanData, this, placeholders::_1));
3526
3527 Subscribe("CHAT/MESSAGE")
3528 (bind(&StateMachineSmartFACT::HandleChatMsg, this, placeholders::_1));
3529
3530
3531 // =================================================================
3532
3533 // State names
3534 AddStateName(kStateDimNetworkNA, "DimNetworkNotAvailable",
3535 "The Dim DNS is not reachable.");
3536
3537 AddStateName(kStateRunning, "Running", "");
3538
3539 // =================================================================
3540
3541 AddEvent("PRINT")
3542 (bind(&StateMachineSmartFACT::Print, this))
3543 ("Print a list of the states of all connected servers.");
3544
3545 }
3546 int EvalOptions(Configuration &conf)
3547 {
3548 if (!fPixelMap.Read(conf.GetPrefixedString("pixel-map-file")))
3549 {
3550 Error("Reading mapping table from "+conf.Get<string>("pixel-map-file")+" failed.");
3551 return 1;
3552 }
3553
3554 fPath = conf.Get<string>("path");
3555 fDatabase = conf.Get<string>("source-database");
3556
3557 struct stat st;
3558 if (stat(fPath.c_str(), &st))
3559 {
3560 Error(fPath+" does not exist!");
3561 return 2;
3562 }
3563
3564 if ((st.st_mode&S_IFDIR)==0)
3565 {
3566 Error(fPath+" not a directory!");
3567 return 3;
3568 }
3569
3570 if ((st.st_mode&S_IWUSR)==0)
3571 {
3572 Error(fPath+" has no write permission!");
3573 return 4;
3574 }
3575
3576 if ((st.st_mode&S_IXUSR)==0)
3577 {
3578 Error(fPath+" has no execute permission!");
3579 return 5;
3580 }
3581
3582 ostringstream out;
3583 out << Time().JavaDate() << '\n';
3584
3585 ofstream(fPath+"/error.data") << out.str();
3586
3587 return -1;
3588 }
3589};
3590
3591bool StateMachineSmartFACT::fIsServer = false;
3592
3593// ------------------------------------------------------------------------
3594
3595#include "Main.h"
3596
3597template<class T>
3598int RunShell(Configuration &conf)
3599{
3600 StateMachineSmartFACT::fIsServer = !conf.Get<bool>("client");
3601 return Main::execute<T, StateMachineSmartFACT>(conf);
3602}
3603
3604void SetupConfiguration(Configuration &conf)
3605{
3606 po::options_description control("Smart FACT");
3607 control.add_options()
3608 ("pixel-map-file", var<string>()->required(), "Pixel mapping file. Used here to get the default reference voltage")
3609 ("path", var<string>("www/smartfact/data"), "Output path for the data-files")
3610 ("source-database", var<string>(""), "Database link as in\n\tuser:password@server[:port]/database[?compress=0|1].")
3611 ("client", po_bool(false), "For a standalone client choose this option.")
3612 ;
3613
3614 conf.AddOptions(control);
3615}
3616
3617/*
3618 Extract usage clause(s) [if any] for SYNOPSIS.
3619 Translators: "Usage" and "or" here are patterns (regular expressions) which
3620 are used to match the usage synopsis in program output. An example from cp
3621 (GNU coreutils) which contains both strings:
3622 Usage: cp [OPTION]... [-T] SOURCE DEST
3623 or: cp [OPTION]... SOURCE... DIRECTORY
3624 or: cp [OPTION]... -t DIRECTORY SOURCE...
3625 */
3626void PrintUsage()
3627{
3628 cout <<
3629 "SmartFACT is a tool writing the files needed for the SmartFACT web interface.\n"
3630 "\n"
3631 "The default is that the program is started without user intercation. "
3632 "All actions are supposed to arrive as DimCommands. Using the -c "
3633 "option, a local shell can be initialized. With h or help a short "
3634 "help message about the usuage can be brought to the screen.\n"
3635 "\n"
3636 "Usage: smartfact [-c type] [OPTIONS]\n"
3637 " or: smartfact [OPTIONS]\n";
3638 cout << endl;
3639}
3640
3641void PrintHelp()
3642{
3643 Main::PrintHelp<StateMachineSmartFACT>();
3644
3645 /* Additional help text which is printed after the configuration
3646 options goes here */
3647
3648 /*
3649 cout << "bla bla bla" << endl << endl;
3650 cout << endl;
3651 cout << "Environment:" << endl;
3652 cout << "environment" << endl;
3653 cout << endl;
3654 cout << "Examples:" << endl;
3655 cout << "test exam" << endl;
3656 cout << endl;
3657 cout << "Files:" << endl;
3658 cout << "files" << endl;
3659 cout << endl;
3660 */
3661}
3662
3663int main(int argc, const char* argv[])
3664{
3665 Configuration conf(argv[0]);
3666 conf.SetPrintUsage(PrintUsage);
3667 Main::SetupConfiguration(conf);
3668 SetupConfiguration(conf);
3669
3670 if (!conf.DoParse(argc, argv, PrintHelp))
3671 return 127;
3672
3673 if (!conf.Has("console"))
3674 return RunShell<LocalStream>(conf);
3675
3676 if (conf.Get<int>("console")==0)
3677 return RunShell<LocalShell>(conf);
3678 else
3679 return RunShell<LocalConsole>(conf);
3680
3681 return 0;
3682}
Note: See TracBrowser for help on using the repository browser.