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

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