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

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