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

Last change on this file since 14040 was 14040, checked in by tbretz, 12 years ago
Added a third column to the astro table and a source-list page with the current source positions.
File size: 81.9 KB
Line 
1#ifdef HAVE_LIBNOVA
2#include <libnova/solar.h>
3#include <libnova/lunar.h>
4#include <libnova/rise_set.h>
5#include <libnova/transform.h>
6#endif
7
8#ifdef HAVE_SQL
9#include <mysql++/mysql++.h>
10#endif
11
12#include "Dim.h"
13#include "Event.h"
14#include "Shell.h"
15#include "StateMachineDim.h"
16#include "Connection.h"
17#include "Configuration.h"
18#include "Console.h"
19#include "Converter.h"
20#include "PixelMap.h"
21
22#include "tools.h"
23
24#include "LocalControl.h"
25
26#include "HeadersFAD.h"
27#include "HeadersBIAS.h"
28#include "HeadersFTM.h"
29#include "HeadersFSC.h"
30#include "HeadersMCP.h"
31#include "HeadersDrive.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 fSunRiseDayTime;
82 Time fSunRiseCivil;
83 Time fSunRiseAstronomical;
84 Time fSunRiseDarkTime;
85
86 Time fSunSetDayTime;
87 Time fSunSetCivil;
88 Time fSunSetAstronomical;
89 Time fSunSetDarkTime;
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 Sun(double lon, double lat, const Time &t=Time()) : time(t)
103 {
104#ifdef HAVE_LIBNOVA
105 ln_lnlat_posn observer;
106 observer.lng = lon;
107 observer.lat = lat;
108
109 // get Julian day from local time
110 const double JD = time.JD();
111
112 ln_rst_time sun_day;
113 ln_rst_time sun_civil;
114 ln_rst_time sun_astronomical;
115 ln_rst_time sun_dark;
116
117 // Warning: return code of 1 means circumpolar and is not checked!
118 ln_get_solar_rst (JD-0.5, &observer, &sun_day);
119 ln_get_solar_rst_horizon(JD-0.5, &observer, - 6, &sun_civil);
120 ln_get_solar_rst_horizon(JD-0.5, &observer, -12, &sun_astronomical);
121 ln_get_solar_rst_horizon(JD-0.5, &observer, -18, &sun_dark);
122
123 fSunSetDayTime = Time(sun_day.set);
124 fSunSetCivil = Time(sun_civil.set);
125 fSunSetAstronomical = Time(sun_astronomical.set);
126 fSunSetDarkTime = Time(sun_dark.set);
127
128 fSunRiseDayTime = Time(sun_day.rise);
129 fSunRiseCivil = Time(sun_civil.rise);
130 fSunRiseAstronomical = Time(sun_astronomical.rise);
131 fSunRiseDarkTime = Time(sun_dark.rise);
132
133 const bool is_day = JD>sun_day.rise;
134 const bool is_night = JD>sun_dark.set;
135
136 ln_get_solar_rst (JD+0.5, &observer, &sun_day);
137 ln_get_solar_rst_horizon(JD+0.5, &observer, - 6, &sun_civil);
138 ln_get_solar_rst_horizon(JD+0.5, &observer, -12, &sun_astronomical);
139 ln_get_solar_rst_horizon(JD+0.5, &observer, -18, &sun_dark);
140
141 if (is_day)
142 {
143 fSunRiseDayTime = Time(sun_day.rise);
144 fSunRiseCivil = Time(sun_civil.rise);
145 fSunRiseAstronomical = Time(sun_astronomical.rise);
146 fSunRiseDarkTime = Time(sun_dark.rise);
147 }
148
149 if (is_night)
150 {
151 fSunSetDayTime = Time(sun_day.set);
152 fSunSetCivil = Time(sun_civil.set);
153 fSunSetAstronomical = Time(sun_astronomical.set);
154 fSunSetDarkTime = Time(sun_dark.set);
155 }
156
157 // case 0: midnight to sun-rise | !is_day && !is_night | rise/set
158 // case 1: sun-rise to sun-set | is_day && !is_night | set /rise
159 // case 2: sun-set to midnight | is_day && is_night | rise/set
160
161 isday = is_day^is_night;
162
163 state = isday ? 4 : 0;
164 if (time>fSunSetDayTime) state++;
165 if (time>fSunSetCivil) state++;
166 if (time>fSunSetAstronomical) state++;
167 if (time>fSunSetDarkTime) state++;
168
169 if (time>fSunRiseDarkTime) state++;
170 if (time>fSunRiseAstronomical) state++;
171 if (time>fSunRiseCivil) state++;
172 if (time>fSunRiseDayTime) state++;
173
174 string name[] =
175 {
176 "dark time",
177 "astron. twilight",
178 "civil twilight",
179 "sunrise",
180 "day time",
181 "sunset",
182 "civil twilight",
183 "astron. twilight",
184 "dark time"
185 };
186
187 description = state[name];
188
189 const string arr = isday ?
190 fSunSetDarkTime.MinutesTo(time)+"&darr;" :
191 fSunRiseDarkTime.MinutesTo(time)+"&uarr;";
192
193 description += " ["+arr+"]";
194
195 switch (state)
196 {
197 case 0: case 1: color = HTML::kGreen; break;
198 case 2: case 3: color = HTML::kYellow; break;
199 case 4: color = HTML::kRed; break;
200 case 5: case 6: color = HTML::kYellow; break;
201 case 7: case 8: color = HTML::kGreen; break;
202 }
203
204 visible = state>=3 && state<=5;
205#endif
206 }
207};
208
209class Moon
210{
211public:
212 Time time;
213
214 double ra;
215 double dec;
216
217 double zd;
218 double az;
219
220 double disk;
221
222 bool visible;
223
224 Time fMoonRise;
225 Time fMoonTransit;
226 Time fMoonSet;
227
228 string description;
229 string color;
230
231 Moon() : time(Time::none)
232 {
233 }
234
235 Moon(double lon, double lat, const Time &t=Time()) : time(t)
236 {
237#ifdef HAVE_LIBNOVA
238 const double JD = time.JD();
239
240 ln_lnlat_posn observer;
241 observer.lng = lon;
242 observer.lat = lat;
243
244 //observer.lng.degrees = -5;
245 //observer.lng.minutes = 36;
246 //observer.lng.seconds = 30;
247 //observer.lat.degrees = 42;
248 //observer.lat.minutes = 35;
249 //observer.lat.seconds = 40;
250
251 ln_rst_time moon;
252 ln_get_lunar_rst(JD-0.5, &observer, &moon);
253
254 fMoonRise = Time(moon.rise);
255 fMoonTransit = Time(moon.transit);
256 fMoonSet = Time(moon.set);
257
258 visible =
259 (JD>moon.rise && JD<moon.set && moon.rise<moon.set) ||
260 ((JD<moon.set || JD>moon.rise) && moon.rise>moon.set);
261
262 const bool is_up = JD>moon.rise;
263 const bool is_sinking = JD>moon.transit;
264 const bool is_dn = JD>moon.set;
265
266 ln_get_lunar_rst(JD+0.5, &observer, &moon);
267 if (is_up)
268 fMoonRise = Time(moon.rise);
269 if (is_sinking)
270 fMoonTransit = Time(moon.transit);
271 if (is_dn)
272 fMoonSet = Time(moon.set);
273
274 ln_equ_posn pos;
275 ln_get_lunar_equ_coords(JD, &pos);
276
277 ln_hrz_posn hrz;
278 ln_get_hrz_from_equ (&pos, &observer, JD, &hrz);
279 az = hrz.az;
280 zd = 90-hrz.alt;
281
282 ra = pos.ra/15;
283 dec = pos.dec;
284
285 disk = ln_get_lunar_disk(JD)*100;
286
287 if (!visible || disk<25)
288 color = HTML::kGreen;
289 else
290 color = disk>75 ? HTML::kRed : HTML::kYellow;
291
292 const string arr = fMoonSet<fMoonRise ?
293 fMoonSet.MinutesTo(time)+"&darr;" :
294 fMoonRise.MinutesTo(time)+"&uarr;";
295
296 ostringstream out;
297 out << setprecision(2);
298 out << (visible?"visible ":"") << (disk<0.1?0:disk) << "% [" << arr << "]";
299
300 description = out.str();
301#endif
302 }
303
304 double Angle(double r, double d) const
305 {
306 const double theta0 = M_PI/2-d*M_PI/180;
307 const double phi0 = r*M_PI/12;
308
309 const double theta1 = M_PI/2-dec*M_PI/180;
310 const double phi1 = ra*M_PI/12;
311
312 const double x0 = sin(theta0) * cos(phi0);
313 const double y0 = sin(theta0) * sin(phi0);
314 const double z0 = cos(theta0);
315
316 const double x1 = sin(theta1) * cos(phi1);
317 const double y1 = sin(theta1) * sin(phi1);
318 const double z1 = cos(theta1);
319
320 double arg = x0*x1 + y0*y1 + z0*z1;
321 if(arg > 1.0) arg = 1.0;
322 if(arg < -1.0) arg = -1.0;
323
324 return acos(arg) * 180/M_PI;
325 }
326
327 static string Color(double angle)
328 {
329 if (angle<10 || angle>150)
330 return HTML::kRed;
331 if (angle<20 || angle>140)
332 return HTML::kYellow;
333 return HTML::kGreen;
334 }
335};
336
337// ========================================================================
338// ========================================================================
339// ========================================================================
340
341class StateMachineSmartFACT : public StateMachineDim
342{
343private:
344 enum states_t
345 {
346 kStateDimNetworkNA = 1,
347 kStateRunning,
348 };
349
350 // ------------------------- Internal variables -----------------------
351
352 const Time fRunTime;
353
354 PixelMap fPixelMap;
355
356 string fDatabase;
357
358 Time fLastUpdate;
359 Time fLastAstroCalc;
360
361 string fPath;
362
363 // ----------------------------- Data storage -------------------------
364
365 deque<string> fControlMessageHist;
366 int32_t fControlScriptDepth;
367
368 uint32_t fMcpConfigurationState; // For consistency
369 int64_t fMcpConfigurationMaxTime;
370 int64_t fMcpConfigurationMaxEvents;
371 string fMcpConfigurationName;
372 Time fMcpConfigurationRunStart;
373
374 enum weather_t { kWeatherBegin=0, kTemp = kWeatherBegin, kDew, kHum, kPress, kWind, kGusts, kDir, kWeatherEnd = kDir+1 };
375 deque<float> fMagicWeatherHist[kWeatherEnd];
376
377 deque<float> fTngWeatherDustHist;
378 Time fTngWeatherDustTime;
379
380 vector<float> fFeedbackCalibration;
381
382 float fFeedbackTempOffset;
383 float fFeedbackUserOffset;
384
385 vector<float> fBiasControlVoltageVec;
386
387 float fBiasControlPowerTot;
388 float fBiasControlVoltageMed;
389 float fBiasControlCurrentMed;
390 float fBiasControlCurrentMax;
391
392 deque<float> fBiasControlCurrentHist;
393 deque<float> fFscControlTemperatureHist;
394
395 float fFscControlHumidityAvg;
396
397 float fDriveControlPointingZd;
398 string fDriveControlPointingAz;
399 string fDriveControlSourceName;
400 float fDriveControlMoonDist;
401
402 deque<float> fDriveControlTrackingDevHist;
403
404 int64_t fFadControlNumEvents;
405 int32_t fFadControlDrsStep;
406 vector<uint32_t> fFadControlDrsRuns;
407
408 deque<float> fFtmControlTriggerRateHist;
409 bool fFtmControlNewRunStarted;
410
411 float fFtmPatchThresholdMed;
412 float fFtmBoardThresholdMed;
413
414 bool fFtmControlFtuOk;
415
416 deque<float> fRateControlThreshold;
417
418 uint64_t fRateScanDataId;
419 uint8_t fRateScanBoard;
420 deque<float> fRateScanDataHist[41];
421
422 set<string> fErrorList;
423 deque<string> fErrorHist;
424
425 Sun fSun;
426 Moon fMoon;
427
428 // --------------------------- File header ----------------------------
429
430 Time fAudioTime;
431 string fAudioName;
432
433 string Header(const Time &d)
434 {
435 ostringstream msg;
436 msg << d.JavaDate() << '\t' << fAudioTime.JavaDate() << '\t' << fAudioName;
437 return msg.str();
438 }
439
440 string Header(const EventImp &d)
441 {
442 return Header(d.GetTime());
443 }
444
445 void SetAudio(const string &name)
446 {
447 fAudioName = name;
448 fAudioTime = Time();
449 }
450
451 // ------------- Initialize variables before the Dim stuff ------------
452
453 DimVersion fDimDNS;
454 DimControl fDimControl;
455 DimDescribedState fDimMcp;
456 DimDescribedState fDimDataLogger;
457 DimDescribedState fDimDriveControl;
458 DimDescribedState fDimTimeCheck;
459 DimDescribedState fDimMagicWeather;
460 DimDescribedState fDimTngWeather;
461 DimDescribedState fDimFeedback;
462 DimDescribedState fDimBiasControl;
463 DimDescribedState fDimFtmControl;
464 DimDescribedState fDimFadControl;
465 DimDescribedState fDimFscControl;
466 DimDescribedState fDimRateControl;
467 DimDescribedState fDimRateScan;
468 DimDescribedState fDimChat;
469 DimDescribedState fDimSkypeClient;
470
471 // -------------------------------------------------------------------
472
473 string GetDir(const double angle)
474 {
475 static const char *dir[] =
476 {
477 "N", "NNE", "NE", "ENE",
478 "E", "ESE", "SE", "SSE",
479 "S", "SSW", "SW", "WSW",
480 "W", "WNW", "NW", "NNW"
481 };
482
483 const uint16_t idx = uint16_t(floor(angle/22.5+16.5))%16;
484 return dir[idx];
485 }
486
487 // -------------------------------------------------------------------
488
489 bool CheckDataSize(const EventImp &d, const char *name, size_t size, bool min=false)
490 {
491 if (d.GetSize()==0)
492 return false;
493
494 if ((!min && d.GetSize()==size) || (min && d.GetSize()>size))
495 return true;
496
497 ostringstream msg;
498 msg << name << " - Received service has " << d.GetSize() << " bytes, but expected ";
499 if (min)
500 msg << "more than ";
501 msg << size << ".";
502 Warn(msg);
503 return false;
504 }
505
506 // -------------------------------------------------------------------
507
508 template<class T>
509 void WriteBinary(const EventImp &d, const string &fname, const T &t, double scale, double offset=0)
510 {
511 const Statistics stat(t);
512
513 vector<uint8_t> val(t.size(), 0);
514 for (uint64_t i=0; i<t.size(); i++)
515 {
516 float range = nearbyint(128*(t[i]-offset)/scale); // [-2V; 2V]
517 if (range>127)
518 range=127;
519 if (range<0)
520 range=0;
521 val[i] = (uint8_t)range;
522 }
523
524 const char *ptr = reinterpret_cast<char*>(val.data());
525
526 ostringstream out;
527 out << d.GetJavaDate() << '\n';
528 out << offset << '\n';
529 out << offset+scale << '\n';
530 out << setprecision(3);
531 out << stat.min << '\n';
532 out << stat.med << '\n';
533 out << stat.max << '\n';
534 out.write(ptr, val.size()*sizeof(uint8_t));
535
536 ofstream(fPath+"/"+fname+".bin") << out.str();
537 }
538
539 // -------------------------------------------------------------------
540
541 struct Statistics
542 {
543 float min;
544 float max;
545 float med;
546 float avg;
547 //float rms;
548
549 template<class T>
550 Statistics(const T &t, size_t offset_min=0, size_t offset_max=0)
551 : min(0), max(0), med(0), avg(0)
552 {
553 if (t.size()==0)
554 return;
555
556 T copy(t);
557 sort(copy.begin(), copy.end());
558
559 if (offset_min>t.size())
560 offset_min = 0;
561 if (offset_max>t.size())
562 offset_max = 0;
563
564 min = copy[offset_min];
565 max = copy[copy.size()-1-offset_max];
566 avg = accumulate (t.begin(), t.end(), 0.)/t.size();
567
568 const size_t p = t.size()/2;
569
570 med = copy[p];
571 }
572 };
573
574 void HandleControlMessageImp(const EventImp &d)
575 {
576 if (d.GetSize()==0)
577 return;
578
579 const string time = d.GetTimeAsStr("%H:%M:%S ");
580
581 string str = " ";
582 for (auto it=fControlMessageHist.rbegin(); it!=fControlMessageHist.rend(); it++)
583 {
584 str = it->substr(0, time.length());
585 if (str!="--:--:-- ")
586 break;
587 }
588
589 ostringstream tst;
590 tst << d.GetQoS();
591
592 string msg;
593 msg += str==time ? "--:--:-- " : time;
594 msg += d.Ptr<char>();
595
596 fControlMessageHist.push_back(msg);
597
598 ostringstream out;
599 out << setprecision(3);
600 out << Header(d) << '\n';
601 out << HTML::kWhite << '\t';
602
603 out << "<->";
604 for (auto it=fControlMessageHist.begin(); it!=fControlMessageHist.end(); it++)
605 out << *it << "<br/>";
606 out << "</->";
607
608 out << '\n';
609
610 ofstream(fPath+"/scriptlog.data") << out.str();
611 }
612
613 int HandleDimControlMessage(const EventImp &d)
614 {
615 if (d.GetSize()==0)
616 return GetCurrentState();
617
618 if (d.GetQoS()==90)
619 HandleControlMessageImp(d);
620
621 return GetCurrentState();
622 }
623
624 void HandleControlStateChange(const EventImp &d)
625 {
626 if (d.GetSize()==0)
627 return;
628
629 if (d.GetQoS()==-2 && fDimControl.scriptdepth==0)
630 fControlMessageHist.clear();
631
632 // Not that this will also "ding" just after program startup
633 // if the dimctrl is still in state -3
634 if (d.GetQoS()==-3 && fDimControl.scriptdepth==0)
635 SetAudio("ding");
636
637 if (d.GetQoS()>=0)
638 return;
639
640#if BOOST_VERSION < 104600
641 const string file = boost::filesystem::path(fDimControl.file).filename();
642#else
643 const string file = boost::filesystem::path(fDimControl.file).filename().string();
644#endif
645
646 HandleControlMessageImp(Event(d, fDimControl.shortmsg.data(), fDimControl.shortmsg.length()+1));
647 if (!file.empty())
648 HandleControlMessageImp(Event(d, file.data(), file.length()+1));
649 }
650
651 void HandleFscControlStateChange()
652 {
653 const int32_t &last = fDimFscControl.last.second;
654 const int32_t &state = fDimFscControl.state();
655
656 if (last==DimState::kOffline || state==DimState::kOffline)
657 return;
658
659 if (last<FSC::State::kConnected && state==FSC::State::kConnected)
660 SetAudio("sound_13");
661
662 if (last==FSC::State::kConnected && state<FSC::State::kConnected)
663 SetAudio("sound_14");
664 }
665
666 int HandleMcpConfiguration(const EventImp &d)
667 {
668 if (!CheckDataSize(d, "Mcp:Configuration", 16, true))
669 {
670 fMcpConfigurationState = DimState::kOffline;
671 fMcpConfigurationMaxTime = 0;
672 fMcpConfigurationMaxEvents = 0;
673 fMcpConfigurationName = "";
674 fMcpConfigurationRunStart = Time(Time::none);
675 return GetCurrentState();
676 }
677
678 // If a run ends and no script is running play a tirili
679 if (fMcpConfigurationState==MCP::State::kTakingData && d.GetQoS()==MCP::State::kIdle && fDimControl.state()<-2)
680 SetAudio("sound_10");
681
682 // If a run ends and a script is running just play a simple 'tick'
683 if (fMcpConfigurationState==MCP::State::kTakingData && d.GetQoS()==MCP::State::kIdle && fDimControl.state()>=-2)
684 SetAudio("losticks");
685
686 if (d.GetQoS()==MCP::State::kTakingData)
687 {
688 fMcpConfigurationRunStart = Time();
689 SetAudio("losticks");
690 }
691
692 fMcpConfigurationState = d.GetQoS();
693 fMcpConfigurationMaxTime = d.Get<uint64_t>();
694 fMcpConfigurationMaxEvents = d.Get<uint64_t>(8);
695 fMcpConfigurationName = d.Ptr<char>(16);
696
697 return GetCurrentState();
698 }
699
700 void WriteWeather(const EventImp &d, const string &name, int i, float min, float max)
701 {
702 const Statistics stat(fMagicWeatherHist[i]);
703
704 ostringstream out;
705 out << setprecision(3);
706 out << d.GetJavaDate() << '\n';
707
708 out << HTML::kWhite << '\t' << fMagicWeatherHist[i].back() << '\n';
709 out << HTML::kWhite << '\t' << stat.min << '\n';
710 out << HTML::kWhite << '\t' << stat.avg << '\n';
711 out << HTML::kWhite << '\t' << stat.max << '\n';
712
713 ofstream(fPath+"/"+name+".data") << out.str();
714
715 WriteBinary(d, "hist-magicweather-"+name, fMagicWeatherHist[i], max-min, min);
716 }
717
718 int HandleMagicWeatherData(const EventImp &d)
719 {
720 if (!CheckDataSize(d, "MagicWeather:Data", 7*4+2))
721 return GetCurrentState();
722
723 // Store a history of the last 300 entries
724 for (int i=kWeatherBegin; i<kWeatherEnd; i++)
725 {
726 fMagicWeatherHist[i].push_back(d.Ptr<float>(2)[i]);
727 if (fMagicWeatherHist[i].size()>300)
728 fMagicWeatherHist[i].pop_front();
729 }
730
731 ostringstream out;
732 out << d.GetJavaDate() << '\n';
733 if (fSun.time.IsValid() && fMoon.time.IsValid())
734 {
735 out << fSun.color << '\t' << fSun.description << '\n';
736 out << setprecision(2);
737 out << (fSun.isday?HTML::kWhite:fMoon.color) << '\t' << fMoon.description << '\n';
738 }
739 else
740 out << "\n\n";
741 out << setprecision(3);
742 for (int i=0; i<6; i++)
743 out << HTML::kWhite << '\t' << fMagicWeatherHist[i].back() << '\n';
744 out << HTML::kWhite << '\t' << GetDir(fMagicWeatherHist[kDir].back()) << '\n';
745 out << HTML::kWhite << '\t';
746 if (fTngWeatherDustHist.size()>0)
747 out << fTngWeatherDustHist.back() << '\t' << fTngWeatherDustTime.GetAsStr("%H:%M") << '\n';
748 else
749 out << "\t\n";
750
751 ofstream(fPath+"/weather.data") << out.str();
752
753 WriteWeather(d, "temp", kTemp, -5, 35);
754 WriteWeather(d, "dew", kDew, -5, 35);
755 WriteWeather(d, "hum", kHum, 0, 100);
756 WriteWeather(d, "wind", kWind, 0, 100);
757 WriteWeather(d, "gusts", kGusts, 0, 100);
758 WriteWeather(d, "press", kPress, 700, 1000);
759
760 return GetCurrentState();
761 }
762
763 int HandleTngWeatherDust(const EventImp &d)
764 {
765 if (!CheckDataSize(d, "TngWeather:Dust", 4))
766 return GetCurrentState();
767
768 fTngWeatherDustTime = d.GetTime();
769
770 fTngWeatherDustHist.push_back(d.GetFloat());
771 if (fTngWeatherDustHist.size()>300)
772 fTngWeatherDustHist.pop_front();
773
774 const Statistics stat(fTngWeatherDustHist);
775
776 const double scale = stat.max>0 ? pow(10, ceil(log10(stat.max))) : 0;
777
778 WriteBinary(d, "hist-tng-dust", fTngWeatherDustHist, scale);
779
780 ostringstream out;
781 out << d.GetJavaDate() << '\n';
782
783 ofstream(fPath+"/tngdust.data") << out.str();
784
785 return GetCurrentState();
786 }
787
788 int HandleDrivePointing(const EventImp &d)
789 {
790 if (!CheckDataSize(d, "DriveControl:Pointing", 16))
791 return GetCurrentState();
792
793 fDriveControlPointingZd = d.Get<double>();
794
795 const double az = d.Get<double>(8);
796
797 fDriveControlPointingAz = GetDir(az);
798
799 ostringstream out;
800 out << d.GetJavaDate() << '\n';
801
802 out << setprecision(5);
803 out << fDriveControlPointingZd << '\n';
804 out << az << '\t' << fDriveControlPointingAz << '\n';
805
806 ofstream(fPath+"/pointing.data") << out.str();
807
808 return GetCurrentState();
809 }
810
811 int HandleDriveTracking(const EventImp &d)
812 {
813 if (!CheckDataSize(d, "DriveControl:Tracking", 56))
814 return GetCurrentState();
815
816 const double Ra = d.Get<double>(0*8);
817 const double Dec = d.Get<double>(1*8);
818 const double Zd = d.Get<double>(3*8);
819 const double Az = d.Get<double>(4*8);
820
821 const double zd = Zd * M_PI / 180;
822 const double dzd = d.Get<double>(5*8) * M_PI / 180;
823 const double daz = d.Get<double>(6*8) * M_PI / 180;
824
825 // Correct:
826 // const double d = cos(del) - sin(zd+dzd)*sin(zd)*(1.-cos(daz));
827
828 // Simplified:
829 double dev = cos(dzd) - sin(zd+dzd)*sin(zd)*(1.-cos(daz));
830 dev = acos(dev) * 180 / M_PI * 3600;
831
832 fDriveControlTrackingDevHist.push_back(dev);
833 if (fDriveControlTrackingDevHist.size()>300)
834 fDriveControlTrackingDevHist.pop_front();
835
836 WriteBinary(d, "hist-control-deviation", fDriveControlTrackingDevHist, 120);
837
838 ostringstream out;
839 out << d.GetJavaDate() << '\n';
840
841 out << HTML::kWhite << '\t' << fDriveControlSourceName << '\n';
842 out << setprecision(5);
843 out << HTML::kWhite << '\t' << Ra << '\n';
844 out << HTML::kWhite << '\t' << Dec << '\n';
845 out << setprecision(3);
846 out << HTML::kWhite << '\t' << Zd << '\n';
847 out << HTML::kWhite << '\t' << Az << '\n';
848 out << HTML::kWhite << '\t' << dev << '\n';
849
850 fDriveControlMoonDist = -1;
851
852 Moon moon(-(17.+53./60+26.525/3600), 28.+45./60+42.462/3600);
853 if (moon.visible)
854 {
855 const double angle = moon.Angle(Ra, Dec);
856 out << Moon::Color(angle) << '\t' << setprecision(3) << angle << '\n';
857
858 fDriveControlMoonDist = angle;
859 }
860 else
861 out << HTML::kWhite << "\t&mdash; \n";
862
863 ofstream(fPath+"/tracking.data") << out.str();
864
865 return GetCurrentState();
866 }
867
868 int HandleDriveSource(const EventImp &d)
869 {
870 if (!CheckDataSize(d, "DriveControl:Source", 7*4+2, true))
871 return GetCurrentState();
872
873 const double *ptr = d.Ptr<double>();
874
875 const double ra = ptr[0]; // Ra[h]
876 const double dec = ptr[1]; // Dec[deg]
877 const double woff = ptr[4]; // Wobble offset [deg]
878 const double wang = ptr[5]; // Wobble angle [deg]
879
880 fDriveControlSourceName = d.Ptr<char>(6*8);
881
882 ostringstream out;
883 out << d.GetJavaDate() << '\n';
884
885 out << HTML::kWhite << '\t' << fDriveControlSourceName << '\n';
886 out << setprecision(5);
887 out << HTML::kWhite << '\t' << ra << '\n';
888 out << HTML::kWhite << '\t' << dec << '\n';
889 out << setprecision(3);
890 out << HTML::kWhite << '\t' << woff << '\n';
891 out << HTML::kWhite << '\t' << wang << '\n';
892
893 ofstream(fPath+"/source.data") << out.str();
894
895 return GetCurrentState();
896 }
897
898 int HandleFeedbackCalibration(const EventImp &d)
899 {
900 if (!CheckDataSize(d, "Feedback:Calibration", 3*4*416))
901 {
902 fFeedbackCalibration.clear();
903 return GetCurrentState();
904 }
905
906 const float *ptr = d.Ptr<float>();
907 fFeedbackCalibration.assign(ptr+2*416, ptr+3*416);
908
909 return GetCurrentState();
910 }
911
912 int HandleFeedbackDeviation(const EventImp &d)
913 {
914 if (!CheckDataSize(d, "Feedback:Deviation", (2*416+2)*4))
915 return GetCurrentState();
916
917 const float *ptr = d.Ptr<float>();
918 vector<float> dev(ptr+416, ptr+416+320);
919
920 fFeedbackTempOffset = ptr[2*416];
921 fFeedbackUserOffset = ptr[2*416+1];
922
923 for (int i=0; i<320; i++)
924 dev[i] -= fFeedbackTempOffset+fFeedbackUserOffset;
925
926 // Write the 160 patch values to a file
927 WriteBinary(d, "cam-feedback-deviation", dev, 1);
928
929 const Statistics stat(dev, 3);
930
931 ostringstream out;
932 out << d.GetJavaDate() << '\n';
933 out << HTML::kWhite << '\t' << fFeedbackUserOffset << '\n';
934 out << setprecision(3);
935 out << HTML::kWhite << '\t' << fFeedbackTempOffset << '\n';
936 out << HTML::kWhite << '\t' << stat.min << '\n';
937 out << HTML::kWhite << '\t' << stat.med << '\n';
938 out << HTML::kWhite << '\t' << stat.avg << '\n';
939 out << HTML::kWhite << '\t' << stat.max << '\n';
940 ofstream(fPath+"/feedback.data") << out.str();
941
942 return GetCurrentState();
943 }
944
945 int HandleBiasVoltage(const EventImp &d)
946 {
947 if (!CheckDataSize(d, "BiasControl:Voltage", 1664))
948 {
949 fBiasControlVoltageVec.clear();
950 return GetCurrentState();
951 }
952
953 fBiasControlVoltageVec.assign(d.Ptr<float>(), d.Ptr<float>()+320);
954
955 const Statistics stat(fBiasControlVoltageVec);
956
957 fBiasControlVoltageMed = stat.med;
958
959 vector<float> val(320, 0);
960 for (int i=0; i<320; i++)
961 {
962 const int idx = (fPixelMap.hv(i).hw()/9)*2+fPixelMap.hv(i).group();
963 val[idx] = fBiasControlVoltageVec[i];
964 }
965
966 if (fDimBiasControl.state()==BIAS::State::kVoltageOn)
967 WriteBinary(d, "cam-biascontrol-voltage", val, 10, 65);
968 else
969 WriteBinary(d, "cam-biascontrol-voltage", val, 75);
970
971 ostringstream out;
972 out << setprecision(3);
973 out << d.GetJavaDate() << '\n';
974 out << HTML::kWhite << '\t' << stat.min << '\n';
975 out << HTML::kWhite << '\t' << stat.med << '\n';
976 out << HTML::kWhite << '\t' << stat.avg << '\n';
977 out << HTML::kWhite << '\t' << stat.max << '\n';
978 ofstream(fPath+"/voltage.data") << out.str();
979
980 return GetCurrentState();
981 }
982
983 int HandleBiasCurrent(const EventImp &d)
984 {
985 if (!CheckDataSize(d, "BiasControl:Current", 832))
986 return GetCurrentState();
987
988 // Convert dac counts to uA
989 vector<float> v(320);
990 for (int i=0; i<320; i++)
991 v[i] = d.Ptr<uint16_t>()[i] * 5000./4096;
992
993 const bool cal = fFeedbackCalibration.size()>0 && fBiasControlVoltageVec.size()>0;
994
995 double power_tot = 0;
996 double power_apd = 0;
997
998 // 3900 Ohm/n + 1000 Ohm + 1100 Ohm (with n=4 or n=5)
999 const double R[2] = { 3075, 2870 };
1000
1001 // Calibrate the data (subtract offset)
1002 if (cal)
1003 for (int i=0; i<320; i++)
1004 {
1005 // Measued current minus leakage current (bias crate calibration)
1006 v[i] -= fBiasControlVoltageVec[i]/fFeedbackCalibration[i]*1e6;
1007
1008 // Total power participated in the camera at the G-APD
1009 // and the serial resistors (total voltage minus voltage
1010 // drop at resistors in bias crate)
1011 power_tot += v[i]*(fBiasControlVoltageVec[i] - 1100e-6*v[i])*1e-6;
1012
1013 // Group index (0 or 1) of the of the pixel (4 or 5 pixel patch)
1014 const int g = fPixelMap.hv(i).group();
1015
1016 // Current per G-APD
1017 v[i] /= g ? 5 : 4;
1018
1019 // Power consumption per G-APD
1020 if (i!=66 && i!=191 && i!=193)
1021 power_apd += v[i]*(fBiasControlVoltageVec[i]-R[g]*v[i]*1e-6)*1e-6;
1022 }
1023
1024 // Divide by number of summed channels, convert to mW
1025 power_apd /= 317e-3; // [mW]
1026
1027 if (power_tot<1e-3)
1028 power_tot = 0;
1029 if (power_apd<1e-3)
1030 power_apd = 0;
1031
1032 fBiasControlPowerTot = power_tot;
1033
1034 // Get the maximum of each patch
1035 vector<float> val(320, 0);
1036 for (int i=0; i<320; i++)
1037 {
1038 const int idx = (fPixelMap.hv(i).hw()/9)*2+fPixelMap.hv(i).group();
1039 val[idx] = v[i];
1040 }
1041
1042 // Write the 160 patch values to a file
1043 WriteBinary(d, "cam-biascontrol-current", val, 100);
1044
1045 const Statistics stat(v, 0, 3);
1046
1047 // Exclude the three crazy channels
1048 fBiasControlCurrentMed = stat.med;
1049 fBiasControlCurrentMax = stat.max;
1050
1051 // Store a history of the last 60 entries
1052 fBiasControlCurrentHist.push_back(fBiasControlCurrentMed);
1053 if (fBiasControlCurrentHist.size()>360)
1054 fBiasControlCurrentHist.pop_front();
1055
1056 // write the history to a file
1057 WriteBinary(d, "hist-biascontrol-current", fBiasControlCurrentHist, 100);
1058
1059 const string col0 = cal ? HTML::kGreen : HTML::kWhite;
1060
1061 string col1 = col0;
1062 string col2 = col0;
1063 string col3 = col0;
1064 string col4 = col0;
1065
1066 if (cal && stat.min>65)
1067 col1 = kYellow;
1068 if (cal && stat.min>80)
1069 col1 = kRed;
1070
1071 if (cal && stat.med>65)
1072 col2 = kYellow;
1073 if (cal && stat.med>80)
1074 col2 = kRed;
1075
1076 if (cal && stat.avg>65)
1077 col3 = kYellow;
1078 if (cal && stat.avg>80)
1079 col3 = kRed;
1080
1081 if (cal && stat.max>65)
1082 col4 = kYellow;
1083 if (cal && stat.max>80)
1084 col4 = kRed;
1085
1086 ostringstream out;
1087 out << setprecision(2);
1088 out << d.GetJavaDate() << '\n';
1089 out << col0 << '\t' << (cal?"yes":"no") << '\n';
1090 out << col1 << '\t' << stat.min << '\n';
1091 out << col2 << '\t' << stat.med << '\n';
1092 out << col3 << '\t' << stat.avg << '\n';
1093 out << col4 << '\t' << stat.max << '\n';
1094 out << HTML::kWhite << '\t' << power_tot << "W [" << power_apd << "mW]\n";
1095 ofstream(fPath+"/current.data") << out.str();
1096
1097 return GetCurrentState();
1098 }
1099
1100 int HandleFadEvents(const EventImp &d)
1101 {
1102 if (!CheckDataSize(d, "FadControl:Events", 4*4))
1103 {
1104 fFadControlNumEvents = -1;
1105 return GetCurrentState();
1106 }
1107
1108 fFadControlNumEvents = d.Get<uint32_t>();
1109
1110 return GetCurrentState();
1111 }
1112
1113 int HandleFadDrsRuns(const EventImp &d)
1114 {
1115 if (!CheckDataSize(d, "FadControl:DrsRuns", 4*4))
1116 {
1117 fFadControlDrsStep = -1;
1118 return GetCurrentState();
1119 }
1120
1121 const uint32_t *ptr = d.Ptr<uint32_t>();
1122 fFadControlDrsStep = ptr[0];
1123 fFadControlDrsRuns[0] = ptr[1];
1124 fFadControlDrsRuns[1] = ptr[2];
1125 fFadControlDrsRuns[2] = ptr[3];
1126
1127 return GetCurrentState();
1128 }
1129
1130 int HandleFadConnections(const EventImp &d)
1131 {
1132 if (!CheckDataSize(d, "FadControl:Connections", 41))
1133 {
1134 //fStatusEventBuilderLabel->setText("Offline");
1135 return GetCurrentState();
1136 }
1137
1138 string rc(40, '-'); // orange/red [45]
1139
1140 const uint8_t *ptr = d.Ptr<uint8_t>();
1141
1142 int c[4] = { '.', '.', '.', '.' };
1143
1144 for (int i=0; i<40; i++)
1145 {
1146 const uint8_t stat1 = ptr[i]&3;
1147 const uint8_t stat2 = ptr[i]>>3;
1148
1149 if (stat1==0 && stat2==0)
1150 rc[i] = '.'; // gray [46]
1151 else
1152 if (stat1>=2 && stat2==8)
1153 rc[i] = stat1==2?'+':'*'; // green [43] : check [42]
1154
1155 if (rc[i]<c[i/10])
1156 c[i/10] = rc[i];
1157 }
1158
1159 string col[4];
1160 for (int i=0; i<4; i++)
1161 switch (c[i])
1162 {
1163 case '.': col[i]=HTML::kWhite; break;
1164 case '-': col[i]=HTML::kRed; break;
1165 case '+': col[i]=HTML::kYellow; break;
1166 case '*': col[i]=HTML::kGreen; break;
1167 }
1168
1169 ostringstream out;
1170 out << setprecision(3);
1171 out << d.GetJavaDate() << '\n';
1172 out << col[0] << '\t' << rc.substr( 0, 10) << '\n';
1173 out << col[1] << '\t' << rc.substr(10, 10) << '\n';
1174 out << col[2] << '\t' << rc.substr(20, 10) << '\n';
1175 out << col[3] << '\t' << rc.substr(30, 10) << '\n';
1176 ofstream(fPath+"/fad.data") << out.str();
1177
1178 return GetCurrentState();
1179 }
1180
1181 int HandleFtmTriggerRates(const EventImp &d)
1182 {
1183 if (!CheckDataSize(d, "FtmControl:TriggerRates", 24+160+640+8))
1184 return GetCurrentState();
1185
1186 const double crate = d.Get<float>(20); // Camera rate
1187
1188 // New run started
1189 if (crate<0)
1190 {
1191 fFtmControlNewRunStarted = true;
1192 return GetCurrentState();
1193 }
1194 fFtmControlNewRunStarted = false;
1195
1196 const float *brates = d.Ptr<float>(24); // Board rate
1197 const float *prates = d.Ptr<float>(24+160); // Patch rate
1198
1199 // Store a history of the last 60 entries
1200 fFtmControlTriggerRateHist.push_back(crate);
1201 if (fFtmControlTriggerRateHist.size()>60)
1202 fFtmControlTriggerRateHist.pop_front();
1203
1204 // FIXME: Add statistics for all kind of rates
1205
1206 WriteBinary(d, "hist-ftmcontrol-triggerrate",
1207 fFtmControlTriggerRateHist, 100);
1208 WriteBinary(d, "cam-ftmcontrol-boardrates",
1209 vector<float>(brates, brates+40), 10);
1210 WriteBinary(d, "cam-ftmcontrol-patchrates",
1211 vector<float>(prates, prates+160), 10);
1212
1213 ostringstream out;
1214 out << setprecision(3);
1215 out << d.GetJavaDate() << '\n';
1216 out << HTML::kWhite << '\t' << crate << '\n';
1217
1218 ofstream(fPath+"/trigger.data") << out.str();
1219
1220 const Statistics bstat(vector<float>(brates, brates+40));
1221 const Statistics pstat(vector<float>(prates, prates+160));
1222
1223 out.str("");
1224 out << d.GetJavaDate() << '\n';
1225 out << HTML::kWhite << '\t' << bstat.min << '\n';
1226 out << HTML::kWhite << '\t' << bstat.med << '\n';
1227 out << HTML::kWhite << '\t' << bstat.avg << '\n';
1228 out << HTML::kWhite << '\t' << bstat.max << '\n';
1229 ofstream(fPath+"/boardrates.data") << out.str();
1230
1231 out.str("");
1232 out << d.GetJavaDate() << '\n';
1233 out << HTML::kWhite << '\t' << pstat.min << '\n';
1234 out << HTML::kWhite << '\t' << pstat.med << '\n';
1235 out << HTML::kWhite << '\t' << pstat.avg << '\n';
1236 out << HTML::kWhite << '\t' << pstat.max << '\n';
1237 ofstream(fPath+"/patchrates.data") << out.str();
1238
1239 return GetCurrentState();
1240 }
1241
1242 int HandleFtmStaticData(const EventImp &d)
1243 {
1244 if (!CheckDataSize(d, "FtmControl:StaticData", sizeof(FTM::DimStaticData)))
1245 return GetCurrentState();
1246
1247 const FTM::DimStaticData &dat = d.Ref<FTM::DimStaticData>();
1248
1249 vector<uint16_t> vecp(dat.fThreshold, dat.fThreshold+160);
1250 vector<uint16_t> vecb(dat.fMultiplicity, dat.fMultiplicity+40);
1251
1252 WriteBinary(d, "cam-ftmcontrol-thresholds-patch", vecp, 1000);
1253 WriteBinary(d, "cam-ftmcontrol-thresholds-board", vecb, 100);
1254
1255 const Statistics statp(vecp);
1256 const Statistics statb(vecb);
1257
1258 fFtmPatchThresholdMed = statp.med;
1259 fFtmBoardThresholdMed = statb.med;
1260
1261 ostringstream out;
1262 out << d.GetJavaDate() << '\n';
1263 out << HTML::kWhite << '\t' << statb.min << '\n';
1264 out << HTML::kWhite << '\t' << statb.med << '\n';
1265 out << HTML::kWhite << '\t' << statb.max << '\n';
1266 ofstream(fPath+"/thresholds-board.data") << out.str();
1267
1268 out.str("");
1269 out << d.GetJavaDate() << '\n';
1270 out << HTML::kWhite << '\t' << statp.min << '\n';
1271 out << HTML::kWhite << '\t' << statp.med << '\n';
1272 out << HTML::kWhite << '\t' << statp.max << '\n';
1273 ofstream(fPath+"/thresholds-patch.data") << out.str();
1274
1275 out.str("");
1276 out << d.GetJavaDate() << '\n';
1277 out << HTML::kWhite << '\t' << statb.med << '\n';
1278 out << HTML::kWhite << '\t' << statp.med << '\n';
1279 ofstream(fPath+"/thresholds.data") << out.str();
1280
1281 out.str("");
1282 out << d.GetJavaDate() << '\n';
1283 out << HTML::kWhite << '\t' << dat.fTriggerInterval << '\n';
1284 out << HTML::kWhite << '\t';
1285 if (dat.HasPedestal())
1286 out << dat.fTriggerSeqPed;
1287 else
1288 out << "&ndash;";
1289 out << ':';
1290 if (dat.HasLPext())
1291 out << dat.fTriggerSeqLPext;
1292 else
1293 out << "&ndash;";
1294 out << ':';
1295 if (dat.HasLPint())
1296 out << dat.fTriggerSeqLPint;
1297 else
1298 out << "&ndash;";
1299 out << '\n';
1300
1301 out << HTML::kWhite << '\t' << (dat.HasTrigger()?"on":"off") << " / " << (dat.HasExt1()?"on":"off") << " / " << (dat.HasExt2()?"on":"off") << '\n';
1302 out << HTML::kWhite << '\t' << (dat.HasVeto()?"on":"off") << " / " << (dat.HasClockConditioner()?"time cal":"marker") << '\n';
1303 out << HTML::kWhite << '\t' << dat.fMultiplicityPhysics << " / " << dat.fMultiplicityCalib << '\n';
1304 out << HTML::kWhite << '\t' << dat.fWindowPhysics << '\t' << dat.fWindowCalib << '\n';
1305 out << HTML::kWhite << '\t' << dat.fDelayTrigger << '\t' << dat.fDelayTimeMarker << '\n';
1306 out << HTML::kWhite << '\t' << dat.fDeadTime << '\n';
1307
1308 int64_t vp = dat.fPrescaling[0];
1309 for (int i=1; i<40; i++)
1310 if (vp!=dat.fPrescaling[i])
1311 vp = -1;
1312
1313 if (vp<0)
1314 out << HTML::kYellow << "\tdifferent\n";
1315 else
1316 out << HTML::kWhite << '\t' << 0.5*vp << "\n";
1317
1318 ofstream(fPath+"/ftm.data") << out.str();
1319
1320 // Active FTUs: IsActive(i)
1321 // Enabled Pix: IsEnabled(i)
1322
1323 return GetCurrentState();
1324 }
1325
1326 int HandleFtmFtuList(const EventImp &d)
1327 {
1328 if (!CheckDataSize(d, "FtmControl:FtuList", sizeof(FTM::DimFtuList)))
1329 return GetCurrentState();
1330
1331 const FTM::DimFtuList &sdata = d.Ref<FTM::DimFtuList>();
1332
1333 ostringstream out;
1334 out << d.GetJavaDate() << '\n';
1335
1336 int cnt = 0;
1337 for (int i=0; i<4; i++)
1338 {
1339 out << HTML::kWhite << '\t';
1340 for (int j=0; j<10; j++)
1341 if (sdata.IsActive(i*10+j))
1342 {
1343 if (sdata.fPing[i*10+j]==1)
1344 {
1345 out << '*';
1346 cnt++;
1347 }
1348 else
1349 out << sdata.fPing[i*10+j];
1350 }
1351 else
1352 out << '-';
1353 out << '\n';
1354 }
1355
1356 fFtmControlFtuOk = cnt==40;
1357
1358 ofstream(fPath+"/ftu.data") << out.str();
1359
1360 return GetCurrentState();
1361 }
1362
1363 int HandleFadEventData(const EventImp &d)
1364 {
1365 if (!CheckDataSize(d, "FadControl:EventData", 23040))
1366 return GetCurrentState();
1367
1368 //const float *avg = d.Ptr<float>();
1369 //const float *rms = d.Ptr<float>(1440*sizeof(float));
1370 const float *dat = d.Ptr<float>(1440*sizeof(float)*2);
1371 //const float *pos = d.Ptr<float>(1440*sizeof(float)*3);
1372
1373 vector<float> max(320, -2);
1374 for (int i=0; i<1440; i++)
1375 {
1376 if (i%9==8)
1377 continue;
1378
1379 const int idx = (fPixelMap.hw(i).hw()/9)*2+fPixelMap.hw(i).group();
1380 const double v = dat[i]/1000;
1381 if (v>max[idx])
1382 max[idx]=v;
1383 }
1384
1385 switch (fFadControlDrsStep)
1386 {
1387 case 0: WriteBinary(d, "cam-fadcontrol-eventdata", max, 2, -1); break;
1388 case 1: WriteBinary(d, "cam-fadcontrol-eventdata", max, 2, 0); break;
1389 default: WriteBinary(d, "cam-fadcontrol-eventdata", max, 0.25, 0); break;
1390 }
1391
1392 return GetCurrentState();
1393 }
1394
1395 int HandleFscTemperature(const EventImp &d)
1396 {
1397 if (!CheckDataSize(d, "FscControl:Temperature", 240))
1398 return GetCurrentState();
1399
1400 const float *ptr = d.Ptr<float>(4);
1401
1402 double avg = 0;
1403 double rms = 0;
1404 double min = 99;
1405 double max = -99;
1406
1407 int num = 0;
1408 for (const float *t=ptr; t<ptr+31; t++)
1409 {
1410 if (*t==0)
1411 continue;
1412
1413 if (*t>max)
1414 max = *t;
1415
1416 if (*t<min)
1417 min = *t;
1418
1419 avg += *t;
1420 rms += *t * *t;
1421
1422 num++;
1423 }
1424
1425 avg /= num;
1426 rms = sqrt(rms/num-avg*avg);
1427
1428 if (fMagicWeatherHist[kTemp].size()>0)
1429 {
1430 fFscControlTemperatureHist.push_back(avg-fMagicWeatherHist[kTemp].back());
1431 if (fFscControlTemperatureHist.size()>300)
1432 fFscControlTemperatureHist.pop_front();
1433 }
1434
1435 const Statistics stat(fFscControlTemperatureHist);
1436
1437 ostringstream out;
1438 out << setprecision(3);
1439 out << d.GetJavaDate() << '\n';
1440 out << HTML::kWhite << '\t' << fFscControlHumidityAvg << '\n';
1441 out << HTML::kWhite << '\t' << min << '\n';
1442 out << HTML::kWhite << '\t' << avg << '\n';
1443 out << HTML::kWhite << '\t' << max << '\n';
1444 out << HTML::kWhite << '\t' << stat.min << '\n';
1445 out << HTML::kWhite << '\t' << stat.avg << '\n';
1446 out << HTML::kWhite << '\t' << stat.max << '\n';
1447
1448 ofstream(fPath+"/fsc.data") << out.str();
1449
1450 WriteBinary(d, "hist-fsccontrol-temperature",
1451 fFscControlTemperatureHist, 10);
1452
1453 return GetCurrentState();
1454 }
1455
1456 int HandleFscHumidity(const EventImp &d)
1457 {
1458 if (!CheckDataSize(d, "FscControl:Humidity", 5*4))
1459 return GetCurrentState();
1460
1461 const float *ptr = d.Ptr<float>(4);
1462
1463 double avg = 0;
1464 int num = 0;
1465
1466 for (const float *t=ptr; t<ptr+4; t++)
1467 if (*t>0)
1468 {
1469 avg += *t;
1470 num++;
1471 }
1472
1473 fFscControlHumidityAvg = avg/num;
1474
1475 return GetCurrentState();
1476 }
1477
1478 int HandleRateScanData(const EventImp &d)
1479 {
1480 if (!CheckDataSize(d, "RateScan:Data", 824))
1481 return GetCurrentState();
1482
1483 const uint64_t id = d.Get<uint64_t>();
1484 const float *rate = d.Ptr<float>(20);
1485
1486 if (fRateScanDataId!=id)
1487 {
1488 for (int i=0; i<41; i++)
1489 fRateScanDataHist[i].clear();
1490 fRateScanDataId = id;
1491 }
1492 fRateScanDataHist[0].push_back(log10(rate[0]));
1493
1494 double max = 0;
1495 for (int i=1; i<41; i++)
1496 {
1497 fRateScanDataHist[i].push_back(log10(rate[i]));
1498 if (rate[i]>max)
1499 max = rate[i];
1500 }
1501
1502 // Cycle by time!
1503 fRateScanBoard ++;
1504 fRateScanBoard %= 40;
1505
1506 WriteBinary(d, "hist-ratescan", fRateScanDataHist[0], 10, -2);
1507 WriteBinary(d, "cam-ratescan-board", fRateScanDataHist[fRateScanBoard+1], 10, -4);
1508
1509 ostringstream out;
1510 out << setprecision(3);
1511 out << d.GetJavaDate() << '\n';
1512 out << HTML::kWhite << '\t' << fFtmBoardThresholdMed << '\n';
1513 out << HTML::kWhite << '\t' << fFtmPatchThresholdMed << '\n';
1514 out << HTML::kWhite << '\t' << pow(10, fRateScanDataHist[0].back()) << '\n';
1515 out << HTML::kWhite << '\t' << max << '\n';
1516
1517 ofstream(fPath+"/ratescan.data") << out.str();
1518
1519 out.str("");
1520 out << d.GetJavaDate() << '\n';
1521 out << HTML::kWhite << '\t' << int(fRateScanBoard) << '\n';
1522 out << HTML::kWhite << '\t' << pow(10, fRateScanDataHist[fRateScanBoard+1].back()) << '\n';
1523
1524 ofstream(fPath+"/ratescan_board.data") << out.str();
1525
1526 return GetCurrentState();
1527 }
1528
1529 int HandleRateControlThreshold(const EventImp &d)
1530 {
1531 if (!CheckDataSize(d, "RateControl:Threshold", 2))
1532 return GetCurrentState();
1533
1534 const uint16_t th = d.Get<uint16_t>();
1535
1536 fRateControlThreshold.push_back(th);
1537 if (fRateControlThreshold.size()>300)
1538 fRateControlThreshold.pop_front();
1539
1540 WriteBinary(d, "hist-ratecontrol-threshold", fRateControlThreshold, 1000);
1541
1542 return GetCurrentState();
1543 }
1544
1545 // -------------------------------------------------------------------
1546
1547 void HandleDoTest(const EventImp &d)
1548 {
1549 ostringstream out;
1550 out << d.GetJavaDate() << '\n';
1551
1552 switch (d.GetQoS())
1553 {
1554 case -3: out << HTML::kWhite << "\tNot running\n"; break;
1555 case -2: out << HTML::kBlue << "\tLoading\n"; break;
1556 case -1: out << HTML::kBlue << "\tStarted\n"; break;
1557 default: out << HTML::kGreen << "\tRunning [" << d.GetQoS() << "]\n"; break;
1558 }
1559
1560 ofstream(fPath+"/dotest.data") << out.str();
1561 }
1562
1563 // -------------------------------------------------------------------
1564
1565 /*
1566 bool CheckEventSize(size_t has, const char *name, size_t size)
1567 {
1568 if (has==size)
1569 return true;
1570
1571 ostringstream msg;
1572 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
1573 Fatal(msg);
1574 return false;
1575 }*/
1576
1577 int Print() const
1578 {
1579 Out() << fDimDNS << endl;
1580 Out() << fDimMcp << endl;
1581 Out() << fDimControl << endl;
1582 Out() << fDimDataLogger << endl;
1583 Out() << fDimDriveControl << endl;
1584 Out() << fDimTimeCheck << endl;
1585 Out() << fDimFadControl << endl;
1586 Out() << fDimFtmControl << endl;
1587 Out() << fDimBiasControl << endl;
1588 Out() << fDimFeedback << endl;
1589 Out() << fDimRateControl << endl;
1590 Out() << fDimFscControl << endl;
1591 Out() << fDimMagicWeather << endl;
1592 Out() << fDimTngWeather << endl;
1593 Out() << fDimRateScan << endl;
1594 Out() << fDimChat << endl;
1595 Out() << fDimSkypeClient << endl;
1596
1597 return GetCurrentState();
1598 }
1599
1600 string GetStateHtml(const DimState &state, int green) const
1601 {
1602 if (!state.online())
1603 return HTML::kWhite+"\t&mdash;\n";
1604
1605 if (&state==&fDimControl)
1606 return HTML::kGreen +'\t'+(state.state()<-2?"Idle":fDimControl.shortmsg)+'\n';
1607
1608 const State rc = state.description();
1609
1610 // Sate not found in list, server online (-3: offline; -2: not found)
1611 if (rc.index==-2)
1612 {
1613 ostringstream out;
1614 out << HTML::kWhite << '\t' << state.state() << '\n';
1615 return out.str();
1616 }
1617
1618 //ostringstream msg;
1619 //msg << HTML::kWhite << '\t' << rc.name << " [" << rc.index << "]\n";
1620 //return msg.str();
1621
1622 if (rc.index<0)
1623 return HTML::kWhite + "\t&mdash;\n";
1624
1625 string col = HTML::kGreen;
1626 if (rc.index<green)
1627 col = HTML::kYellow;
1628 if (rc.index>0xff)
1629 col = HTML::kRed;
1630
1631 return col + '\t' + rc.name + '\n';
1632 }
1633
1634 bool SetError(bool b, const string &err)
1635 {
1636 if (!b)
1637 {
1638 fErrorList.erase(err);
1639 return 0;
1640 }
1641
1642 const bool isnew = fErrorList.insert(err).second;
1643
1644 if (isnew)
1645 {
1646 ostringstream msg;
1647 msg << "<pre>" << Time().GetAsStr("%m-%d %H:%M") << "</pre> <->" << err << "</->";
1648 if (find(fErrorHist.begin(), fErrorHist.end(), msg.str())==fErrorHist.end())
1649 {
1650 fErrorHist.push_front(msg.str());
1651 if (fErrorHist.size()>80)
1652 fErrorHist.pop_back();
1653 }
1654 }
1655
1656 return isnew;
1657 }
1658
1659 void UpdateAstronomy()
1660 {
1661 const double lon = -(17.+53./60+26.525/3600);
1662 const double lat = 28.+45./60+42.462/3600;
1663
1664 Time now;
1665
1666 fSun = Sun (lon, lat, now);
1667 fMoon = Moon(lon, lat, now);
1668
1669 ostringstream out;
1670 out << setprecision(3);
1671 out << now.JavaDate() << '\n';
1672 out << HTML::kWhite << '\t' << fSun.fSunRiseDarkTime.GetAsStr("%H:%M") << '\n';
1673 out << HTML::kWhite << '\t' << fSun.fSunRiseAstronomical.GetAsStr("%H:%M") << '\n';
1674 out << HTML::kWhite << '\t' << fSun.fSunRiseCivil.GetAsStr("%H:%M") << '\n';
1675 out << HTML::kWhite << '\t' << fSun.fSunRiseDayTime.GetAsStr("%H:%M") << '\n';
1676
1677 out << HTML::kWhite << '\t' << fSun.fSunSetDayTime.GetAsStr("%H:%M") << '\n';
1678 out << HTML::kWhite << '\t' << fSun.fSunSetCivil.GetAsStr("%H:%M") << '\n';
1679 out << HTML::kWhite << '\t' << fSun.fSunSetAstronomical.GetAsStr("%H:%M") << '\n';
1680 out << HTML::kWhite << '\t' << fSun.fSunSetDarkTime.GetAsStr("%H:%M") << '\n';
1681
1682 ofstream(fPath+"/sun.data") << out.str();
1683
1684 out.str("");
1685 out << now.JavaDate() << '\n';
1686
1687 out << HTML::kWhite << '\t' << fMoon.fMoonRise.GetAsStr("%H:%M") << '\n';
1688 out << HTML::kWhite << '\t' << fMoon.fMoonTransit.GetAsStr("%H:%M") << '\n';
1689 out << HTML::kWhite << '\t' << fMoon.fMoonSet.GetAsStr("%H:%M") << '\n';
1690
1691 out << (fSun.visible?HTML::kWhite:fMoon.color) << '\t' << fMoon.description << '\n';
1692
1693 if (!fMoon.visible)
1694 out << HTML::kWhite << "\t&mdash;\n";
1695 else
1696 {
1697 string col = HTML::kGreen;
1698 if (fMoon.zd>25)
1699 col = HTML::kYellow;
1700 if (fMoon.zd>45 && fMoon.zd<85)
1701 col = HTML::kRed;
1702 out << col << '\t' << fMoon.zd << '\n';
1703 }
1704
1705 ostringstream out2;
1706 out2 << setprecision(3);
1707 out2 << now.JavaDate() << '\n';
1708
1709#ifdef HAVE_SQL
1710 try
1711 {
1712 static const boost::regex expr("(([[:word:].-]+)(:(.+))?@)?([[:word:].-]+)(:([[:digit:]]+))?(/([[:word:].-]+))");
1713
1714 boost::smatch what;
1715 if (!boost::regex_match(fDatabase, what, expr, boost::match_extra))
1716 throw runtime_error("Couldn't parse '"+fDatabase+"'.");
1717
1718 if (what.size()!=10)
1719 throw runtime_error("Error parsing '"+fDatabase+"'.");
1720
1721 const string user = what[2];
1722 const string passwd = what[4];
1723 const string server = what[5];
1724 const string db = what[9];
1725 const int port = atoi(string(what[7]).c_str());
1726
1727 mysqlpp::Connection conn(db.c_str(), server.c_str(), user.c_str(), passwd.c_str(), port);
1728
1729 const mysqlpp::StoreQueryResult res =
1730 conn.query("SELECT fSourceName, fRightAscension, fDeclination FROM source").store();
1731
1732 out << HTML::kWhite << '\t';
1733 out2 << HTML::kWhite << '\t';
1734
1735 for (vector<mysqlpp::Row>::const_iterator v=res.begin(); v<res.end(); v++)
1736 {
1737 const string name = (*v)[0].c_str();
1738 const double ra = (*v)[1];
1739 const double dec = (*v)[2];
1740#ifdef HAVE_NOVA
1741 ln_lnlat_posn observer;
1742 observer.lng = lon;
1743 observer.lat = lat;
1744
1745 ln_hrz_posn hrz;
1746 ln_equ_posn pos;
1747 pos.ra = ra*15;
1748 pos.dec = dec;
1749
1750 ln_get_hrz_from_equ(&pos, &observer, now.JD(), &hrz);
1751 //az = hrz.az;
1752
1753 string col = HTML::kWhite;
1754 if (hrz.alt>5)
1755 col = HTML::kRed;
1756 if (hrz.alt>25)
1757 col = HTML::kYellow;
1758 if (hrz.alt>60)
1759 col = HTML::kGreen;
1760
1761 out2 << "<tr bgcolor='" << col << "'>";
1762 out2 << "<td>" << name << "</td>";
1763 if (hrz.alt>5)
1764 {
1765 out2 << "<td>" << 90-hrz.alt << "&deg;</td>";
1766 out2 << "<td>" << GetDir(hrz.az) << "</td>";
1767 }
1768 else
1769 out2 << "<td/><td/>";
1770 out2 << "</tr>";
1771#endif
1772 const int32_t angle = fMoon.Angle(ra, dec);
1773
1774 out << "<tr bgcolor='" << Moon::Color(angle) << "'>";
1775 out << "<td>" << name << "</td>";
1776 out << "<td>" << round(angle) << "&deg;</td>";
1777 out << "</tr>";
1778 }
1779 out << '\n';
1780 out2 << '\n';
1781 out << HTML::kWhite << '\t' << Time()-now << '\n';
1782 out2 << HTML::kWhite << '\t' << Time()-now << '\n';
1783 }
1784 catch (const exception &e)
1785 {
1786 out << '\n';
1787 out2 << '\n';
1788 out << HTML::kWhite << '\t' << "ERROR - "+string(e.what()) << '\n';
1789 out2 << HTML::kWhite << '\t' << "ERROR - "+string(e.what()) << '\n';
1790 }
1791#endif
1792
1793 ofstream(fPath+"/moon.data") << out.str();
1794 ofstream(fPath+"/source-list.data") << out2.str();
1795 }
1796
1797 int Execute()
1798 {
1799 // Dispatch (execute) at most one handler from the queue. In contrary
1800 // to run_one(), it doesn't wait until a handler is available
1801 // which can be dispatched, so poll_one() might return with 0
1802 // handlers dispatched. The handlers are always dispatched/executed
1803 // synchronously, i.e. within the call to poll_one()
1804 //poll_one();
1805
1806 Time now;
1807 if (now-fLastUpdate<boost::posix_time::seconds(1))
1808 return fDimDNS.online() ? kStateRunning : kStateDimNetworkNA;
1809 fLastUpdate=now;
1810
1811 // ==============================================================
1812
1813 const bool data_taking =
1814 fDimMcp.state()==MCP::State::kTriggerOn ||
1815 fDimMcp.state()==MCP::State::kTakingData;
1816
1817 const bool data_run =
1818 fMcpConfigurationName=="data" ||
1819 fMcpConfigurationName=="data-rt";
1820
1821 const bool haderr = fErrorList.size()>0;
1822
1823 bool newerr = false;
1824
1825 newerr |= SetError(!fDimDNS.online(),
1826 "<b><#darkred>DIM network not available</#></b>");
1827 newerr |= SetError(!fDimControl.online(),
1828 "<b>dimctrl offline</b>");
1829
1830 //newerr |= SetError(fDimDriveControl.state()==Drive::State::kLocked,
1831 // "<b><#darkred>Drive in LOCKED state, drive was automatically parked</#></b>");
1832
1833 newerr |= SetError(fDimDriveControl.state()>0xff && data_taking && data_run,
1834 "Drive in ERROR state during data-taking");
1835 newerr |= SetError(fDriveControlMoonDist>155,
1836 "Moon within the field-of-view of the cones");
1837 newerr |= SetError(fDriveControlMoonDist>=0 && fDriveControlMoonDist<3,
1838 "Moon within the field-of-view of the camera");
1839
1840 newerr |= SetError(fDimBiasControl.state()<BIAS::State::kRamping && data_taking,
1841 "BIAS not operating during data-taking");
1842 newerr |= SetError(fDimBiasControl.state()==BIAS::State::kOverCurrent,
1843 "BIAS channels in OverCurrent");
1844 newerr |= SetError(fDimBiasControl.state()==BIAS::State::kNotReferenced,
1845 "BIAS voltage not at reference");
1846
1847 newerr |= SetError(fFeedbackCalibration.size()>0 && fBiasControlCurrentMed>80,
1848 "Median current exceeds 80&micro;A/pix");
1849 newerr |= SetError(fFeedbackCalibration.size()>0 && fBiasControlCurrentMax>100,
1850 "Maximum current exceeds 100&micro;A/pix");
1851
1852 newerr |= SetError(fFscControlHumidityAvg>60,
1853 "Average camera humidity exceed 60%");
1854
1855 newerr |= SetError(fMagicWeatherHist[kHum].size()>0 && fMagicWeatherHist[kHum].back()>98 && data_taking,
1856 "Outside humidity exceeds 98% during data-taking");
1857 newerr |= SetError(fMagicWeatherHist[kGusts].size()>0 && fMagicWeatherHist[kGusts].back()>98 && fDimDriveControl.state()==Drive::State::kTracking,
1858 "Wind gusts exceed 50km/h during tracking");
1859
1860 newerr |= SetError(fFscControlTemperatureHist.size()>0 && fFscControlTemperatureHist.back()>8,
1861 "Sensor temperature exceeds outside temperature by more than 8&deg;C");
1862
1863 if (fFtmControlTriggerRateHist.size()>0 && !fFtmControlNewRunStarted)
1864 {
1865 newerr |= SetError(fFtmControlTriggerRateHist.size()>0 && fFtmControlTriggerRateHist.back()<0.01 && fDimMcp.state()==MCP::State::kTakingData,
1866 "Trigger rate below 10mHz during data taking");
1867 }
1868
1869 newerr |= SetError(fDimTimeCheck.state()==1,
1870 "Warning NTP time difference of drive PC exceeds 1s");
1871 newerr |= SetError(fDimTimeCheck.state()<1,
1872 "Warning timecheck not running");
1873
1874 newerr |= SetError(fDimFeedback.state()!=Feedback::State::kCalibrating &&
1875 fDimBiasControl.state()==BIAS::State::kVoltageOn &&
1876 fBiasControlVoltageMed>3 &&
1877 fFeedbackCalibration.size()==0,
1878 "Bias voltage switched on, but bias crate not calibrated");
1879
1880 // FTM in Connected instead of Idle --> power cyclen
1881
1882 /* // Check offline and disconnected status?
1883 Out() << fDimMcp << endl;
1884 Out() << fDimControl << endl;
1885 Out() << fDimDataLogger << endl;
1886 Out() << fDimDriveControl << endl;
1887 Out() << fDimFadControl << endl;
1888 Out() << fDimFtmControl << endl;
1889 Out() << fDimBiasControl << endl;
1890 Out() << fDimFeedback << endl;
1891 Out() << fDimRateControl << endl;
1892 Out() << fDimFscControl << endl;
1893 Out() << fDimMagicWeather << endl;
1894 Out() << fDimRateScan << endl;
1895 Out() << fDimChat << endl;
1896 */
1897
1898 // FTU in error
1899 // FAD lost
1900
1901 // --------------------------------------------------------------
1902 ostringstream out;
1903
1904 if (newerr)
1905 {
1906 SetAudio("sound_7");
1907
1908 out << now.JavaDate() << '\n';
1909 out << HTML::kWhite << '\t';
1910 for (auto it=fErrorHist.begin(); it!=fErrorHist.end(); it++)
1911 out << *it << "<br/>";
1912 out << '\n';
1913
1914 ofstream(fPath+"/errorhist.data") << out.str();
1915 }
1916
1917 out.str("");
1918 out << Header(now) << '\t' << (fErrorList.size()>0) << '\t' << (fDimControl.state()>-3) << '\n';
1919 out << setprecision(3);
1920 out << HTML::kWhite << '\t';
1921 for (auto it=fErrorList.begin(); it!=fErrorList.end(); it++)
1922 out << *it << "<br/>";
1923 out << '\n';
1924
1925 if (haderr || fErrorList.size()>0)
1926 ofstream(fPath+"/error.data") << out.str();
1927
1928 // ==============================================================
1929
1930 out.str("");
1931 out << Header(now) << '\t' << (fErrorList.size()>0) << '\t' << (fDimControl.state()>-3) << '\n';
1932 out << setprecision(3);
1933
1934 // -------------- System status --------------
1935 if (fDimDNS.online() && fDimMcp.state()>=MCP::State::kIdle) // Idle
1936 {
1937 string col = HTML::kBlue;
1938 switch (fMcpConfigurationState)
1939 {
1940 case MCP::State::kIdle:
1941 col = HTML::kWhite;
1942 break;
1943 case MCP::State::kConfiguring1:
1944 case MCP::State::kConfiguring2:
1945 case MCP::State::kConfiguring3:
1946 case MCP::State::kConfigured:
1947 case MCP::State::kTriggerOn:
1948 col = HTML::kBlue;
1949 break;
1950 case MCP::State::kTakingData:
1951 col = HTML::kBlue;
1952 if (fDimFadControl.state()==FAD::State::kWritingData)
1953 col = HTML::kGreen;
1954 break;
1955 }
1956
1957 const bool other =
1958 fDimRateControl.state()==RateControl::State::kSettingGlobalThreshold ||
1959 fDimRateScan.state()==RateScan::State::kInProgress;
1960
1961 if (other)
1962 col = HTML::kBlue;
1963
1964 out << col << '\t';
1965
1966 if (!other)
1967 {
1968 switch (fMcpConfigurationState)
1969 {
1970 case MCP::State::kIdle:
1971 out << "Idle [" << fMcpConfigurationName << "]";
1972 break;
1973 case MCP::State::kConfiguring1:
1974 case MCP::State::kConfiguring2:
1975 case MCP::State::kConfiguring3:
1976 out << "Configuring [" << fMcpConfigurationName << "]";
1977 break;
1978 case MCP::State::kConfigured:
1979 out << "Configured [" << fMcpConfigurationName << "]";
1980 break;
1981 case MCP::State::kTriggerOn:
1982 case MCP::State::kTakingData:
1983 out << fMcpConfigurationName;
1984 if (fFadControlDrsRuns[2]>0)
1985 out << "(" << fFadControlDrsRuns[2] << ")";
1986 break;
1987 }
1988 }
1989 else
1990 if (fDimRateControl.state()==RateControl::State::kSettingGlobalThreshold)
1991 out << "Calibrating threshold";
1992 else
1993 if (fDimRateScan.state()==RateScan::State::kInProgress)
1994 out << "Rate scan in progress";
1995
1996 if (fMcpConfigurationState>MCP::State::kConfigured &&
1997 fDimRateControl.state()!=RateControl::State::kSettingGlobalThreshold)
1998 {
1999 ostringstream evt;
2000 if (fMcpConfigurationMaxEvents>0)
2001 {
2002 const int64_t de = int64_t(fMcpConfigurationMaxEvents) - int64_t(fFadControlNumEvents);
2003 if (de>=0 && fMcpConfigurationState==MCP::State::kTakingData)
2004 evt << de;
2005 else
2006 evt << fMcpConfigurationMaxEvents;
2007 }
2008 else
2009 {
2010 if (fMcpConfigurationState==MCP::State::kTakingData)
2011 {
2012 if (fFadControlNumEvents>2999)
2013 evt << floor(fFadControlNumEvents/1000) << 'k';
2014 else
2015 evt << fFadControlNumEvents;
2016 }
2017 }
2018
2019 ostringstream tim;
2020 if (fMcpConfigurationMaxTime>0)
2021 {
2022 const uint32_t dt = (Time()-fMcpConfigurationRunStart).total_seconds();
2023 if (dt<=fMcpConfigurationMaxTime && fMcpConfigurationState==MCP::State::kTakingData)
2024 tim << fMcpConfigurationMaxTime-dt << 's';
2025 else
2026 tim << fMcpConfigurationMaxTime << 's';
2027 }
2028 else
2029 {
2030 if (fMcpConfigurationState==MCP::State::kTakingData)
2031 tim << fMcpConfigurationRunStart.SecondsTo();
2032 }
2033
2034 const bool has_evt = !evt.str().empty();
2035 const bool has_tim = !tim.str().empty();
2036
2037 if (has_evt || has_tim)
2038 out << " [";
2039 out << evt.str();
2040 if (has_evt && has_tim)
2041 out << '/';
2042 out << tim.str();
2043 if (has_evt || has_tim)
2044 out << ']';
2045 }
2046 }
2047 else
2048 out << HTML::kWhite;
2049 out << '\n';
2050
2051 // ------------------ Drive -----------------
2052 if (fDimDNS.online() && fDimDriveControl.state()>=Drive::State::kArmed) // Armed, Moving, Tracking
2053 {
2054 const double dev = fDriveControlTrackingDevHist.size()>0 ? fDriveControlTrackingDevHist.back() : 0;
2055 const State rc = fDimDriveControl.description();
2056 string col = HTML::kGreen;
2057 if (rc.index==Drive::State::kMoving) // Moving
2058 col = HTML::kBlue;
2059 if (rc.index==Drive::State::kArmed) // Armed
2060 col = HTML::kWhite;
2061 if (rc.index==Drive::State::kTracking) // Tracking
2062 {
2063 if (dev>60) // ~1.5mm
2064 col = HTML::kYellow;
2065 if (dev>100) // ~1/4 of a pixel ~ 2.5mm
2066 col = HTML::kRed;
2067 }
2068 if (rc.index==0x100)
2069 col = HTML::kRed;
2070 out << col << '\t';
2071
2072 //out << rc.name << '\t';
2073 out << fDriveControlPointingAz << ' ';
2074 out << fDriveControlPointingZd << "&deg;";
2075 out << setprecision(2);
2076 if (fDimDriveControl.state()==Drive::State::kTracking)
2077 {
2078 out << " &plusmn; " << dev << '"';
2079 if (!fDriveControlSourceName.empty())
2080 out << " [" << fDriveControlSourceName << ']';
2081 }
2082 if (fDimDriveControl.state()==Drive::State::kMoving)
2083 out << " &#10227;";
2084 out << setprecision(3);
2085 }
2086 else
2087 out << HTML::kWhite << '\t';
2088 if (fSun.time.IsValid() && fMoon.time.IsValid())
2089 {
2090 if (fSun.visible)
2091 {
2092 out << " &#9788;";
2093 if (fDimDriveControl.state()<Drive::State::kArmed)
2094 out << " [" << fSun.fSunSetCivil.MinutesTo() << "&darr;]";
2095 }
2096 else
2097 if (!fSun.visible && fMoon.visible)
2098 {
2099 out << " &#9790;";
2100 if (fDimDriveControl.state()<Drive::State::kArmed)
2101 out << " [" << fMoon.disk << "%]";
2102 }
2103 out << '\n';
2104 }
2105
2106 // ------------------- FSC ------------------
2107 if (fDimDNS.online() && fDimFscControl.state()>FSC::State::kDisconnected && fFscControlTemperatureHist.size()>0)
2108 {
2109 out << HTML::kGreen << '\t' << fFscControlTemperatureHist.back() << '\n';
2110 }
2111 else
2112 out << HTML::kWhite << '\n';
2113
2114 // --------------- MagicWeather -------------
2115 if (fDimDNS.online() && fDimMagicWeather.state()==MagicWeather::State::kReceiving && fMagicWeatherHist[kWeatherBegin].size()>0)
2116 {
2117 /*
2118 const float diff = fMagicWeatherHist[kTemp].back()-fMagicWeatherHist[kDew].back();
2119 string col1 = HTML::kRed;
2120 if (diff>0.3)
2121 col1 = HTML::kYellow;
2122 if (diff>0.7)
2123 col1 = HTML::kGreen;
2124 */
2125
2126 const float wind = fMagicWeatherHist[kGusts].back();
2127 const float hum = fMagicWeatherHist[kHum].back();
2128 string col = HTML::kGreen;
2129 if (wind>35 || hum>95)
2130 col = HTML::kYellow;
2131 if (wind>50 || hum>98)
2132 col = HTML::kRed;
2133
2134 out << col << '\t';
2135 out << fMagicWeatherHist[kHum].back() << '\t';
2136 out << setprecision(2);
2137 out << fMagicWeatherHist[kGusts].back() << '\n';
2138 out << setprecision(3);
2139 }
2140 else
2141 out << HTML::kWhite << "\n";
2142
2143 // --------------- FtmControl -------------
2144 if (fDimDNS.online() && fDimFtmControl.state()==FTM::State::kTriggerOn)
2145 {
2146 string col = HTML::kGreen;
2147 if (fFtmControlTriggerRateHist.size()>0 && !fFtmControlNewRunStarted)
2148 {
2149 if (fFtmControlTriggerRateHist.back()<15)
2150 col = HTML::kYellow;
2151 if (fFtmControlTriggerRateHist.back()>100)
2152 col = HTML::kRed;
2153
2154 out << col << '\t' << fFtmControlTriggerRateHist.back() << " Hz";
2155 }
2156
2157 if (fDimBiasControl.state()==BIAS::State::kVoltageOn)
2158 out << " (" << fFtmPatchThresholdMed << ')';
2159 out << '\n';
2160 }
2161 else
2162 out << HTML::kWhite << '\n';
2163
2164 // --------------- BiasControl -------------
2165 if (fDimDNS.online() &&
2166 (fDimBiasControl.state()==BIAS::State::kRamping ||
2167 fDimBiasControl.state()==BIAS::State::kOverCurrent ||
2168 fDimBiasControl.state()==BIAS::State::kVoltageOn ||
2169 fDimBiasControl.state()==BIAS::State::kVoltageOff))
2170 {
2171 const bool off = fDimBiasControl.state()==BIAS::State::kVoltageOff;
2172 const bool oc = fDimBiasControl.state()==BIAS::State::kOverCurrent;
2173
2174 string col = fBiasControlVoltageMed>3?HTML::kGreen:HTML::kWhite;
2175 if (fBiasControlCurrentMed>60 || fBiasControlCurrentMax>80)
2176 col = HTML::kYellow;
2177 if (fBiasControlCurrentMed>70 || fBiasControlCurrentMax>90)
2178 col = HTML::kRed;
2179
2180 // Bias in overcurrent => Red
2181 if (fDimBiasControl.state()==BIAS::State::kOverCurrent)
2182 col = HTML::kRed;
2183
2184 // MCP in ReadyForDatataking/Configuring/Configured/TriggerOn/TakingData
2185 // and Bias not in "data-taking state' => Red
2186 if (fMcpConfigurationState>MCP::State::kIdle &&
2187 fDimBiasControl.state()!=BIAS::State::kVoltageOn &&
2188 fDimBiasControl.state()!=BIAS::State::kVoltageOff)
2189 col = HTML::kRed;
2190
2191 const bool cal = fFeedbackCalibration.size();
2192
2193 // Feedback is currently calibrating => Blue
2194 if (fDimFeedback.state()==Feedback::State::kCalibrating)
2195 {
2196 out << HTML::kBlue << '\t';
2197 out << "***\t";
2198 out << "***\t";
2199 }
2200 else
2201 {
2202 out << setprecision(2);
2203 out << col << '\t';
2204 out << (off ? 0 : fBiasControlCurrentMed) << '\t';
2205 if (oc)
2206 out << "(OC) ";
2207 else
2208 {
2209 if (cal)
2210 out << (off ? 0 : fBiasControlCurrentMax);
2211 else
2212 out << "&mdash; ";
2213 }
2214 out << '\t';
2215 out << setprecision(3);
2216 }
2217 if (cal && fDimFeedback.state()!=Feedback::State::kCalibrating)
2218 out << setprecision(2) << fBiasControlPowerTot << " W" << setprecision(3);
2219 else
2220 out << (off ? 0 : fBiasControlVoltageMed);
2221 out << '\n';
2222 }
2223 else
2224 out << HTML::kWhite << '\n';
2225
2226 ofstream(fPath+"/fact.data") << out.str();
2227
2228 // ==============================================================
2229
2230 out.str("");
2231 out << Header(now) << '\t' << (fErrorList.size()>0) << '\t' << (fDimControl.state()>-3) << '\n';
2232
2233 if (!fDimDNS.online())
2234 out << HTML::kWhite << "\tOffline\n\n\n\n\n\n\n\n\n\n\n\n\n";
2235 else
2236 {
2237 ostringstream dt;
2238 dt << (Time()-fRunTime);
2239
2240 out << HTML::kGreen << '\t' << fDimDNS.version() << '\n';
2241
2242 out << GetStateHtml(fDimControl, 0);
2243 out << GetStateHtml(fDimMcp, 4);
2244 out << GetStateHtml(fDimDataLogger, 1);
2245 out << GetStateHtml(fDimDriveControl, 2);
2246 out << GetStateHtml(fDimTimeCheck, 1);
2247 out << GetStateHtml(fDimFadControl, FAD::State::kConnected);
2248 out << GetStateHtml(fDimFtmControl, FTM::State::kConnected);
2249 out << GetStateHtml(fDimBiasControl, BIAS::State::kConnected);
2250 out << GetStateHtml(fDimFeedback, 4);
2251 out << GetStateHtml(fDimRateControl, 4);
2252 out << GetStateHtml(fDimFscControl, 2);
2253 out << GetStateHtml(fDimMagicWeather, 2);
2254 out << GetStateHtml(fDimTngWeather, 2);
2255 out << GetStateHtml(fDimRateScan, 4);
2256 out << GetStateHtml(fDimChat, 0);
2257 out << GetStateHtml(fDimSkypeClient, 1);
2258
2259 out << HTML::kGreen << '\t' << dt.str().substr(0, dt.str().length()-7) << '\n';
2260 }
2261
2262 ofstream(fPath+"/status.data") << out.str();
2263
2264 if (now-fLastAstroCalc>boost::posix_time::seconds(15))
2265 {
2266 UpdateAstronomy();
2267 fLastAstroCalc = now;
2268 }
2269
2270 return fDimDNS.online() ? kStateRunning : kStateDimNetworkNA;
2271 }
2272
2273
2274public:
2275 StateMachineSmartFACT(ostream &out=cout) : StateMachineDim(out, "SMART_FACT"),
2276 fLastAstroCalc(boost::date_time::neg_infin),
2277 fPath("www/smartfact/data"),
2278 fControlScriptDepth(0),
2279 fMcpConfigurationState(DimState::kOffline),
2280 fMcpConfigurationMaxTime(0),
2281 fMcpConfigurationMaxEvents(0),
2282 fTngWeatherDustTime(Time::none),
2283 fBiasControlVoltageMed(0),
2284 fBiasControlCurrentMed(0),
2285 fBiasControlCurrentMax(0),
2286 fFscControlHumidityAvg(0),
2287 fDriveControlMoonDist(-1),
2288 fFadControlNumEvents(0),
2289 fFadControlDrsRuns(3),
2290 fRateScanDataId(0),
2291 fRateScanBoard(0),
2292 // ---
2293 fDimMcp ("MCP"),
2294 fDimDataLogger ("DATA_LOGGER"),
2295 fDimDriveControl("DRIVE_CONTROL"),
2296 fDimTimeCheck ("TIME_CHECK"),
2297 fDimMagicWeather("MAGIC_WEATHER"),
2298 fDimTngWeather ("TNG_WEATHER"),
2299 fDimFeedback ("FEEDBACK"),
2300 fDimBiasControl ("BIAS_CONTROL"),
2301 fDimFtmControl ("FTM_CONTROL"),
2302 fDimFadControl ("FAD_CONTROL"),
2303 fDimFscControl ("FSC_CONTROL"),
2304 fDimRateControl ("RATE_CONTROL"),
2305 fDimRateScan ("RATE_SCAN"),
2306 fDimChat ("CHAT"),
2307 fDimSkypeClient ("SKYPE_CLIENT")
2308 {
2309 fDimDNS.Subscribe(*this);
2310 fDimControl.Subscribe(*this);
2311 fDimMcp.Subscribe(*this);
2312 fDimDataLogger.Subscribe(*this);
2313 fDimDriveControl.Subscribe(*this);
2314 fDimTimeCheck.Subscribe(*this);
2315 fDimMagicWeather.Subscribe(*this);
2316 fDimTngWeather.Subscribe(*this);
2317 fDimFeedback.Subscribe(*this);
2318 fDimBiasControl.Subscribe(*this);
2319 fDimFtmControl.Subscribe(*this);
2320 fDimFadControl.Subscribe(*this);
2321 fDimFscControl.Subscribe(*this);
2322 fDimRateControl.Subscribe(*this);
2323 fDimRateScan.Subscribe(*this);
2324 fDimChat.Subscribe(*this);
2325 fDimSkypeClient.Subscribe(*this);
2326
2327 fDimFscControl.SetCallback(bind(&StateMachineSmartFACT::HandleFscControlStateChange, this));
2328 fDimControl.SetCallback(bind(&StateMachineSmartFACT::HandleControlStateChange, this, placeholders::_1));
2329 fDimControl.AddCallback("dotest.dim", bind(&StateMachineSmartFACT::HandleDoTest, this, placeholders::_1));
2330
2331 Subscribe("DIM_CONTROL/MESSAGE")
2332 (bind(&StateMachineSmartFACT::HandleDimControlMessage, this, placeholders::_1));
2333
2334 Subscribe("MCP/CONFIGURATION")
2335 (bind(&StateMachineSmartFACT::HandleMcpConfiguration, this, placeholders::_1));
2336
2337 Subscribe("DRIVE_CONTROL/POINTING_POSITION")
2338 (bind(&StateMachineSmartFACT::HandleDrivePointing, this, placeholders::_1));
2339 Subscribe("DRIVE_CONTROL/TRACKING_POSITION")
2340 (bind(&StateMachineSmartFACT::HandleDriveTracking, this, placeholders::_1));
2341 Subscribe("DRIVE_CONTROL/SOURCE_POSITION")
2342 (bind(&StateMachineSmartFACT::HandleDriveSource, this, placeholders::_1));
2343
2344 Subscribe("FSC_CONTROL/TEMPERATURE")
2345 (bind(&StateMachineSmartFACT::HandleFscTemperature, this, placeholders::_1));
2346 Subscribe("FSC_CONTROL/HUMIDITY")
2347 (bind(&StateMachineSmartFACT::HandleFscHumidity, this, placeholders::_1));
2348
2349 Subscribe("MAGIC_WEATHER/DATA")
2350 (bind(&StateMachineSmartFACT::HandleMagicWeatherData, this, placeholders::_1));
2351 Subscribe("TNG_WEATHER/DUST")
2352 (bind(&StateMachineSmartFACT::HandleTngWeatherDust, this, placeholders::_1));
2353
2354 Subscribe("FEEDBACK/DEVIATION")
2355 (bind(&StateMachineSmartFACT::HandleFeedbackDeviation, this, placeholders::_1));
2356 Subscribe("FEEDBACK/CALIBRATION")
2357 (bind(&StateMachineSmartFACT::HandleFeedbackCalibration, this, placeholders::_1));
2358
2359 Subscribe("BIAS_CONTROL/VOLTAGE")
2360 (bind(&StateMachineSmartFACT::HandleBiasVoltage, this, placeholders::_1));
2361 Subscribe("BIAS_CONTROL/CURRENT")
2362 (bind(&StateMachineSmartFACT::HandleBiasCurrent, this, placeholders::_1));
2363
2364 Subscribe("FAD_CONTROL/CONNECTIONS")
2365 (bind(&StateMachineSmartFACT::HandleFadConnections, this, placeholders::_1));
2366 Subscribe("FAD_CONTROL/EVENTS")
2367 (bind(&StateMachineSmartFACT::HandleFadEvents, this, placeholders::_1));
2368 Subscribe("FAD_CONTROL/DRS_RUNS")
2369 (bind(&StateMachineSmartFACT::HandleFadDrsRuns, this, placeholders::_1));
2370
2371 Subscribe("FTM_CONTROL/TRIGGER_RATES")
2372 (bind(&StateMachineSmartFACT::HandleFtmTriggerRates, this, placeholders::_1));
2373 Subscribe("FTM_CONTROL/STATIC_DATA")
2374 (bind(&StateMachineSmartFACT::HandleFtmStaticData, this, placeholders::_1));
2375 Subscribe("FTM_CONTROL/FTU_LIST")
2376 (bind(&StateMachineSmartFACT::HandleFtmFtuList, this, placeholders::_1));
2377
2378 Subscribe("RATE_CONTROL/THRESHOLD")
2379 (bind(&StateMachineSmartFACT::HandleRateControlThreshold,this, placeholders::_1));
2380
2381 Subscribe("RATE_SCAN/DATA")
2382 (bind(&StateMachineSmartFACT::HandleRateScanData, this, placeholders::_1));
2383
2384 Subscribe("FAD_CONTROL/EVENT_DATA")
2385 (bind(&StateMachineSmartFACT::HandleFadEventData, this, placeholders::_1));
2386
2387 // =================================================================
2388
2389 // State names
2390 AddStateName(kStateDimNetworkNA, "DimNetworkNotAvailable",
2391 "The Dim DNS is not reachable.");
2392
2393 AddStateName(kStateRunning, "Running", "");
2394
2395 // =================================================================
2396
2397 AddEvent("PRINT")
2398 (bind(&StateMachineSmartFACT::Print, this))
2399 ("Print a list of the states of all connected servers.");
2400
2401 }
2402 int EvalOptions(Configuration &conf)
2403 {
2404 if (!fPixelMap.Read(conf.Get<string>("pixel-map-file")))
2405 {
2406 Error("Reading mapping table from "+conf.Get<string>("pixel-map-file")+" failed.");
2407 return 1;
2408 }
2409
2410 fPath = conf.Get<string>("path");
2411 fDatabase = conf.Get<string>("source-database");
2412
2413 ostringstream out;
2414 out << Time().JavaDate() << '\n';
2415
2416 ofstream(fPath+"/errorhist.data") << out.str();
2417 ofstream(fPath+"/error.data") << out.str();
2418
2419 return -1;
2420 }
2421};
2422
2423// ------------------------------------------------------------------------
2424
2425#include "Main.h"
2426
2427template<class T>
2428int RunShell(Configuration &conf)
2429{
2430 return Main::execute<T, StateMachineSmartFACT>(conf);
2431}
2432
2433void SetupConfiguration(Configuration &conf)
2434{
2435 po::options_description control("Smart FACT");
2436 control.add_options()
2437 ("pixel-map-file", var<string>("FACTmapV5a.txt"), "Pixel mapping file. Used here to get the default reference voltage")
2438 ("path", var<string>("www/smartfact/data"), "Output path for the data-files")
2439 ("source-database", var<string>(), "Database link as in\n\tuser:password@server[:port]/database.")
2440 ;
2441
2442 conf.AddOptions(control);
2443}
2444
2445/*
2446 Extract usage clause(s) [if any] for SYNOPSIS.
2447 Translators: "Usage" and "or" here are patterns (regular expressions) which
2448 are used to match the usage synopsis in program output. An example from cp
2449 (GNU coreutils) which contains both strings:
2450 Usage: cp [OPTION]... [-T] SOURCE DEST
2451 or: cp [OPTION]... SOURCE... DIRECTORY
2452 or: cp [OPTION]... -t DIRECTORY SOURCE...
2453 */
2454void PrintUsage()
2455{
2456 cout <<
2457 "SmartFACT is a tool writing the files needed for the SmartFACT web interface.\n"
2458 "\n"
2459 "The default is that the program is started without user intercation. "
2460 "All actions are supposed to arrive as DimCommands. Using the -c "
2461 "option, a local shell can be initialized. With h or help a short "
2462 "help message about the usuage can be brought to the screen.\n"
2463 "\n"
2464 "Usage: smartfact [-c type] [OPTIONS]\n"
2465 " or: smartfact [OPTIONS]\n";
2466 cout << endl;
2467}
2468
2469void PrintHelp()
2470{
2471 Main::PrintHelp<StateMachineSmartFACT>();
2472
2473 /* Additional help text which is printed after the configuration
2474 options goes here */
2475
2476 /*
2477 cout << "bla bla bla" << endl << endl;
2478 cout << endl;
2479 cout << "Environment:" << endl;
2480 cout << "environment" << endl;
2481 cout << endl;
2482 cout << "Examples:" << endl;
2483 cout << "test exam" << endl;
2484 cout << endl;
2485 cout << "Files:" << endl;
2486 cout << "files" << endl;
2487 cout << endl;
2488 */
2489}
2490
2491int main(int argc, const char* argv[])
2492{
2493 Configuration conf(argv[0]);
2494 conf.SetPrintUsage(PrintUsage);
2495 Main::SetupConfiguration(conf);
2496 SetupConfiguration(conf);
2497
2498 if (!conf.DoParse(argc, argv, PrintHelp))
2499 return 127;
2500
2501 if (!conf.Has("console"))
2502 return RunShell<LocalStream>(conf);
2503
2504 if (conf.Get<int>("console")==0)
2505 return RunShell<LocalShell>(conf);
2506 else
2507 return RunShell<LocalConsole>(conf);
2508
2509 return 0;
2510}
Note: See TracBrowser for help on using the repository browser.