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

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