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

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