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

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