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

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