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

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