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

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