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

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