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

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