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

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