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

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