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

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