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

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