source: branches/fscctrl_safety_limits/src/smartfact.cc@ 18752

Last change on this file since 18752 was 18193, checked in by dneise, 10 years ago
Modified reaction on FSC state change. FSC connection state has nothing to do with the camera being switched on or off, it is merely a hint.
File size: 108.9 KB
Line 
1#ifdef HAVE_NOVA
2#include "externals/Prediction.h"
3#endif
4
5#ifdef HAVE_SQL
6#include "Database.h"
7#endif
8
9#include <sys/stat.h> //for file stats
10
11#include "Dim.h"
12#include "Event.h"
13#include "Shell.h"
14#include "StateMachineDim.h"
15#include "Connection.h"
16#include "Configuration.h"
17#include "Console.h"
18#include "DimWriteStatistics.h"
19#include "externals/PixelMap.h"
20
21#include "tools.h"
22
23#include "LocalControl.h"
24
25#include "HeadersFAD.h"
26#include "HeadersBIAS.h"
27#include "HeadersFTM.h"
28#include "HeadersFSC.h"
29#include "HeadersGPS.h"
30#include "HeadersSQM.h"
31#include "HeadersMCP.h"
32#include "HeadersLid.h"
33#include "HeadersDrive.h"
34#include "HeadersPower.h"
35#include "HeadersAgilent.h"
36#include "HeadersFeedback.h"
37#include "HeadersRateScan.h"
38#include "HeadersRateControl.h"
39#include "HeadersTNGWeather.h"
40#include "HeadersMagicLidar.h"
41#include "HeadersMagicWeather.h"
42#include "HeadersTemperature.h"
43
44#include <boost/filesystem.hpp>
45
46using namespace std;
47
48// ------------------------------------------------------------------------
49
50#include "DimDescriptionService.h"
51#include "DimState.h"
52
53// ------------------------------------------------------------------------
54/*
55template<class T>
56 class buffer : public deque<T>
57 {
58 int32_t max_size;
59
60 public:
61 buffer(int32_t max=-1) : max_size(max) { }
62 const T &operator=(const T &t) const { push_back(t); if (max_size>0 && deque<T>::size()>max_size) deque<T>::pop_front(); }
63 operator T() const { return deque<T>::size()>0 ? deque<T>::back() : T(); }
64 bool valid() const { return deque<T>::size()>0; }
65 };
66*/
67
68// ------------------------------------------------------------------------
69
70namespace HTML
71{
72 const static string kWhite = "#ffffff";
73 const static string kYellow = "#fffff0";
74 const static string kRed = "#fff8f0";
75 const static string kGreen = "#f0fff0";
76 const static string kBlue = "#f0f0ff";
77};
78
79// ========================================================================
80// ========================================================================
81// ========================================================================
82
83class Sun
84{
85public:
86 Time time;
87
88 Time fRiseDayTime;
89 Time fRiseCivil;
90 Time fRiseAstronomical;
91 Time fRiseDarkTime;
92
93 Time fSetDayTime;
94 Time fSetCivil;
95 Time fSetAstronomical;
96 Time fSetDarkTime;
97
98 int state;
99 string description;
100 string color;
101
102 bool isday;
103 bool visible;
104
105public:
106 Sun() : time(Time::none)
107 {
108 }
109
110 // Could be done more efficient: Only recalcuate if
111 // the current time exceeds at least on of the stored times
112 Sun(const Time &t) : time(t)
113 {
114#ifdef HAVE_LIBNOVA
115 // get Julian day from local time
116 const double JD = time.JD();
117
118 // Warning: return code of 1 means circumpolar and is not checked!
119 Nova::RstTime sun_day = Nova::GetSolarRst(JD-0.5);
120 Nova::RstTime sun_civil = Nova::GetSolarRst(JD-0.5, -6);
121 Nova::RstTime sun_astronomical = Nova::GetSolarRst(JD-0.5, -12);
122 Nova::RstTime sun_dark = Nova::GetSolarRst(JD-0.5, -18);
123
124 fSetDayTime = Time(sun_day.set);
125 fSetCivil = Time(sun_civil.set);
126 fSetAstronomical = Time(sun_astronomical.set);
127 fSetDarkTime = Time(sun_dark.set);
128
129 fRiseDayTime = Time(sun_day.rise);
130 fRiseCivil = Time(sun_civil.rise);
131 fRiseAstronomical = Time(sun_astronomical.rise);
132 fRiseDarkTime = Time(sun_dark.rise);
133
134 const bool is_day = JD>sun_day.rise;
135 const bool is_night = JD>sun_dark.set;
136
137 sun_day = Nova::GetSolarRst(JD+0.5);
138 sun_civil = Nova::GetSolarRst(JD+0.5, -6);
139 sun_astronomical = Nova::GetSolarRst(JD+0.5, -12);
140 sun_dark = Nova::GetSolarRst(JD+0.5, -18);
141
142 if (is_day)
143 {
144 fRiseDayTime = Time(sun_day.rise);
145 fRiseCivil = Time(sun_civil.rise);
146 fRiseAstronomical = Time(sun_astronomical.rise);
147 fRiseDarkTime = Time(sun_dark.rise);
148 }
149
150 if (is_night)
151 {
152 fSetDayTime = Time(sun_day.set);
153 fSetCivil = Time(sun_civil.set);
154 fSetAstronomical = Time(sun_astronomical.set);
155 fSetDarkTime = Time(sun_dark.set);
156 }
157
158 // case 0: midnight to sun-rise | !is_day && !is_night | rise/set
159 // case 1: sun-rise to sun-set | is_day && !is_night | set /rise
160 // case 2: sun-set to midnight | is_day && is_night | rise/set
161
162 isday = is_day^is_night;
163
164 state = isday ? 4 : 0;
165 if (time>fSetDayTime) state++;
166 if (time>fSetCivil) state++;
167 if (time>fSetAstronomical) state++;
168 if (time>fSetDarkTime) state++;
169
170 if (time>fRiseDarkTime) state++;
171 if (time>fRiseAstronomical) state++;
172 if (time>fRiseCivil) state++;
173 if (time>fRiseDayTime) state++;
174
175 string name[] =
176 {
177 "dark time",
178 "astron. twilight",
179 "civil twilight",
180 "sunrise",
181 "day time",
182 "sunset",
183 "civil twilight",
184 "astron. twilight",
185 "dark time"
186 };
187
188 description = name[state];
189
190 const string arr = isday ?
191 fSetDarkTime.MinutesTo(time)+"&darr;" :
192 fRiseDarkTime.MinutesTo(time)+"&uarr;";
193
194 description += " ["+arr+"]";
195
196 switch (state)
197 {
198 case 0: case 1: color = HTML::kGreen; break;
199 case 2: case 3: color = HTML::kYellow; break;
200 case 4: color = HTML::kRed; break;
201 case 5: case 6: color = HTML::kYellow; break;
202 case 7: case 8: color = HTML::kGreen; break;
203 }
204
205 visible = state>=3 && state<=5;
206#endif
207 }
208};
209
210class Moon
211{
212public:
213 Time time;
214
215 double ra;
216 double dec;
217
218 double zd;
219 double az;
220
221 double disk;
222
223 bool visible;
224
225 Time fRise;
226 Time fTransit;
227 Time fSet;
228
229 string description;
230 string color;
231
232 int state;
233
234 Moon() : time(Time::none)
235 {
236 }
237
238 // Could be done more efficient: Only recalcuate if
239 // the current time exceeds at least on of the stored times
240 Moon(const Time &t) : time(t)
241 {
242#ifdef HAVE_LIBNOVA
243 const double JD = time.JD();
244
245 Nova::RstTime moon = Nova::GetLunarRst(JD-0.5);
246
247 fRise = Time(moon.rise);
248 fTransit = Time(moon.transit);
249 fSet = Time(moon.set);
250
251 //visible =
252 // ((JD>moon.rise && JD<moon.set ) && moon.rise<moon.set) ||
253 // ((JD<moon.set || JD>moon.rise) && moon.rise>moon.set);
254
255 const bool is_up = JD>moon.rise;
256 const bool is_sinking = JD>moon.transit;
257 const bool is_dn = JD>moon.set;
258
259 moon = Nova::GetLunarRst(JD+0.5);
260 if (is_up)
261 fRise = Time(moon.rise);
262 if (is_sinking)
263 fTransit = Time(moon.transit);
264 if (is_dn)
265 fSet = Time(moon.set);
266
267 const Nova::EquPosn pos = Nova::GetLunarEquCoords(JD);
268 const Nova::ZdAzPosn hrz = Nova::GetHrzFromEqu(pos, JD);
269
270 az = hrz.az;
271 zd = hrz.zd;
272
273 ra = pos.ra/15;
274 dec = pos.dec;
275
276 disk = Nova::GetLunarDisk(JD)*100;
277 state = 0;
278 if (fRise <fTransit && fRise <fSet) state = 0; // not visible
279 if (fTransit<fSet && fTransit<fRise) state = 1; // before culm
280 if (fSet <fRise && fSet <fTransit) state = 2; // after culm
281
282 visible = state!=0;
283
284 // 0: not visible
285 // 1: visible before cul
286 // 2: visible after cul
287
288 if (!visible || disk<25)
289 color = HTML::kGreen;
290 else
291 color = disk>75 ? HTML::kRed : HTML::kYellow;
292
293 const string arr = fSet<fRise ?
294 fSet.MinutesTo(time) +"&darr;" :
295 fRise.MinutesTo(time)+"&uarr;";
296
297 ostringstream out;
298 out << setprecision(2);
299 out << (visible?"visible ":"") << (disk<0.1?0:disk) << "% [" << arr << "]";
300
301 description = out.str();
302#endif
303 }
304
305 double Angle(double r, double d) const
306 {
307 const double theta0 = M_PI/2-d*M_PI/180;
308 const double phi0 = r*M_PI/12;
309
310 const double theta1 = M_PI/2-dec*M_PI/180;
311 const double phi1 = ra*M_PI/12;
312
313 const double x0 = sin(theta0) * cos(phi0);
314 const double y0 = sin(theta0) * sin(phi0);
315 const double z0 = cos(theta0);
316
317 const double x1 = sin(theta1) * cos(phi1);
318 const double y1 = sin(theta1) * sin(phi1);
319 const double z1 = cos(theta1);
320
321 double arg = x0*x1 + y0*y1 + z0*z1;
322 if(arg > 1.0) arg = 1.0;
323 if(arg < -1.0) arg = -1.0;
324
325 return acos(arg) * 180/M_PI;
326 }
327
328 static string Color(double angle)
329 {
330 if (angle<10 || angle>150)
331 return HTML::kRed;
332 if (angle<20 || angle>140)
333 return HTML::kYellow;
334 return HTML::kGreen;
335 }
336};
337
338// ========================================================================
339// ========================================================================
340// ========================================================================
341
342class StateMachineSmartFACT : public StateMachineDim
343{
344public:
345 static bool fIsServer;
346
347private:
348 enum states_t
349 {
350 kStateDimNetworkNA = 1,
351 kStateRunning,
352 };
353
354 // ------------------------- History classes -----------------------
355
356 struct EventElement
357 {
358 Time time;
359 string msg;
360
361 EventElement(const Time &t, const string &s) : time(t), msg(s) { }
362 };
363
364 class EventHist : public list<EventElement>
365 {
366 const boost::posix_time::time_duration deltat; //boost::posix_time::pos_infin
367 const uint64_t max;
368
369 public:
370 EventHist(const boost::posix_time::time_duration &dt=boost::posix_time::hours(12), uint64_t mx=UINT64_MAX) : deltat(dt), max(mx) { }
371
372 void add(const string &s, const Time &t=Time())
373 {
374 while (!empty() && (front().time+deltat<t || size()>max))
375 pop_front();
376
377 emplace_back(t, s);
378 }
379
380 void clean()
381 {
382 for (auto it=begin(); it!=end();)
383 if (!it->time)
384 {
385 const auto is = it++;
386 erase(is);
387 }
388 }
389
390 string get() const
391 {
392 ostringstream out;
393
394 string last = "";
395 for (auto it=begin(); it!=end(); it++)
396 {
397 const string tm = it->time.GetAsStr("%H:%M:%S ");
398 out << (tm!=last?tm:"--:--:-- ") << it->msg << "<br/>";
399 last = tm;
400 }
401
402 return out.str();
403 }
404 string rget() const
405 {
406 ostringstream out;
407
408 for (auto it=rbegin(); it!=rend(); it++)
409 out << it->time.GetAsStr("%H:%M:%S ") << it->msg << "<br/>";
410
411 return out.str();
412 }
413 };
414
415 // ------------------------- Internal variables -----------------------
416
417 const Time fRunTime;
418
419 PixelMap fPixelMap;
420
421 string fDatabase;
422
423 Time fLastUpdate;
424 Time fLastAstroCalc;
425
426 string fPath;
427
428 // ----------------------------- Data storage -------------------------
429
430 EventHist fControlMessageHist;
431 EventHist fControlAlarmHist;
432 int32_t fControlScriptDepth;
433
434 int32_t fMcpConfigurationState; // For consistency
435 int64_t fMcpConfigurationMaxTime;
436 int64_t fMcpConfigurationMaxEvents;
437 string fMcpConfigurationName;
438 Time fMcpConfigurationRunStart;
439 EventHist fMcpConfigurationHist;
440 bool fLastRunFinishedWithZeroEvents;
441
442 enum weather_t { kWeatherBegin=0, kTemp = kWeatherBegin, kDew, kHum, kPress, kWind, kGusts, kDir, kWeatherEnd = kDir+1 };
443 deque<float> fMagicWeatherHist[kWeatherEnd];
444
445 deque<float> fTngWeatherDustHist;
446 Time fTngWeatherDustTime;
447
448 vector<float> fBiasControlVoltageVec;
449
450 float fBiasControlPowerTot;
451 float fBiasControlVoltageMed;
452 float fBiasControlCurrentMed;
453 float fBiasControlCurrentMax;
454
455 deque<float> fBiasControlCurrentHist;
456 deque<float> fFscControlTemperatureHist;
457
458 float fFscControlHumidityAvg;
459
460 deque<float> fTemperatureControlHist;
461
462 float fDriveControlPointingZd;
463 string fDriveControlPointingAz;
464 string fDriveControlSourceName;
465 float fDriveControlMoonDist;
466
467 deque<float> fDriveControlTrackingDevHist;
468
469 int64_t fFadControlNumEvents;
470 int64_t fFadControlStartRun;
471 int32_t fFadControlDrsStep;
472 vector<uint32_t> fFadControlDrsRuns;
473
474 deque<float> fFtmControlTriggerRateHist;
475 int32_t fFtmControlTriggerRateTooLow;
476 int fFtmControlState;
477
478 float fFtmPatchThresholdMed;
479 float fFtmBoardThresholdMed;
480
481 bool fFtmControlFtuOk;
482
483 deque<float> fRateControlThreshold;
484
485 uint64_t fRateScanDataId;
486 uint8_t fRateScanBoard;
487 deque<float> fRateScanDataHist[41];
488
489 set<string> fErrorList;
490 EventHist fErrorHist;
491 EventHist fChatHist;
492
493 uint64_t fFreeSpace;
494
495 Sun fSun;
496 Moon fMoon;
497
498 // --------------------------- File header ----------------------------
499
500 Time fAudioTime;
501 string fAudioName;
502
503 string Header(const Time &d)
504 {
505 ostringstream msg;
506 msg << d.JavaDate() << '\t' << fAudioTime.JavaDate() << '\t' << fAudioName;
507 return msg.str();
508 }
509
510 string Header(const EventImp &d)
511 {
512 return Header(d.GetTime());
513 }
514
515 void SetAudio(const string &name)
516 {
517 fAudioName = name;
518 fAudioTime = Time();
519 }
520
521 // ------------- Initialize variables before the Dim stuff ------------
522
523 DimVersion fDimDNS;
524 DimControl fDimControl;
525 DimDescribedState fDimMcp;
526 DimDescribedState fDimDataLogger;
527 DimDescribedState fDimDriveControl;
528 DimDescribedState fDimTimeCheck;
529 DimDescribedState fDimMagicWeather;
530 DimDescribedState fDimMagicLidar;
531 DimDescribedState fDimTngWeather;
532 DimDescribedState fDimTemperature;
533 DimDescribedState fDimFeedback;
534 DimDescribedState fDimBiasControl;
535 DimDescribedState fDimFtmControl;
536 DimDescribedState fDimFadControl;
537 DimDescribedState fDimFscControl;
538 DimDescribedState fDimGpsControl;
539 DimDescribedState fDimSqmControl;
540 DimDescribedState fDimAgilentControl24;
541 DimDescribedState fDimAgilentControl50;
542 DimDescribedState fDimAgilentControl80;
543 DimDescribedState fDimPwrControl;
544 DimDescribedState fDimLidControl;
545 DimDescribedState fDimRateControl;
546 DimDescribedState fDimRateScan;
547 DimDescribedState fDimChat;
548 DimDescribedState fDimSkypeClient;
549
550 // -------------------------------------------------------------------
551
552 string GetDir(const double angle)
553 {
554 static const char *dir[] =
555 {
556 "N", "NNE", "NE", "ENE",
557 "E", "ESE", "SE", "SSE",
558 "S", "SSW", "SW", "WSW",
559 "W", "WNW", "NW", "NNW"
560 };
561
562 const uint16_t idx = uint16_t(floor(angle/22.5+16.5))%16;
563 return dir[idx];
564 }
565
566 // -------------------------------------------------------------------
567
568 bool CheckDataSize(const EventImp &d, const char *name, size_t size, bool min=false)
569 {
570 if (d.GetSize()==0)
571 return false;
572
573 if ((!min && d.GetSize()==size) || (min && d.GetSize()>size))
574 return true;
575
576 ostringstream msg;
577 msg << name << " - Received service has " << d.GetSize() << " bytes, but expected ";
578 if (min)
579 msg << "more than ";
580 msg << size << ".";
581 Warn(msg);
582 return false;
583 }
584
585 // -------------------------------------------------------------------
586
587 template<class T>
588 void WriteBinaryVec(const Time &tm, const string &fname, const vector<T> &vec, double scale, double offset=0, const string &title="", const string &col="")
589 {
590 if (vec.empty())
591 return;
592
593 ostringstream out;
594 out << tm.JavaDate() << '\n';
595 out << offset << '\n';
596 out << offset+scale << '\n';
597 out << setprecision(3);
598 if (!title.empty())
599 out << title << '\x7f';
600 else
601 {
602 const Statistics stat(vec[0]);
603 out << stat.min << '\n';
604 out << stat.med << '\n';
605 out << stat.max << '\x7f';
606 }
607 if (!col.empty())
608 out << col;
609 for (auto it=vec.cbegin(); it!=vec.cend(); it++)
610 {
611 // The valid range is from 1 to 127
612 // \0 is used to seperate different curves
613 vector<uint8_t> val(it->size());
614 for (uint64_t i=0; i<it->size(); i++)
615 {
616 float range = nearbyint(126*(double(it->at(i))-offset)/scale); // [-2V; 2V]
617 if (range>126)
618 range=126;
619 if (range<0)
620 range=0;
621 val[i] = (uint8_t)range;
622 }
623
624 const char *ptr = reinterpret_cast<char*>(val.data());
625 out.write(ptr, val.size()*sizeof(uint8_t));
626 out << '\x7f';
627 }
628
629 ofstream(fPath+"/"+fname+".bin") << out.str();
630 }
631 /*
632 template<class T>
633 void WriteBinaryVec(const EventImp &d, const string &fname, const vector<T> &vec, double scale, double offset=0, const string &title="")
634 {
635 WriteBinaryVec(d.GetTime(), fname, vec, scale, offset, title);
636 }
637
638 template<class T>
639 void WriteBinary(const Time &tm, const string &fname, const T &t, double scale, double offset=0)
640 {
641 WriteBinaryVec(tm, fname, vector<T>(&t, &t+1), scale, offset);
642 }
643
644 template<class T>
645 void WriteBinary(const EventImp &d, const string &fname, const T &t, double scale, double offset=0)
646 {
647 WriteBinaryVec(d.GetTime(), fname, vector<T>(&t, &t+1), scale, offset);
648 }*/
649
650 template<class T>
651 void WriteHist(const EventImp &d, const string &fname, const T &t, double scale, double offset=0)
652 {
653 WriteBinaryVec(d.GetTime(), fname, vector<T>(&t, &t+1), scale, offset, "", "000");
654 }
655
656 template<class T>
657 void WriteCam(const EventImp &d, const string &fname, const T &t, double scale, double offset=0)
658 {
659 WriteBinaryVec(d.GetTime(), fname, vector<T>(&t, &t+1), scale, offset, "", "");
660 }
661
662
663 // -------------------------------------------------------------------
664
665 struct Statistics
666 {
667 float min;
668 float max;
669 float med;
670 float avg;
671 //float rms;
672
673 template<class T>
674 Statistics(const T &t, size_t offset_min=0, size_t offset_max=0)
675 : min(0), max(0), med(0), avg(0)
676 {
677 if (t.empty())
678 return;
679
680 T copy(t);
681 sort(copy.begin(), copy.end());
682
683 if (offset_min>t.size())
684 offset_min = 0;
685 if (offset_max>t.size())
686 offset_max = 0;
687
688 min = copy[offset_min];
689 max = copy[copy.size()-1-offset_max];
690 avg = accumulate (t.begin(), t.end(), 0.)/t.size();
691
692 const size_t p = copy.size()/2;
693 med = copy.size()%2 ? copy[p] : (copy[p-1]+copy[p])/2.;
694 }
695 };
696
697 void HandleControlMessageImp(const EventImp &d)
698 {
699 if (d.GetSize()==0)
700 return;
701
702 fControlMessageHist.add(d.GetText(), d.GetTime());
703
704 ostringstream out;
705 out << setprecision(3);
706 out << Header(d) << '\n';
707 out << HTML::kWhite << '\t';
708 out << "<->" << fControlMessageHist.get() << "</->";
709 out << '\n';
710
711 ofstream(fPath+"/scriptlog.data") << out.str();
712 }
713
714 int HandleDimControlMessage(const EventImp &d)
715 {
716 if (d.GetSize()==0)
717 return GetCurrentState();
718
719 if (d.GetQoS()==MessageImp::kAlarm)
720 {
721 if (d.GetSize()<2)
722 for (auto it=fControlAlarmHist.begin(); it!=fControlAlarmHist.end(); it++)
723 it->time = Time(Time::none);
724 else
725 fControlAlarmHist.add(d.GetText(), d.GetTime());
726 }
727
728 if (d.GetQoS()==MessageImp::kComment && d.GetSize()>1)
729 HandleControlMessageImp(d);
730
731 return GetCurrentState();
732 }
733
734 int HandleControlStateChange(const EventImp &d)
735 {
736 if (d.GetSize()==0)
737 return StateMachineImp::kSM_KeepState;
738
739 if (fDimControl.scriptdepth>0)
740 return StateMachineImp::kSM_KeepState;
741
742 if (d.GetQoS()>=2)
743 return StateMachineImp::kSM_KeepState;
744
745#if BOOST_VERSION < 104600
746 const string file = boost::filesystem::path(fDimControl.file).filename();
747#else
748 const string file = boost::filesystem::path(fDimControl.file).filename().string();
749#endif
750
751 // [0] DimControl::kIdle
752 // [1] DimControl::kLoading
753 // [2] DimControl::kCompiling
754 // [3] DimControl::kRunning
755 if (d.GetQoS()==1)
756 {
757 fControlMessageHist.clear();
758 HandleControlMessageImp(Event(d, "========================================", 41));
759 }
760
761 HandleControlMessageImp(Event(d, ("----- "+fDimControl.shortmsg+" -----").data(), fDimControl.shortmsg.length()+13));
762 if (!file.empty() && d.GetQoS()<2)
763 HandleControlMessageImp(Event(d, file.data(), file.length()+1));
764
765 // Note that this will also "ding" just after program startup
766 // if the dimctrl is still in state -3
767 if (d.GetQoS()==0)
768 {
769 HandleControlMessageImp(Event(d, "========================================", 41));
770 if (fDimControl.last.second!=DimState::kOffline)
771 SetAudio("ding");
772 }
773
774 return StateMachineImp::kSM_KeepState;
775 }
776
777 void AddMcpConfigurationHist(const EventImp &d, const string &msg)
778 {
779 fMcpConfigurationHist.add(msg, d.GetTime());
780
781 ostringstream out;
782 out << d.GetJavaDate() << '\n';
783 out << HTML::kWhite << '\t';
784 out << "<->" << fMcpConfigurationHist.rget() << "</->";
785 out << '\n';
786
787 ofstream(fPath+"/observations.data") << out.str();
788 }
789
790 int HandleFscControlStateChange(const EventImp &d)
791 {
792 const int32_t &last = fDimFscControl.last.second;
793 const int32_t &state = fDimFscControl.state();
794
795 if (last==DimState::kOffline || state==DimState::kOffline)
796 return StateMachineImp::kSM_KeepState;
797
798 if (last<FSC::State::kConnected && state==FSC::State::kConnected)
799 {
800 AddMcpConfigurationHist(d, "<B>FSC swiched on</B>");
801 //SetAudio("startup");
802 }
803
804 if (last==FSC::State::kConnected && state<FSC::State::kConnected)
805 {
806 AddMcpConfigurationHist(d, "<B>FSC swiched off</B>");
807 //SetAudio("shutdown");
808 }
809
810 return StateMachineImp::kSM_KeepState;
811 }
812
813 int HandleMcpConfiguration(const EventImp &d)
814 {
815 if (!CheckDataSize(d, "Mcp:Configuration", 16, true))
816 {
817 fMcpConfigurationState = DimState::kOffline;
818 fMcpConfigurationMaxTime = 0;
819 fMcpConfigurationMaxEvents = 0;
820 fMcpConfigurationName = "";
821 fMcpConfigurationRunStart = Time(Time::none);
822 return GetCurrentState();
823 }
824
825 // If a run ends...
826 if (fMcpConfigurationState==MCP::State::kTakingData && d.GetQoS()==MCP::State::kIdle)
827 {
828 // ...and no script is running just play a simple 'tick'
829 // ...and a script is running just play a simple 'tick'
830 if (/*fDimControl.state()<-2 &&*/ fDimControl.scriptdepth==0)
831 SetAudio("dong");
832 else
833 SetAudio("losticks");
834
835 fLastRunFinishedWithZeroEvents = fFadControlNumEvents==0;
836
837 ostringstream out;
838 out << "<#darkred>" << d.Ptr<char>(16);
839 if (!fDriveControlSourceName.empty())
840 out << " [" << fDriveControlSourceName << ']';
841 out << " (N=" << fFadControlNumEvents << ')';
842 out << "</#>";
843
844 AddMcpConfigurationHist(d, out.str());
845 }
846
847 if (d.GetQoS()==MCP::State::kTakingData)
848 {
849 fMcpConfigurationRunStart = Time();
850 SetAudio("losticks");
851
852 ostringstream out;
853 out << "<#darkgreen>" << fMcpConfigurationName;
854 if (!fDriveControlSourceName.empty())
855 out << " [" << fDriveControlSourceName << ']';
856 if (fFadControlStartRun>0)
857 out << " (Run " << fFadControlStartRun << ')';
858 out << "</#>";
859
860 AddMcpConfigurationHist(d, out.str());
861 }
862
863 fMcpConfigurationState = d.GetQoS();
864 fMcpConfigurationMaxTime = d.Get<uint64_t>();
865 fMcpConfigurationMaxEvents = d.Get<uint64_t>(8);
866 fMcpConfigurationName = d.Ptr<char>(16);
867
868 return GetCurrentState();
869 }
870
871 void WriteWeather(const EventImp &d, const string &name, int i, float min, float max)
872 {
873 const Statistics stat(fMagicWeatherHist[i]);
874
875 ostringstream out;
876 out << setprecision(3);
877 out << d.GetJavaDate() << '\n';
878
879 out << HTML::kWhite << '\t' << fMagicWeatherHist[i].back() << '\n';
880 out << HTML::kWhite << '\t' << stat.min << '\n';
881 out << HTML::kWhite << '\t' << stat.avg << '\n';
882 out << HTML::kWhite << '\t' << stat.max << '\n';
883
884 ofstream(fPath+"/"+name+".data") << out.str();
885
886 WriteHist(d, "hist-magicweather-"+name, fMagicWeatherHist[i], max-min, min);
887 }
888
889 int HandleMagicWeatherData(const EventImp &d)
890 {
891 if (!CheckDataSize(d, "MagicWeather:Data", 7*4+2))
892 return GetCurrentState();
893
894 // Store a history of the last 300 entries
895 for (int i=kWeatherBegin; i<kWeatherEnd; i++)
896 {
897 fMagicWeatherHist[i].push_back(d.Ptr<float>(2)[i]);
898 if (fMagicWeatherHist[i].size()>300)
899 fMagicWeatherHist[i].pop_front();
900 }
901
902 ostringstream out;
903 out << d.GetJavaDate() << '\n';
904 if (fSun.time.IsValid() && fMoon.time.IsValid())
905 {
906 out << fSun.color << '\t' << fSun.description << '\n';
907 out << setprecision(2);
908 out << (fSun.isday?HTML::kWhite:fMoon.color) << '\t' << fMoon.description << '\n';
909 }
910 else
911 out << "\n\n";
912 out << setprecision(3);
913 for (int i=0; i<6; i++)
914 out << HTML::kWhite << '\t' << fMagicWeatherHist[i].back() << '\n';
915 out << HTML::kWhite << '\t' << GetDir(fMagicWeatherHist[kDir].back()) << '\n';
916 out << HTML::kWhite << '\t';
917 if (!fTngWeatherDustHist.empty())
918 out << fTngWeatherDustHist.back() << '\t' << fTngWeatherDustTime.GetAsStr("%H:%M") << '\n';
919 else
920 out << "\t\n";
921
922 ofstream(fPath+"/weather.data") << out.str();
923
924 WriteWeather(d, "temp", kTemp, -5, 35);
925 WriteWeather(d, "dew", kDew, -5, 35);
926 WriteWeather(d, "hum", kHum, 0, 100);
927 WriteWeather(d, "wind", kWind, 0, 100);
928 WriteWeather(d, "gusts", kGusts, 0, 100);
929 WriteWeather(d, "press", kPress, 700, 1000);
930
931 return GetCurrentState();
932 }
933
934 int HandleTngWeatherDust(const EventImp &d)
935 {
936 if (!CheckDataSize(d, "TngWeather:Dust", 4))
937 return GetCurrentState();
938
939 fTngWeatherDustTime = d.GetTime();
940
941 fTngWeatherDustHist.push_back(d.GetFloat());
942 if (fTngWeatherDustHist.size()>300)
943 fTngWeatherDustHist.pop_front();
944
945 const Statistics stat(fTngWeatherDustHist);
946
947 const double scale = stat.max>0 ? pow(10, ceil(log10(stat.max))) : 0;
948
949 WriteHist(d, "hist-tng-dust", fTngWeatherDustHist, scale);
950
951 ostringstream out;
952 out << d.GetJavaDate() << '\n';
953
954 ofstream(fPath+"/tngdust.data") << out.str();
955
956 return GetCurrentState();
957 }
958
959 int HandleDriveControlStateChange(const EventImp &d)
960 {
961 const int32_t &last = fDimFscControl.last.second;
962 const int32_t &state = fDimFscControl.state();
963
964 if (last==DimState::kOffline || state==DimState::kOffline)
965 return StateMachineImp::kSM_KeepState;
966
967 if (last<Drive::State::kArmed && state>=Drive::State::kArmed)
968 AddMcpConfigurationHist(d, "Drive connected");
969
970 if (last>=Drive::State::kArmed && state<Drive::State::kArmed)
971 AddMcpConfigurationHist(d, "Drive disconnected");
972
973 return StateMachineImp::kSM_KeepState;
974 }
975
976 int HandleDrivePointing(const EventImp &d)
977 {
978 if (!CheckDataSize(d, "DriveControl:Pointing", 16))
979 return GetCurrentState();
980
981 fDriveControlPointingZd = d.Get<double>();
982
983 const double az = d.Get<double>(8);
984
985 fDriveControlPointingAz = GetDir(az);
986
987 ostringstream out;
988 out << d.GetJavaDate() << '\n';
989
990 out << setprecision(0) << fixed;
991 out << HTML::kWhite << '\t' << az << '\t' << fDriveControlPointingAz << '\n';
992 out << HTML::kWhite << '\t' << fDriveControlPointingZd << '\n';
993
994 ofstream(fPath+"/pointing.data") << out.str();
995
996 return GetCurrentState();
997 }
998
999 int HandleDriveTracking(const EventImp &d)
1000 {
1001 if (!CheckDataSize(d, "DriveControl:Tracking", 64))
1002 return GetCurrentState();
1003
1004 const double Ra = d.Get<double>(0*8);
1005 const double Dec = d.Get<double>(1*8);
1006 const double Zd = d.Get<double>(3*8);
1007 const double Az = d.Get<double>(4*8);
1008
1009 const double dev = d.Get<double>(7*8);
1010
1011 fDriveControlTrackingDevHist.push_back(dev);
1012 if (fDriveControlTrackingDevHist.size()>300)
1013 fDriveControlTrackingDevHist.pop_front();
1014
1015 WriteHist(d, "hist-control-deviation", fDriveControlTrackingDevHist, 120);
1016
1017 ostringstream out;
1018 out << d.GetJavaDate() << '\n';
1019
1020 out << HTML::kWhite << '\t' << fDriveControlSourceName << '\n';
1021 out << setprecision(5);
1022 out << HTML::kWhite << '\t' << Ra << '\n';
1023 out << HTML::kWhite << '\t' << Dec << '\n';
1024 out << setprecision(3);
1025 out << HTML::kWhite << '\t' << Zd << '\n';
1026 out << HTML::kWhite << '\t' << Az << '\n';
1027 out << HTML::kWhite << '\t' << dev << '\n';
1028
1029 fDriveControlMoonDist = -1;
1030
1031 if (fMoon.visible)
1032 {
1033 const double angle = fMoon.Angle(Ra, Dec);
1034 out << Moon::Color(angle) << '\t' << setprecision(3) << angle << '\n';
1035
1036 fDriveControlMoonDist = angle;
1037 }
1038 else
1039 out << HTML::kWhite << "\t&mdash; \n";
1040
1041 ofstream(fPath+"/tracking.data") << out.str();
1042
1043 return GetCurrentState();
1044 }
1045
1046 int HandleDriveSource(const EventImp &d)
1047 {
1048 if (!CheckDataSize(d, "DriveControl:Source", 7*4+2, true))
1049 return GetCurrentState();
1050
1051 const double *ptr = d.Ptr<double>();
1052
1053 const double ra = ptr[0]; // Ra[h]
1054 const double dec = ptr[1]; // Dec[deg]
1055 const double woff = ptr[4]; // Wobble offset [deg]
1056 const double wang = ptr[5]; // Wobble angle [deg]
1057
1058 fDriveControlSourceName = d.Ptr<char>(6*8);
1059
1060 ostringstream out;
1061 out << d.GetJavaDate() << '\n';
1062
1063 out << HTML::kWhite << '\t' << fDriveControlSourceName << '\n';
1064 out << setprecision(5);
1065 out << HTML::kWhite << '\t' << ra << '\n';
1066 out << HTML::kWhite << '\t' << dec << '\n';
1067 out << setprecision(3);
1068 out << HTML::kWhite << '\t' << woff << '\n';
1069 out << HTML::kWhite << '\t' << wang << '\n';
1070
1071 ofstream(fPath+"/source.data") << out.str();
1072
1073 return GetCurrentState();
1074 }
1075
1076 int HandleFeedbackCalibratedCurrents(const EventImp &d)
1077 {
1078 if (!CheckDataSize(d, "Feedback:CalibratedCurrents", (416+1+1+1+1+1+416+1+1)*sizeof(float)+sizeof(uint32_t)))
1079 return GetCurrentState();
1080
1081 const float *ptr = d.Ptr<float>();
1082
1083 double power_tot = 0;
1084 double power_apd = 0;
1085
1086 if (fBiasControlVoltageVec.size()>0)
1087 {
1088 // Calibrate the data (subtract offset)
1089 for (int i=0; i<320; i++)
1090 {
1091 // Group index (0 or 1) of the of the pixel (4 or 5 pixel patch)
1092 const int N = fPixelMap.hv(i).count();
1093
1094 // Serial resistor of the individual G-APDs
1095 double R5 = 3900/N;
1096
1097 // This is also valid for the patches with wrong resistors,
1098 // because Iapd is a factor f larger but R a factor f smaller
1099 double Iapd = ptr[i] * 1e-6; // [A]
1100 double Iout = Iapd*N; // [A]
1101
1102 double UdrpCam = 1000 *Iout; // Voltage seen by everything in Camera
1103 double UdrpApd = (R5+2000)*Iout; // Voltage seen by G-APD
1104
1105 const double pwrCam = Iout * (fBiasControlVoltageVec[i]-UdrpCam);
1106 const double pwrApd = Iout * (fBiasControlVoltageVec[i]-UdrpApd);
1107
1108 // Total power participated in the camera at the G-APD
1109 // and the serial resistors (total voltage minus voltage
1110 // drop at resistors in bias crate)
1111 power_tot += pwrCam;
1112
1113 // Power consumption per G-APD
1114 power_apd += pwrApd;
1115 }
1116 }
1117
1118 // Divide by number of summed channels, convert to mW
1119 power_apd /= 320e-3; // [mW]
1120
1121 if (power_tot<1e-3)
1122 power_tot = 0;
1123 if (power_apd<1e-3)
1124 power_apd = 0;
1125
1126 fBiasControlPowerTot = power_tot;
1127
1128 // --------------------------------------------------------
1129
1130 // Get the maximum of each patch
1131 vector<float> val(320, 0);
1132 for (int i=0; i<320; i++)
1133 {
1134 const int idx = (fPixelMap.hv(i).hw()/9)*2+fPixelMap.hv(i).group();
1135 val[idx] = ptr[i];
1136 }
1137
1138 // Write the 160 patch values to a file
1139 WriteCam(d, "cam-biascontrol-current", val, 100);
1140
1141 // --------------------------------------------------------
1142
1143 const Statistics stat(vector<float>(ptr, ptr+320));
1144
1145 // Exclude the three crazy channels
1146 fBiasControlCurrentMed = stat.med;
1147 fBiasControlCurrentMax = stat.max;
1148
1149 // Store a history of the last 60 entries
1150 fBiasControlCurrentHist.push_back(fBiasControlCurrentMed);
1151 if (fBiasControlCurrentHist.size()>360)
1152 fBiasControlCurrentHist.pop_front();
1153
1154 // write the history to a file
1155 WriteHist(d, "hist-biascontrol-current", fBiasControlCurrentHist, 125);
1156
1157 // --------------------------------------------------------
1158
1159 string col1 = HTML::kGreen;
1160 string col2 = HTML::kGreen;
1161 string col3 = HTML::kGreen;
1162 string col4 = HTML::kGreen;
1163
1164 if (stat.min>90)
1165 col1 = HTML::kYellow;
1166 if (stat.min>110)
1167 col1 = HTML::kRed;
1168
1169 if (stat.med>90)
1170 col2 = HTML::kYellow;
1171 if (stat.med>110)
1172 col2 = HTML::kRed;
1173
1174 if (stat.avg>90)
1175 col3 = HTML::kYellow;
1176 if (stat.avg>110)
1177 col3 = HTML::kRed;
1178
1179 if (stat.max>90)
1180 col4 = HTML::kYellow;
1181 if (stat.max>110)
1182 col4 = HTML::kRed;
1183
1184 ostringstream out;
1185 out << setprecision(3);
1186 out << d.GetJavaDate() << '\n';
1187 out << HTML::kGreen << '\t' << "yes" << '\n';
1188 out << col1 << '\t' << stat.min << '\n';
1189 out << col2 << '\t' << stat.med << '\n';
1190 out << col3 << '\t' << stat.avg << '\n';
1191 out << col4 << '\t' << stat.max << '\n';
1192 out << HTML::kWhite << '\t' << power_tot << "W [" << power_apd << "mW]\n";
1193 ofstream(fPath+"/current.data") << out.str();
1194
1195 // --------------------------------------------------------
1196
1197 const float Unom = ptr[2*416+6];
1198 const float Utmp = ptr[2*416+7];
1199
1200 vector<float> Uov(ptr+416+6, ptr+416+6+320);
1201
1202 WriteCam(d, "cam-feedback-overvoltage", Uov, 0.2, 1.0);
1203
1204 const Statistics stat2(Uov);
1205
1206 out.str("");
1207 out << d.GetJavaDate() << '\n';
1208 out << setprecision(3);
1209 out << HTML::kWhite << '\t' << Utmp << '\n';
1210 out << HTML::kWhite << '\t' << Unom << '\n';
1211 out << HTML::kWhite << '\t' << stat2.min << '\n';
1212 out << HTML::kWhite << '\t' << stat2.med << '\n';
1213 out << HTML::kWhite << '\t' << stat2.avg << '\n';
1214 out << HTML::kWhite << '\t' << stat2.max << '\n';
1215 ofstream(fPath+"/feedback.data") << out.str();
1216
1217 return GetCurrentState();
1218 }
1219
1220 int HandleBiasCurrent(const EventImp &d)
1221 {
1222 if (fDimFeedback.state()>=Feedback::State::kCalibrated)
1223 return GetCurrentState();
1224
1225 if (!CheckDataSize(d, "BiasControl:Current", 832))
1226 return GetCurrentState();
1227
1228 // Convert dac counts to uA
1229 vector<float> v(320);
1230 for (int i=0; i<320; i++)
1231 v[i] = d.Ptr<uint16_t>()[i] * 5000./4096;
1232
1233 fBiasControlPowerTot = 0;
1234
1235 // Get the maximum of each patch
1236 vector<float> val(320, 0);
1237 for (int i=0; i<320; i++)
1238 {
1239 const PixelMapEntry &hv = fPixelMap.hv(i);
1240 if (!hv)
1241 continue;
1242
1243 const int idx = (hv.hw()/9)*2+hv.group();
1244 val[idx] = v[i];
1245 }
1246
1247 // Write the 160 patch values to a file
1248 WriteCam(d, "cam-biascontrol-current", val, 1000);
1249
1250 const Statistics stat(v, 0, 3);
1251
1252 // Exclude the three crazy channels
1253 fBiasControlCurrentMed = stat.med;
1254 fBiasControlCurrentMax = stat.max;
1255
1256 // Store a history of the last 60 entries
1257 fBiasControlCurrentHist.push_back(fBiasControlCurrentMed);
1258 if (fBiasControlCurrentHist.size()>360)
1259 fBiasControlCurrentHist.pop_front();
1260
1261 // write the history to a file
1262 WriteHist(d, "hist-biascontrol-current", fBiasControlCurrentHist, 1000);
1263
1264 ostringstream out;
1265 out << setprecision(3);
1266 out << d.GetJavaDate() << '\n';
1267 out << HTML::kWhite<< '\t' << "no" << '\n';
1268 out << HTML::kWhite << '\t' << stat.min << '\n';
1269 out << HTML::kWhite << '\t' << stat.med << '\n';
1270 out << HTML::kWhite << '\t' << stat.avg << '\n';
1271 out << HTML::kWhite << '\t' << stat.max << '\n';
1272 out << HTML::kWhite << '\t' << "---\n";
1273 ofstream(fPath+"/current.data") << out.str();
1274
1275 return GetCurrentState();
1276 }
1277
1278 int HandleBiasVoltage(const EventImp &d)
1279 {
1280 if (!CheckDataSize(d, "BiasControl:Voltage", 1664))
1281 {
1282 fBiasControlVoltageVec.clear();
1283 return GetCurrentState();
1284 }
1285
1286 fBiasControlVoltageVec.assign(d.Ptr<float>(), d.Ptr<float>()+320);
1287
1288 const Statistics stat(fBiasControlVoltageVec);
1289
1290 fBiasControlVoltageMed = stat.med;
1291
1292 vector<float> val(320, 0);
1293 for (int i=0; i<320; i++)
1294 {
1295 const int idx = (fPixelMap.hv(i).hw()/9)*2+fPixelMap.hv(i).group();
1296 val[idx] = fBiasControlVoltageVec[i];
1297 }
1298
1299 if (fDimBiasControl.state()==BIAS::State::kVoltageOn || fDimBiasControl.state()==BIAS::State::kRamping)
1300 WriteCam(d, "cam-biascontrol-voltage", val, 10, 65);
1301 else
1302 WriteCam(d, "cam-biascontrol-voltage", val, 75);
1303
1304 ostringstream out;
1305 out << setprecision(3);
1306 out << d.GetJavaDate() << '\n';
1307 out << HTML::kWhite << '\t' << stat.min << '\n';
1308 out << HTML::kWhite << '\t' << stat.med << '\n';
1309 out << HTML::kWhite << '\t' << stat.avg << '\n';
1310 out << HTML::kWhite << '\t' << stat.max << '\n';
1311 ofstream(fPath+"/voltage.data") << out.str();
1312
1313 return GetCurrentState();
1314 }
1315
1316 int HandleFadEvents(const EventImp &d)
1317 {
1318 if (!CheckDataSize(d, "FadControl:Events", 4*4))
1319 {
1320 fFadControlNumEvents = -1;
1321 return GetCurrentState();
1322 }
1323
1324 fFadControlNumEvents = d.Get<uint32_t>();
1325
1326 return GetCurrentState();
1327 }
1328
1329 int HandleFadStartRun(const EventImp &d)
1330 {
1331 if (!CheckDataSize(d, "FadControl:StartRun", 16))
1332 {
1333 fFadControlStartRun = -1;
1334 return GetCurrentState();
1335 }
1336
1337 fFadControlStartRun = d.Get<int64_t>();
1338
1339 return GetCurrentState();
1340 }
1341
1342 int HandleFadDrsRuns(const EventImp &d)
1343 {
1344 if (!CheckDataSize(d, "FadControl:DrsRuns", 5*4))
1345 {
1346 fFadControlDrsStep = -1;
1347 return GetCurrentState();
1348 }
1349
1350 const uint32_t *ptr = d.Ptr<uint32_t>();
1351 fFadControlDrsStep = ptr[0];
1352 fFadControlDrsRuns[0] = ptr[1];
1353 fFadControlDrsRuns[1] = ptr[2];
1354 fFadControlDrsRuns[2] = ptr[3];
1355
1356 return GetCurrentState();
1357 }
1358
1359 int HandleFadConnections(const EventImp &d)
1360 {
1361 if (!CheckDataSize(d, "FadControl:Connections", 41))
1362 {
1363 //fStatusEventBuilderLabel->setText("Offline");
1364 return GetCurrentState();
1365 }
1366
1367 string rc(40, '-'); // orange/red [45]
1368
1369 const uint8_t *ptr = d.Ptr<uint8_t>();
1370
1371 int c[4] = { '.', '.', '.', '.' };
1372
1373 for (int i=0; i<40; i++)
1374 {
1375 const uint8_t stat1 = ptr[i]&3;
1376 const uint8_t stat2 = ptr[i]>>3;
1377
1378 if (stat1==0 && stat2==0)
1379 rc[i] = '.'; // gray [46]
1380 else
1381 if (stat1>=2 && stat2==8)
1382 rc[i] = stat1==2?'+':'*'; // green [43] : check [42]
1383
1384 if (rc[i]<c[i/10])
1385 c[i/10] = rc[i];
1386 }
1387
1388 string col[4];
1389 for (int i=0; i<4; i++)
1390 switch (c[i])
1391 {
1392 case '.': col[i]=HTML::kWhite; break;
1393 case '-': col[i]=HTML::kRed; break;
1394 case '+': col[i]=HTML::kYellow; break;
1395 case '*': col[i]=HTML::kGreen; break;
1396 }
1397
1398 ostringstream out;
1399 out << setprecision(3);
1400 out << d.GetJavaDate() << '\n';
1401 out << col[0] << '\t' << rc.substr( 0, 10) << '\n';
1402 out << col[1] << '\t' << rc.substr(10, 10) << '\n';
1403 out << col[2] << '\t' << rc.substr(20, 10) << '\n';
1404 out << col[3] << '\t' << rc.substr(30, 10) << '\n';
1405 ofstream(fPath+"/fad.data") << out.str();
1406
1407 return GetCurrentState();
1408 }
1409
1410 /*
1411 int HandleFtmControlStateChange()
1412 {
1413 const int32_t &last = fDimFtmControl.last.second;
1414 const int32_t &state = fDimFtmControl.state();
1415
1416 // If a new run has been started ensure that the counter
1417 // is reset. The reset in HandleFtmTriggerRates might
1418 // arrive only after the run was started.
1419 if (last!=FTM::State::kTriggerOn && state==MCP::State::kTriggerOn)
1420 fFtmControlTriggerRateTooLow = -1;
1421
1422 return StateMachineImp::kSM_KeepState;
1423 }*/
1424
1425
1426 int HandleFtmTriggerRates(const EventImp &d)
1427 {
1428 if (!CheckDataSize(d, "FtmControl:TriggerRates", 24+160+640+8))
1429 {
1430 fFtmControlTriggerRateTooLow = 0;
1431 return GetCurrentState();
1432 }
1433
1434 const FTM::DimTriggerRates &dim = d.Ref<FTM::DimTriggerRates>();
1435
1436 // If the trigger rate is too low...
1437 // ... and the run was not just started (can lead to very small elapsed times)
1438 // ... and the trigger is switched on
1439 // ... and there was no state change (then the trigger was started or stopped)
1440 fFtmControlTriggerRateTooLow =
1441 dim.fTriggerRate<1 && dim.fElapsedTime>0.45 &&
1442 (fFtmControlState&FTM::kFtmStates)==FTM::kFtmRunning &&
1443 (fFtmControlState&FTM::kFtmStates)==(d.GetQoS()&FTM::kFtmStates);
1444
1445 fFtmControlState = d.GetQoS();
1446
1447 const float *brates = dim.fBoardRate; // Board rate
1448 const float *prates = dim.fPatchRate; // Patch rate
1449
1450 // Store a history of the last 60 entries
1451 fFtmControlTriggerRateHist.push_back(dim.fTriggerRate);
1452 if (fFtmControlTriggerRateHist.size()>300)
1453 fFtmControlTriggerRateHist.pop_front();
1454
1455 // FIXME: Add statistics for all kind of rates
1456
1457 WriteHist(d, "hist-ftmcontrol-triggerrate",
1458 fFtmControlTriggerRateHist, 100);
1459 WriteCam(d, "cam-ftmcontrol-boardrates",
1460 vector<float>(brates, brates+40), 10);
1461 WriteCam(d, "cam-ftmcontrol-patchrates",
1462 vector<float>(prates, prates+160), 10);
1463
1464 ostringstream out;
1465 out << setprecision(3);
1466 out << d.GetJavaDate() << '\n';
1467 out << HTML::kWhite << '\t' << dim.fTriggerRate << '\n';
1468
1469 ofstream(fPath+"/trigger.data") << out.str();
1470
1471 const Statistics bstat(vector<float>(brates, brates+ 40));
1472 const Statistics pstat(vector<float>(prates, prates+160));
1473
1474 out.str("");
1475 out << d.GetJavaDate() << '\n';
1476 out << HTML::kWhite << '\t' << bstat.min << '\n';
1477 out << HTML::kWhite << '\t' << bstat.med << '\n';
1478 out << HTML::kWhite << '\t' << bstat.avg << '\n';
1479 out << HTML::kWhite << '\t' << bstat.max << '\n';
1480 ofstream(fPath+"/boardrates.data") << out.str();
1481
1482 out.str("");
1483 out << d.GetJavaDate() << '\n';
1484 out << HTML::kWhite << '\t' << pstat.min << '\n';
1485 out << HTML::kWhite << '\t' << pstat.med << '\n';
1486 out << HTML::kWhite << '\t' << pstat.avg << '\n';
1487 out << HTML::kWhite << '\t' << pstat.max << '\n';
1488 ofstream(fPath+"/patchrates.data") << out.str();
1489
1490 return GetCurrentState();
1491 }
1492
1493 int HandleFtmStaticData(const EventImp &d)
1494 {
1495 if (!CheckDataSize(d, "FtmControl:StaticData", sizeof(FTM::DimStaticData)))
1496 return GetCurrentState();
1497
1498 // If the FTM is in state Configuring, the clock conditioner
1499 // is always reported to be unlocked
1500 fFtmControlState = d.GetQoS();
1501
1502 const FTM::DimStaticData &dat = d.Ref<FTM::DimStaticData>();
1503
1504 vector<uint16_t> vecp(dat.fThreshold, dat.fThreshold+160);
1505 vector<uint16_t> vecb(dat.fMultiplicity, dat.fMultiplicity+40);
1506
1507 WriteCam(d, "cam-ftmcontrol-thresholds-patch", vecp, 1000);
1508 WriteCam(d, "cam-ftmcontrol-thresholds-board", vecb, 100);
1509
1510 const Statistics statp(vecp);
1511 const Statistics statb(vecb);
1512
1513 fFtmPatchThresholdMed = statp.med;
1514 fFtmBoardThresholdMed = statb.med;
1515
1516 ostringstream out;
1517 out << d.GetJavaDate() << '\n';
1518 out << HTML::kWhite << '\t' << statb.min << '\n';
1519 out << HTML::kWhite << '\t' << statb.med << '\n';
1520 out << HTML::kWhite << '\t' << statb.max << '\n';
1521 ofstream(fPath+"/thresholds-board.data") << out.str();
1522
1523 out.str("");
1524 out << d.GetJavaDate() << '\n';
1525 out << HTML::kWhite << '\t' << statp.min << '\n';
1526 out << HTML::kWhite << '\t' << statp.med << '\n';
1527 out << HTML::kWhite << '\t' << statp.max << '\n';
1528 ofstream(fPath+"/thresholds-patch.data") << out.str();
1529
1530 out.str("");
1531 out << d.GetJavaDate() << '\n';
1532 out << HTML::kWhite << '\t' << statb.med << '\n';
1533 out << HTML::kWhite << '\t' << statp.med << '\n';
1534 ofstream(fPath+"/thresholds.data") << out.str();
1535
1536 out.str("");
1537 out << d.GetJavaDate() << '\n';
1538 out << HTML::kWhite << '\t' << dat.fTriggerInterval << '\n';
1539 out << HTML::kWhite << '\t';
1540 if (dat.HasPedestal())
1541 out << dat.fTriggerSeqPed;
1542 else
1543 out << "&ndash;";
1544 out << ':';
1545 if (dat.HasLPext())
1546 out << dat.fTriggerSeqLPext;
1547 else
1548 out << "&ndash;";
1549 out << ':';
1550 if (dat.HasLPint())
1551 out << dat.fTriggerSeqLPint;
1552 else
1553 out << "&ndash;";
1554 out << '\n';
1555
1556 out << HTML::kWhite << '\t' << (dat.HasTrigger()?"on":"off") << " / " << (dat.HasExt1()?"on":"off") << " / " << (dat.HasExt2()?"on":"off") << '\n';
1557 out << HTML::kWhite << '\t' << (dat.HasVeto()?"on":"off") << " / " << (dat.HasClockConditioner()?"time cal":"marker") << '\n';
1558 out << HTML::kWhite << '\t' << dat.fMultiplicityPhysics << " / " << dat.fMultiplicityCalib << '\n';
1559 out << HTML::kWhite << '\t' << dat.fWindowPhysics << '\t' << dat.fWindowCalib << '\n';
1560 out << HTML::kWhite << '\t' << dat.fDelayTrigger << '\t' << dat.fDelayTimeMarker << '\n';
1561 out << HTML::kWhite << '\t' << dat.fDeadTime << '\n';
1562
1563 int64_t vp = dat.fPrescaling[0];
1564 for (int i=1; i<40; i++)
1565 if (vp!=dat.fPrescaling[i])
1566 vp = -1;
1567
1568 if (vp<0)
1569 out << HTML::kYellow << "\tdifferent\n";
1570 else
1571 out << HTML::kWhite << '\t' << 0.5*vp << "\n";
1572
1573 ofstream(fPath+"/ftm.data") << out.str();
1574
1575 // Active FTUs: IsActive(i)
1576 // Enabled Pix: IsEnabled(i)
1577
1578 return GetCurrentState();
1579 }
1580
1581 int HandleFtmFtuList(const EventImp &d)
1582 {
1583 if (!CheckDataSize(d, "FtmControl:FtuList", sizeof(FTM::DimFtuList)))
1584 return GetCurrentState();
1585
1586 const FTM::DimFtuList &sdata = d.Ref<FTM::DimFtuList>();
1587
1588 ostringstream out;
1589 out << d.GetJavaDate() << '\n';
1590
1591 int cnt = 0;
1592 for (int i=0; i<4; i++)
1593 {
1594 out << HTML::kWhite << '\t';
1595 for (int j=0; j<10; j++)
1596 if (sdata.IsActive(i*10+j))
1597 {
1598 if (sdata.fPing[i*10+j]==1)
1599 {
1600 out << '*';
1601 cnt++;
1602 }
1603 else
1604 out << sdata.fPing[i*10+j];
1605 }
1606 else
1607 out << '-';
1608 out << '\n';
1609 }
1610
1611 fFtmControlFtuOk = cnt==40;
1612
1613 ofstream(fPath+"/ftu.data") << out.str();
1614
1615 return GetCurrentState();
1616 }
1617
1618 int HandleFadEventData(const EventImp &d)
1619 {
1620 if (!CheckDataSize(d, "FadControl:EventData", 23040))
1621 return GetCurrentState();
1622
1623 const float *dat = d.Ptr<float>(1440*sizeof(float)*2);
1624
1625 /*
1626 vector<float> max(320, 0);
1627 for (int i=0; i<1440; i++)
1628 {
1629 if (i%9==8)
1630 continue;
1631
1632 const int idx = (fPixelMap.hw(i).hw()/9)*2+fPixelMap.hw(i).group();
1633 const double v = dat[i]/1000;
1634 //if (v>max[idx])
1635 // max[idx]=v;
1636
1637 max[idx] += v/4;
1638 } */
1639
1640 vector<float> val(1440);
1641 for (int i=0; i<1440; i++)
1642 val[i] = dat[i%9==8 ? i-2 : i]/1000;
1643
1644 vector<float> sorted(val);
1645 nth_element(sorted.begin(), sorted.begin()+3, sorted.end(),
1646 std::greater<float>());
1647
1648 const uint32_t trig = d.GetQoS() & FAD::EventHeader::kLPext;
1649
1650 const float min = fFadControlDrsRuns[0]==0 ? -1 : 0;
1651
1652 float scale = 2;
1653 if (trig&FAD::EventHeader::kLPext)
1654 scale = 1;
1655 if (trig&FAD::EventHeader::kPedestal)
1656 scale = 0.25;
1657 if (trig==0)
1658 scale = max(0.25f, sorted[3]);
1659
1660 // assume it is drs-gain
1661 //if ((trig&FAD::EventHeader::kPedestal) && fFadControlDrsRuns[0]>0 && fFadControlDrsRuns[1]==0)
1662 // min = 0.75;
1663
1664 WriteCam(d, "cam-fadcontrol-eventdata", val, scale, min);
1665
1666 return GetCurrentState();
1667 }
1668
1669 int HandleStats(const EventImp &d)
1670 {
1671 if (!CheckDataSize(d, "Stats", 4*8))
1672 {
1673 fFreeSpace = UINT64_MAX;
1674 return GetCurrentState();
1675 }
1676
1677 const DimWriteStatistics::Stats &s = d.Ref<DimWriteStatistics::Stats>();
1678 fFreeSpace = s.freeSpace;
1679
1680 return GetCurrentState();
1681 }
1682
1683 int HandleFscTemperature(const EventImp &d)
1684 {
1685 if (!CheckDataSize(d, "FscControl:Temperature", 240))
1686 return GetCurrentState();
1687
1688 const float *ptr = d.Ptr<float>(4);
1689
1690 double avg = 0;
1691 double rms = 0;
1692 double min = 99;
1693 double max = -99;
1694
1695 int num = 0;
1696 for (const float *t=ptr; t<ptr+31; t++)
1697 {
1698 if (*t==0)
1699 continue;
1700
1701 if (*t>max)
1702 max = *t;
1703
1704 if (*t<min)
1705 min = *t;
1706
1707 avg += *t;
1708 rms += *t * *t;
1709
1710 num++;
1711 }
1712
1713 avg /= num;
1714 rms /= num;
1715 rms += avg*avg;
1716 rms = rms<0 ? 0 : sqrt(rms);
1717
1718 // Clean broken reports
1719 static double pre_rms1 = 1.5;
1720 static double pre_rms2 = 0;
1721
1722 const double cut = pre_rms1 + 0.1;
1723
1724 const bool reject = rms>cut && pre_rms2<cut;
1725
1726 pre_rms2 = pre_rms1;
1727 pre_rms1 = rms;
1728
1729 if (reject)
1730 return GetCurrentState();
1731
1732
1733 if (!fMagicWeatherHist[kTemp].empty())
1734 {
1735 fFscControlTemperatureHist.push_back(avg-fMagicWeatherHist[kTemp].back());
1736 if (fFscControlTemperatureHist.size()>300)
1737 fFscControlTemperatureHist.pop_front();
1738 }
1739
1740 const Statistics stat(fFscControlTemperatureHist);
1741
1742 ostringstream out;
1743 out << setprecision(3);
1744 out << d.GetJavaDate() << '\n';
1745 out << HTML::kWhite << '\t' << fFscControlHumidityAvg << '\n';
1746 out << HTML::kWhite << '\t' << stat.min << '\n';
1747 out << HTML::kWhite << '\t' << stat.avg << '\n';
1748 out << HTML::kWhite << '\t' << stat.max << '\n';
1749
1750 ofstream(fPath+"/fsc.data") << out.str();
1751
1752 WriteHist(d, "hist-fsccontrol-temperature",
1753 fFscControlTemperatureHist, 10);
1754
1755 out.str("");
1756 out << setprecision(3);
1757 out << d.GetJavaDate() << '\n';
1758 out << HTML::kWhite << '\t' << max << '\n';
1759 out << HTML::kWhite << '\t' << avg << '\n';
1760 out << HTML::kWhite << '\t' << min << '\n';
1761
1762 ofstream(fPath+"/camtemp.data") << out.str();
1763
1764 return GetCurrentState();
1765 }
1766
1767 int HandleFscBiasTemp(const EventImp &d)
1768 {
1769 if (!CheckDataSize(d, "FscControl:BiasTemp", 323*4))
1770 return GetCurrentState();
1771
1772 const float *ptr = d.Ptr<float>(4);
1773 const float avg = d.Get<float>(321*4);
1774 //const float rms = d.Get<float>(322*4);
1775
1776 vector<double> tout(320);
1777 for (int i=0; i<320; i++)
1778 {
1779 const int idx = (fPixelMap.hv(i).hw()/9)*2+fPixelMap.hv(i).group();
1780 tout[idx] = ptr[i];
1781 }
1782
1783 WriteCam(d, "cam-fsccontrol-temperature", tout, 3, avg-1.75);
1784
1785 return GetCurrentState();
1786 }
1787
1788 int HandleFscHumidity(const EventImp &d)
1789 {
1790 if (!CheckDataSize(d, "FscControl:Humidity", 5*4))
1791 return GetCurrentState();
1792
1793 const float *ptr = d.Ptr<float>(4);
1794
1795 double avg =0;
1796 int num = 0;
1797
1798 for (const float *t=ptr; t<ptr+4; t++)
1799 if (*t>0 && *t<=100 && t!=ptr+2 /*excl broken sensor*/)
1800 {
1801 avg += *t;
1802 num++;
1803 }
1804
1805 fFscControlHumidityAvg = num>0 ? avg/num : 0;
1806
1807 return GetCurrentState();
1808 }
1809
1810 int HandleGpsNema(const EventImp &d)
1811 {
1812 if (!CheckDataSize(d, "GpsControl:Nema", sizeof(GPS::NEMA)))
1813 return GetCurrentState();
1814
1815 const GPS::NEMA &nema = d.Ref<GPS::NEMA>();
1816
1817 ostringstream out;
1818
1819 out << fixed;
1820 out << d.GetJavaDate() << '\n';
1821
1822 switch (nema.qos)
1823 {
1824 case 1: out << HTML::kGreen << "\tGPS fix [1]\n"; break;
1825 case 2: out << HTML::kGreen << "\tDifferential fix [2]\n"; break;
1826 default: out << HTML::kRed << "\tinvalid [" << nema.qos << "]\n"; break;
1827 }
1828
1829 out << HTML::kWhite << '\t' << nema.count << '\n';
1830 out << HTML::kWhite << '\t' << Time(floor(Time().Mjd())+nema.time).GetAsStr("%H:%M:%S") << '\n';
1831 out << HTML::kWhite << '\t' << setprecision(4) << nema.lat << '\n';
1832 out << HTML::kWhite << '\t' << setprecision(4) << nema.lng << '\n';
1833 out << HTML::kWhite << '\t' << setprecision(1) << nema.height << "\n";
1834 out << HTML::kWhite << '\t' << setprecision(1) << nema.hdop << "\n";
1835 out << HTML::kWhite << '\t' << setprecision(1) << nema.geosep << "\n";
1836
1837 ofstream(fPath+"/gps.data") << out.str();
1838
1839 return GetCurrentState();
1840 }
1841
1842 int HandleSqmData(const EventImp &d)
1843 {
1844 if (!CheckDataSize(d, "SqmControl:Data", sizeof(SQM::Data)))
1845 return GetCurrentState();
1846
1847 const SQM::Data &data = d.Ref<SQM::Data>();
1848
1849 ostringstream out;
1850
1851 out << fixed;
1852 out << d.GetJavaDate() << '\n';
1853 out << HTML::kWhite << '\t' << setprecision(2) << data.mag << '\n';
1854 out << HTML::kWhite << '\t' << data.freq << '\n';
1855 out << HTML::kWhite << '\t' << data.counts << '\n';
1856 out << HTML::kWhite << '\t' << setprecision(3) << data.period << '\n';
1857 out << HTML::kWhite << '\t' << setprecision(1) << data.temp << "\n";
1858
1859 ofstream(fPath+"/sqm.data") << out.str();
1860
1861 return GetCurrentState();
1862 }
1863
1864 string GetTempColor(float t)
1865 {
1866 if (t>25 && t<30)
1867 return HTML::kGreen;
1868
1869 if (t<20 || t>35)
1870 return HTML::kRed;
1871
1872 return HTML::kYellow;
1873 }
1874
1875 int HandleTemperatureData(const EventImp &d)
1876 {
1877 if (!CheckDataSize(d, "Temperature:Data", 3*sizeof(float)))
1878 return GetCurrentState();
1879
1880 const float *temp = d.Ptr<float>();
1881
1882 ostringstream out;
1883
1884 out << fixed << setprecision(1);
1885 out << d.GetJavaDate() << '\n';
1886
1887 out << GetTempColor(temp[1]) << '\t' << temp[1] << '\n';
1888 out << GetTempColor(temp[0]) << '\t' << temp[0] << '\n';
1889 out << GetTempColor(temp[2]) << '\t' << temp[2] << '\n';
1890
1891 ofstream(fPath+"/temperature.data") << out.str();
1892
1893 fTemperatureControlHist.push_back(temp[0]);
1894 if (fTemperatureControlHist.size()>60) // 1h
1895 fTemperatureControlHist.pop_front();
1896
1897 WriteHist(d, "hist-temperaturecontrol",
1898 fTemperatureControlHist, 45, 0);
1899
1900 return GetCurrentState();
1901 }
1902
1903 int HandleAgilentData(const EventImp &d, const string &ext)
1904 {
1905 if (!CheckDataSize(d, ("Agilent"+ext+":Data").c_str(), 4*sizeof(float)))
1906 return GetCurrentState();
1907
1908 const float *data = d.Ptr<float>();
1909
1910 ostringstream out;
1911
1912 out << fixed << setprecision(1);
1913 out << d.GetJavaDate() << '\n';
1914
1915 out << HTML::kWhite << '\t' << data[0] << '\n';
1916 out << HTML::kWhite << '\t' << data[1] << '\n';
1917 out << HTML::kWhite << '\t' << data[2] << '\n';
1918 out << HTML::kWhite << '\t' << data[3] << '\n';
1919
1920 ofstream(fPath+"/agilent"+ext+".data") << out.str();
1921
1922 return GetCurrentState();
1923 }
1924
1925 int HandleRateScanData(const EventImp &d)
1926 {
1927 if (!CheckDataSize(d, "RateScan:Data", 824))
1928 return GetCurrentState();
1929
1930 const uint64_t id = d.Get<uint64_t>();
1931 const float *rate = d.Ptr<float>(20);
1932
1933 if (fRateScanDataId!=id)
1934 {
1935 for (int i=0; i<41; i++)
1936 fRateScanDataHist[i].clear();
1937 fRateScanDataId = id;
1938 }
1939 fRateScanDataHist[0].push_back(log10(rate[0]));
1940
1941 double max = 0;
1942 for (int i=1; i<41; i++)
1943 {
1944 fRateScanDataHist[i].push_back(log10(rate[i]));
1945 if (rate[i]>max)
1946 max = rate[i];
1947 }
1948
1949 // Cycle by time!
1950 fRateScanBoard ++;
1951 fRateScanBoard %= 40;
1952
1953 WriteHist(d, "hist-ratescan", fRateScanDataHist[0], 10, -2);
1954 WriteCam(d, "cam-ratescan-board", fRateScanDataHist[fRateScanBoard+1], 10, -4);
1955
1956 ostringstream out;
1957 out << setprecision(3);
1958 out << d.GetJavaDate() << '\n';
1959 out << HTML::kWhite << '\t' << fFtmBoardThresholdMed << '\n';
1960 out << HTML::kWhite << '\t' << fFtmPatchThresholdMed << '\n';
1961 out << HTML::kWhite << '\t' << floor(pow(10, fRateScanDataHist[0].back())+.5) << '\n';
1962 out << HTML::kWhite << '\t' << floor(max+.5) << '\n';
1963
1964 ofstream(fPath+"/ratescan.data") << out.str();
1965
1966 out.str("");
1967 out << d.GetJavaDate() << '\n';
1968 out << HTML::kWhite << '\t' << int(fRateScanBoard) << '\n';
1969 out << HTML::kWhite << '\t' << pow(10, fRateScanDataHist[fRateScanBoard+1].back()) << '\n';
1970
1971 ofstream(fPath+"/ratescan_board.data") << out.str();
1972
1973 return GetCurrentState();
1974 }
1975
1976 int HandleRateControlThreshold(const EventImp &d)
1977 {
1978 if (!CheckDataSize(d, "RateControl:Threshold", 18))
1979 return GetCurrentState();
1980
1981 const uint16_t th = d.Get<uint16_t>();
1982
1983 fRateControlThreshold.push_back(th);
1984 if (fRateControlThreshold.size()>300)
1985 fRateControlThreshold.pop_front();
1986
1987 WriteHist(d, "hist-ratecontrol-threshold", fRateControlThreshold, 1000);
1988
1989 return GetCurrentState();
1990 }
1991
1992 int HandleChatMsg(const EventImp &d)
1993 {
1994 if (d.GetSize()==0 || d.GetQoS()!=MessageImp::kComment)
1995 return GetCurrentState();
1996
1997 if (Time()<d.GetTime()+boost::posix_time::minutes(1))
1998 SetAudio("message");
1999
2000 fChatHist.add(d.GetText(), d.GetTime());
2001
2002 ostringstream out;
2003 out << setprecision(3);
2004 out << Header(d) << '\n';
2005 out << HTML::kWhite << '\t';
2006 out << "<->" << fChatHist.rget() << "</->";
2007 out << '\n';
2008
2009 ofstream(fPath+"/chat.data") << out.str();
2010
2011 return GetCurrentState();
2012 }
2013
2014 // -------------------------------------------------------------------
2015
2016 int HandleDoTest(const EventImp &d)
2017 {
2018 ostringstream out;
2019 out << d.GetJavaDate() << '\n';
2020
2021 switch (d.GetQoS())
2022 {
2023 case -3: out << HTML::kWhite << "\tNot running\n"; break;
2024 case -2: out << HTML::kBlue << "\tLoading\n"; break;
2025 case -1: out << HTML::kBlue << "\tStarted\n"; break;
2026 default: out << HTML::kGreen << "\tRunning [" << d.GetQoS() << "]\n"; break;
2027 }
2028
2029 ofstream(fPath+"/dotest.data") << out.str();
2030
2031 return StateMachineImp::kSM_KeepState;
2032 }
2033
2034 // -------------------------------------------------------------------
2035
2036 /*
2037 bool CheckEventSize(size_t has, const char *name, size_t size)
2038 {
2039 if (has==size)
2040 return true;
2041
2042 ostringstream msg;
2043 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
2044 Fatal(msg);
2045 return false;
2046 }*/
2047
2048 int Print() const
2049 {
2050 Out() << fDimDNS << endl;
2051 Out() << fDimMcp << endl;
2052 Out() << fDimControl << endl;
2053 Out() << fDimDataLogger << endl;
2054 Out() << fDimDriveControl << endl;
2055 Out() << fDimTimeCheck << endl;
2056 Out() << fDimFadControl << endl;
2057 Out() << fDimFtmControl << endl;
2058 Out() << fDimBiasControl << endl;
2059 Out() << fDimFeedback << endl;
2060 Out() << fDimRateControl << endl;
2061 Out() << fDimFscControl << endl;
2062 Out() << fDimAgilentControl24 << endl;
2063 Out() << fDimAgilentControl50 << endl;
2064 Out() << fDimAgilentControl80 << endl;
2065 Out() << fDimPwrControl << endl;
2066 Out() << fDimLidControl << endl;
2067 Out() << fDimMagicWeather << endl;
2068 Out() << fDimTngWeather << endl;
2069 Out() << fDimMagicLidar << endl;
2070 Out() << fDimTemperature << endl;
2071 Out() << fDimRateScan << endl;
2072 Out() << fDimChat << endl;
2073 Out() << fDimSkypeClient << endl;
2074
2075 return GetCurrentState();
2076 }
2077
2078 string GetStateHtml(const DimState &state, int green) const
2079 {
2080 if (!state.online())
2081 return HTML::kWhite+"\t&mdash;\n";
2082
2083 if (&state==&fDimControl)
2084 return HTML::kGreen +'\t'+(state.state()==0?"Idle":fDimControl.shortmsg)+'\n';
2085
2086 const State rc = state.description();
2087
2088 // Sate not found in list, server online (-3: offline; -2: not found)
2089 if (rc.index==-2)
2090 {
2091 ostringstream out;
2092 out << HTML::kWhite << '\t' << state.state() << '\n';
2093 return out.str();
2094 }
2095
2096 //ostringstream msg;
2097 //msg << HTML::kWhite << '\t' << rc.name << " [" << rc.index << "]\n";
2098 //return msg.str();
2099
2100 if (rc.index<0)
2101 return HTML::kWhite + "\t&mdash;\n";
2102
2103 string col = HTML::kGreen;
2104 if (rc.index<green)
2105 col = HTML::kYellow;
2106 if (rc.index>0xff)
2107 col = HTML::kRed;
2108
2109 return col + '\t' + rc.name + '\n';
2110 }
2111
2112 bool SetError(bool b, const string &err)
2113 {
2114 if (!b)
2115 {
2116 fErrorList.erase(err);
2117 return 0;
2118 }
2119
2120 const bool isnew = fErrorList.insert(err).second;
2121 if (isnew)
2122 fErrorHist.add(err);
2123
2124 return isnew;
2125 }
2126
2127#ifdef HAVE_NOVA
2128
2129 //vector<pair<Nova::EquPosn, double>> fMoonCoords;
2130
2131 vector<Nova::SolarObjects> fCoordinates;
2132
2133 void CalcCoordinates(double jd)
2134 {
2135 jd = floor(jd);
2136
2137 fCoordinates.clear();
2138 for (double h=0; h<1; h+=1./(24*12))
2139 fCoordinates.emplace_back(jd+h);
2140 }
2141
2142 pair<vector<float>, pair<Time, float>> GetVisibility(Nova::EquPosn *src=0)
2143 {
2144 const double sunset = fSun.fSetAstronomical.JD();
2145 const double sunrise = fSun.fRiseAstronomical.JD();
2146
2147 Nova::EquPosn moon;
2148 Nova::EquPosn *pos = src ? src : &moon;
2149
2150 double max = 0;
2151 double maxjd = 0;
2152
2153 int cnt = 0;
2154
2155 vector<float> alt;
2156 for (auto it=fCoordinates.begin(); it!=fCoordinates.end(); it++)
2157 {
2158 if (src==0)
2159 moon = it->fMoonEqu;
2160
2161 const Nova::HrzPosn hrz = Nova::GetHrzFromEqu(*pos, it->fJD);
2162
2163 if (it->fJD>sunset && it->fJD<sunrise)
2164 alt.push_back(hrz.alt);
2165
2166 if (hrz.alt>max)
2167 {
2168 max = hrz.alt;
2169 maxjd = it->fJD;
2170 }
2171
2172 if (it->fJD>sunset && it->fJD<sunrise && hrz.alt>15)
2173 cnt++;
2174 }
2175
2176 if (max<=15 || cnt==0)
2177 return make_pair(vector<float>(), make_pair(Time(), 0));
2178
2179 return make_pair(alt, make_pair(maxjd, maxjd>sunset&&maxjd<sunrise?max:0));
2180 }
2181
2182 pair<vector<float>, pair<Time, float>> GetLightCondition(const Nova::EquPosn &src_pos)
2183 {
2184 const double sunset = fSun.fSetAstronomical.JD();
2185 const double sunrise = fSun.fRiseAstronomical.JD();
2186
2187 double max = -1;
2188 double maxjd = 0;
2189
2190 int cnt = 0;
2191
2192 vector<float> vec;
2193 for (auto it=fCoordinates.begin(); it!=fCoordinates.end(); it++)
2194 {
2195 double cur = -1;
2196
2197 if (it->fJD>sunset && it->fJD<sunrise)
2198 {
2199 cur = FACT::PredictI(*it, src_pos);
2200 vec.push_back(cur);
2201 }
2202
2203 if (cur>max)
2204 {
2205 max = cur;
2206 maxjd = it->fJD;
2207 }
2208
2209 if (it->fJD>sunset && it->fJD<sunrise && cur>0)
2210 cnt++;
2211 }
2212
2213 if (max<=0 || cnt==0)
2214 return make_pair(vector<float>(), make_pair(Time(), 0));
2215
2216 return make_pair(vec, make_pair(maxjd, maxjd>sunset&&maxjd<sunrise?max:-1));
2217 }
2218#endif
2219
2220 void UpdateAstronomy()
2221 {
2222 Time now;
2223
2224 CalcCoordinates(now.JD());
2225
2226 fSun = Sun (now);
2227 fMoon = Moon(now);
2228
2229 vector<string> color(8, HTML::kWhite);
2230 color[fSun.state%8] = HTML::kBlue;
2231
2232 ostringstream out;
2233 out << setprecision(3);
2234 out << now.JavaDate() << '\n';
2235 out << color[0] << '\t' << fSun.fRiseDarkTime.GetAsStr("%H:%M") << '\n';
2236 out << color[1] << '\t' << fSun.fRiseAstronomical.GetAsStr("%H:%M") << '\n';
2237 out << color[2] << '\t' << fSun.fRiseCivil.GetAsStr("%H:%M") << '\n';
2238 out << color[3] << '\t' << fSun.fRiseDayTime.GetAsStr("%H:%M") << '\n';
2239
2240 out << color[4] << '\t' << fSun.fSetDayTime.GetAsStr("%H:%M") << '\n';
2241 out << color[5] << '\t' << fSun.fSetCivil.GetAsStr("%H:%M") << '\n';
2242 out << color[6] << '\t' << fSun.fSetAstronomical.GetAsStr("%H:%M") << '\n';
2243 out << color[7] << '\t' << fSun.fSetDarkTime.GetAsStr("%H:%M") << '\n';
2244
2245 ofstream(fPath+"/sun.data") << out.str();
2246
2247 color.assign(3, HTML::kWhite);
2248 color[fMoon.state%3] = HTML::kBlue;
2249
2250 out.str("");
2251 out << now.JavaDate() << '\n';
2252
2253 out << color[0] << '\t' << fMoon.fRise.GetAsStr("%H:%M") << '\n';
2254 out << color[1] << '\t' << fMoon.fTransit.GetAsStr("%H:%M") << '\n';
2255 out << color[2] << '\t' << fMoon.fSet.GetAsStr("%H:%M") << '\n';
2256
2257 out << (fSun.isday?HTML::kWhite:fMoon.color) << '\t' << fMoon.description << '\n';
2258
2259 if (!fMoon.visible)
2260 out << HTML::kWhite << "\t&mdash;\t\n";
2261 else
2262 {
2263 string col = HTML::kWhite;
2264 if (!fSun.isday)
2265 {
2266 col = HTML::kGreen;
2267 if (fMoon.zd>25)
2268 col = HTML::kYellow;
2269 if (fMoon.zd>45 && fMoon.zd<80)
2270 col = HTML::kRed;
2271 if (fMoon.zd>=80)
2272 col = HTML::kRed;
2273 }
2274 out << col << '\t' << fMoon.zd << '\t' << GetDir(fMoon.az) << '\n';
2275 }
2276
2277 ostringstream out2, out3, out4;
2278 out2 << setprecision(3);
2279 out2 << now.JavaDate() << '\n';
2280 out3 << now.JavaDate() << '\n';
2281 out4 << now.JavaDate() << '\n';
2282
2283 struct Entry
2284 {
2285 string name;
2286 float value;
2287 int color;
2288 Entry(const string &n, float v, int c) : name(n), value(v), color(c%8) { }
2289
2290 const string &Col() const
2291 {
2292 // If this list is updatd the number count in the constructor needs
2293 // to be updated, too
2294 static const string hcol[] = { "888", "8cf", "c8f", "bbb", "8fc", "cf8", "f8c", "fc8" };
2295 return hcol[color];
2296 }
2297
2298 vector<float> GetColor(double scale, double offset=0) const
2299 {
2300 vector<float> rc(3);
2301 rc[0] = double(Col()[0])*scale/126+offset;
2302 rc[1] = double(Col()[1])*scale/126+offset;
2303 rc[2] = double(Col()[2])*scale/126+offset;
2304 return rc;
2305 }
2306 };
2307
2308 multimap<Time, Entry> culmination;
2309 multimap<Time, Entry> lightcond;
2310 vector<vector<float>> alt;
2311 vector<vector<float>> cur;
2312
2313#ifdef HAVE_NOVA
2314 int ccol = 0;
2315 int lcol = 0;
2316
2317 /*const*/ pair<vector<float>, pair<Time, float>> vism = GetVisibility();
2318 if (!vism.first.empty())
2319 {
2320 const Entry entry("Moon", vism.second.second, ccol);
2321 culmination.insert(make_pair(vism.second.first, entry));
2322 const vector<float> col = entry.GetColor(75, 15);
2323 vism.first.insert(vism.first.begin(), col.begin(), col.end());
2324 alt.push_back(vism.first);
2325
2326 ccol++;
2327 }
2328#endif
2329
2330#ifdef HAVE_SQL
2331 try
2332 {
2333 const mysqlpp::StoreQueryResult res =
2334 Database(fDatabase).query("SELECT fSourceName, fRightAscension, fDeclination FROM Source WHERE fSourceTypeKEY=1").store();
2335
2336 out << HTML::kWhite << '\t';
2337 out2 << HTML::kWhite << '\t';
2338 out3 << HTML::kWhite << '\t';
2339 out4 << HTML::kWhite << '\t';
2340
2341 for (vector<mysqlpp::Row>::const_iterator v=res.begin(); v<res.end(); v++)
2342 {
2343 const string name = (*v)[0].c_str();
2344 const double ra = (*v)[1];
2345 const double dec = (*v)[2];
2346#ifdef HAVE_NOVA
2347 Nova::EquPosn pos;
2348 pos.ra = ra*15;
2349 pos.dec = dec;
2350
2351 const Nova::ZdAzPosn hrz = Nova::GetHrzFromEqu(pos, now.JD());
2352
2353 /*const*/ pair<vector<float>, pair<Time, float>> vis = GetVisibility(&pos);
2354 if (!vis.first.empty())
2355 {
2356 const Entry entry(name, vis.second.second, ccol);
2357 culmination.insert(make_pair(vis.second.first, entry));
2358 const vector<float> col = entry.GetColor(75, 15);
2359 vis.first.insert(vis.first.begin(), col.begin(), col.end());
2360 alt.push_back(vis.first);
2361
2362 ccol++;
2363
2364 /*const*/ pair<vector<float>, pair<Time, float>> lc = GetLightCondition(pos);
2365 if (!lc.first.empty())
2366 {
2367 const Entry entry2(name, lc.second.second, lcol);
2368 lightcond.insert(make_pair(lc.second.first, entry2));
2369 const vector<float> col2 = entry2.GetColor(100);
2370 lc.first.insert(lc.first.begin(), col2.begin(), col2.end());
2371 cur.push_back(lc.first);
2372
2373 lcol++;
2374 }
2375 }
2376
2377 string col = HTML::kWhite;
2378 if (hrz.zd<85)
2379 col = HTML::kRed;
2380 if (hrz.zd<65)
2381 col = HTML::kYellow;
2382 if (hrz.zd<30)
2383 col = HTML::kGreen;
2384
2385 out2 << "<tr bgcolor='" << col << "'>";
2386 out2 << "<td>" << name << "</td>";
2387 if (hrz.zd<85)
2388 {
2389 out2 << "<td>" << hrz.zd << "&deg;</td>";
2390 out2 << "<td>" << GetDir(hrz.az) << "</td>";
2391 }
2392 else
2393 out2 << "<td/><td/>";
2394 out2 << "</tr>";
2395#endif
2396 const int32_t angle = fMoon.Angle(ra, dec);
2397
2398 out << "<tr bgcolor='" << Moon::Color(angle) << "'>";
2399 out << "<td>" << name << "</td>";
2400 out << "<td>" << round(angle) << "&deg;</td>";
2401 out << "</tr>";
2402 }
2403
2404 for (auto it=culmination.begin(); it!=culmination.end(); it++)
2405 {
2406 const Entry &e = it->second;
2407 if (it!=culmination.begin())
2408 out3 << ", ";
2409 out3 << "<B#" << e.Col() << ">" << e.name << "</B>";
2410 if (e.value>0)
2411 out3 << " [" << nearbyint(90-e.value) << "&deg;]";
2412 }
2413
2414 out4 << setprecision(3);
2415
2416 for (auto it=lightcond.begin(); it!=lightcond.end(); it++)
2417 {
2418 const Entry &e = it->second;
2419 if (it!=lightcond.begin())
2420 out4 << ", ";
2421 out4 << "<B#" << e.Col() << ">" << e.name << "</B>";
2422 if (e.value>0)
2423 out4 << " [" << nearbyint(e.value) << "]";
2424 }
2425
2426 if (fSun.fSetAstronomical>fSun.fRiseAstronomical)
2427 fSun.fSetAstronomical += boost::posix_time::hours(24);
2428
2429 ostringstream title;
2430 title << fSun.fSetAstronomical.GetAsStr("%H:%M");
2431 title << " / ";
2432 title << ((fSun.fRiseAstronomical-fSun.fSetAstronomical)/20).minutes();
2433 title << "' / ";
2434 title << fSun.fRiseAstronomical.GetAsStr("%H:%M");
2435
2436 out << '\n';
2437 out2 << '\n';
2438 out3 << '\n';
2439 out4 << '\n';
2440 out << HTML::kWhite << '\t' << Time()-now << '\n';
2441 out2 << HTML::kWhite << '\t' << Time()-now << '\n';
2442
2443 WriteBinaryVec(now, "hist-visibility", alt, 75, 15, "Alt "+title.str());
2444 WriteBinaryVec(now, "hist-current-prediction", cur, 100, 0, "I " +title.str());
2445 }
2446 catch (const exception &e)
2447 {
2448 out << '\n';
2449 out2 << '\n';
2450 out << HTML::kWhite << '\t' << "ERROR - "+string(e.what()) << '\n';
2451 out2 << HTML::kWhite << '\t' << "ERROR - "+string(e.what()) << '\n';
2452 out3 << HTML::kWhite << '\t' << "ERROR - "+string(e.what()) << '\n';
2453 out4 << HTML::kWhite << '\t' << "ERROR - "+string(e.what()) << '\n';
2454 }
2455#endif
2456
2457 ofstream(fPath+"/moon.data") << out.str();
2458 ofstream(fPath+"/source-list.data") << out2.str();
2459 ofstream(fPath+"/visibility.data") << out3.str();
2460 ofstream(fPath+"/current-prediction.data") << out4.str();
2461 }
2462
2463 int Execute()
2464 {
2465 Time now;
2466 if (now-fLastUpdate<boost::posix_time::seconds(1))
2467 return fDimDNS.online() ? kStateRunning : kStateDimNetworkNA;
2468 fLastUpdate=now;
2469
2470 // ==============================================================
2471
2472 const bool data_taking =
2473 fDimMcp.state()==MCP::State::kTriggerOn ||
2474 fDimMcp.state()==MCP::State::kTakingData;
2475
2476 const bool data_run =
2477 fMcpConfigurationName=="data" ||
2478 fMcpConfigurationName=="data-rt";
2479
2480 const bool bias_on =
2481 fDimBiasControl.state()==BIAS::State::kRamping ||
2482 fDimBiasControl.state()==BIAS::State::kOverCurrent ||
2483 fDimBiasControl.state()==BIAS::State::kVoltageOn;
2484
2485 const bool calibrated =
2486 fDimFeedback.state()>=Feedback::State::kCalibrated;
2487
2488 const bool haderr = !fErrorList.empty();
2489
2490 bool newerr = false;
2491
2492 newerr |= SetError(!fDimDNS.online(),
2493 "<b><#darkred>DIM network not available</#></b>");
2494 newerr |= SetError(!fDimControl.online(),
2495 "<b>no dimctrl server available</b>");
2496 newerr |= SetError(fDimDataLogger.state()<20 || fDimDataLogger.state()>40,
2497 "<b>datalogger not ready</b>");
2498
2499 //newerr |= SetError(fDimDriveControl.state()==Drive::State::kLocked,
2500 // "<b><#darkred>Drive in LOCKED state, drive was automatically parked</#></b>");
2501
2502 newerr |= SetError(fDimDriveControl.state()>0xff && data_taking && data_run,
2503 "Drive in ERROR state during data-run");
2504 newerr |= SetError(fDriveControlMoonDist>155,
2505 "Moon within the field-of-view of the cones");
2506 newerr |= SetError(fDriveControlMoonDist>=0 && fDriveControlMoonDist<3,
2507 "Moon within the field-of-view of the camera");
2508
2509 newerr |= SetError(fDimBiasControl.state()<BIAS::State::kRamping && data_taking && data_run,
2510 "BIAS not operating during data-run");
2511 newerr |= SetError(fDimBiasControl.state()==BIAS::State::kOverCurrent,
2512 "BIAS channels in OverCurrent");
2513 newerr |= SetError(fDimBiasControl.state()==BIAS::State::kNotReferenced,
2514 "BIAS voltage not at reference");
2515
2516
2517 newerr |= SetError(bias_on && calibrated && fBiasControlCurrentMed>90,
2518 "Median current exceeds 90&micro;A/pix");
2519 newerr |= SetError(bias_on && calibrated && fBiasControlCurrentMax>110,
2520 "Maximum current exceeds 110&micro;A/pix");
2521
2522 newerr |= SetError(fFscControlHumidityAvg>60,
2523 "Average camera humidity exceed 60%");
2524
2525 newerr |= SetError(!fMagicWeatherHist[kHum].empty() && fMagicWeatherHist[kHum].back()>98 && fDimLidControl.state()==Lid::State::kOpen,
2526 "Outside humidity exceeds 98% while lid is open");
2527 newerr |= SetError(!fMagicWeatherHist[kGusts].empty() && fMagicWeatherHist[kGusts].back()>50 && (fDimDriveControl.state()==Drive::State::kTracking||fDimDriveControl.state()==Drive::State::kOnTrack),
2528 "Wind gusts exceed 50km/h during tracking");
2529
2530 newerr |= SetError(fDimFscControl.state()>=FSC::State::kConnected && !fFscControlTemperatureHist.empty() && fFscControlTemperatureHist.back()>15,
2531 "Sensor temperature exceeds outside temperature by more than 15&deg;C");
2532
2533 newerr |= SetError(fFtmControlTriggerRateTooLow>0,
2534 "Trigger rate below 1Hz while trigger switched on");
2535
2536 newerr |= SetError(fFtmControlState!=FTM::kFtmConfig && (fFtmControlState&FTM::kFtmLocked)==0,
2537 "FTM - clock conditioner not locked!");
2538
2539 newerr |= SetError(fDimTimeCheck.state()==1,
2540 "Warning NTP time difference of drive PC exceeds 1s");
2541 newerr |= SetError(fDimTimeCheck.state()<1,
2542 "Warning timecheck not running");
2543
2544 newerr |= SetError(fDimBiasControl.state()==BIAS::State::kVoltageOn &&
2545 fDimFeedback.state()<Feedback::State::kCalibrating &&
2546 fBiasControlVoltageMed>3,
2547 "Bias voltage switched on, but bias crate not calibrated");
2548
2549 newerr |= SetError(fLastRunFinishedWithZeroEvents,
2550 "Last run finshed, but contained zero events.");
2551
2552 newerr |= SetError(fFreeSpace<50000000000,
2553 "Less than 50GB disk space left.");
2554
2555 newerr |= SetError(fDimPwrControl.state()==Power::State::kCoolingFailure,
2556 "Cooling unit reports failure!");
2557
2558 for (auto it=fControlAlarmHist.begin(); it!=fControlAlarmHist.end(); it++)
2559 newerr |= SetError(it->time.IsValid(), it->msg);
2560 fControlAlarmHist.clean();;
2561
2562 fLastRunFinishedWithZeroEvents = false;
2563
2564 // FTM in Connected instead of Idle --> power cyclen
2565
2566 /* // Check offline and disconnected status?
2567 Out() << fDimMcp << endl;
2568 Out() << fDimControl << endl;
2569 Out() << fDimDataLogger << endl;
2570 Out() << fDimDriveControl << endl;
2571 Out() << fDimFadControl << endl;
2572 Out() << fDimFtmControl << endl;
2573 Out() << fDimBiasControl << endl;
2574 Out() << fDimFeedback << endl;
2575 Out() << fDimRateControl << endl;
2576 Out() << fDimFscControl << endl;
2577 Out() << fDimMagicWeather << endl;
2578 Out() << fDimRateScan << endl;
2579 Out() << fDimChat << endl;
2580 */
2581
2582 // FTU in error
2583 // FAD lost
2584
2585 // --------------------------------------------------------------
2586 ostringstream out;
2587
2588 if (newerr)
2589 {
2590 SetAudio("error");
2591
2592 out << now.JavaDate() << '\n';
2593 out << HTML::kWhite << '\t';
2594 out << "<->" << fErrorHist.rget() << "<->";
2595 out << '\n';
2596
2597 ofstream(fPath+"/errorhist.data") << out.str();
2598 }
2599
2600 out.str("");
2601 out << Header(now) << '\t' << (!fErrorList.empty()) << '\t' << (fDimControl.state()>0) << '\n';
2602 out << setprecision(3);
2603 out << HTML::kWhite << '\t';
2604 for (auto it=fErrorList.begin(); it!=fErrorList.end(); it++)
2605 out << *it << "<br/>";
2606 out << '\n';
2607
2608 if (haderr || !fErrorList.empty())
2609 ofstream(fPath+"/error.data") << out.str();
2610
2611 // ==============================================================
2612
2613 out.str("");
2614 out << Header(now) << '\t' << (!fErrorList.empty()) << '\t' << (fDimControl.state()>0) << '\n';
2615 out << setprecision(3);
2616
2617 // -------------- System status --------------
2618 if (fDimDNS.online() && fDimMcp.state()>=MCP::State::kIdle) // Idle
2619 {
2620 string col = HTML::kBlue;
2621 switch (fMcpConfigurationState)
2622 {
2623 case MCP::State::kIdle:
2624 case DimState::kOffline:
2625 col = HTML::kWhite;
2626 break;
2627 case MCP::State::kConfiguring1:
2628 case MCP::State::kConfiguring2:
2629 case MCP::State::kConfiguring3:
2630 case MCP::State::kConfigured:
2631 case MCP::State::kTriggerOn:
2632 col = HTML::kBlue;
2633 break;
2634 case MCP::State::kTakingData:
2635 col = HTML::kBlue;
2636 if (fDimFadControl.state()==FAD::State::kRunInProgress)
2637 col = HTML::kGreen;
2638 break;
2639 }
2640
2641 const bool other =
2642 fDimRateControl.state()==RateControl::State::kSettingGlobalThreshold ||
2643 fDimLidControl.state()==Lid::State::kMoving ||
2644 fDimRateScan.state()==RateScan::State::kInProgress;
2645
2646 if (other)
2647 col = HTML::kBlue;
2648
2649 out << col << '\t';
2650
2651 if (!other)
2652 {
2653 const string conf = fMcpConfigurationName.length()>0?" ["+fMcpConfigurationName+"]":"";
2654 switch (fMcpConfigurationState)
2655 {
2656 case MCP::State::kIdle:
2657 out << "Idle" << conf;
2658 break;
2659 case MCP::State::kConfiguring1:
2660 case MCP::State::kConfiguring2:
2661 case MCP::State::kConfiguring3:
2662 out << "Configuring" << conf;
2663 break;
2664 case MCP::State::kConfigured:
2665 out << "Configured" << conf;
2666 break;
2667 case MCP::State::kTriggerOn:
2668 case MCP::State::kTakingData:
2669 out << fMcpConfigurationName;
2670 if (fFadControlDrsRuns[2]>0)
2671 out << "(" << fFadControlDrsRuns[2] << ")";
2672 break;
2673 }
2674 }
2675 else
2676 if (fDimRateControl.state()==RateControl::State::kSettingGlobalThreshold)
2677 out << "Calibrating threshold";
2678 else
2679 if (fDimRateScan.state()==RateScan::State::kInProgress)
2680 out << "Rate scan in progress";
2681 else
2682 if (fDimLidControl.state()==Lid::State::kMoving)
2683 out << "Lid moving";
2684
2685
2686 if (fMcpConfigurationState>MCP::State::kConfigured &&
2687 fDimRateControl.state()!=RateControl::State::kSettingGlobalThreshold)
2688 {
2689 ostringstream evt;
2690 if (fMcpConfigurationMaxEvents>0)
2691 {
2692 const int64_t de = int64_t(fMcpConfigurationMaxEvents) - int64_t(fFadControlNumEvents);
2693 if (de>=0 && fMcpConfigurationState==MCP::State::kTakingData)
2694 evt << de;
2695 else
2696 evt << fMcpConfigurationMaxEvents;
2697 }
2698 else
2699 {
2700 if (fMcpConfigurationState==MCP::State::kTakingData)
2701 {
2702 if (fFadControlNumEvents>2999)
2703 evt << floor(fFadControlNumEvents/1000) << 'k';
2704 else
2705 evt << fFadControlNumEvents;
2706 }
2707 }
2708
2709 ostringstream tim;
2710 if (fMcpConfigurationMaxTime>0)
2711 {
2712 const uint32_t dt = (Time()-fMcpConfigurationRunStart).total_seconds();
2713 if (dt<=fMcpConfigurationMaxTime && fMcpConfigurationState==MCP::State::kTakingData)
2714 tim << fMcpConfigurationMaxTime-dt << 's';
2715 else
2716 tim << fMcpConfigurationMaxTime << 's';
2717 }
2718 else
2719 {
2720 if (fMcpConfigurationState==MCP::State::kTakingData)
2721 tim << fMcpConfigurationRunStart.SecondsTo();
2722 }
2723
2724 const bool has_evt = !evt.str().empty();
2725 const bool has_tim = !tim.str().empty();
2726
2727 if (has_evt || has_tim)
2728 out << " [";
2729 out << evt.str();
2730 if (has_evt && has_tim)
2731 out << '/';
2732 out << tim.str();
2733 if (has_evt || has_tim)
2734 out << ']';
2735 }
2736 }
2737 else
2738 out << HTML::kWhite;
2739 out << '\n';
2740
2741 // ------------------ Drive -----------------
2742 if (fDimDNS.online() && fDimDriveControl.state()>=Drive::State::kArmed) // Armed, Moving, Tracking, OnTrack, Error
2743 {
2744 const uint32_t dev = !fDriveControlTrackingDevHist.empty() ? round(fDriveControlTrackingDevHist.back()) : 0;
2745 const State rc = fDimDriveControl.description();
2746 string col = HTML::kGreen;
2747 if (fDimDriveControl.state()==Drive::State::kMoving) // Moving
2748 col = HTML::kBlue;
2749 if (fDimDriveControl.state()==Drive::State::kArmed) // Armed
2750 col = HTML::kWhite;
2751 if (fDimDriveControl.state()==Drive::State::kTracking || fDimDriveControl.state()==Drive::State::kOnTrack) // Tracking
2752 {
2753 if (dev>60) // ~1.5mm
2754 col = HTML::kYellow;
2755 if (dev>120) // ~1/4 of a pixel ~ 2.5mm
2756 col = HTML::kRed;
2757 }
2758 if (fDimDriveControl.state()==0x100)
2759 col = HTML::kRed;
2760 out << col << '\t';
2761
2762 //out << rc.name << '\t';
2763 out << fDriveControlPointingAz << ' ';
2764 out << fDriveControlPointingZd << "&deg;";
2765 out << setprecision(2);
2766 if (fDimDriveControl.state()==Drive::State::kTracking || fDimDriveControl.state()==Drive::State::kOnTrack)
2767 {
2768 out << " &plusmn; " << dev << '"';
2769 if (!fDriveControlSourceName.empty())
2770 out << " [" << fDriveControlSourceName << ']';
2771 }
2772 if (fDimDriveControl.state()==Drive::State::kMoving)
2773 out << " &#10227;";
2774 out << setprecision(3);
2775 }
2776 else
2777 out << HTML::kWhite << '\t';
2778
2779 if (fSun.time.IsValid() && fMoon.time.IsValid())
2780 {
2781 if (fSun.visible)
2782 {
2783 out << " &#9788;";
2784 if (fDimDriveControl.state()<Drive::State::kArmed)
2785 out << " [" << fSun.fSetCivil.MinutesTo() << "&darr;]";
2786 }
2787 else
2788 if (!fSun.visible && fMoon.visible)
2789 {
2790 out << " &#9790;";
2791 if (fDimDriveControl.state()<Drive::State::kArmed)
2792 out << " [" << fMoon.disk << "%]";
2793 }
2794 }
2795 if (fDimDNS.online() && fDimDriveControl.state()==0x100)
2796 out << " <ERR>";
2797 if (fDimDNS.online() && fDimDriveControl.state()==Drive::State::kLocked)
2798 out << " &otimes;";
2799 out << '\n';
2800
2801 // ------------------- FSC ------------------
2802 if (fDimDNS.online() && fDimFscControl.state()>FSC::State::kDisconnected && !fFscControlTemperatureHist.empty())
2803 {
2804 string col = HTML::kGreen;
2805 if (fFscControlTemperatureHist.back()>9)
2806 col = HTML::kYellow;
2807 if (fFscControlTemperatureHist.back()>15)
2808 col = HTML::kRed;
2809
2810 out << col << '\t' << fFscControlTemperatureHist.back() << '\n';
2811 }
2812 else
2813 out << HTML::kWhite << '\n';
2814
2815 // --------------- MagicWeather -------------
2816 if (fDimDNS.online() && fDimMagicWeather.state()==MagicWeather::State::kReceiving && !fMagicWeatherHist[kWeatherBegin].empty())
2817 {
2818 /*
2819 const float diff = fMagicWeatherHist[kTemp].back()-fMagicWeatherHist[kDew].back();
2820 string col1 = HTML::kRed;
2821 if (diff>0.3)
2822 col1 = HTML::kYellow;
2823 if (diff>0.7)
2824 col1 = HTML::kGreen;
2825 */
2826
2827 const float wind = fMagicWeatherHist[kGusts].back();
2828 const float hum = fMagicWeatherHist[kHum].back();
2829 string col = HTML::kGreen;
2830 if (wind>35 || hum>95)
2831 col = HTML::kYellow;
2832 if (wind>45 || hum>98)
2833 col = HTML::kRed;
2834
2835 out << col << '\t';
2836 out << fMagicWeatherHist[kHum].back() << '\t';
2837 out << setprecision(2);
2838 out << fMagicWeatherHist[kGusts].back() << '\n';
2839 out << setprecision(3);
2840 }
2841 else
2842 out << HTML::kWhite << "\n";
2843
2844 // --------------- FtmControl -------------
2845 if (fDimDNS.online() && fDimFtmControl.state()==FTM::State::kTriggerOn)
2846 {
2847 string col = HTML::kGreen;
2848 if (!fFtmControlTriggerRateHist.empty())
2849 {
2850 if (fFtmControlTriggerRateHist.back()<15)
2851 col = HTML::kYellow;
2852 if (fFtmControlTriggerRateHist.back()>100)
2853 col = HTML::kRed;
2854
2855 out << col << '\t' << fFtmControlTriggerRateHist.back() << " Hz";
2856 }
2857
2858 if (bias_on)
2859 out << " (" << setprecision(4) << fFtmPatchThresholdMed << ')';
2860 out << '\n';
2861 }
2862 else
2863 out << HTML::kWhite << '\n';
2864
2865 // --------------- BiasControl -------------
2866 const bool bias_off = fDimBiasControl.state()==BIAS::State::kVoltageOff;
2867 const bool bias_oc = fDimBiasControl.state()==BIAS::State::kOverCurrent;
2868
2869 if (fDimDNS.online() && (bias_on || bias_off))
2870 {
2871
2872 string col = fBiasControlVoltageMed>3?HTML::kGreen:HTML::kWhite;
2873 if (bias_on)
2874 {
2875 if (fBiasControlCurrentMed>70 || fBiasControlCurrentMax>90)
2876 col = HTML::kYellow;
2877 if (fBiasControlCurrentMed>90 || fBiasControlCurrentMax>110)
2878 col = HTML::kRed;
2879 }
2880
2881 // Bias in overcurrent => Red
2882 if (bias_oc)
2883 col = HTML::kRed;
2884
2885 // MCP in ReadyForDatataking/Configuring/Configured/TriggerOn/TakingData
2886 // and Bias not in "data-taking state' => Red
2887 if (fMcpConfigurationState>MCP::State::kIdle && !bias_on)
2888 col = HTML::kWhite;
2889
2890 const bool cal = fDimFeedback.state()>=Feedback::State::kCalibrated;
2891
2892 // Feedback is currently calibrating => Blue
2893 if (fDimFeedback.state()==Feedback::State::kCalibrating)
2894 {
2895 out << HTML::kBlue << '\t';
2896 out << "***\t";
2897 out << "***\t";
2898 }
2899 else
2900 {
2901 out << col << '\t';
2902 out << setprecision(fBiasControlCurrentMed<100?2:3);
2903 out << (bias_off ? 0 : (fBiasControlCurrentMed<10?fBiasControlCurrentMed:floor(fBiasControlCurrentMed))) << '\t';
2904 if (bias_oc)
2905 out << "(OC) ";
2906 else
2907 {
2908 if (cal)
2909 {
2910 out << setprecision(fBiasControlCurrentMax<100?2:3);
2911 out << (bias_off ? 0 : (fBiasControlCurrentMax<10?fBiasControlCurrentMax:floor(fBiasControlCurrentMax)));
2912 }
2913 else
2914 out << "&mdash; ";
2915 }
2916 out << '\t';
2917 }
2918 if (cal && fDimFeedback.state()!=Feedback::State::kCalibrating)
2919 out << setprecision(2) << fBiasControlPowerTot << " W";
2920 else
2921 out << setprecision(3) << (bias_off ? 0 : fBiasControlVoltageMed) << " V";
2922 out << '\n';
2923 }
2924 else
2925 out << HTML::kWhite << '\n';
2926
2927 ofstream(fPath+"/fact.data") << out.str();
2928
2929 // ==============================================================
2930
2931 out.str("");
2932 out << Header(now) << '\t' << (!fErrorList.empty()) << '\t' << (fDimControl.state()>0) << '\n';
2933
2934 if (!fDimDNS.online())
2935 out << HTML::kWhite << "\tOffline\n\n\n\n\n\n\n\n\n\n\n\n\n";
2936 else
2937 {
2938 ostringstream dt;
2939 dt << (Time()-fRunTime);
2940
2941 out << HTML::kGreen << '\t' << fDimDNS.version() << '\n';
2942
2943 out << GetStateHtml(fDimControl, 0);
2944 out << GetStateHtml(fDimMcp, MCP::State::kConnected);
2945 out << GetStateHtml(fDimDataLogger, 1);
2946 out << GetStateHtml(fDimDriveControl, Drive::State::kConnected);
2947 out << GetStateHtml(fDimTimeCheck, 1);
2948 out << GetStateHtml(fDimFadControl, FAD::State::kConnected);
2949 out << GetStateHtml(fDimFtmControl, FTM::State::kConnected);
2950 out << GetStateHtml(fDimBiasControl, BIAS::State::kConnected);
2951 out << GetStateHtml(fDimFeedback, Feedback::State::kConnected);
2952 out << GetStateHtml(fDimRateControl, RateControl::State::kConnected);
2953 out << GetStateHtml(fDimFscControl, FSC::State::kConnected);
2954 out << GetStateHtml(fDimGpsControl, GPS::State::kConnected);
2955 out << GetStateHtml(fDimSqmControl, SQM::State::kConnected);
2956 out << GetStateHtml(fDimAgilentControl24, Agilent::State::kVoltageOff);
2957 out << GetStateHtml(fDimAgilentControl50, Agilent::State::kVoltageOff);
2958 out << GetStateHtml(fDimAgilentControl80, Agilent::State::kVoltageOff);
2959 out << GetStateHtml(fDimPwrControl, Power::State::kSystemOff);
2960 out << GetStateHtml(fDimLidControl, Lid::State::kConnected);
2961 out << GetStateHtml(fDimRateScan, RateScan::State::kConnected);
2962 out << GetStateHtml(fDimMagicWeather, MagicWeather::State::kConnected);
2963 out << GetStateHtml(fDimTngWeather, TNGWeather::State::kConnected);
2964 out << GetStateHtml(fDimMagicLidar, MagicLidar::State::kConnected);
2965 out << GetStateHtml(fDimTemperature, Temperature::State::kValid);
2966 out << GetStateHtml(fDimChat, 0);
2967 out << GetStateHtml(fDimSkypeClient, 1);
2968
2969 string col = HTML::kRed;
2970 if (fFreeSpace>uint64_t(199999999999))
2971 col = HTML::kYellow;
2972 if (fFreeSpace>uint64_t(999999999999))
2973 col = HTML::kGreen;
2974 if (fFreeSpace==UINT64_MAX)
2975 col = HTML::kWhite;
2976
2977 out << col << '\t' << Tools::Scientific(fFreeSpace) << "B\n";
2978
2979 out << HTML::kGreen << '\t' << dt.str().substr(0, dt.str().length()-7) << '\n';
2980 }
2981
2982 ofstream(fPath+"/status.data") << out.str();
2983
2984 if (now-fLastAstroCalc>boost::posix_time::seconds(15))
2985 {
2986 UpdateAstronomy();
2987 fLastAstroCalc = now;
2988 }
2989
2990 return fDimDNS.online() ? kStateRunning : kStateDimNetworkNA;
2991 }
2992
2993
2994public:
2995 StateMachineSmartFACT(ostream &out=cout) : StateMachineDim(out, fIsServer?"SMART_FACT":""),
2996 fLastAstroCalc(boost::date_time::neg_infin),
2997 fPath("www/smartfact/data"),
2998 fControlScriptDepth(0),
2999 fMcpConfigurationState(DimState::kOffline),
3000 fMcpConfigurationMaxTime(0),
3001 fMcpConfigurationMaxEvents(0),
3002 fLastRunFinishedWithZeroEvents(false),
3003 fTngWeatherDustTime(Time::none),
3004 fBiasControlVoltageMed(0),
3005 fBiasControlCurrentMed(0),
3006 fBiasControlCurrentMax(0),
3007 fFscControlHumidityAvg(0),
3008 fDriveControlMoonDist(-1),
3009 fFadControlNumEvents(0),
3010 fFadControlDrsRuns(3),
3011 fFtmControlState(FTM::kFtmLocked),
3012 fRateScanDataId(0),
3013 fRateScanBoard(0),
3014 fFreeSpace(UINT64_MAX),
3015 // ---
3016 fDimMcp ("MCP"),
3017 fDimDataLogger ("DATA_LOGGER"),
3018 fDimDriveControl ("DRIVE_CONTROL"),
3019 fDimTimeCheck ("TIME_CHECK"),
3020 fDimMagicWeather ("MAGIC_WEATHER"),
3021 fDimMagicLidar ("MAGIC_LIDAR"),
3022 fDimTngWeather ("TNG_WEATHER"),
3023 fDimTemperature ("TEMPERATURE"),
3024 fDimFeedback ("FEEDBACK"),
3025 fDimBiasControl ("BIAS_CONTROL"),
3026 fDimFtmControl ("FTM_CONTROL"),
3027 fDimFadControl ("FAD_CONTROL"),
3028 fDimFscControl ("FSC_CONTROL"),
3029 fDimGpsControl ("GPS_CONTROL"),
3030 fDimSqmControl ("SQM_CONTROL"),
3031 fDimAgilentControl24("AGILENT_CONTROL_24V"),
3032 fDimAgilentControl50("AGILENT_CONTROL_50V"),
3033 fDimAgilentControl80("AGILENT_CONTROL_80V"),
3034 fDimPwrControl ("PWR_CONTROL"),
3035 fDimLidControl ("LID_CONTROL"),
3036 fDimRateControl ("RATE_CONTROL"),
3037 fDimRateScan ("RATE_SCAN"),
3038 fDimChat ("CHAT"),
3039 fDimSkypeClient ("SKYPE_CLIENT")
3040 {
3041 fDimDNS.Subscribe(*this);
3042 fDimControl.Subscribe(*this);
3043 fDimMcp.Subscribe(*this);
3044 fDimDataLogger.Subscribe(*this);
3045 fDimDriveControl.Subscribe(*this);
3046 fDimTimeCheck.Subscribe(*this);
3047 fDimMagicWeather.Subscribe(*this);
3048 fDimMagicLidar.Subscribe(*this);
3049 fDimTngWeather.Subscribe(*this);
3050 fDimTemperature.Subscribe(*this);
3051 fDimFeedback.Subscribe(*this);
3052 fDimBiasControl.Subscribe(*this);
3053 fDimFtmControl.Subscribe(*this);
3054 fDimFadControl.Subscribe(*this);
3055 fDimFscControl.Subscribe(*this);
3056 fDimGpsControl.Subscribe(*this);
3057 fDimSqmControl.Subscribe(*this);
3058 fDimAgilentControl24.Subscribe(*this);
3059 fDimAgilentControl50.Subscribe(*this);
3060 fDimAgilentControl80.Subscribe(*this);
3061 fDimPwrControl.Subscribe(*this);
3062 fDimLidControl.Subscribe(*this);
3063 fDimRateControl.Subscribe(*this);
3064 fDimRateScan.Subscribe(*this);
3065 fDimChat.Subscribe(*this);
3066 fDimSkypeClient.Subscribe(*this);
3067
3068 fDimFscControl.SetCallback(bind(&StateMachineSmartFACT::HandleFscControlStateChange, this, placeholders::_1));
3069 //fDimFtmControl.SetCallback(bind(&StateMachineSmartFACT::HandleFtmControlStateChange, this));
3070 fDimDriveControl.SetCallback(bind(&StateMachineSmartFACT::HandleDriveControlStateChange, this, placeholders::_1));
3071 fDimControl.SetCallback(bind(&StateMachineSmartFACT::HandleControlStateChange, this, placeholders::_1));
3072 fDimControl.AddCallback("dotest.dim", bind(&StateMachineSmartFACT::HandleDoTest, this, placeholders::_1));
3073
3074 Subscribe("DIM_CONTROL/MESSAGE")
3075 (bind(&StateMachineSmartFACT::HandleDimControlMessage, this, placeholders::_1));
3076
3077 Subscribe("MCP/CONFIGURATION")
3078 (bind(&StateMachineSmartFACT::HandleMcpConfiguration, this, placeholders::_1));
3079
3080 Subscribe("DRIVE_CONTROL/POINTING_POSITION")
3081 (bind(&StateMachineSmartFACT::HandleDrivePointing, this, placeholders::_1));
3082 Subscribe("DRIVE_CONTROL/TRACKING_POSITION")
3083 (bind(&StateMachineSmartFACT::HandleDriveTracking, this, placeholders::_1));
3084 Subscribe("DRIVE_CONTROL/SOURCE_POSITION")
3085 (bind(&StateMachineSmartFACT::HandleDriveSource, this, placeholders::_1));
3086
3087 Subscribe("FSC_CONTROL/TEMPERATURE")
3088 (bind(&StateMachineSmartFACT::HandleFscTemperature, this, placeholders::_1));
3089 Subscribe("FSC_CONTROL/HUMIDITY")
3090 (bind(&StateMachineSmartFACT::HandleFscHumidity, this, placeholders::_1));
3091 Subscribe("FSC_CONTROL/BIAS_TEMP")
3092 (bind(&StateMachineSmartFACT::HandleFscBiasTemp, this, placeholders::_1));
3093
3094 Subscribe("GPS_CONTROL/NEMA")
3095 (bind(&StateMachineSmartFACT::HandleGpsNema, this, placeholders::_1));
3096
3097 Subscribe("SQM_CONTROL/DATA")
3098 (bind(&StateMachineSmartFACT::HandleSqmData, this, placeholders::_1));
3099
3100 Subscribe("TEMPERATURE/DATA")
3101 (bind(&StateMachineSmartFACT::HandleTemperatureData, this, placeholders::_1));
3102
3103 Subscribe("AGILENT_CONTROL_24V/DATA")
3104 (bind(&StateMachineSmartFACT::HandleAgilentData, this, placeholders::_1, "24"));
3105 Subscribe("AGILENT_CONTROL_50V/DATA")
3106 (bind(&StateMachineSmartFACT::HandleAgilentData, this, placeholders::_1, "50"));
3107 Subscribe("AGILENT_CONTROL_80V/DATA")
3108 (bind(&StateMachineSmartFACT::HandleAgilentData, this, placeholders::_1, "80"));
3109
3110 Subscribe("MAGIC_WEATHER/DATA")
3111 (bind(&StateMachineSmartFACT::HandleMagicWeatherData, this, placeholders::_1));
3112 Subscribe("TNG_WEATHER/DUST")
3113 (bind(&StateMachineSmartFACT::HandleTngWeatherDust, this, placeholders::_1));
3114
3115 Subscribe("FEEDBACK/CALIBRATED_CURRENTS")
3116 (bind(&StateMachineSmartFACT::HandleFeedbackCalibratedCurrents, this, placeholders::_1));
3117
3118 Subscribe("BIAS_CONTROL/VOLTAGE")
3119 (bind(&StateMachineSmartFACT::HandleBiasVoltage, this, placeholders::_1));
3120 Subscribe("BIAS_CONTROL/CURRENT")
3121 (bind(&StateMachineSmartFACT::HandleBiasCurrent, this, placeholders::_1));
3122
3123 Subscribe("FAD_CONTROL/CONNECTIONS")
3124 (bind(&StateMachineSmartFACT::HandleFadConnections, this, placeholders::_1));
3125 Subscribe("FAD_CONTROL/EVENTS")
3126 (bind(&StateMachineSmartFACT::HandleFadEvents, this, placeholders::_1));
3127 Subscribe("FAD_CONTROL/START_RUN")
3128 (bind(&StateMachineSmartFACT::HandleFadStartRun, this, placeholders::_1));
3129 Subscribe("FAD_CONTROL/DRS_RUNS")
3130 (bind(&StateMachineSmartFACT::HandleFadDrsRuns, this, placeholders::_1));
3131 Subscribe("FAD_CONTROL/EVENT_DATA")
3132 (bind(&StateMachineSmartFACT::HandleFadEventData, this, placeholders::_1));
3133 Subscribe("FAD_CONTROL/STATS")
3134 (bind(&StateMachineSmartFACT::HandleStats, this, placeholders::_1));
3135
3136 Subscribe("DATA_LOGGER/STATS")
3137 (bind(&StateMachineSmartFACT::HandleStats, this, placeholders::_1));
3138
3139 Subscribe("FTM_CONTROL/TRIGGER_RATES")
3140 (bind(&StateMachineSmartFACT::HandleFtmTriggerRates, this, placeholders::_1));
3141 Subscribe("FTM_CONTROL/STATIC_DATA")
3142 (bind(&StateMachineSmartFACT::HandleFtmStaticData, this, placeholders::_1));
3143 Subscribe("FTM_CONTROL/FTU_LIST")
3144 (bind(&StateMachineSmartFACT::HandleFtmFtuList, this, placeholders::_1));
3145
3146 Subscribe("RATE_CONTROL/THRESHOLD")
3147 (bind(&StateMachineSmartFACT::HandleRateControlThreshold,this, placeholders::_1));
3148
3149 Subscribe("RATE_SCAN/DATA")
3150 (bind(&StateMachineSmartFACT::HandleRateScanData, this, placeholders::_1));
3151
3152 Subscribe("CHAT/MESSAGE")
3153 (bind(&StateMachineSmartFACT::HandleChatMsg, this, placeholders::_1));
3154
3155
3156 // =================================================================
3157
3158 // State names
3159 AddStateName(kStateDimNetworkNA, "DimNetworkNotAvailable",
3160 "The Dim DNS is not reachable.");
3161
3162 AddStateName(kStateRunning, "Running", "");
3163
3164 // =================================================================
3165
3166 AddEvent("PRINT")
3167 (bind(&StateMachineSmartFACT::Print, this))
3168 ("Print a list of the states of all connected servers.");
3169
3170 }
3171 int EvalOptions(Configuration &conf)
3172 {
3173 if (!fPixelMap.Read(conf.Get<string>("pixel-map-file")))
3174 {
3175 Error("Reading mapping table from "+conf.Get<string>("pixel-map-file")+" failed.");
3176 return 1;
3177 }
3178
3179 fPath = conf.Get<string>("path");
3180 fDatabase = conf.Get<string>("source-database");
3181
3182 struct stat st;
3183 if (stat(fPath.c_str(), &st))
3184 {
3185 Error(fPath+" does not exist!");
3186 return 2;
3187 }
3188
3189 if ((st.st_mode&S_IFDIR)==0)
3190 {
3191 Error(fPath+" not a directory!");
3192 return 3;
3193 }
3194
3195 if ((st.st_mode&S_IWUSR)==0)
3196 {
3197 Error(fPath+" has no write permission!");
3198 return 4;
3199 }
3200
3201 if ((st.st_mode&S_IXUSR)==0)
3202 {
3203 Error(fPath+" has no execute permission!");
3204 return 5;
3205 }
3206
3207 ostringstream out;
3208 out << Time().JavaDate() << '\n';
3209
3210 ofstream(fPath+"/error.data") << out.str();
3211
3212 return -1;
3213 }
3214};
3215
3216bool StateMachineSmartFACT::fIsServer = false;
3217
3218// ------------------------------------------------------------------------
3219
3220#include "Main.h"
3221
3222template<class T>
3223int RunShell(Configuration &conf)
3224{
3225 StateMachineSmartFACT::fIsServer = !conf.Get<bool>("client");
3226 return Main::execute<T, StateMachineSmartFACT>(conf);
3227}
3228
3229void SetupConfiguration(Configuration &conf)
3230{
3231 po::options_description control("Smart FACT");
3232 control.add_options()
3233 ("pixel-map-file", var<string>()->required(), "Pixel mapping file. Used here to get the default reference voltage")
3234 ("path", var<string>("www/smartfact/data"), "Output path for the data-files")
3235 ("source-database", var<string>(""), "Database link as in\n\tuser:password@server[:port]/database.")
3236 ("client", po_bool(false), "For a standalone client choose this option.")
3237 ;
3238
3239 conf.AddOptions(control);
3240}
3241
3242/*
3243 Extract usage clause(s) [if any] for SYNOPSIS.
3244 Translators: "Usage" and "or" here are patterns (regular expressions) which
3245 are used to match the usage synopsis in program output. An example from cp
3246 (GNU coreutils) which contains both strings:
3247 Usage: cp [OPTION]... [-T] SOURCE DEST
3248 or: cp [OPTION]... SOURCE... DIRECTORY
3249 or: cp [OPTION]... -t DIRECTORY SOURCE...
3250 */
3251void PrintUsage()
3252{
3253 cout <<
3254 "SmartFACT is a tool writing the files needed for the SmartFACT web interface.\n"
3255 "\n"
3256 "The default is that the program is started without user intercation. "
3257 "All actions are supposed to arrive as DimCommands. Using the -c "
3258 "option, a local shell can be initialized. With h or help a short "
3259 "help message about the usuage can be brought to the screen.\n"
3260 "\n"
3261 "Usage: smartfact [-c type] [OPTIONS]\n"
3262 " or: smartfact [OPTIONS]\n";
3263 cout << endl;
3264}
3265
3266void PrintHelp()
3267{
3268 Main::PrintHelp<StateMachineSmartFACT>();
3269
3270 /* Additional help text which is printed after the configuration
3271 options goes here */
3272
3273 /*
3274 cout << "bla bla bla" << endl << endl;
3275 cout << endl;
3276 cout << "Environment:" << endl;
3277 cout << "environment" << endl;
3278 cout << endl;
3279 cout << "Examples:" << endl;
3280 cout << "test exam" << endl;
3281 cout << endl;
3282 cout << "Files:" << endl;
3283 cout << "files" << endl;
3284 cout << endl;
3285 */
3286}
3287
3288int main(int argc, const char* argv[])
3289{
3290 Configuration conf(argv[0]);
3291 conf.SetPrintUsage(PrintUsage);
3292 Main::SetupConfiguration(conf);
3293 SetupConfiguration(conf);
3294
3295 if (!conf.DoParse(argc, argv, PrintHelp))
3296 return 127;
3297
3298 if (!conf.Has("console"))
3299 return RunShell<LocalStream>(conf);
3300
3301 if (conf.Get<int>("console")==0)
3302 return RunShell<LocalShell>(conf);
3303 else
3304 return RunShell<LocalConsole>(conf);
3305
3306 return 0;
3307}
Note: See TracBrowser for help on using the repository browser.