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

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