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

Last change on this file since 17188 was 17172, checked in by tbretz, 12 years ago
Read bias patch wise tenmperatures from fscctrl instead of calculating itself so that all programs have consistent values and we store them in our slow control files.
File size: 103.8 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#include "externals/Interpolator2D.h"
21
22#include "tools.h"
23
24#include "LocalControl.h"
25
26#include "HeadersFAD.h"
27#include "HeadersBIAS.h"
28#include "HeadersFTM.h"
29#include "HeadersFSC.h"
30#include "HeadersMCP.h"
31#include "HeadersLid.h"
32#include "HeadersDrive.h"
33#include "HeadersPower.h"
34#include "HeadersAgilent.h"
35#include "HeadersFeedback.h"
36#include "HeadersRateScan.h"
37#include "HeadersRateControl.h"
38#include "HeadersTNGWeather.h"
39#include "HeadersMagicLidar.h"
40#include "HeadersMagicWeather.h"
41#include "HeadersTemperature.h"
42
43#include <boost/filesystem.hpp>
44
45using namespace std;
46
47// ------------------------------------------------------------------------
48
49#include "DimDescriptionService.h"
50#include "DimState.h"
51
52// ------------------------------------------------------------------------
53/*
54template<class T>
55 class buffer : public deque<T>
56 {
57 int32_t max_size;
58
59 public:
60 buffer(int32_t max=-1) : max_size(max) { }
61 const T &operator=(const T &t) const { push_back(t); if (max_size>0 && deque<T>::size()>max_size) deque<T>::pop_front(); }
62 operator T() const { return deque<T>::size()>0 ? deque<T>::back() : T(); }
63 bool valid() const { return deque<T>::size()>0; }
64 };
65*/
66
67// ------------------------------------------------------------------------
68
69namespace HTML
70{
71 const static string kWhite = "#ffffff";
72 const static string kYellow = "#fffff0";
73 const static string kRed = "#fff8f0";
74 const static string kGreen = "#f0fff0";
75 const static string kBlue = "#f0f0ff";
76};
77
78// ========================================================================
79// ========================================================================
80// ========================================================================
81
82class Sun
83{
84public:
85 Time time;
86
87 Time fRiseDayTime;
88 Time fRiseCivil;
89 Time fRiseAstronomical;
90 Time fRiseDarkTime;
91
92 Time fSetDayTime;
93 Time fSetCivil;
94 Time fSetAstronomical;
95 Time fSetDarkTime;
96
97 int state;
98 string description;
99 string color;
100
101 bool isday;
102 bool visible;
103
104public:
105 Sun() : time(Time::none)
106 {
107 }
108
109 // Could be done more efficient: Only recalcuate if
110 // the current time exceeds at least on of the stored times
111 Sun(const Time &t) : time(t)
112 {
113#ifdef HAVE_LIBNOVA
114 // get Julian day from local time
115 const double JD = time.JD();
116
117 // Warning: return code of 1 means circumpolar and is not checked!
118 Nova::RstTime sun_day = Nova::GetSolarRst(JD-0.5);
119 Nova::RstTime sun_civil = Nova::GetSolarRst(JD-0.5, -6);
120 Nova::RstTime sun_astronomical = Nova::GetSolarRst(JD-0.5, -12);
121 Nova::RstTime sun_dark = Nova::GetSolarRst(JD-0.5, -18);
122
123 fSetDayTime = Time(sun_day.set);
124 fSetCivil = Time(sun_civil.set);
125 fSetAstronomical = Time(sun_astronomical.set);
126 fSetDarkTime = Time(sun_dark.set);
127
128 fRiseDayTime = Time(sun_day.rise);
129 fRiseCivil = Time(sun_civil.rise);
130 fRiseAstronomical = Time(sun_astronomical.rise);
131 fRiseDarkTime = Time(sun_dark.rise);
132
133 const bool is_day = JD>sun_day.rise;
134 const bool is_night = JD>sun_dark.set;
135
136 sun_day = Nova::GetSolarRst(JD+0.5);
137 sun_civil = Nova::GetSolarRst(JD+0.5, -6);
138 sun_astronomical = Nova::GetSolarRst(JD+0.5, -12);
139 sun_dark = Nova::GetSolarRst(JD+0.5, -18);
140
141 if (is_day)
142 {
143 fRiseDayTime = Time(sun_day.rise);
144 fRiseCivil = Time(sun_civil.rise);
145 fRiseAstronomical = Time(sun_astronomical.rise);
146 fRiseDarkTime = Time(sun_dark.rise);
147 }
148
149 if (is_night)
150 {
151 fSetDayTime = Time(sun_day.set);
152 fSetCivil = Time(sun_civil.set);
153 fSetAstronomical = Time(sun_astronomical.set);
154 fSetDarkTime = Time(sun_dark.set);
155 }
156
157 // case 0: midnight to sun-rise | !is_day && !is_night | rise/set
158 // case 1: sun-rise to sun-set | is_day && !is_night | set /rise
159 // case 2: sun-set to midnight | is_day && is_night | rise/set
160
161 isday = is_day^is_night;
162
163 state = isday ? 4 : 0;
164 if (time>fSetDayTime) state++;
165 if (time>fSetCivil) state++;
166 if (time>fSetAstronomical) state++;
167 if (time>fSetDarkTime) state++;
168
169 if (time>fRiseDarkTime) state++;
170 if (time>fRiseAstronomical) state++;
171 if (time>fRiseCivil) state++;
172 if (time>fRiseDayTime) state++;
173
174 string name[] =
175 {
176 "dark time",
177 "astron. twilight",
178 "civil twilight",
179 "sunrise",
180 "day time",
181 "sunset",
182 "civil twilight",
183 "astron. twilight",
184 "dark time"
185 };
186
187 description = name[state];
188
189 const string arr = isday ?
190 fSetDarkTime.MinutesTo(time)+"&darr;" :
191 fRiseDarkTime.MinutesTo(time)+"&uarr;";
192
193 description += " ["+arr+"]";
194
195 switch (state)
196 {
197 case 0: case 1: color = HTML::kGreen; break;
198 case 2: case 3: color = HTML::kYellow; break;
199 case 4: color = HTML::kRed; break;
200 case 5: case 6: color = HTML::kYellow; break;
201 case 7: case 8: color = HTML::kGreen; break;
202 }
203
204 visible = state>=3 && state<=5;
205#endif
206 }
207};
208
209class Moon
210{
211public:
212 Time time;
213
214 double ra;
215 double dec;
216
217 double zd;
218 double az;
219
220 double disk;
221
222 bool visible;
223
224 Time fRise;
225 Time fTransit;
226 Time fSet;
227
228 string description;
229 string color;
230
231 int state;
232
233 Moon() : time(Time::none)
234 {
235 }
236
237 // Could be done more efficient: Only recalcuate if
238 // the current time exceeds at least on of the stored times
239 Moon(const Time &t) : time(t)
240 {
241#ifdef HAVE_LIBNOVA
242 const double JD = time.JD();
243
244 Nova::RstTime moon = Nova::GetLunarRst(JD-0.5);
245
246 fRise = Time(moon.rise);
247 fTransit = Time(moon.transit);
248 fSet = Time(moon.set);
249
250 //visible =
251 // ((JD>moon.rise && JD<moon.set ) && moon.rise<moon.set) ||
252 // ((JD<moon.set || JD>moon.rise) && moon.rise>moon.set);
253
254 const bool is_up = JD>moon.rise;
255 const bool is_sinking = JD>moon.transit;
256 const bool is_dn = JD>moon.set;
257
258 moon = Nova::GetLunarRst(JD+0.5);
259 if (is_up)
260 fRise = Time(moon.rise);
261 if (is_sinking)
262 fTransit = Time(moon.transit);
263 if (is_dn)
264 fSet = Time(moon.set);
265
266 const Nova::EquPosn pos = Nova::GetLunarEquCoords(JD);
267 const Nova::ZdAzPosn hrz = Nova::GetHrzFromEqu(pos, JD);
268
269 az = hrz.az;
270 zd = hrz.zd;
271
272 ra = pos.ra/15;
273 dec = pos.dec;
274
275 disk = Nova::GetLunarDisk(JD)*100;
276 state = 0;
277 if (fRise <fTransit && fRise <fSet) state = 0; // not visible
278 if (fTransit<fSet && fTransit<fRise) state = 1; // before culm
279 if (fSet <fRise && fSet <fTransit) state = 2; // after culm
280
281 visible = state!=0;
282
283 // 0: not visible
284 // 1: visible before cul
285 // 2: visible after cul
286
287 if (!visible || disk<25)
288 color = HTML::kGreen;
289 else
290 color = disk>75 ? HTML::kRed : HTML::kYellow;
291
292 const string arr = fSet<fRise ?
293 fSet.MinutesTo(time) +"&darr;" :
294 fRise.MinutesTo(time)+"&uarr;";
295
296 ostringstream out;
297 out << setprecision(2);
298 out << (visible?"visible ":"") << (disk<0.1?0:disk) << "% [" << arr << "]";
299
300 description = out.str();
301#endif
302 }
303
304 double Angle(double r, double d) const
305 {
306 const double theta0 = M_PI/2-d*M_PI/180;
307 const double phi0 = r*M_PI/12;
308
309 const double theta1 = M_PI/2-dec*M_PI/180;
310 const double phi1 = ra*M_PI/12;
311
312 const double x0 = sin(theta0) * cos(phi0);
313 const double y0 = sin(theta0) * sin(phi0);
314 const double z0 = cos(theta0);
315
316 const double x1 = sin(theta1) * cos(phi1);
317 const double y1 = sin(theta1) * sin(phi1);
318 const double z1 = cos(theta1);
319
320 double arg = x0*x1 + y0*y1 + z0*z1;
321 if(arg > 1.0) arg = 1.0;
322 if(arg < -1.0) arg = -1.0;
323
324 return acos(arg) * 180/M_PI;
325 }
326
327 static string Color(double angle)
328 {
329 if (angle<10 || angle>150)
330 return HTML::kRed;
331 if (angle<20 || angle>140)
332 return HTML::kYellow;
333 return HTML::kGreen;
334 }
335};
336
337// ========================================================================
338// ========================================================================
339// ========================================================================
340
341class StateMachineSmartFACT : public StateMachineDim
342{
343public:
344 static bool fIsServer;
345
346private:
347 enum states_t
348 {
349 kStateDimNetworkNA = 1,
350 kStateRunning,
351 };
352
353 // ------------------------- History classes -----------------------
354
355 struct EventElement
356 {
357 Time time;
358 string msg;
359
360 EventElement(const Time &t, const string &s) : time(t), msg(s) { }
361 };
362
363 class EventHist : public list<EventElement>
364 {
365 const boost::posix_time::time_duration deltat; //boost::posix_time::pos_infin
366 const uint64_t max;
367
368 public:
369 EventHist(const boost::posix_time::time_duration &dt=boost::posix_time::hours(12), uint64_t mx=UINT64_MAX) : deltat(dt), max(mx) { }
370
371 void add(const string &s, const Time &t=Time())
372 {
373 while (!empty() && (front().time+deltat<t || size()>max))
374 pop_front();
375
376 emplace_back(t, s);
377 }
378
379 void clean()
380 {
381 for (auto it=begin(); it!=end();)
382 if (!it->time)
383 {
384 const auto is = it++;
385 erase(is);
386 }
387 }
388
389 string get() const
390 {
391 ostringstream out;
392
393 string last = "";
394 for (auto it=begin(); it!=end(); it++)
395 {
396 const string tm = it->time.GetAsStr("%H:%M:%S ");
397 out << (tm!=last?tm:"--:--:-- ") << it->msg << "<br/>";
398 last = tm;
399 }
400
401 return out.str();
402 }
403 string rget() const
404 {
405 ostringstream out;
406
407 for (auto it=rbegin(); it!=rend(); it++)
408 out << it->time.GetAsStr("%H:%M:%S ") << it->msg << "<br/>";
409
410 return out.str();
411 }
412 };
413
414 // ------------------------- Internal variables -----------------------
415
416 const Time fRunTime;
417
418 PixelMap fPixelMap;
419
420 string fDatabase;
421
422 Time fLastUpdate;
423 Time fLastAstroCalc;
424
425 string fPath;
426
427 // ----------------------------- Data storage -------------------------
428
429 EventHist fControlMessageHist;
430 EventHist fControlAlarmHist;
431 int32_t fControlScriptDepth;
432
433 int32_t fMcpConfigurationState; // For consistency
434 int64_t fMcpConfigurationMaxTime;
435 int64_t fMcpConfigurationMaxEvents;
436 string fMcpConfigurationName;
437 Time fMcpConfigurationRunStart;
438 EventHist fMcpConfigurationHist;
439
440 bool fLastRunFinishedWithZeroEvents;
441
442 enum weather_t { kWeatherBegin=0, kTemp = kWeatherBegin, kDew, kHum, kPress, kWind, kGusts, kDir, kWeatherEnd = kDir+1 };
443 deque<float> fMagicWeatherHist[kWeatherEnd];
444
445 deque<float> fTngWeatherDustHist;
446 Time fTngWeatherDustTime;
447
448 vector<float> fBiasControlVoltageVec;
449
450 float fBiasControlPowerTot;
451 float fBiasControlVoltageMed;
452 float fBiasControlCurrentMed;
453 float fBiasControlCurrentMax;
454
455 deque<float> fBiasControlCurrentHist;
456 deque<float> fFscControlTemperatureHist;
457
458 float fFscControlHumidityAvg;
459
460 float fDriveControlPointingZd;
461 string fDriveControlPointingAz;
462 string fDriveControlSourceName;
463 float fDriveControlMoonDist;
464
465 deque<float> fDriveControlTrackingDevHist;
466
467 int64_t fFadControlNumEvents;
468 int64_t fFadControlStartRun;
469 int32_t fFadControlDrsStep;
470 vector<uint32_t> fFadControlDrsRuns;
471
472 deque<float> fFtmControlTriggerRateHist;
473 int32_t fFtmControlTriggerRateTooLow;
474 int fFtmControlState;
475
476 float fFtmPatchThresholdMed;
477 float fFtmBoardThresholdMed;
478
479 bool fFtmControlFtuOk;
480
481 deque<float> fRateControlThreshold;
482
483 uint64_t fRateScanDataId;
484 uint8_t fRateScanBoard;
485 deque<float> fRateScanDataHist[41];
486
487 set<string> fErrorList;
488 EventHist fErrorHist;
489 EventHist fChatHist;
490
491 uint64_t fFreeSpace;
492
493 Sun fSun;
494 Moon fMoon;
495
496 // --------------------------- File header ----------------------------
497
498 Time fAudioTime;
499 string fAudioName;
500
501 string Header(const Time &d)
502 {
503 ostringstream msg;
504 msg << d.JavaDate() << '\t' << fAudioTime.JavaDate() << '\t' << fAudioName;
505 return msg.str();
506 }
507
508 string Header(const EventImp &d)
509 {
510 return Header(d.GetTime());
511 }
512
513 void SetAudio(const string &name)
514 {
515 fAudioName = name;
516 fAudioTime = Time();
517 }
518
519 // ------------- Initialize variables before the Dim stuff ------------
520
521 DimVersion fDimDNS;
522 DimControl fDimControl;
523 DimDescribedState fDimMcp;
524 DimDescribedState fDimDataLogger;
525 DimDescribedState fDimDriveControl;
526 DimDescribedState fDimTimeCheck;
527 DimDescribedState fDimMagicWeather;
528 DimDescribedState fDimMagicLidar;
529 DimDescribedState fDimTngWeather;
530 DimDescribedState fDimTemperature;
531 DimDescribedState fDimFeedback;
532 DimDescribedState fDimBiasControl;
533 DimDescribedState fDimFtmControl;
534 DimDescribedState fDimFadControl;
535 DimDescribedState fDimFscControl;
536 DimDescribedState fDimAgilentControl;
537 DimDescribedState fDimPwrControl;
538 DimDescribedState fDimLidControl;
539 DimDescribedState fDimRateControl;
540 DimDescribedState fDimRateScan;
541 DimDescribedState fDimChat;
542 DimDescribedState fDimSkypeClient;
543
544 // -------------------------------------------------------------------
545
546 string GetDir(const double angle)
547 {
548 static const char *dir[] =
549 {
550 "N", "NNE", "NE", "ENE",
551 "E", "ESE", "SE", "SSE",
552 "S", "SSW", "SW", "WSW",
553 "W", "WNW", "NW", "NNW"
554 };
555
556 const uint16_t idx = uint16_t(floor(angle/22.5+16.5))%16;
557 return dir[idx];
558 }
559
560 // -------------------------------------------------------------------
561
562 bool CheckDataSize(const EventImp &d, const char *name, size_t size, bool min=false)
563 {
564 if (d.GetSize()==0)
565 return false;
566
567 if ((!min && d.GetSize()==size) || (min && d.GetSize()>size))
568 return true;
569
570 ostringstream msg;
571 msg << name << " - Received service has " << d.GetSize() << " bytes, but expected ";
572 if (min)
573 msg << "more than ";
574 msg << size << ".";
575 Warn(msg);
576 return false;
577 }
578
579 // -------------------------------------------------------------------
580
581 template<class T>
582 void WriteBinaryVec(const Time &tm, const string &fname, const vector<T> &vec, double scale, double offset=0, const string &title="", const string &col="")
583 {
584 if (vec.empty())
585 return;
586
587 ostringstream out;
588 out << tm.JavaDate() << '\n';
589 out << offset << '\n';
590 out << offset+scale << '\n';
591 out << setprecision(3);
592 if (!title.empty())
593 out << title << '\x7f';
594 else
595 {
596 const Statistics stat(vec[0]);
597 out << stat.min << '\n';
598 out << stat.med << '\n';
599 out << stat.max << '\x7f';
600 }
601 if (!col.empty())
602 out << col;
603 for (auto it=vec.begin(); it!=vec.end(); it++)
604 {
605 // The valid range is from 1 to 127
606 // \0 is used to seperate different curves
607 vector<uint8_t> val(it->size());
608 for (uint64_t i=0; i<it->size(); i++)
609 {
610 float range = nearbyint(126*(double(it->at(i))-offset)/scale); // [-2V; 2V]
611 if (range>126)
612 range=126;
613 if (range<0)
614 range=0;
615 val[i] = (uint8_t)range;
616 }
617
618 const char *ptr = reinterpret_cast<char*>(val.data());
619 out.write(ptr, val.size()*sizeof(uint8_t));
620 out << '\x7f';
621 }
622
623 ofstream(fPath+"/"+fname+".bin") << out.str();
624 }
625 /*
626 template<class T>
627 void WriteBinaryVec(const EventImp &d, const string &fname, const vector<T> &vec, double scale, double offset=0, const string &title="")
628 {
629 WriteBinaryVec(d.GetTime(), fname, vec, scale, offset, title);
630 }
631
632 template<class T>
633 void WriteBinary(const Time &tm, const string &fname, const T &t, double scale, double offset=0)
634 {
635 WriteBinaryVec(tm, fname, vector<T>(&t, &t+1), scale, offset);
636 }
637
638 template<class T>
639 void WriteBinary(const EventImp &d, const string &fname, const T &t, double scale, double offset=0)
640 {
641 WriteBinaryVec(d.GetTime(), fname, vector<T>(&t, &t+1), scale, offset);
642 }*/
643
644 template<class T>
645 void WriteHist(const EventImp &d, const string &fname, const T &t, double scale, double offset=0)
646 {
647 WriteBinaryVec(d.GetTime(), fname, vector<T>(&t, &t+1), scale, offset, "", "000");
648 }
649
650 template<class T>
651 void WriteCam(const EventImp &d, const string &fname, const T &t, double scale, double offset=0)
652 {
653 WriteBinaryVec(d.GetTime(), fname, vector<T>(&t, &t+1), scale, offset, "", "");
654 }
655
656
657 // -------------------------------------------------------------------
658
659 struct Statistics
660 {
661 float min;
662 float max;
663 float med;
664 float avg;
665 //float rms;
666
667 template<class T>
668 Statistics(const T &t, size_t offset_min=0, size_t offset_max=0)
669 : min(0), max(0), med(0), avg(0)
670 {
671 if (t.empty())
672 return;
673
674 T copy(t);
675 sort(copy.begin(), copy.end());
676
677 if (offset_min>t.size())
678 offset_min = 0;
679 if (offset_max>t.size())
680 offset_max = 0;
681
682 min = copy[offset_min];
683 max = copy[copy.size()-1-offset_max];
684 avg = accumulate (t.begin(), t.end(), 0.)/t.size();
685
686 const size_t p = t.size()/2;
687 med = p%2 ? copy[p] : (copy[p-1]+copy[p])/2.;
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 /= num;
1699 rms += avg*avg;
1700 rms = rms<0 ? 0 : sqrt(rms);
1701
1702 // Clean broken reports
1703 static double pre_rms1 = 1.5;
1704 static double pre_rms2 = 0;
1705
1706 const double cut = pre_rms1 + 0.1;
1707
1708 const bool reject = rms>cut && pre_rms2<cut;
1709
1710 pre_rms2 = pre_rms1;
1711 pre_rms1 = rms;
1712
1713 if (reject)
1714 return GetCurrentState();
1715
1716
1717 if (!fMagicWeatherHist[kTemp].empty())
1718 {
1719 fFscControlTemperatureHist.push_back(avg-fMagicWeatherHist[kTemp].back());
1720 if (fFscControlTemperatureHist.size()>300)
1721 fFscControlTemperatureHist.pop_front();
1722 }
1723
1724 const Statistics stat(fFscControlTemperatureHist);
1725
1726 ostringstream out;
1727 out << setprecision(3);
1728 out << d.GetJavaDate() << '\n';
1729 out << HTML::kWhite << '\t' << fFscControlHumidityAvg << '\n';
1730 out << HTML::kWhite << '\t' << stat.min << '\n';
1731 out << HTML::kWhite << '\t' << stat.avg << '\n';
1732 out << HTML::kWhite << '\t' << stat.max << '\n';
1733
1734 ofstream(fPath+"/fsc.data") << out.str();
1735
1736 WriteHist(d, "hist-fsccontrol-temperature",
1737 fFscControlTemperatureHist, 10);
1738
1739 out.str("");
1740 out << setprecision(3);
1741 out << d.GetJavaDate() << '\n';
1742 out << HTML::kWhite << '\t' << max << '\n';
1743 out << HTML::kWhite << '\t' << avg << '\n';
1744 out << HTML::kWhite << '\t' << min << '\n';
1745
1746 ofstream(fPath+"/camtemp.data") << out.str();
1747
1748 return GetCurrentState();
1749 }
1750
1751 int HandleFscBiasTemp(const EventImp &d)
1752 {
1753 if (!CheckDataSize(d, "FscControl:BiasTemp", 321*4))
1754 return GetCurrentState();
1755
1756 const float *ptr = d.Ptr<float>(4);
1757
1758 vector<double> tout(320);
1759 double pavg = 0;
1760 for (int i=0; i<320; i++)
1761 {
1762 const int idx = (fPixelMap.hv(i).hw()/9)*2+fPixelMap.hv(i).group();
1763 tout[idx] = ptr[i];
1764 pavg += ptr[i];
1765 }
1766
1767 WriteCam(d, "cam-fsccontrol-temperature", tout, 3, pavg/320-1.75);
1768
1769 return GetCurrentState();
1770 }
1771
1772 int HandleFscHumidity(const EventImp &d)
1773 {
1774 if (!CheckDataSize(d, "FscControl:Humidity", 5*4))
1775 return GetCurrentState();
1776
1777 const float *ptr = d.Ptr<float>(4);
1778
1779 double avg =0;
1780 int num = 0;
1781
1782 for (const float *t=ptr; t<ptr+4; t++)
1783 if (*t>0 && *t<=100 && t!=ptr+2 /*excl broken sensor*/)
1784 {
1785 avg += *t;
1786 num++;
1787 }
1788
1789 fFscControlHumidityAvg = num>0 ? avg/num : 0;
1790
1791 return GetCurrentState();
1792 }
1793
1794 int HandleRateScanData(const EventImp &d)
1795 {
1796 if (!CheckDataSize(d, "RateScan:Data", 824))
1797 return GetCurrentState();
1798
1799 const uint64_t id = d.Get<uint64_t>();
1800 const float *rate = d.Ptr<float>(20);
1801
1802 if (fRateScanDataId!=id)
1803 {
1804 for (int i=0; i<41; i++)
1805 fRateScanDataHist[i].clear();
1806 fRateScanDataId = id;
1807 }
1808 fRateScanDataHist[0].push_back(log10(rate[0]));
1809
1810 double max = 0;
1811 for (int i=1; i<41; i++)
1812 {
1813 fRateScanDataHist[i].push_back(log10(rate[i]));
1814 if (rate[i]>max)
1815 max = rate[i];
1816 }
1817
1818 // Cycle by time!
1819 fRateScanBoard ++;
1820 fRateScanBoard %= 40;
1821
1822 WriteHist(d, "hist-ratescan", fRateScanDataHist[0], 10, -2);
1823 WriteCam(d, "cam-ratescan-board", fRateScanDataHist[fRateScanBoard+1], 10, -4);
1824
1825 ostringstream out;
1826 out << setprecision(3);
1827 out << d.GetJavaDate() << '\n';
1828 out << HTML::kWhite << '\t' << fFtmBoardThresholdMed << '\n';
1829 out << HTML::kWhite << '\t' << fFtmPatchThresholdMed << '\n';
1830 out << HTML::kWhite << '\t' << floor(pow(10, fRateScanDataHist[0].back())+.5) << '\n';
1831 out << HTML::kWhite << '\t' << floor(max+.5) << '\n';
1832
1833 ofstream(fPath+"/ratescan.data") << out.str();
1834
1835 out.str("");
1836 out << d.GetJavaDate() << '\n';
1837 out << HTML::kWhite << '\t' << int(fRateScanBoard) << '\n';
1838 out << HTML::kWhite << '\t' << pow(10, fRateScanDataHist[fRateScanBoard+1].back()) << '\n';
1839
1840 ofstream(fPath+"/ratescan_board.data") << out.str();
1841
1842 return GetCurrentState();
1843 }
1844
1845 int HandleRateControlThreshold(const EventImp &d)
1846 {
1847 if (!CheckDataSize(d, "RateControl:Threshold", 18))
1848 return GetCurrentState();
1849
1850 const uint16_t th = d.Get<uint16_t>();
1851
1852 fRateControlThreshold.push_back(th);
1853 if (fRateControlThreshold.size()>300)
1854 fRateControlThreshold.pop_front();
1855
1856 WriteHist(d, "hist-ratecontrol-threshold", fRateControlThreshold, 1000);
1857
1858 return GetCurrentState();
1859 }
1860
1861 int HandleChatMsg(const EventImp &d)
1862 {
1863 if (d.GetSize()==0 || d.GetQoS()!=MessageImp::kComment)
1864 return GetCurrentState();
1865
1866 if (Time()<d.GetTime()+boost::posix_time::minutes(1))
1867 SetAudio("message");
1868
1869 fChatHist.add(d.GetText(), d.GetTime());
1870
1871 ostringstream out;
1872 out << setprecision(3);
1873 out << Header(d) << '\n';
1874 out << HTML::kWhite << '\t';
1875 out << "<->" << fChatHist.rget() << "</->";
1876 out << '\n';
1877
1878 ofstream(fPath+"/chat.data") << out.str();
1879
1880 return GetCurrentState();
1881 }
1882
1883 // -------------------------------------------------------------------
1884
1885 int HandleDoTest(const EventImp &d)
1886 {
1887 ostringstream out;
1888 out << d.GetJavaDate() << '\n';
1889
1890 switch (d.GetQoS())
1891 {
1892 case -3: out << HTML::kWhite << "\tNot running\n"; break;
1893 case -2: out << HTML::kBlue << "\tLoading\n"; break;
1894 case -1: out << HTML::kBlue << "\tStarted\n"; break;
1895 default: out << HTML::kGreen << "\tRunning [" << d.GetQoS() << "]\n"; break;
1896 }
1897
1898 ofstream(fPath+"/dotest.data") << out.str();
1899
1900 return StateMachineImp::kSM_KeepState;
1901 }
1902
1903 // -------------------------------------------------------------------
1904
1905 /*
1906 bool CheckEventSize(size_t has, const char *name, size_t size)
1907 {
1908 if (has==size)
1909 return true;
1910
1911 ostringstream msg;
1912 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
1913 Fatal(msg);
1914 return false;
1915 }*/
1916
1917 int Print() const
1918 {
1919 Out() << fDimDNS << endl;
1920 Out() << fDimMcp << endl;
1921 Out() << fDimControl << endl;
1922 Out() << fDimDataLogger << endl;
1923 Out() << fDimDriveControl << endl;
1924 Out() << fDimTimeCheck << endl;
1925 Out() << fDimFadControl << endl;
1926 Out() << fDimFtmControl << endl;
1927 Out() << fDimBiasControl << endl;
1928 Out() << fDimFeedback << endl;
1929 Out() << fDimRateControl << endl;
1930 Out() << fDimFscControl << endl;
1931 Out() << fDimAgilentControl << endl;
1932 Out() << fDimPwrControl << endl;
1933 Out() << fDimLidControl << endl;
1934 Out() << fDimMagicWeather << endl;
1935 Out() << fDimTngWeather << endl;
1936 Out() << fDimMagicLidar << endl;
1937 Out() << fDimTemperature << endl;
1938 Out() << fDimRateScan << endl;
1939 Out() << fDimChat << endl;
1940 Out() << fDimSkypeClient << endl;
1941
1942 return GetCurrentState();
1943 }
1944
1945 string GetStateHtml(const DimState &state, int green) const
1946 {
1947 if (!state.online())
1948 return HTML::kWhite+"\t&mdash;\n";
1949
1950 if (&state==&fDimControl)
1951 return HTML::kGreen +'\t'+(state.state()==0?"Idle":fDimControl.shortmsg)+'\n';
1952
1953 const State rc = state.description();
1954
1955 // Sate not found in list, server online (-3: offline; -2: not found)
1956 if (rc.index==-2)
1957 {
1958 ostringstream out;
1959 out << HTML::kWhite << '\t' << state.state() << '\n';
1960 return out.str();
1961 }
1962
1963 //ostringstream msg;
1964 //msg << HTML::kWhite << '\t' << rc.name << " [" << rc.index << "]\n";
1965 //return msg.str();
1966
1967 if (rc.index<0)
1968 return HTML::kWhite + "\t&mdash;\n";
1969
1970 string col = HTML::kGreen;
1971 if (rc.index<green)
1972 col = HTML::kYellow;
1973 if (rc.index>0xff)
1974 col = HTML::kRed;
1975
1976 return col + '\t' + rc.name + '\n';
1977 }
1978
1979 bool SetError(bool b, const string &err)
1980 {
1981 if (!b)
1982 {
1983 fErrorList.erase(err);
1984 return 0;
1985 }
1986
1987 const bool isnew = fErrorList.insert(err).second;
1988 if (isnew)
1989 fErrorHist.add(err);
1990
1991 return isnew;
1992 }
1993
1994#ifdef HAVE_NOVA
1995
1996 vector<pair<Nova::EquPosn, double>> fMoonCoords;
1997
1998 void CalcMoonCoords(double jd)
1999 {
2000 jd = floor(jd);
2001
2002 fMoonCoords.clear();
2003 for (double h=0; h<1; h+=1./(24*12))
2004 {
2005 const Nova::EquPosn moon = Nova::GetLunarEquCoords(jd+h,0.01);
2006
2007 const double disk = Nova::GetLunarDisk(jd+h);
2008
2009 fMoonCoords.emplace_back(moon, disk);
2010 }
2011 }
2012
2013 pair<vector<float>, pair<Time, float>> GetVisibility(Nova::EquPosn *src, 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 Nova::EquPosn moon;
2021 Nova::EquPosn *pos = src ? src : &moon;
2022
2023 double max = 0;
2024 double maxjd = 0;
2025
2026 int cnt = 0;
2027
2028 int i=0;
2029
2030 vector<float> alt;
2031 for (double h=0; h<1; h+=1./(24*12), i++)
2032 {
2033 if (src==0)
2034 moon = fMoonCoords[i].first; //ln_get_lunar_equ_coords_prec(jd+h, &moon, 0.01);
2035
2036 const Nova::HrzPosn hrz = Nova::GetHrzFromEqu(*pos, jd+h);
2037
2038 if (h>jd0 && h<jd1)
2039 alt.push_back(hrz.alt);
2040
2041 if (hrz.alt>max)
2042 {
2043 max = hrz.alt;
2044 maxjd = jd+h;
2045 }
2046
2047 if (h>jd0 && h<jd1 && hrz.alt>15)
2048 cnt++;
2049 }
2050
2051 if (max<=15 || cnt==0)
2052 return make_pair(vector<float>(), make_pair(Time(), 0));
2053
2054 return make_pair(alt, make_pair(maxjd, maxjd>jd+jd0&&maxjd<jd+jd1?max:0));
2055 }
2056
2057 pair<vector<float>, pair<Time, float>> GetLightCondition(double jd)
2058 {
2059 jd = floor(jd);
2060
2061 const double jd0 = fmod(fSun.fSetAstronomical.JD(), 1);
2062 const double jd1 = fmod(fSun.fRiseAstronomical.JD(), 1);
2063
2064 double max = -1;
2065 double maxjd = 0;
2066
2067 int cnt = 0;
2068 int i = 0;
2069
2070 vector<float> vec;
2071 for (double h=0; h<1; h+=1./(24*12), i++)
2072 {
2073 double cur = -1;
2074
2075 if (h>jd0 && h<jd1)
2076 {
2077 Nova::EquPosn moon = fMoonCoords[i].first;//ln_get_lunar_equ_coords_prec(jd+h, &moon, 0.01);
2078 const double disk = fMoonCoords[i].second;//ln_get_lunar_disk(jd+h);
2079
2080 const Nova::HrzPosn hrz = Nova::GetHrzFromEqu(moon, jd+h);
2081 const double dist = Nova::GetLunarEarthDist(jd+h);
2082
2083 // Current prediction
2084 const double calt = sin(hrz.alt*M_PI/180);
2085
2086 const double lc = calt>0 ? calt*pow(disk, 2.2)*pow(dist, -2) : -1;
2087 cur = lc>0 ? 4+103*lc : -1;
2088
2089 vec.push_back(cur); // Covert LC to pixel current in uA
2090 }
2091
2092 if (cur>max)
2093 {
2094 max = cur;
2095 maxjd = jd+h;
2096 }
2097
2098 if (h>jd0 && h<jd1 && cur>0)
2099 cnt++;
2100 }
2101
2102 if (max<=0 || cnt==0)
2103 return make_pair(vector<float>(), make_pair(Time(), 0));
2104
2105 return make_pair(vec, make_pair(maxjd, maxjd>jd+jd0&&maxjd<jd+jd1?max:-1));
2106 }
2107#endif
2108
2109 void UpdateAstronomy()
2110 {
2111 Time now;
2112
2113 CalcMoonCoords(now.JD());
2114
2115 fSun = Sun (now);
2116 fMoon = Moon(now);
2117
2118 vector<string> color(8, HTML::kWhite);
2119 color[fSun.state%8] = HTML::kBlue;
2120
2121 ostringstream out;
2122 out << setprecision(3);
2123 out << now.JavaDate() << '\n';
2124 out << color[0] << '\t' << fSun.fRiseDarkTime.GetAsStr("%H:%M") << '\n';
2125 out << color[1] << '\t' << fSun.fRiseAstronomical.GetAsStr("%H:%M") << '\n';
2126 out << color[2] << '\t' << fSun.fRiseCivil.GetAsStr("%H:%M") << '\n';
2127 out << color[3] << '\t' << fSun.fRiseDayTime.GetAsStr("%H:%M") << '\n';
2128
2129 out << color[4] << '\t' << fSun.fSetDayTime.GetAsStr("%H:%M") << '\n';
2130 out << color[5] << '\t' << fSun.fSetCivil.GetAsStr("%H:%M") << '\n';
2131 out << color[6] << '\t' << fSun.fSetAstronomical.GetAsStr("%H:%M") << '\n';
2132 out << color[7] << '\t' << fSun.fSetDarkTime.GetAsStr("%H:%M") << '\n';
2133
2134 ofstream(fPath+"/sun.data") << out.str();
2135
2136 color.assign(3, HTML::kWhite);
2137 color[fMoon.state%3] = HTML::kBlue;
2138
2139 out.str("");
2140 out << now.JavaDate() << '\n';
2141
2142 out << color[0] << '\t' << fMoon.fRise.GetAsStr("%H:%M") << '\n';
2143 out << color[1] << '\t' << fMoon.fTransit.GetAsStr("%H:%M") << '\n';
2144 out << color[2] << '\t' << fMoon.fSet.GetAsStr("%H:%M") << '\n';
2145
2146 out << (fSun.isday?HTML::kWhite:fMoon.color) << '\t' << fMoon.description << '\n';
2147
2148 if (!fMoon.visible)
2149 out << HTML::kWhite << "\t&mdash;\t\n";
2150 else
2151 {
2152 string col = HTML::kWhite;
2153 if (!fSun.isday)
2154 {
2155 col = HTML::kGreen;
2156 if (fMoon.zd>25)
2157 col = HTML::kYellow;
2158 if (fMoon.zd>45 && fMoon.zd<80)
2159 col = HTML::kRed;
2160 if (fMoon.zd>=80)
2161 col = HTML::kRed;
2162 }
2163 out << col << '\t' << fMoon.zd << '\t' << GetDir(fMoon.az) << '\n';
2164 }
2165
2166 ostringstream out2, out3, out4;
2167 out2 << setprecision(3);
2168 out2 << now.JavaDate() << '\n';
2169 out3 << now.JavaDate() << '\n';
2170 out4 << now.JavaDate() << '\n';
2171
2172 struct Entry
2173 {
2174 string name;
2175 float value;
2176 int color;
2177 Entry(const string &n, float v, int c) : name(n), value(v), color(c%8) { }
2178
2179 const string &Col() const
2180 {
2181 // If this list is updatd the number count in the constructor needs
2182 // to be updated, too
2183 static const string hcol[] = { "888", "8cf", "c8f", "bbb", "8fc", "cf8", "f8c", "fc8" };
2184 return hcol[color];
2185 }
2186
2187 vector<float> GetColor(double scale, double offset=0) const
2188 {
2189 vector<float> rc(3);
2190 rc[0] = double(Col()[0])*scale/126+offset;
2191 rc[1] = double(Col()[1])*scale/126+offset;
2192 rc[2] = double(Col()[2])*scale/126+offset;
2193 return rc;
2194 }
2195 };
2196
2197 multimap<Time, Entry> culmination;
2198 multimap<Time, Entry> lightcond;
2199 vector<vector<float>> alt;
2200 vector<vector<float>> cur;
2201
2202#ifdef HAVE_NOVA
2203 int ccol = 0;
2204 int lcol = 0;
2205
2206 /*const*/ pair<vector<float>, pair<Time, float>> vism = GetVisibility(0, now.JD());
2207 if (!vism.first.empty())
2208 {
2209 const Entry entry("Moon", vism.second.second, ccol);
2210 culmination.insert(make_pair(vism.second.first, entry));
2211 const vector<float> col = entry.GetColor(75, 15);
2212 vism.first.insert(vism.first.begin(), col.begin(), col.end());
2213 alt.push_back(vism.first);
2214
2215 ccol++;
2216 }
2217#endif
2218
2219#ifdef HAVE_SQL
2220 try
2221 {
2222 const mysqlpp::StoreQueryResult res =
2223 Database(fDatabase).query("SELECT fSourceName, fRightAscension, fDeclination FROM source WHERE fSourceTypeKEY=1").store();
2224
2225 out << HTML::kWhite << '\t';
2226 out2 << HTML::kWhite << '\t';
2227 out3 << HTML::kWhite << '\t';
2228 out4 << HTML::kWhite << '\t';
2229
2230 for (vector<mysqlpp::Row>::const_iterator v=res.begin(); v<res.end(); v++)
2231 {
2232 const string name = (*v)[0].c_str();
2233 const double ra = (*v)[1];
2234 const double dec = (*v)[2];
2235#ifdef HAVE_NOVA
2236 Nova::EquPosn pos;
2237 pos.ra = ra*15;
2238 pos.dec = dec;
2239
2240 const Nova::ZdAzPosn hrz = Nova::GetHrzFromEqu(pos, now.JD());
2241
2242 /*const*/ pair<vector<float>, pair<Time, float>> vis = GetVisibility(&pos, now.JD());
2243 if (!vis.first.empty())
2244 {
2245 const Entry entry(name, vis.second.second, ccol);
2246 culmination.insert(make_pair(vis.second.first, entry));
2247 const vector<float> col = entry.GetColor(75, 15);
2248 vis.first.insert(vis.first.begin(), col.begin(), col.end());
2249 alt.push_back(vis.first);
2250
2251 ccol++;
2252
2253 /*const*/ pair<vector<float>, pair<Time, float>> lc = GetLightCondition(now.JD());
2254 if (!lc.first.empty())
2255 {
2256 const Entry entry2(name, lc.second.second, lcol);
2257 lightcond.insert(make_pair(lc.second.first, entry2));
2258 const vector<float> col2 = entry2.GetColor(100);
2259 lc.first.insert(lc.first.begin(), col2.begin(), col2.end());
2260 cur.push_back(lc.first);
2261
2262 lcol++;
2263 }
2264 }
2265
2266 string col = HTML::kWhite;
2267 if (hrz.zd<85)
2268 col = HTML::kRed;
2269 if (hrz.zd<65)
2270 col = HTML::kYellow;
2271 if (hrz.zd<30)
2272 col = HTML::kGreen;
2273
2274 out2 << "<tr bgcolor='" << col << "'>";
2275 out2 << "<td>" << name << "</td>";
2276 if (hrz.zd<85)
2277 {
2278 out2 << "<td>" << hrz.zd << "&deg;</td>";
2279 out2 << "<td>" << GetDir(hrz.az) << "</td>";
2280 }
2281 else
2282 out2 << "<td/><td/>";
2283 out2 << "</tr>";
2284#endif
2285 const int32_t angle = fMoon.Angle(ra, dec);
2286
2287 out << "<tr bgcolor='" << Moon::Color(angle) << "'>";
2288 out << "<td>" << name << "</td>";
2289 out << "<td>" << round(angle) << "&deg;</td>";
2290 out << "</tr>";
2291 }
2292
2293 for (auto it=culmination.begin(); it!=culmination.end(); it++)
2294 {
2295 const Entry &e = it->second;
2296 if (it!=culmination.begin())
2297 out3 << ", ";
2298 out3 << "<B#" << e.Col() << ">" << e.name << "</B>";
2299 if (e.value>0)
2300 out3 << " [" << nearbyint(90-e.value) << "&deg;]";
2301 }
2302
2303 out4 << setprecision(3);
2304
2305 for (auto it=lightcond.begin(); it!=lightcond.end(); it++)
2306 {
2307 const Entry &e = it->second;
2308 if (it!=lightcond.begin())
2309 out4 << ", ";
2310 out4 << "<B#" << e.Col() << ">" << e.name << "</B>";
2311 if (e.value>0)
2312 out4 << " [" << nearbyint(e.value) << "]";
2313 }
2314
2315 if (fSun.fSetAstronomical>fSun.fRiseAstronomical)
2316 fSun.fSetAstronomical += boost::posix_time::hours(24);
2317
2318 ostringstream title;
2319 title << fSun.fSetAstronomical.GetAsStr("%H:%M");
2320 title << " / ";
2321 title << ((fSun.fRiseAstronomical-fSun.fSetAstronomical)/20).minutes();
2322 title << "' / ";
2323 title << fSun.fRiseAstronomical.GetAsStr("%H:%M");
2324
2325 out << '\n';
2326 out2 << '\n';
2327 out3 << '\n';
2328 out4 << '\n';
2329 out << HTML::kWhite << '\t' << Time()-now << '\n';
2330 out2 << HTML::kWhite << '\t' << Time()-now << '\n';
2331
2332 WriteBinaryVec(now, "hist-visibility", alt, 75, 15, "Alt "+title.str());
2333 WriteBinaryVec(now, "hist-current-prediction", cur, 100, 0, "I " +title.str());
2334 }
2335 catch (const exception &e)
2336 {
2337 out << '\n';
2338 out2 << '\n';
2339 out << HTML::kWhite << '\t' << "ERROR - "+string(e.what()) << '\n';
2340 out2 << HTML::kWhite << '\t' << "ERROR - "+string(e.what()) << '\n';
2341 out3 << HTML::kWhite << '\t' << "ERROR - "+string(e.what()) << '\n';
2342 out4 << HTML::kWhite << '\t' << "ERROR - "+string(e.what()) << '\n';
2343 }
2344#endif
2345
2346 ofstream(fPath+"/moon.data") << out.str();
2347 ofstream(fPath+"/source-list.data") << out2.str();
2348 ofstream(fPath+"/visibility.data") << out3.str();
2349 ofstream(fPath+"/current-prediction.data") << out4.str();
2350 }
2351
2352 int Execute()
2353 {
2354 Time now;
2355 if (now-fLastUpdate<boost::posix_time::seconds(1))
2356 return fDimDNS.online() ? kStateRunning : kStateDimNetworkNA;
2357 fLastUpdate=now;
2358
2359 // ==============================================================
2360
2361 const bool data_taking =
2362 fDimMcp.state()==MCP::State::kTriggerOn ||
2363 fDimMcp.state()==MCP::State::kTakingData;
2364
2365 const bool data_run =
2366 fMcpConfigurationName=="data" ||
2367 fMcpConfigurationName=="data-rt";
2368
2369 const bool bias_on =
2370 fDimBiasControl.state()==BIAS::State::kRamping ||
2371 fDimBiasControl.state()==BIAS::State::kOverCurrent ||
2372 fDimBiasControl.state()==BIAS::State::kVoltageOn;
2373
2374 const bool calibrated =
2375 fDimFeedback.state()>=Feedback::State::kCalibrated;
2376
2377 const bool haderr = !fErrorList.empty();
2378
2379 bool newerr = false;
2380
2381 newerr |= SetError(!fDimDNS.online(),
2382 "<b><#darkred>DIM network not available</#></b>");
2383 newerr |= SetError(!fDimControl.online(),
2384 "<b>no dimctrl server available</b>");
2385 newerr |= SetError(fDimDataLogger.state()<20 || fDimDataLogger.state()>40,
2386 "<b>datalogger not ready</b>");
2387
2388 //newerr |= SetError(fDimDriveControl.state()==Drive::State::kLocked,
2389 // "<b><#darkred>Drive in LOCKED state, drive was automatically parked</#></b>");
2390
2391 newerr |= SetError(fDimDriveControl.state()>0xff && data_taking && data_run,
2392 "Drive in ERROR state during data-run");
2393 newerr |= SetError(fDriveControlMoonDist>155,
2394 "Moon within the field-of-view of the cones");
2395 newerr |= SetError(fDriveControlMoonDist>=0 && fDriveControlMoonDist<3,
2396 "Moon within the field-of-view of the camera");
2397
2398 newerr |= SetError(fDimBiasControl.state()<BIAS::State::kRamping && data_taking && data_run,
2399 "BIAS not operating during data-run");
2400 newerr |= SetError(fDimBiasControl.state()==BIAS::State::kOverCurrent,
2401 "BIAS channels in OverCurrent");
2402 newerr |= SetError(fDimBiasControl.state()==BIAS::State::kNotReferenced,
2403 "BIAS voltage not at reference");
2404
2405
2406 newerr |= SetError(bias_on && calibrated && fBiasControlCurrentMed>90,
2407 "Median current exceeds 90&micro;A/pix");
2408 newerr |= SetError(bias_on && calibrated && fBiasControlCurrentMax>110,
2409 "Maximum current exceeds 110&micro;A/pix");
2410
2411 newerr |= SetError(fFscControlHumidityAvg>60,
2412 "Average camera humidity exceed 60%");
2413
2414 newerr |= SetError(!fMagicWeatherHist[kHum].empty() && fMagicWeatherHist[kHum].back()>98 && data_taking,
2415 "Outside humidity exceeds 98% during data-taking");
2416 newerr |= SetError(!fMagicWeatherHist[kGusts].empty() && fMagicWeatherHist[kGusts].back()>98 && (fDimDriveControl.state()==Drive::State::kTracking||fDimDriveControl.state()==Drive::State::kOnTrack),
2417 "Wind gusts exceed 50km/h during tracking");
2418
2419 newerr |= SetError(fDimFscControl.state()>=FSC::State::kConnected && !fFscControlTemperatureHist.empty() && fFscControlTemperatureHist.back()>9,
2420 "Sensor temperature exceeds outside temperature by more than 9&deg;C");
2421
2422 newerr |= SetError(fFtmControlTriggerRateTooLow>0,
2423 "Trigger rate below 1Hz while trigger switched on");
2424
2425 newerr |= SetError(fFtmControlState!=FTM::kFtmConfig && (fFtmControlState&FTM::kFtmLocked)==0,
2426 "FTM - clock conditioner not locked!");
2427
2428 newerr |= SetError(fDimTimeCheck.state()==1,
2429 "Warning NTP time difference of drive PC exceeds 1s");
2430 newerr |= SetError(fDimTimeCheck.state()<1,
2431 "Warning timecheck not running");
2432
2433 newerr |= SetError(fDimBiasControl.state()==BIAS::State::kVoltageOn &&
2434 fDimFeedback.state()<Feedback::State::kCalibrating &&
2435 fBiasControlVoltageMed>3,
2436 "Bias voltage switched on, but bias crate not calibrated");
2437
2438 newerr |= SetError(fLastRunFinishedWithZeroEvents,
2439 "Last run finshed, but contained zero events.");
2440
2441 newerr |= SetError(fFreeSpace<50000000000,
2442 "Less than 50GB disk space left.");
2443
2444 newerr |= SetError(fDimPwrControl.state()==Power::State::kCoolingFailure,
2445 "Cooling unit reports failure!");
2446
2447 for (auto it=fControlAlarmHist.begin(); it!=fControlAlarmHist.end(); it++)
2448 newerr |= SetError(it->time.IsValid(), it->msg);
2449 fControlAlarmHist.clean();;
2450
2451 fLastRunFinishedWithZeroEvents = false;
2452
2453 // FTM in Connected instead of Idle --> power cyclen
2454
2455 /* // Check offline and disconnected status?
2456 Out() << fDimMcp << endl;
2457 Out() << fDimControl << endl;
2458 Out() << fDimDataLogger << endl;
2459 Out() << fDimDriveControl << endl;
2460 Out() << fDimFadControl << endl;
2461 Out() << fDimFtmControl << endl;
2462 Out() << fDimBiasControl << endl;
2463 Out() << fDimFeedback << endl;
2464 Out() << fDimRateControl << endl;
2465 Out() << fDimFscControl << endl;
2466 Out() << fDimMagicWeather << endl;
2467 Out() << fDimRateScan << endl;
2468 Out() << fDimChat << endl;
2469 */
2470
2471 // FTU in error
2472 // FAD lost
2473
2474 // --------------------------------------------------------------
2475 ostringstream out;
2476
2477 if (newerr)
2478 {
2479 SetAudio("error");
2480
2481 out << now.JavaDate() << '\n';
2482 out << HTML::kWhite << '\t';
2483 out << "<->" << fErrorHist.rget() << "<->";
2484 out << '\n';
2485
2486 ofstream(fPath+"/errorhist.data") << out.str();
2487 }
2488
2489 out.str("");
2490 out << Header(now) << '\t' << (!fErrorList.empty()) << '\t' << (fDimControl.state()>0) << '\n';
2491 out << setprecision(3);
2492 out << HTML::kWhite << '\t';
2493 for (auto it=fErrorList.begin(); it!=fErrorList.end(); it++)
2494 out << *it << "<br/>";
2495 out << '\n';
2496
2497 if (haderr || !fErrorList.empty())
2498 ofstream(fPath+"/error.data") << out.str();
2499
2500 // ==============================================================
2501
2502 out.str("");
2503 out << Header(now) << '\t' << (!fErrorList.empty()) << '\t' << (fDimControl.state()>0) << '\n';
2504 out << setprecision(3);
2505
2506 // -------------- System status --------------
2507 if (fDimDNS.online() && fDimMcp.state()>=MCP::State::kIdle) // Idle
2508 {
2509 string col = HTML::kBlue;
2510 switch (fMcpConfigurationState)
2511 {
2512 case MCP::State::kIdle:
2513 col = HTML::kWhite;
2514 break;
2515 case MCP::State::kConfiguring1:
2516 case MCP::State::kConfiguring2:
2517 case MCP::State::kConfiguring3:
2518 case MCP::State::kConfigured:
2519 case MCP::State::kTriggerOn:
2520 col = HTML::kBlue;
2521 break;
2522 case MCP::State::kTakingData:
2523 col = HTML::kBlue;
2524 if (fDimFadControl.state()==FAD::State::kRunInProgress)
2525 col = HTML::kGreen;
2526 break;
2527 }
2528
2529 const bool other =
2530 fDimRateControl.state()==RateControl::State::kSettingGlobalThreshold ||
2531 fDimRateScan.state()==RateScan::State::kInProgress;
2532
2533 if (other)
2534 col = HTML::kBlue;
2535
2536 out << col << '\t';
2537
2538 if (!other)
2539 {
2540 const string conf = fMcpConfigurationName.length()>0?" ["+fMcpConfigurationName+"]":"";
2541 switch (fMcpConfigurationState)
2542 {
2543 case MCP::State::kIdle:
2544 out << "Idle" << conf;
2545 break;
2546 case MCP::State::kConfiguring1:
2547 case MCP::State::kConfiguring2:
2548 case MCP::State::kConfiguring3:
2549 out << "Configuring" << conf;
2550 break;
2551 case MCP::State::kConfigured:
2552 out << "Configured" << conf;
2553 break;
2554 case MCP::State::kTriggerOn:
2555 case MCP::State::kTakingData:
2556 out << fMcpConfigurationName;
2557 if (fFadControlDrsRuns[2]>0)
2558 out << "(" << fFadControlDrsRuns[2] << ")";
2559 break;
2560 }
2561 }
2562 else
2563 if (fDimRateControl.state()==RateControl::State::kSettingGlobalThreshold)
2564 out << "Calibrating threshold";
2565 else
2566 if (fDimRateScan.state()==RateScan::State::kInProgress)
2567 out << "Rate scan in progress";
2568
2569 if (fMcpConfigurationState>MCP::State::kConfigured &&
2570 fDimRateControl.state()!=RateControl::State::kSettingGlobalThreshold)
2571 {
2572 ostringstream evt;
2573 if (fMcpConfigurationMaxEvents>0)
2574 {
2575 const int64_t de = int64_t(fMcpConfigurationMaxEvents) - int64_t(fFadControlNumEvents);
2576 if (de>=0 && fMcpConfigurationState==MCP::State::kTakingData)
2577 evt << de;
2578 else
2579 evt << fMcpConfigurationMaxEvents;
2580 }
2581 else
2582 {
2583 if (fMcpConfigurationState==MCP::State::kTakingData)
2584 {
2585 if (fFadControlNumEvents>2999)
2586 evt << floor(fFadControlNumEvents/1000) << 'k';
2587 else
2588 evt << fFadControlNumEvents;
2589 }
2590 }
2591
2592 ostringstream tim;
2593 if (fMcpConfigurationMaxTime>0)
2594 {
2595 const uint32_t dt = (Time()-fMcpConfigurationRunStart).total_seconds();
2596 if (dt<=fMcpConfigurationMaxTime && fMcpConfigurationState==MCP::State::kTakingData)
2597 tim << fMcpConfigurationMaxTime-dt << 's';
2598 else
2599 tim << fMcpConfigurationMaxTime << 's';
2600 }
2601 else
2602 {
2603 if (fMcpConfigurationState==MCP::State::kTakingData)
2604 tim << fMcpConfigurationRunStart.SecondsTo();
2605 }
2606
2607 const bool has_evt = !evt.str().empty();
2608 const bool has_tim = !tim.str().empty();
2609
2610 if (has_evt || has_tim)
2611 out << " [";
2612 out << evt.str();
2613 if (has_evt && has_tim)
2614 out << '/';
2615 out << tim.str();
2616 if (has_evt || has_tim)
2617 out << ']';
2618 }
2619 }
2620 else
2621 out << HTML::kWhite;
2622 out << '\n';
2623
2624 // ------------------ Drive -----------------
2625 if (fDimDNS.online() && fDimDriveControl.state()>=Drive::State::kArmed) // Armed, Moving, Tracking, OnTrack, Error
2626 {
2627 const uint32_t dev = !fDriveControlTrackingDevHist.empty() ? round(fDriveControlTrackingDevHist.back()) : 0;
2628 const State rc = fDimDriveControl.description();
2629 string col = HTML::kGreen;
2630 if (fDimDriveControl.state()==Drive::State::kMoving) // Moving
2631 col = HTML::kBlue;
2632 if (fDimDriveControl.state()==Drive::State::kArmed) // Armed
2633 col = HTML::kWhite;
2634 if (fDimDriveControl.state()==Drive::State::kTracking || fDimDriveControl.state()==Drive::State::kOnTrack) // Tracking
2635 {
2636 if (dev>60) // ~1.5mm
2637 col = HTML::kYellow;
2638 if (dev>120) // ~1/4 of a pixel ~ 2.5mm
2639 col = HTML::kRed;
2640 }
2641 if (fDimDriveControl.state()==0x100)
2642 col = HTML::kRed;
2643 out << col << '\t';
2644
2645 //out << rc.name << '\t';
2646 out << fDriveControlPointingAz << ' ';
2647 out << fDriveControlPointingZd << "&deg;";
2648 out << setprecision(2);
2649 if (fDimDriveControl.state()==Drive::State::kTracking || fDimDriveControl.state()==Drive::State::kOnTrack)
2650 {
2651 out << " &plusmn; " << dev << '"';
2652 if (!fDriveControlSourceName.empty())
2653 out << " [" << fDriveControlSourceName << ']';
2654 }
2655 if (fDimDriveControl.state()==Drive::State::kMoving)
2656 out << " &#10227;";
2657 out << setprecision(3);
2658 }
2659 else
2660 out << HTML::kWhite << '\t';
2661
2662 if (fSun.time.IsValid() && fMoon.time.IsValid())
2663 {
2664 if (fSun.visible)
2665 {
2666 out << " &#9788;";
2667 if (fDimDriveControl.state()<Drive::State::kArmed)
2668 out << " [" << fSun.fSetCivil.MinutesTo() << "&darr;]";
2669 }
2670 else
2671 if (!fSun.visible && fMoon.visible)
2672 {
2673 out << " &#9790;";
2674 if (fDimDriveControl.state()<Drive::State::kArmed)
2675 out << " [" << fMoon.disk << "%]";
2676 }
2677 }
2678 if (fDimDNS.online() && fDimDriveControl.state()==0x100)
2679 out << " <ERR>";
2680 if (fDimDNS.online() && fDimDriveControl.state()==Drive::State::kLocked)
2681 out << " &otimes;";
2682 out << '\n';
2683
2684 // ------------------- FSC ------------------
2685 if (fDimDNS.online() && fDimFscControl.state()>FSC::State::kDisconnected && !fFscControlTemperatureHist.empty())
2686 {
2687 string col = HTML::kGreen;
2688 if (fFscControlTemperatureHist.back()>5)
2689 col = HTML::kYellow;
2690 if (fFscControlTemperatureHist.back()>8)
2691 col = HTML::kRed;
2692
2693 out << col << '\t' << fFscControlTemperatureHist.back() << '\n';
2694 }
2695 else
2696 out << HTML::kWhite << '\n';
2697
2698 // --------------- MagicWeather -------------
2699 if (fDimDNS.online() && fDimMagicWeather.state()==MagicWeather::State::kReceiving && !fMagicWeatherHist[kWeatherBegin].empty())
2700 {
2701 /*
2702 const float diff = fMagicWeatherHist[kTemp].back()-fMagicWeatherHist[kDew].back();
2703 string col1 = HTML::kRed;
2704 if (diff>0.3)
2705 col1 = HTML::kYellow;
2706 if (diff>0.7)
2707 col1 = HTML::kGreen;
2708 */
2709
2710 const float wind = fMagicWeatherHist[kGusts].back();
2711 const float hum = fMagicWeatherHist[kHum].back();
2712 string col = HTML::kGreen;
2713 if (wind>35 || hum>95)
2714 col = HTML::kYellow;
2715 if (wind>45 || hum>98)
2716 col = HTML::kRed;
2717
2718 out << col << '\t';
2719 out << fMagicWeatherHist[kHum].back() << '\t';
2720 out << setprecision(2);
2721 out << fMagicWeatherHist[kGusts].back() << '\n';
2722 out << setprecision(3);
2723 }
2724 else
2725 out << HTML::kWhite << "\n";
2726
2727 // --------------- FtmControl -------------
2728 if (fDimDNS.online() && fDimFtmControl.state()==FTM::State::kTriggerOn)
2729 {
2730 string col = HTML::kGreen;
2731 if (!fFtmControlTriggerRateHist.empty())
2732 {
2733 if (fFtmControlTriggerRateHist.back()<15)
2734 col = HTML::kYellow;
2735 if (fFtmControlTriggerRateHist.back()>100)
2736 col = HTML::kRed;
2737
2738 out << col << '\t' << fFtmControlTriggerRateHist.back() << " Hz";
2739 }
2740
2741 if (fDimBiasControl.state()==BIAS::State::kVoltageOn)
2742 out << " (" << fFtmPatchThresholdMed << ')';
2743 out << '\n';
2744 }
2745 else
2746 out << HTML::kWhite << '\n';
2747
2748 // --------------- BiasControl -------------
2749 if (fDimDNS.online() &&
2750 (fDimBiasControl.state()==BIAS::State::kRamping ||
2751 fDimBiasControl.state()==BIAS::State::kOverCurrent ||
2752 fDimBiasControl.state()==BIAS::State::kVoltageOn ||
2753 fDimBiasControl.state()==BIAS::State::kVoltageOff))
2754 {
2755 const bool off = fDimBiasControl.state()==BIAS::State::kVoltageOff;
2756 const bool oc = fDimBiasControl.state()==BIAS::State::kOverCurrent;
2757
2758 string col = fBiasControlVoltageMed>3?HTML::kGreen:HTML::kWhite;
2759 if (fDimBiasControl.state()!=BIAS::State::kVoltageOff)
2760 {
2761 if (fBiasControlCurrentMed>60 || fBiasControlCurrentMax>80)
2762 col = HTML::kYellow;
2763 if (fBiasControlCurrentMed>70 || fBiasControlCurrentMax>90)
2764 col = HTML::kRed;
2765 }
2766
2767 // Bias in overcurrent => Red
2768 if (fDimBiasControl.state()==BIAS::State::kOverCurrent)
2769 col = HTML::kRed;
2770
2771 // MCP in ReadyForDatataking/Configuring/Configured/TriggerOn/TakingData
2772 // and Bias not in "data-taking state' => Red
2773 if (fMcpConfigurationState>MCP::State::kIdle &&
2774 fDimBiasControl.state()!=BIAS::State::kVoltageOn &&
2775 fDimBiasControl.state()!=BIAS::State::kVoltageOff)
2776 col = HTML::kRed;
2777
2778 const bool cal = fDimFeedback.state()>=Feedback::State::kCalibrated;
2779
2780 // Feedback is currently calibrating => Blue
2781 if (fDimFeedback.state()==Feedback::State::kCalibrating)
2782 {
2783 out << HTML::kBlue << '\t';
2784 out << "***\t";
2785 out << "***\t";
2786 }
2787 else
2788 {
2789 out << setprecision(2);
2790 out << col << '\t';
2791 out << (off ? 0 : fBiasControlCurrentMed) << '\t';
2792 if (oc)
2793 out << "(OC) ";
2794 else
2795 {
2796 if (cal)
2797 out << (off ? 0 : fBiasControlCurrentMax);
2798 else
2799 out << "&mdash; ";
2800 }
2801 out << '\t';
2802 out << setprecision(3);
2803 }
2804 if (cal && fDimFeedback.state()!=Feedback::State::kCalibrating)
2805 out << setprecision(2) << fBiasControlPowerTot << " W" << setprecision(3);
2806 else
2807 out << (off ? 0 : fBiasControlVoltageMed) << " V";
2808 out << '\n';
2809 }
2810 else
2811 out << HTML::kWhite << '\n';
2812
2813 ofstream(fPath+"/fact.data") << out.str();
2814
2815 // ==============================================================
2816
2817 out.str("");
2818 out << Header(now) << '\t' << (!fErrorList.empty()) << '\t' << (fDimControl.state()>0) << '\n';
2819
2820 if (!fDimDNS.online())
2821 out << HTML::kWhite << "\tOffline\n\n\n\n\n\n\n\n\n\n\n\n\n";
2822 else
2823 {
2824 ostringstream dt;
2825 dt << (Time()-fRunTime);
2826
2827 out << HTML::kGreen << '\t' << fDimDNS.version() << '\n';
2828
2829 out << GetStateHtml(fDimControl, 0);
2830 out << GetStateHtml(fDimMcp, MCP::State::kConnected);
2831 out << GetStateHtml(fDimDataLogger, 1);
2832 out << GetStateHtml(fDimDriveControl, Drive::State::kConnected);
2833 out << GetStateHtml(fDimTimeCheck, 1);
2834 out << GetStateHtml(fDimFadControl, FAD::State::kConnected);
2835 out << GetStateHtml(fDimFtmControl, FTM::State::kConnected);
2836 out << GetStateHtml(fDimBiasControl, BIAS::State::kConnected);
2837 out << GetStateHtml(fDimFeedback, Feedback::State::kConnected);
2838 out << GetStateHtml(fDimRateControl, RateControl::State::kConnected);
2839 out << GetStateHtml(fDimFscControl, FSC::State::kConnected);
2840 out << GetStateHtml(fDimAgilentControl, Agilent::State::kVoltageOff);
2841 out << GetStateHtml(fDimPwrControl, Power::State::kSystemOff);
2842 out << GetStateHtml(fDimLidControl, Lid::State::kConnected);
2843 out << GetStateHtml(fDimRateScan, RateScan::State::kConnected);
2844 out << GetStateHtml(fDimMagicWeather, MagicWeather::State::kConnected);
2845 out << GetStateHtml(fDimTngWeather, TNGWeather::State::kConnected);
2846 out << GetStateHtml(fDimMagicLidar, MagicLidar::State::kConnected);
2847 out << GetStateHtml(fDimTemperature, Temperature::State::kValid);
2848 out << GetStateHtml(fDimChat, 0);
2849 out << GetStateHtml(fDimSkypeClient, 1);
2850
2851 string col = HTML::kRed;
2852 if (fFreeSpace>uint64_t(199999999999))
2853 col = HTML::kYellow;
2854 if (fFreeSpace>uint64_t(999999999999))
2855 col = HTML::kGreen;
2856 if (fFreeSpace==UINT64_MAX)
2857 col = HTML::kWhite;
2858
2859 out << col << '\t' << Tools::Scientific(fFreeSpace) << "B\n";
2860
2861 out << HTML::kGreen << '\t' << dt.str().substr(0, dt.str().length()-7) << '\n';
2862 }
2863
2864 ofstream(fPath+"/status.data") << out.str();
2865
2866 if (now-fLastAstroCalc>boost::posix_time::seconds(15))
2867 {
2868 UpdateAstronomy();
2869 fLastAstroCalc = now;
2870 }
2871
2872 return fDimDNS.online() ? kStateRunning : kStateDimNetworkNA;
2873 }
2874
2875
2876public:
2877 StateMachineSmartFACT(ostream &out=cout) : StateMachineDim(out, fIsServer?"SMART_FACT":""),
2878 fLastAstroCalc(boost::date_time::neg_infin),
2879 fPath("www/smartfact/data"),
2880 fControlScriptDepth(0),
2881 fMcpConfigurationState(DimState::kOffline),
2882 fMcpConfigurationMaxTime(0),
2883 fMcpConfigurationMaxEvents(0),
2884 fLastRunFinishedWithZeroEvents(false),
2885 fTngWeatherDustTime(Time::none),
2886 fBiasControlVoltageMed(0),
2887 fBiasControlCurrentMed(0),
2888 fBiasControlCurrentMax(0),
2889 fFscControlHumidityAvg(0),
2890 fDriveControlMoonDist(-1),
2891 fFadControlNumEvents(0),
2892 fFadControlDrsRuns(3),
2893 fFtmControlState(FTM::kFtmLocked),
2894 fRateScanDataId(0),
2895 fRateScanBoard(0),
2896 fFreeSpace(UINT64_MAX),
2897 // ---
2898 fDimMcp ("MCP"),
2899 fDimDataLogger ("DATA_LOGGER"),
2900 fDimDriveControl ("DRIVE_CONTROL"),
2901 fDimTimeCheck ("TIME_CHECK"),
2902 fDimMagicWeather ("MAGIC_WEATHER"),
2903 fDimMagicLidar ("MAGIC_LIDAR"),
2904 fDimTngWeather ("TNG_WEATHER"),
2905 fDimTemperature ("TEMPERATURE"),
2906 fDimFeedback ("FEEDBACK"),
2907 fDimBiasControl ("BIAS_CONTROL"),
2908 fDimFtmControl ("FTM_CONTROL"),
2909 fDimFadControl ("FAD_CONTROL"),
2910 fDimFscControl ("FSC_CONTROL"),
2911 fDimAgilentControl("AGILENT_CONTROL"),
2912 fDimPwrControl ("PWR_CONTROL"),
2913 fDimLidControl ("LID_CONTROL"),
2914 fDimRateControl ("RATE_CONTROL"),
2915 fDimRateScan ("RATE_SCAN"),
2916 fDimChat ("CHAT"),
2917 fDimSkypeClient ("SKYPE_CLIENT")
2918 {
2919 fDimDNS.Subscribe(*this);
2920 fDimControl.Subscribe(*this);
2921 fDimMcp.Subscribe(*this);
2922 fDimDataLogger.Subscribe(*this);
2923 fDimDriveControl.Subscribe(*this);
2924 fDimTimeCheck.Subscribe(*this);
2925 fDimMagicWeather.Subscribe(*this);
2926 fDimMagicLidar.Subscribe(*this);
2927 fDimTngWeather.Subscribe(*this);
2928 fDimTemperature.Subscribe(*this);
2929 fDimFeedback.Subscribe(*this);
2930 fDimBiasControl.Subscribe(*this);
2931 fDimFtmControl.Subscribe(*this);
2932 fDimFadControl.Subscribe(*this);
2933 fDimFscControl.Subscribe(*this);
2934 fDimAgilentControl.Subscribe(*this);
2935 fDimPwrControl.Subscribe(*this);
2936 fDimLidControl.Subscribe(*this);
2937 fDimRateControl.Subscribe(*this);
2938 fDimRateScan.Subscribe(*this);
2939 fDimChat.Subscribe(*this);
2940 fDimSkypeClient.Subscribe(*this);
2941
2942 fDimFscControl.SetCallback(bind(&StateMachineSmartFACT::HandleFscControlStateChange, this, placeholders::_1));
2943 //fDimFtmControl.SetCallback(bind(&StateMachineSmartFACT::HandleFtmControlStateChange, this));
2944 fDimDriveControl.SetCallback(bind(&StateMachineSmartFACT::HandleDriveControlStateChange, this, placeholders::_1));
2945 fDimControl.SetCallback(bind(&StateMachineSmartFACT::HandleControlStateChange, this, placeholders::_1));
2946 fDimControl.AddCallback("dotest.dim", bind(&StateMachineSmartFACT::HandleDoTest, this, placeholders::_1));
2947
2948 Subscribe("DIM_CONTROL/MESSAGE")
2949 (bind(&StateMachineSmartFACT::HandleDimControlMessage, this, placeholders::_1));
2950
2951 Subscribe("MCP/CONFIGURATION")
2952 (bind(&StateMachineSmartFACT::HandleMcpConfiguration, this, placeholders::_1));
2953
2954 Subscribe("DRIVE_CONTROL/POINTING_POSITION")
2955 (bind(&StateMachineSmartFACT::HandleDrivePointing, this, placeholders::_1));
2956 Subscribe("DRIVE_CONTROL/TRACKING_POSITION")
2957 (bind(&StateMachineSmartFACT::HandleDriveTracking, this, placeholders::_1));
2958 Subscribe("DRIVE_CONTROL/SOURCE_POSITION")
2959 (bind(&StateMachineSmartFACT::HandleDriveSource, this, placeholders::_1));
2960
2961 Subscribe("FSC_CONTROL/TEMPERATURE")
2962 (bind(&StateMachineSmartFACT::HandleFscTemperature, this, placeholders::_1));
2963 Subscribe("FSC_CONTROL/HUMIDITY")
2964 (bind(&StateMachineSmartFACT::HandleFscHumidity, this, placeholders::_1));
2965 Subscribe("FSC_CONTROL/BIAS_TEMP")
2966 (bind(&StateMachineSmartFACT::HandleFscBiasTemp, this, placeholders::_1));
2967
2968 Subscribe("MAGIC_WEATHER/DATA")
2969 (bind(&StateMachineSmartFACT::HandleMagicWeatherData, this, placeholders::_1));
2970 Subscribe("TNG_WEATHER/DUST")
2971 (bind(&StateMachineSmartFACT::HandleTngWeatherDust, this, placeholders::_1));
2972
2973 Subscribe("FEEDBACK/CALIBRATED_CURRENTS")
2974 (bind(&StateMachineSmartFACT::HandleFeedbackCalibratedCurrents, this, placeholders::_1));
2975
2976 Subscribe("BIAS_CONTROL/VOLTAGE")
2977 (bind(&StateMachineSmartFACT::HandleBiasVoltage, this, placeholders::_1));
2978 Subscribe("BIAS_CONTROL/CURRENT")
2979 (bind(&StateMachineSmartFACT::HandleBiasCurrent, this, placeholders::_1));
2980
2981 Subscribe("FAD_CONTROL/CONNECTIONS")
2982 (bind(&StateMachineSmartFACT::HandleFadConnections, this, placeholders::_1));
2983 Subscribe("FAD_CONTROL/EVENTS")
2984 (bind(&StateMachineSmartFACT::HandleFadEvents, this, placeholders::_1));
2985 Subscribe("FAD_CONTROL/START_RUN")
2986 (bind(&StateMachineSmartFACT::HandleFadStartRun, this, placeholders::_1));
2987 Subscribe("FAD_CONTROL/DRS_RUNS")
2988 (bind(&StateMachineSmartFACT::HandleFadDrsRuns, this, placeholders::_1));
2989 Subscribe("FAD_CONTROL/EVENT_DATA")
2990 (bind(&StateMachineSmartFACT::HandleFadEventData, this, placeholders::_1));
2991 Subscribe("FAD_CONTROL/STATS")
2992 (bind(&StateMachineSmartFACT::HandleStats, this, placeholders::_1));
2993
2994 Subscribe("DATA_LOGGER/STATS")
2995 (bind(&StateMachineSmartFACT::HandleStats, this, placeholders::_1));
2996
2997 Subscribe("FTM_CONTROL/TRIGGER_RATES")
2998 (bind(&StateMachineSmartFACT::HandleFtmTriggerRates, this, placeholders::_1));
2999 Subscribe("FTM_CONTROL/STATIC_DATA")
3000 (bind(&StateMachineSmartFACT::HandleFtmStaticData, this, placeholders::_1));
3001 Subscribe("FTM_CONTROL/FTU_LIST")
3002 (bind(&StateMachineSmartFACT::HandleFtmFtuList, this, placeholders::_1));
3003
3004 Subscribe("RATE_CONTROL/THRESHOLD")
3005 (bind(&StateMachineSmartFACT::HandleRateControlThreshold,this, placeholders::_1));
3006
3007 Subscribe("RATE_SCAN/DATA")
3008 (bind(&StateMachineSmartFACT::HandleRateScanData, this, placeholders::_1));
3009
3010 Subscribe("CHAT/MESSAGE")
3011 (bind(&StateMachineSmartFACT::HandleChatMsg, this, placeholders::_1));
3012
3013
3014 // =================================================================
3015
3016 // State names
3017 AddStateName(kStateDimNetworkNA, "DimNetworkNotAvailable",
3018 "The Dim DNS is not reachable.");
3019
3020 AddStateName(kStateRunning, "Running", "");
3021
3022 // =================================================================
3023
3024 AddEvent("PRINT")
3025 (bind(&StateMachineSmartFACT::Print, this))
3026 ("Print a list of the states of all connected servers.");
3027
3028 }
3029 int EvalOptions(Configuration &conf)
3030 {
3031 if (!fPixelMap.Read(conf.Get<string>("pixel-map-file")))
3032 {
3033 Error("Reading mapping table from "+conf.Get<string>("pixel-map-file")+" failed.");
3034 return 1;
3035 }
3036
3037 fPath = conf.Get<string>("path");
3038 fDatabase = conf.Get<string>("source-database");
3039
3040 struct stat st;
3041 if (stat(fPath.c_str(), &st))
3042 {
3043 Error(fPath+" does not exist!");
3044 return 2;
3045 }
3046
3047 if ((st.st_mode&S_IFDIR)==0)
3048 {
3049 Error(fPath+" not a directory!");
3050 return 3;
3051 }
3052
3053 if ((st.st_mode&S_IWUSR)==0)
3054 {
3055 Error(fPath+" has no write permission!");
3056 return 4;
3057 }
3058
3059 if ((st.st_mode&S_IXUSR)==0)
3060 {
3061 Error(fPath+" has no execute permission!");
3062 return 5;
3063 }
3064
3065 ostringstream out;
3066 out << Time().JavaDate() << '\n';
3067
3068 ofstream(fPath+"/error.data") << out.str();
3069
3070 return -1;
3071 }
3072};
3073
3074bool StateMachineSmartFACT::fIsServer = false;
3075
3076// ------------------------------------------------------------------------
3077
3078#include "Main.h"
3079
3080template<class T>
3081int RunShell(Configuration &conf)
3082{
3083 StateMachineSmartFACT::fIsServer = !conf.Get<bool>("client");
3084 return Main::execute<T, StateMachineSmartFACT>(conf);
3085}
3086
3087void SetupConfiguration(Configuration &conf)
3088{
3089 po::options_description control("Smart FACT");
3090 control.add_options()
3091 ("pixel-map-file", var<string>()->required(), "Pixel mapping file. Used here to get the default reference voltage")
3092 ("path", var<string>("www/smartfact/data"), "Output path for the data-files")
3093 ("source-database", var<string>(""), "Database link as in\n\tuser:password@server[:port]/database.")
3094 ("client", po_bool(false), "For a standalone client choose this option.")
3095 ;
3096
3097 conf.AddOptions(control);
3098}
3099
3100/*
3101 Extract usage clause(s) [if any] for SYNOPSIS.
3102 Translators: "Usage" and "or" here are patterns (regular expressions) which
3103 are used to match the usage synopsis in program output. An example from cp
3104 (GNU coreutils) which contains both strings:
3105 Usage: cp [OPTION]... [-T] SOURCE DEST
3106 or: cp [OPTION]... SOURCE... DIRECTORY
3107 or: cp [OPTION]... -t DIRECTORY SOURCE...
3108 */
3109void PrintUsage()
3110{
3111 cout <<
3112 "SmartFACT is a tool writing the files needed for the SmartFACT web interface.\n"
3113 "\n"
3114 "The default is that the program is started without user intercation. "
3115 "All actions are supposed to arrive as DimCommands. Using the -c "
3116 "option, a local shell can be initialized. With h or help a short "
3117 "help message about the usuage can be brought to the screen.\n"
3118 "\n"
3119 "Usage: smartfact [-c type] [OPTIONS]\n"
3120 " or: smartfact [OPTIONS]\n";
3121 cout << endl;
3122}
3123
3124void PrintHelp()
3125{
3126 Main::PrintHelp<StateMachineSmartFACT>();
3127
3128 /* Additional help text which is printed after the configuration
3129 options goes here */
3130
3131 /*
3132 cout << "bla bla bla" << endl << endl;
3133 cout << endl;
3134 cout << "Environment:" << endl;
3135 cout << "environment" << endl;
3136 cout << endl;
3137 cout << "Examples:" << endl;
3138 cout << "test exam" << endl;
3139 cout << endl;
3140 cout << "Files:" << endl;
3141 cout << "files" << endl;
3142 cout << endl;
3143 */
3144}
3145
3146int main(int argc, const char* argv[])
3147{
3148 Configuration conf(argv[0]);
3149 conf.SetPrintUsage(PrintUsage);
3150 Main::SetupConfiguration(conf);
3151 SetupConfiguration(conf);
3152
3153 if (!conf.DoParse(argc, argv, PrintHelp))
3154 return 127;
3155
3156 if (!conf.Has("console"))
3157 return RunShell<LocalStream>(conf);
3158
3159 if (conf.Get<int>("console")==0)
3160 return RunShell<LocalShell>(conf);
3161 else
3162 return RunShell<LocalConsole>(conf);
3163
3164 return 0;
3165}
Note: See TracBrowser for help on using the repository browser.