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

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