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

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