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

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