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

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