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

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