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

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