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

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