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

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