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

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