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

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