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

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