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

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