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

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