source: trunk/FACT++/src/scheduler.cc@ 20115

Last change on this file since 20115 was 19717, checked in by tbretz, 5 years ago
Typo
File size: 49.8 KB
Line 
1#include "Prediction.h"
2
3#include <boost/algorithm/string/join.hpp>
4
5#include "Database.h"
6
7#include "HeadersToO.h"
8#include "HeadersGCN.h"
9
10#include "EventImp.h"
11#include "LocalControl.h"
12#include "StateMachineDim.h"
13
14#include "Dim.h"
15#include "tools.h"
16#include "Time.h"
17#include "Configuration.h"
18
19using namespace std;
20using namespace Nova;
21
22// -----------------------------------------------------------------------
23
24enum VisibilityCriterion
25{
26 kMoonMin,
27 kMoonMax,
28 kSunMax,
29 kZenithMax,
30 kCurrentMax,
31 kThresholdMax,
32};
33
34struct VisibilityConditions : map<VisibilityCriterion, float>
35{
36 VisibilityConditions()
37 {
38 operator[](kMoonMin) = 10;
39 operator[](kMoonMax) = 170;
40 operator[](kSunMax) = -12;
41 operator[](kZenithMax) = 75;
42 operator[](kCurrentMax) = 110;
43 operator[](kThresholdMax) = 10;
44 }
45};
46
47struct CheckVisibility
48{
49 VisibilityConditions conditions;
50
51 // Output
52 Nova::SolarObjects solarobj;
53 Nova::ZdAzPosn position;
54 Nova::RstTime rst_sun;
55 Nova::RstTime rst_obj;
56
57 int vis_obj = -1;
58
59 double moon_dist = -1;
60 double current = -1;
61 double threshold = -1;
62
63 bool valid_zd = false;
64 bool valid_current = false;
65 bool valid_sun = false;
66 bool valid_moon = false;
67 bool valid_threshold = false;
68
69 bool visible = false; // And of all the above except valid_threshold
70
71 void calc(const Nova::EquPosn &equ, const double &jd)
72 {
73 solarobj = Nova::SolarObjects(jd);
74 position = Nova::GetHrzFromEqu(equ, jd);
75 moon_dist = Nova::GetAngularSeparation(equ, solarobj.fMoonEqu);
76 rst_sun = Nova::GetSolarRst(jd, -12);
77 vis_obj = Nova::GetObjectRst(rst_obj, equ, jd, 90-conditions[kZenithMax]);
78
79 current = FACT::PredictI(solarobj, equ);
80
81 const double ratio = pow(cos(position.zd*M_PI/180), -2.664);
82
83 threshold = position.zd<90 ? ratio*pow(current/6.2, 0.394) : -1;
84
85 valid_moon = moon_dist>conditions[kMoonMin] && moon_dist<conditions[kMoonMax];
86 valid_zd = position.zd<conditions[kZenithMax];
87 valid_sun = solarobj.fSunHrz.alt<conditions[kSunMax];
88 valid_current = current<conditions[kCurrentMax];
89 valid_threshold = threshold>0 && threshold<conditions[kThresholdMax];
90
91 visible = valid_moon && valid_zd && valid_sun && valid_current;
92 }
93
94 CheckVisibility(const VisibilityConditions &cond, const Nova::EquPosn &equ, const double &jd) : conditions(cond)
95 {
96 calc(equ, jd);
97 }
98
99 CheckVisibility(const VisibilityConditions &cond, const Nova::EquPosn &equ) : conditions(cond)
100 {
101 calc(equ, Time().JD());
102 }
103
104 CheckVisibility(const VisibilityConditions &cond, const double &ra, const double &dec, const double &jd): conditions(cond)
105 {
106 Nova::EquPosn equ;
107 equ.ra = ra;
108 equ.dec = dec;
109 calc(equ, jd);
110 }
111
112 CheckVisibility(const VisibilityConditions &cond, const double &ra, const double &dec) : conditions(cond)
113 {
114 Nova::EquPosn equ;
115 equ.ra = ra;
116 equ.dec = dec;
117 calc(equ, Time().JD());
118 }
119};
120
121/*
122
123 * Visibility check
124 * Source eintragen
125 * RELOAD SOURCES
126 * Schedule eintragen
127 * Send 'RESCHEDULE' interrupt
128
129*/
130/*
131int mainx(int argc, const char* argv[])
132{
133 Configuration conf(argv[0]);
134 conf.SetPrintUsage(PrintUsage);
135 SetupConfiguration(conf);
136
137 if (!conf.DoParse(argc, argv, PrintHelp))
138 return 127;
139
140 // ------------------ Eval config ---------------------
141
142 const string uri = conf.Get<string>("schedule-database");
143
144 const uint16_t verbose = 1;
145 const bool dry_run = true;
146
147 Time stopwatch;
148
149 Database connection(uri); // Keep alive while fetching rows
150
151 cout << Time()-stopwatch << endl;
152
153 if (verbose>0)
154 cout << "Server Version: " << connection.server_version() << endl;
155
156 const int source_key = 999; // source_key of the source to be scheduled.
157
158 const uint16_t contingency = 0; // The contingency to delete earlier entries from the Table
159 const uint16_t duration = 300; // The target duration of the ToO
160 const uint16_t required = 20; // If the ToO would be less than required, skip_it
161 const uint16_t skip_time = 20; // If following observation would be less than skip_time, skip it
162
163 if (duration<required)
164 {
165 cerr << "Requested duration is smaller than required time." << endl;
166 return 0;
167 }
168
169 Time day(2019, 1, 24, 17, 0, 0);
170 Time now(2019, 1, 24, 19, 59, 0);
171 Time next = day+boost::posix_time::hours(14);
172 Time restart = now+boost::posix_time::minutes(duration);
173
174 // Safety margin
175 now -= boost::posix_time::seconds(contingency);
176
177 cout << '\n';
178 cout << "Contingency: " << contingency << " s\n";
179 cout << "Start of ToO: " << now.GetAsStr() << '\n';
180 cout << "End of ToO: " << restart.GetAsStr() << '\n';
181 cout << "Duration: " << duration << " min\n\n";
182
183 cout << "Schedule:\n";
184
185 const string queryS =
186 "SELECT *,"
187 " (fMeasurementTypeKey=4 AND fStart<='"+restart.GetAsStr()+"') AS `Reschedule`,"
188 " (fStart>'"+restart.GetAsStr()+"') AS `Following`,"
189 " (fMeasurementTypeKey=4 AND fStart BETWEEN '"+now.GetAsStr()+"' AND '"+restart.GetAsStr()+"') AS `Delete`"
190 " FROM Schedule"
191 " WHERE fStart BETWEEN '"+day.GetAsStr()+"' AND '"+next.GetAsStr()+"'"
192 " AND fMeasurementID=0"
193 " ORDER BY fStart ASC, fMeasurementID ASC";
194
195 const mysqlpp::StoreQueryResult resS =
196 connection.query(queryS).store();
197
198 vector<string> list;
199
200 int32_t firstdata = -1;
201 int32_t reschedule = -1;
202 int32_t following = -1;
203 for (size_t i=0; i<resS.num_rows(); i++)
204 {
205 const mysqlpp::Row &row = resS[i];
206
207 cout << setw(2) << i << " | ";
208 cout << row["Reschedule"] << " | ";
209 cout << row["Following"] << " | ";
210 cout << row["fScheduleID"] << " | ";
211 cout << row["fStart"] << " | ";
212 if (bool(row["Delete"]))
213 {
214 cout << " < delete > | ";
215 list.push_back((string)row["fScheduleID"]);
216 }
217 else
218 cout << row["fLastUpdate"] << " | ";
219 cout << row["fMeasurementID"] << " | ";
220 cout << row["fUser"] << " | ";
221 cout << row["fData"] << " | " << setw(4);
222 cout << row["fSourceKey"] << " | ";
223 cout << row["fMeasurementTypeKey"] << '\n';
224
225 if (bool(row["Reschedule"]))
226 reschedule = i;
227
228 if (following==-1 && bool(row["Following"]))
229 following = i;
230
231 uint16_t type = row["fMeasurementTypeKey"];
232 if (firstdata==-1 && type==4)
233 firstdata = i;
234 }
235
236 // --------------------------------------------------------------------
237
238 if (firstdata==-1)
239 {
240 cerr << "No data run found." << endl;
241 return 0;
242 }
243
244 // --------------------------------------------------------------------
245
246 if (reschedule>=0)
247 cout << "\nLast data run before restart at " << restart.GetAsStr() << ": idx=" << reschedule << " / ID=" << resS[reschedule]["fScheduleID"] << "\n";
248
249 // --------------------------------------------------------------------
250
251 const Time first((string)resS[firstdata]["fStart"]); // First data run of the night
252 const Time last( (string)resS[resS.num_rows()-1]["fStart"]); // Last scheduled observation of the night
253
254 // --------------------------------------------------------------------
255
256 if (restart<=first)
257 {
258 cout << "ToO ends before first data taking... skipped." << endl;
259 return 0;
260 }
261 if (now>=last)
262 {
263 cout << "ToO starts after data taking... skipped." << endl;
264 return 0;
265 }
266
267 // --------------------------------------------------------------------
268
269 if (now<first)
270 {
271 cout << "ToO starts before first data taking... rescheduling start time." << endl;
272 now = first;
273 }
274 if (restart>last)
275 {
276 cout << "ToO ends after data taking... rescheduling end time!" << endl;
277 restart = last;
278 }
279
280 // --------------------------------------------------------------------
281
282 if (restart<now+boost::posix_time::minutes(required))
283 {
284 cout << "Can not schedule more than " << required << "minutes... skipped." << endl;
285 return 0;
286 }
287
288 // --------------------------------------------------------------------
289
290 if (following>=0)
291 {
292 const Time follow((string)resS[following]["fStart"]);
293 if (follow<restart+boost::posix_time::minutes(skip_time))
294 {
295 cout << "Following observation would be less than " << skip_time << " min... skipping rescheduled source." << endl;
296 reschedule = -1;
297 }
298 }
299
300 // ====================================================================
301
302 if (!list.empty())
303 {
304 cout << "\nDelete entries:\n";
305 for (const auto &l : list)
306 cout << l << "\n";
307 cout << endl;
308 }
309
310 // ====================================================================
311
312 vector<string> insert;
313
314 cout << "New entries:\n";
315 cout << " | auto | " << now.GetAsStr() << " | < start > | 0 | ToO | NULL | | 4\n";
316
317 insert.emplace_back("('"+now.GetAsStr()+"',0,'ToO',NULL,"+to_string(source_key)+",4)");
318
319 // --------------------------------------------------------------------
320
321 if (reschedule>=0 && restart!=last)
322 {
323 cout << " | auto | " << restart.GetAsStr() << " | < end > | 0 | ToO | NULL | " << setw(4) << resS[reschedule]["fSourceKey"] << " | 4\n";
324
325 const string fData = (string)resS[reschedule]["fData"];
326 const string fKey = (string)resS[reschedule]["fSourceKey"];
327
328 const string data = resS[reschedule]["fData"] ? "'"+fData+"'" : "NULL";
329 insert.emplace_back("('"+restart.GetAsStr()+"',0,'ToO',"+data+","+fKey+",4)");
330 }
331
332 // ====================================================================
333
334 cout << Time()-stopwatch << endl;
335
336 // ====================================================================
337
338 if (insert.empty())
339 {
340 cout << "No new schedule." << endl;
341 return 0;
342 }
343
344 const string queryD = string("DELETE FROM Schedule WHERE fScheduleID IN (")+boost::algorithm::join(list, ",")+")";
345
346 const string queryI =
347 "INSERT (fStart, fMeasurementID, fUser, fData, fSoureKey, fMeasurementTypeKey) "
348 "VALUES\n "+string(boost::algorithm::join(insert, ",\n "));
349
350 if (dry_run)
351 {
352 cout << "\n";
353 if (!list.empty())
354 cout << queryD << "\n\n";
355 if (!insert.empty())
356 cout << queryI << "\n\n";
357 cout << flush;
358 return 0;
359 }
360
361 // Empty interrupt to stop data taking as early as possible
362 Dim::SendCommandNB("DIM_CONTROL/INTERRUPT");
363
364 // Reload sources as early as possible
365 Dim::SendCommandNB("DRIVE_CONTROL/RELOAD_SOURCES");
366
367 // Start pointing procedure as early as possible
368 //Dim::SendCommand("DRIVE_CONTROL/TRACK_WOBBLE", wobble, obs[sub].source);
369
370 try
371 {
372 // ---------------------------------------------------------
373
374 connection.query("LOCK TABLES Schedule WRITE");
375
376 // ---------------------------------------------------------
377
378 if (!list.empty())
379 {
380 const mysqlpp::SimpleResult res = connection.query(queryD).execute();
381 cout << res.rows() << " row(s) deleted.\n" << endl;
382 }
383
384 // ---------------------------------------------------------
385
386 if (!insert.empty())
387 {
388 auto q = connection.query(queryI);
389 q.execute();
390 cout << q.info() << '\n' << endl;
391 }
392
393 // ---------------------------------------------------------
394
395 connection.query("UNLOCK TABLES");
396
397 // ---------------------------------------------------------
398 }
399 catch (const exception &e)
400 {
401 cerr << "SQL query failed: " << e.what() << endl;
402 return 7;
403 }
404
405 Dim::SendCommand("DIM_CONTROL/INTERRUPT", "reschedule");
406
407 return 0;
408}
409*/
410// ------------------------------------------------------------------------
411
412class StateMachineToO : public StateMachineDim
413{
414private:
415 map<uint16_t, VisibilityConditions> fVisibilityCriteria;
416
417 string fUri;
418 Database fConnection;
419 uint16_t fVerbose;
420
421 Time fKeepAliveDeadline;
422 uint16_t fKeepAliveInterval;
423
424 struct Source
425 {
426 int16_t key { -1 };
427 bool isToO { false };
428 bool satellite { false };
429 };
430
431 Source GetSourceKey(const string &name, const double &ra, const double &dec)
432 {
433 const double min_dist = 0.1;
434 const double check_dist = 2.0;
435
436 // ----------------------- Check if source with same name exists -----------------------
437
438 const string query2 =
439 "SELECT fSourceKey, fIsToO FROM Source WHERE fSourceName='"+name+"'";
440
441 const mysqlpp::StoreQueryResult res2 = fConnection.query(query2).store();
442
443 if (res2.num_rows())
444 {
445 Source source;
446 source.key = res2[0]["fSourceKey"];
447 source.isToO = res2[0]["fIsToO"];
448
449 Info("A source with the same name (key="+to_string(source.key)+") was found in the Source table.");
450
451 return source;
452 }
453
454 // ----------------- Check if source with similar coordinates exists -------------------
455
456 const string query1 =
457 "SELECT\n"
458 " fSourceKey,\n"
459 " fSourceName,\n"
460 " ADIST(fDeclination, fRightAscension*15, "+to_string(dec)+", "+to_string(ra)+") AS Dist,\n"
461 " fIsToO,\n"
462 " fFollowSatellite\n"
463 "FROM Source\n"
464 "WHERE fSourceTypeKEY=1\n"
465 "HAVING Dist<"+to_string(check_dist)+"\n"
466 "ORDER BY Dist ASC";
467
468 const mysqlpp::StoreQueryResult res1 = fConnection.query(query1).store();
469
470 if (res1.num_rows())
471 {
472 Info("Found "+to_string(res1.num_rows())+" sources with a distance less than "+Tools::Form("%.2f", check_dist)+"\u00b0");
473
474 for (size_t i=0; i<res1.num_rows(); i++)
475 {
476 const mysqlpp::Row &row = res1[i];
477 Info(" "+string(row["fSourceName"])+" ["+string(row["fSourceKey"])+"]: D="+Tools::Form("%.2f", double(row["Dist"]))+"\u00b0");
478 }
479
480 const mysqlpp::Row &row = res1[0];
481 if (double(row["Dist"])<min_dist)
482 {
483 Source source;
484 source.key = res1[0]["fSourceKey"];
485 source.isToO = res1[0]["fIsToO"];
486 source.satellite= res1[0]["fFollowSatellite"];
487
488 Warn("Sources closer than "+Tools::Form("%.2f", check_dist)+"\u00b0 detected.");
489 Warn("Overwriting source key with: "+string(row["fSourceName"])+" ["+to_string(source.key)+"]: D="+Tools::Form("%.2f", double(row["Dist"]))+"\u00b0");
490
491 return source;
492 }
493 }
494
495 return Source();
496 }
497
498 float GetWobbleAngle(const double &ra, const double &dec)
499 {
500 const double wobble_offset = 0.6;
501 const double camera_radius = 2.3;
502 const double magnitude_max = 4.5;
503
504 /*
505 Mag Cnt
506 -2 1
507 -1 3
508 0 11
509 1 33
510 2 121
511 3 321
512 4 871
513 5 1364
514 6 404
515 7 12
516 */
517
518 // The wobble position lay in the plane which is normal the to the plane source-center-star
519 // and goes through
520
521 double wobble_angle = 0;
522
523 const string query0 =
524 "SELECT fSourceKEY, fRightAscension, fDeclination, fMagnitude,\n"
525 " ADIST(fDeclination, fRightAscension*15, "+to_string(dec)+", "+to_string(ra)+") AS Dist\n"
526 " FROM Source\n"
527 " WHERE (fSourceTypeKey=2 OR fSourceTypeKey=3) AND fMagnitude<"+to_string(magnitude_max)+"\n"
528 " HAVING Dist<"+to_string(camera_radius+wobble_offset)+"\n"
529 " ORDER BY fMagnitude ASC";
530
531 // Out() << query0 << endl;
532
533 const mysqlpp::StoreQueryResult res0 = fConnection.query(query0).store();
534
535 Info("Found "+to_string(res0.num_rows())+" stars in the camera field-of-view with magnitude less than "+to_string(magnitude_max));
536
537 for (size_t i=0; i<::min<size_t>(10, res0.num_rows()); i++)
538 {
539 const mysqlpp::Row &row = res0[i];
540
541 // TVector3 souce, star;
542 // source.SetMagThetaPhi(1, rad(90-dec), rad(ra));
543 // star.SetMagThetaPhi( 1, rad(90-dec), rad(ra));
544 //
545 // star.RotateZ( -source.Phi());
546 // source.RotateZ(-source.Phi());
547 //
548 // star.RotateY( 180-source.Theta());
549 // source.RotateY(180-source.Theta());
550 //
551 // rho = star.Phi();
552
553 const double rad = M_PI/180;
554
555 const double dec0 = rad * dec;
556 const double ra0 = rad * ra;
557
558 const double dec1 = rad * row["fDeclination"];
559 const double ra1 = rad * row["fRightAscension"] * 15;
560
561 const double s2 = sin(ra0-ra1);
562 const double c2 = cos(ra0-ra1);
563
564 const double s0 = cos(dec0);
565 const double c0 = sin(dec0);
566
567 const double c1 = cos(dec1);
568 const double s1 = sin(dec1);
569
570 const double k0 = -s2*c1;
571 const double k1 = s0*s1 - c0*c2*c1;
572
573 const double rho = atan(k0/k1) / rad; // atan2(k0, k1)/rad
574
575 if (i==0)
576 wobble_angle = rho;
577
578 Info(" "+Tools::Form("Mag=%5.2f Dist=%5.1f Phi=%6.1f", (double)row["fMagnitude"], (double)row["Dist"], rho));
579 }
580
581 if (res0.num_rows())
582 {
583 Info("The brightest star (M="+string(res0[0]["fMagnitude"])+") at distance is visible at a wobble angle of "+Tools::Form("%.2f", wobble_angle)+"\u00b0");
584 Info("Wobble angles determined as "+Tools::Form("%.2f", wobble_angle-90)+"\u00b0 and "+Tools::Form("%.2f", wobble_angle+90)+"\u000b");
585 }
586 else
587 {
588 Info("Using default wobble angles.");
589 }
590
591 return wobble_angle;
592 }
593
594 uint32_t AddSource(const string &name, const double &ra, const double &dec, const bool fDryRun=false)
595 {
596 const double wobble_angle = GetWobbleAngle(ra, dec);
597
598 const string query =
599 "INSERT INTO Source\n"
600 " (fSourceName, fRightAscension, fDeclination, fWobbleAngle0, fWobbleAngle1, fSourceTypeKey, fIsToO) VALUES\n"
601 " ('"+name+"', "+to_string(ra/15.)+", "+to_string(dec)+", "+to_string(wobble_angle-90)+", "+to_string(wobble_angle+90)+", 1, 1)";
602
603 if (fDryRun)
604 {
605 Out() << query << endl;
606 return -1;
607 }
608
609 auto q = fConnection.query(query);
610 q.execute();
611 if (!q.info().empty())
612 Info(q.info());
613
614 const uint32_t source_key = q.insert_id();
615
616 Info(string(fDryRun?"[dry-run] ":"")+"The new source got the key "+to_string(source_key));
617
618 return source_key;
619 }
620
621 bool ScheduleImp(const string &name, const ToO::DataGRB &grb, const CheckVisibility &check, const bool &is_pointing)
622 {
623 const bool fDryRun = GetCurrentState()!=ToO::State::kArmed;
624
625 if (fDryRun)
626 Warn("Scheduler not armed!");
627
628 Time stopwatch;
629
630 // This is just a desperate try which will most likely fail
631 if (!fConnection.connected())
632 fConnection.reconnect();
633
634 /*
635 +-----------------+---------------------+------+-----+---------+----------------+
636 | Field | Type | Null | Key | Default | Extra |
637 +-----------------+---------------------+------+-----+---------+----------------+
638 | fSourceKEY | int(11) | NO | PRI | NULL | auto_increment |
639 | fSourceName | varchar(30) | NO | | NULL | |
640 | fRightAscension | double(10,6) | NO | | NULL | |
641 | fDeclination | double(10,6) | NO | | NULL | |
642 | fEpochKEY | tinyint(4) | YES | | NULL | |
643 | fFlux | float | YES | | NULL | |
644 | fSlope | float | YES | | NULL | |
645 | fSourceTypeKey | int(11) | NO | MUL | NULL | |
646 | fWobbleOffset | float | NO | | 0.6 | |
647 | fWobbleAngle0 | int(11) | NO | | 90 | |
648 | fWobbleAngle1 | int(11) | NO | | -90 | |
649 | fMagnitude | float(4,2) | YES | | NULL | |
650 | fIsToO | tinyint(3) unsigned | NO | | 0 | |
651 +-----------------+---------------------+------+-----+---------+----------------+
652 */
653
654 Source source = GetSourceKey(name, grb.ra, grb.dec);
655
656 Out() << Time()-stopwatch << endl;
657
658 // This is not a real alert but a pointing information of a satellite
659 if (is_pointing)
660 {
661 // We only observe known sources in paralell with satellites...
662 if (source.key<0)
663 {
664 Warn("Observation of only known sources requested but no known source found.");
665 return false;
666 }
667
668 // ... and only if they are 'standard' sources and not ToO sources.
669 if (source.isToO)
670 {
671 Warn("Known source with ToO flag... skipping.");
672 return false;
673 }
674
675 // For non prioritized sources we require a threshold <3
676 // and for prioritized sources a threshold <10
677
678 // Special check for the threshold. Threshold condition atm only
679 // applies for pointings and is different for prioritized sources
680 if ((check.threshold>3 && !source.satellite) || !check.valid_threshold)
681 {
682 Warn(Tools::Form("Relative threshold [%6.2f] too high... skipping.", check.threshold));
683 return false;
684 }
685 }
686
687
688 /*
689 +---------------------+--------------+------+-----+-------------------+-----------------------------+
690 | Field | Type | Null | Key | Default | Extra |
691 +---------------------+--------------+------+-----+-------------------+-----------------------------+
692 | fScheduleID | int(11) | NO | PRI | NULL | auto_increment |
693 | fStart | datetime | NO | MUL | NULL | |
694 | fLastUpdate | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
695 | fMeasurementID | int(11) | NO | | NULL | |
696 | fUser | varchar(32) | NO | | NULL | |
697 | fData | varchar(256) | YES | | NULL | |
698 | fSourceKey | smallint(6) | YES | | NULL | |
699 | fMeasurementTypeKey | tinyint(4) | NO | | NULL | |
700 +---------------------+--------------+------+-----+-------------------+-----------------------------+
701 */
702
703 const uint16_t contingency = 1; // The contingency to delete earlier entries from the Table
704 const uint16_t duration = 60; // The target duration of the ToO
705 const uint16_t required = 20; // If the ToO would be less than required, skip_it
706 const uint16_t skip_time = 20; // If following observation would be less than skip_time, skip it
707
708 if (duration<required)
709 {
710 Error("Requested duration is smaller than required time.");
711 return false;
712 }
713
714 const Time now;
715
716 const Time sunset = now.GetPrevSunSet();
717 const Time sunrise = now.GetNextSunRise();
718
719 if (sunset>sunrise || sunrise>sunset+boost::posix_time::hours(24))
720 {
721 Error("Unable to schedule a ToO during daytime.");
722 return false;
723 }
724
725 // Safety margin
726 Time schedtime = now-boost::posix_time::seconds(contingency);
727 Time restart = now+boost::posix_time::minutes(duration);
728
729 Out() << '\n';
730 Out() << "Contingency: " << contingency << " s\n";
731 Out() << "Start of ToO: " << now.GetAsStr() << '\n';
732 Out() << "End of ToO: " << restart.GetAsStr() << '\n';
733 Out() << "Duration: " << duration << " min\n\n";
734
735 Out() << "Schedule:\n";
736
737 const string queryS =
738 "SELECT *,\n"
739 " (fMeasurementTypeKey=4 AND fStart<='"+restart.GetAsStr()+"') AS `Reschedule`,\n"
740 " (fStart>'"+restart.GetAsStr()+"') AS `Following`,\n"
741 " (fMeasurementTypeKey=4 AND fStart BETWEEN '"+schedtime.GetAsStr()+"' AND '"+restart.GetAsStr()+"') AS `Delete`\n"
742 " FROM Schedule\n"
743 " WHERE fStart BETWEEN '"+sunset.GetAsStr()+"' AND '"+sunrise.GetAsStr()+"'\n"
744 " AND fMeasurementID=0\n"
745 " ORDER BY fStart ASC, fMeasurementID ASC";
746
747 const mysqlpp::StoreQueryResult resS = fConnection.query(queryS).store();
748
749 if (resS.num_rows()<2)
750 {
751 Error("Available schedule is too short to be evaluated.");
752 return false;
753 }
754
755 // `Reschedule`: Start time before end time of ToO (type==data)
756 // `Following`: Start time after end time of ToO (type==any)
757 // `Delete`: Start time between start and end time of ToO (type==data)
758
759 vector<string> list;
760
761 int32_t firstdata = -1; // First observation with type==data
762 int32_t reschedule = -1; // Last observation with: Start time before end time of ToO (type==data)
763 int32_t following = -1; // First observation with: Start time after end time of ToO (type==any)
764 int32_t last_resch = -1; // Last observation with: Start time before end time of ToO (type=data) and start time not between start and end time of ToO (type==any)
765 for (size_t i=0; i<resS.num_rows(); i++)
766 {
767 const mysqlpp::Row &row = resS[i];
768
769 Out() << setw(2) << i << " | ";
770 Out() << row["Reschedule"] << " | ";
771 Out() << row["Following"] << " | ";
772 Out() << row["fScheduleID"] << " | ";
773 Out() << row["fStart"] << " | ";
774 if (bool(row["Delete"]))
775 {
776 Out() << " < delete > | ";
777 list.push_back((string)row["fScheduleID"]);
778 }
779 else
780 Out() << row["fLastUpdate"] << " | ";
781 Out() << row["fMeasurementID"] << " | ";
782 Out() << row["fUser"] << " | ";
783 Out() << row["fData"] << " | " << setw(4);
784 Out() << row["fSourceKey"] << " | ";
785 Out() << row["fMeasurementTypeKey"] << '\n';
786
787 if (bool(row["Reschedule"]))
788 reschedule = i;
789
790 if (bool(row["Reschedule"]) && !row["Delete"])
791 last_resch = i;
792
793 if (following==-1 && bool(row["Following"]))
794 following = i;
795
796 const uint16_t type = row["fMeasurementTypeKey"];
797 if (firstdata==-1 && type==4)
798 firstdata = i;
799 }
800
801 // --------------------------------------------------------------------
802
803 if (firstdata==-1)
804 {
805 Warn("No data run found.");
806 return false;
807 }
808
809 // --------------------------------------------------------------------
810
811 if (reschedule>=0)
812 Out() << "\nLast data run before restart at " << restart.GetAsStr() << ": idx=" << reschedule << " / ID=" << resS[reschedule]["fScheduleID"] << endl;
813
814 // --------------------------------------------------------------------
815
816 const Time first((string)resS[firstdata]["fStart"]); // First data run of the night
817 const Time last( (string)resS[resS.num_rows()-1]["fStart"]); // Last scheduled observation of the night
818
819 // --------------------------------------------------------------------
820
821 if (restart<=first)
822 {
823 Warn("ToO ends before first data taking... skipped.");
824 return false;
825 }
826 if (schedtime>=last)
827 {
828 Warn("ToO starts after data taking... skipped.");
829 return false;
830 }
831
832 // --------------------------------------------------------------------
833
834 if (schedtime<first)
835 {
836 Info("ToO starts before first data taking... rescheduling start time.");
837 schedtime = first;
838 }
839 if (restart>last)
840 {
841 Info("ToO ends after data taking... rescheduling end time!");
842 restart = last;
843 }
844
845 // --------------------------------------------------------------------
846
847 if (restart<schedtime+boost::posix_time::minutes(required))
848 {
849 Warn("Could not schedule more than the required "+to_string(required)+" minutes... skipped.");
850 return false;
851 }
852
853 // --------------------------------------------------------------------
854
855 if (following>=0)
856 {
857 const Time follow((string)resS[following]["fStart"]);
858 if (follow<restart+boost::posix_time::minutes(skip_time))
859 {
860 Info("Following observation would be less than "+to_string(skip_time)+" min... skipping rescheduled source.");
861 reschedule = -1;
862 }
863 }
864
865 // ====================================================================
866
867 if (!list.empty())
868 {
869 Out() << "\nDelete entries:\n";
870 for (const auto &l : list)
871 Out() << l << "\n";
872 Out() << endl;
873 }
874
875 // ====================================================================
876
877 if (source.key<0)
878 source.key = AddSource(name, grb.ra, grb.dec, fDryRun);
879
880 Out() << Time()-stopwatch << endl;
881
882 // ====================================================================
883
884 Info("Source will be scheduled.");
885
886 Out() << "New entries:\n";
887
888 vector<string> insert;
889
890 const bool ongoing = last_resch>=0 && source.key==uint16_t(resS[last_resch]["fSourceKey"]);
891
892 if (ongoing)
893 Out() << " | | | < ongoing > | 0 | | | " << setw(4) << source.key << " | 4\n";
894 else
895 {
896 Out() << " | auto | " << schedtime.GetAsStr() << " | < start > | 0 | ToO | " << (is_pointing?" nodrs ":"nodrs,grb") << " | " << setw(4) << source.key << " | 4\n";
897 insert.emplace_back("('"+schedtime.GetAsStr()+"',0,'ToO','"+(is_pointing?"nodrs:true":"nodrs:true,grb:true")+"',"+to_string(source.key)+",4)");
898 }
899
900 // --------------------------------------------------------------------
901
902 if (reschedule>=0 && restart!=last)
903 {
904 Out() << " | auto | " << restart.GetAsStr() << " | < end > | 0 | ToO | NULL | " << setw(4) << resS[reschedule]["fSourceKey"] << " | 4\n";
905
906 const string fData = (string)resS[reschedule]["fData"];
907 const string fKey = (string)resS[reschedule]["fSourceKey"];
908
909 const string data = resS[reschedule]["fData"] ? "'"+fData+"'" : "NULL";
910 insert.emplace_back("('"+restart.GetAsStr()+"',0,'ToO',"+data+","+fKey+",4)");
911 }
912
913 // ====================================================================
914
915 Out() << Time()-stopwatch << endl;
916
917 // ====================================================================
918
919 const string queryD = string("DELETE FROM Schedule WHERE fScheduleID IN (")+boost::algorithm::join(list, ",")+")";
920
921 const string queryI =
922 "INSERT INTO Schedule\n (fStart,fMeasurementID,fUser,fData,fSourceKey,fMeasurementTypeKey)\n"
923 "VALUES\n "+string(boost::algorithm::join(insert, ",\n "));
924
925 if (fDryRun)
926 {
927 Out() << "\n";
928 if (!list.empty())
929 Out() << queryD << "\n\n";
930 if (!insert.empty())
931 Out() << queryI << "\n\n";
932 Out() << flush;
933 return false;
934 }
935
936 // Empty interrupt to stop data taking as early as possible
937 // Triggers also RELOAD_SOURCES
938 if (!ongoing)
939 Dim::SendCommandNB("DIM_CONTROL/INTERRUPT", "prepare");
940
941 // ---------------------------------------------------------
942
943 fConnection.query("LOCK TABLES Schedule WRITE");
944
945 // ---------------------------------------------------------
946 if (!list.empty())
947 {
948 const mysqlpp::SimpleResult res = fConnection.query(queryD).execute();
949 Info(to_string(res.rows())+" row(s) deleted from Schedule.");
950 }
951
952 // ---------------------------------------------------------
953
954 if (!insert.empty())
955 {
956 auto q = fConnection.query(queryI);
957 q.execute();
958 if (!q.info().empty())
959 Info(q.info());
960 }
961 // ---------------------------------------------------------
962
963 fConnection.query("UNLOCK TABLES");
964
965 // ---------------------------------------------------------
966
967 if (!ongoing)
968 Dim::SendCommand("DIM_CONTROL/INTERRUPT", "reschedule");
969
970 ostringstream out;
971 out << Time()-stopwatch;
972 Info(out);
973
974 // ---------------------------------------------------------
975
976 const string queryT =
977 "INSERT INTO ToOs\n"
978 " (fTypeID, fRightAscension, fDeclination, fSourceKEY) VALUES\n"
979 " ('"+to_string(grb.type)+"', "+to_string(grb.ra/15)+", "+to_string(grb.dec)+", "+to_string(source.key)+")";
980
981 if (!fDryRun)
982 {
983 auto q = fConnection.query(queryT);
984 q.execute();
985 if (!q.info().empty())
986 Info(q.info());
987 }
988 else
989 Out() << queryT << endl;
990
991 // ---------------------------------------------------------
992
993 return true;
994 }
995/*
996 bool Schedule(const string &name, const ToO::DataGRB &grb, const CheckVisibility &check, const bool &is_pointing)
997 {
998 try
999 {
1000 return ScheduleImp(name, grb, check, is_pointing);
1001 }
1002 catch (const exception &e)
1003 {
1004 Error(string("SQL query failed: ")+e.what());
1005 fConnection.disconnect();
1006 return false;
1007 }
1008 }
1009*/
1010 // ========================================================================
1011
1012
1013 // ========================================================================
1014
1015 bool CheckEventSize(size_t has, const char *name, size_t size)
1016 {
1017 if (has==size)
1018 return true;
1019
1020 ostringstream msg;
1021 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
1022 Fatal(msg);
1023 return false;
1024 }
1025
1026 bool CheckMinEventSize(size_t has, const char *name, size_t size)
1027 {
1028 if (has>size)
1029 return true;
1030
1031 ostringstream msg;
1032 msg << name << " - Received event has " << has << " bytes, but expected more.";
1033 Fatal(msg);
1034 return false;
1035 }
1036
1037 void EnterIntoAlertDB(const string &name, const ToO::DataGRB &grb, const CheckVisibility &check, const bool &is_pointing)
1038 {
1039 // Do not enter satellite pointing alerts (as there are too many)
1040 if (is_pointing)
1041 return;
1042
1043 // Check for general visibility of the source during the night
1044 // If required: For better performance, both (sun and object)
1045 // RstTime could be calculated only here
1046 const double sun_set = fmod(check.rst_sun.set, 1);
1047 const double sun_rise = fmod(check.rst_sun.rise, 1);
1048
1049 const double rise = fmod(check.rst_obj.rise, 1);
1050 const double trans = fmod(check.rst_obj.transit, 1);
1051 const double set = fmod(check.rst_obj.set, 1);
1052
1053 // Object is at at least visible at some time during the
1054 // night (above the required horizon) or is circumpolar
1055 // above that horizon
1056 switch (check.vis_obj)
1057 {
1058 case -1:
1059 // Object is not visible above the requested horizon
1060 return;
1061
1062 case 0:
1063 // Object crosses the local meridian
1064 // Rises after sun-rise or before sun-set
1065 // Culminates after sun-rise or before sun-set
1066 // Sets after sun-rise or before sun-set
1067 if ((rise <sun_set || rise >sun_rise) &&
1068 (trans<sun_set || trans>sun_rise) &&
1069 (set <sun_set || set >sun_rise))
1070 return;
1071
1072 break;
1073
1074 case 1:
1075 // Object is always visible above the requested horizon
1076 break;
1077 }
1078
1079 Info("Source "+name+" might be observable.");
1080
1081 // Make an entry in the alter databse to issue a call
1082 const string queryS =
1083 "INSERT FlareAlerts.FlareTriggers SET\n"
1084 " fTriggerInserted=Now(),\n"
1085 " fNight="+to_string(Time().NightAsInt())+",\n"
1086 " fPacketTypeKey="+to_string(grb.type)+",\n"
1087 " fRa="+to_string(grb.ra/15)+",\n"
1088 " fDec="+to_string(grb.dec)+",\n"
1089 " fErr="+to_string(grb.err)+",\n"
1090 " fName=\""+name+"\",\n"
1091 " fTriggerType=7";
1092
1093 const bool fDryRun = GetCurrentState()!=ToO::State::kArmed;
1094
1095 if (!fDryRun)
1096 {
1097 auto q = fConnection.query(queryS);
1098 q.execute();
1099 if (!q.info().empty())
1100 Info(q.info());
1101 }
1102 else
1103 Out() << queryS << endl;
1104 }
1105
1106 int ScheduleGCN(const EventImp &evt)
1107 {
1108 if (!CheckMinEventSize(evt.GetSize(), "ScheduleGCN", sizeof(ToO::DataGRB)))
1109 return kSM_FatalError;
1110
1111 const ToO::DataGRB &grb = evt.Ref<ToO::DataGRB>();
1112
1113 const auto it = GCN::PaketTypes.find(grb.type);
1114 if (it==GCN::PaketTypes.end())
1115 {
1116 Warn("Unknown Packet Type: "+to_string(grb.type));
1117 return GetCurrentState();
1118 }
1119
1120
1121 const string name = evt.Ptr<char>(sizeof(ToO::DataGRB));
1122
1123 const auto &paket = it->second;
1124
1125 ostringstream out;
1126 out << name << " [" << paket.name << ":" << grb.type << "]: RA=" << grb.ra/15. << "h DEC=" << grb.dec << "\u00b0 ERR=" << grb.err << "\u00b0";
1127
1128 Info(out);
1129 Info(paket.description);
1130
1131 // The []-operator automatically creates a default entry if not yet existing
1132 auto &conditions = fVisibilityCriteria[grb.type];
1133
1134 const CheckVisibility check(conditions, grb.ra, grb.dec);
1135
1136 Info("Sun altitude: "+Tools::Form("%5.1f\u00b0 ", check.solarobj.fSunHrz.alt)+(check.valid_sun?"OK ":"failed")+Tools::Form(" [alt < %5.1f\u00b0]", conditions[kSunMax]));
1137 if (check.valid_sun)
1138 {
1139 Info("Moon distance: "+Tools::Form("%5.1f\u00b0 ", check.moon_dist) +(check.valid_moon ?"OK ":"failed")+Tools::Form(" [%5.1f\u00b0 < d < %5.1f\u00b0]", conditions[kMoonMin], conditions[kMoonMax]));
1140 Info("Zenith angle: "+Tools::Form("%5.1f\u00b0 ", check.position.zd)+(check.valid_zd ?"OK ":"failed")+Tools::Form(" [zd < %5.1f\u00b0]", conditions[kZenithMax]));
1141 Info("Current: "+Tools::Form("%5.1f\u00b5A ", check.current) +(check.valid_current?"OK ":"failed")+Tools::Form(" [I < %5.1f\u00b5A]", conditions[kCurrentMax]));
1142 //Info(string("Rel. threshold: ")+(check.valid_threshold?"OK ":"failed")+" ["+Tools::Form("%5.1f", check.threshold)+"]");
1143 }
1144
1145/*
1146 * Swift: https://gcn.gsfc.nasa.gov/swift.html
1147 relevante Paket-Typen:
1148 60 BAT_Alert
1149 97 BAT_QL_Pos
1150 61 BAT_Pos
1151 62 BAT_Pos_Nack
1152 Es kann wohl vorkommen, dass ein Alert danach anders klassifiziert ist
1153 -> BAT_Trans (84) und es steht dass dann in der GRB notice eine catalog
1154 ID im comment steht. Da ist es mir noch nicht so ganz klar, woran man
1155 echte Alerts erkennt.
1156
1157 * Fermi: https://gcn.gsfc.nasa.gov/fermi.html
1158 relevante Paket-Typen:
1159 110 GBM_Alert P B B B&A
1160 111 GBM_Flt_Pos P B B B&A
1161 112 GBM_Gnd_Pos P B B B&A
1162 115 GBM_Final_Pos P B B B&A
1163 Bei GBM müssen wir usn überlegen wie wir es mit den Positionsupdates
1164 machen - das können auch mal ein paar Grad sein. siehe zB
1165 https://gcn.gsfc.nasa.gov/other/568833894.fermi
1166
1167 Wenn ich das richtig sehe, kommt bei GBM_Alert noch keine
1168 Position, sndern erst mit GBM_Flt_Pos (in dem Bsp kommt das 3 Sek
1169 später)
1170
1171 * INTEGRAL: https://gcn.gsfc.nasa.gov/integral.html
1172 wenn ich es richtig verstehe, sind die interessanten Alerts die WAKEUP
1173 (tw kommt ein Positionsupdate via OFFLINE oder REFINED). Es gibt auch
1174 noch WEAK, aber das sind scheinbar subthreshold alerts - würde ich
1175 jetzt erst mal weglassen. Im letzten Jahr gab es 8 WAKEUP alerts. 2017
1176 sogar nur 3, 2016 7, 2015 waren es einige mehr, aber das war V404_Cyg -
1177 in dem Fall steht dann aber ein Object-Name dabei - ansonsten steht as
1178 "Possbibly real GRB event" - zT steht auch, dass es coincident mit GBM
1179 events ist
1180
1181 * KONUS: https://gcn.gsfc.nasa.gov/konus.html
1182 Ist mir noch etwas unklar, was das genau ist - die schicken Lichtkurven
1183 von GRBs und Transients - muss man nochmal genauer schauen.
1184
1185 * MAXI: https://gcn.gsfc.nasa.gov/maxi.html
1186 wenn ich das richtig verstehe, sind das nicht nur GRBs -> müsste man
1187 nochmal genauer schauen bevor man da was implementiert
1188
1189 * AGILE: https://gcn.gsfc.nasa.gov/agile.html
1190 Wahrscheinlich eher nicht relevant, da steht was von 30min nach dem
1191 Burst kommt das 'Wakeup'. Die relevanten Paket-Typen wären 100
1192 (Wakeup_Pos), 101 (Ground_Pos) und 102 (Refined_Pos)
1193
1194 * AMON: https://gcn.gsfc.nasa.gov/amon.html
1195 sind bisher nur neutrinos - da macht MAGIC glaub ich schon automatic
1196 Follow-Up - müssen wir also kucken was Sinn macht
1197
1198 * Ligo-Virgo GW: https://gcn.gsfc.nasa.gov/lvc.html
1199 da ist natürlich die Error-Region groß - müsste man schauen, was man
1200 da will
1201
1202 Fazit: am relevantesten sind Fermi/GBM und Swift/BAT. Evtl könnte
1203 INTEGRAL noch Sinn machen und über AMON und LVC sollte man nachdenken,
1204 ob/was man haben will.
1205*/
1206
1207 const bool is_pointing = grb.type==51 || grb.type==83;
1208
1209 try
1210 {
1211 if (!check.visible)
1212 {
1213 Info("Source not observable... skipped.");
1214 EnterIntoAlertDB(name, grb, check, is_pointing);
1215
1216 return fConnection.connected() ? GetCurrentState() : ToO::State::kDisconnected;
1217 }
1218
1219 Info("Source is visible... scheduling.");
1220
1221 if (!ScheduleImp(name, grb, check, is_pointing))
1222 EnterIntoAlertDB(name, grb, check, is_pointing);
1223 }
1224 catch (const exception &e)
1225 {
1226 Error(string("SQL query failed: ")+e.what());
1227 fConnection.disconnect();
1228 }
1229
1230 return fConnection.connected() ? GetCurrentState() : ToO::State::kDisconnected;
1231 }
1232
1233 int AddNewSource(const EventImp &evt)
1234 {
1235 if (!CheckMinEventSize(evt.GetSize(), "AddNewSource", 2*sizeof(double)))
1236 return kSM_FatalError;
1237
1238 const double &ra = evt.Ptr<double>()[0];
1239 const double &dec = evt.Ptr<double>()[1];
1240 const string name = evt.Ptr<char>(sizeof(double)*2);
1241
1242 ostringstream out;
1243 out << "New Source: '" << name << "' RA=" << ra << "h DEC=" << dec << "\u00b0";
1244
1245 Info(out);
1246
1247 try
1248 {
1249 const Source source = GetSourceKey(name, ra*15, dec);
1250
1251 if (source.key>=0) // Not a known source
1252 Warn("Source already known with key="+to_string(source.key)+".");
1253 else
1254 /*source.key =*/ AddSource(name, ra*15, dec);
1255
1256 }
1257 catch (const exception &e)
1258 {
1259 Error(string("SQL query failed: ")+e.what());
1260 fConnection.disconnect();
1261 }
1262
1263 return fConnection.connected() ? GetCurrentState() : ToO::State::kDisconnected;
1264 }
1265
1266 int Enable()
1267 {
1268 return GetCurrentState()==ToO::State::kConnected ? ToO::State::kArmed : GetCurrentState();
1269 }
1270 int Disable()
1271 {
1272 return GetCurrentState()==ToO::State::kArmed ? ToO::State::kConnected : GetCurrentState();
1273 }
1274
1275 int Execute()
1276 {
1277 Time now;
1278 if (now>fKeepAliveDeadline)
1279 {
1280 static int ernum = 0;
1281 try
1282 {
1283 // Unfortunately, reconnecting [Takes about 1s] is
1284 // the only non-blocking way to ensure an open
1285 // connection. Ping would be nice but is blocking.
1286 fConnection = Database(fUri);
1287 ernum = 0;
1288 }
1289 catch (const mysqlpp::ConnectionFailed &e)
1290 {
1291 if (ernum!=e.errnum())
1292 Error(e.what());
1293 ernum = e.errnum();
1294 }
1295
1296 fKeepAliveDeadline += boost::posix_time::seconds(fKeepAliveInterval);
1297 }
1298
1299 if (!fConnection.connected())
1300 return ToO::State::kDisconnected;
1301
1302 if (fConnection.connected() && GetCurrentState()<=ToO::State::kDisconnected)
1303 return ToO::State::kArmed;
1304
1305 return GetCurrentState();
1306 }
1307
1308public:
1309 StateMachineToO(ostream &out=cout) : StateMachineDim(out, "SCHEDULER"),
1310 fConnection(""), fKeepAliveDeadline(Time())
1311 {
1312 AddStateName(ToO::State::kDisconnected, "Disconnected",
1313 "The Dim DNS is reachable, but the required subsystems are not available.");
1314
1315 AddStateName(ToO::State::kConnected, "Connected",
1316 "All needed subsystems are connected to their hardware, no action is performed.");
1317
1318 AddStateName(ToO::State::kArmed, "Armed",
1319 "All needed subsystems are connected to their hardware, scheduling in progress.");
1320
1321 AddEvent("GCN", "S:1;D:1;D:1;D:1;C")
1322 (bind(&StateMachineToO::ScheduleGCN, this, placeholders::_1))
1323 ("Schedule a ToO"
1324 "|type[int16]:Pakage or type ID (see HeadersToO.h)"
1325 "|ra[deg]:Right ascension"
1326 "|dec[deg]:Declination"
1327 "|err[deg]:Error radius"
1328 "|name[string]:Tentative or known source name");
1329
1330 AddEvent("ADD_SOURCE", "D:1;D:1;C")
1331 (bind(&StateMachineToO::AddNewSource, this, placeholders::_1))
1332 ("Add a new source to the databse if not yet available"
1333 "|ra[h]:Right ascension"
1334 "|dec[deg]:Declination"
1335 "|name[string]:Monitored source name");
1336
1337 AddEvent("START", "")
1338 (bind(&StateMachineToO::Enable, this/*, placeholders::_1*/))
1339 ("");
1340
1341 AddEvent("STOP", "")
1342 (bind(&StateMachineToO::Disable, this/*, placeholders::_1*/))
1343 ("");
1344 }
1345
1346 /*
1347 bool GetConfig(Configuration &conf, const string &name, const string &sub, uint16_t &rc)
1348 {
1349 if (conf.HasDef(name, sub))
1350 {
1351 rc = conf.GetDef<uint16_t>(name, sub);
1352 return true;
1353 }
1354
1355 Error("Neither "+name+"default nor "+name+sub+" found.");
1356 return false;
1357 }*/
1358
1359 void AddCriteria(const vector<uint16_t> &ids, const VisibilityCriterion &crit, const float &limit)
1360 {
1361 // The []-operator automatically creates a default entry if not yet existing
1362 for (auto id=ids.begin(); id!=ids.end(); id++)
1363 fVisibilityCriteria[*id][crit] = limit;
1364 }
1365
1366 int EvalOptions(Configuration &conf)
1367 {
1368 fVerbose = !conf.Get<bool>("quiet");
1369 fKeepAliveInterval = conf.Get<uint16_t>("keep-alive");
1370 fUri = conf.Get<string>("schedule-database");
1371
1372 const vector<uint16_t> GRBs =
1373 {
1374 60, 61, 62, 97, 110, 111, 112, 115, 53, 54, 55, 56, 100, 101, 102
1375 };
1376
1377 AddCriteria(GRBs, kCurrentMax, 30);
1378 AddCriteria(GRBs, kZenithMax, 45);
1379
1380 return -1;
1381 }
1382};
1383
1384
1385// ------------------------------------------------------------------------
1386
1387#include "Main.h"
1388
1389template<class T>
1390int RunShell(Configuration &conf)
1391{
1392 return Main::execute<T, StateMachineToO>(conf);
1393}
1394
1395void SetupConfiguration(Configuration &conf)
1396{
1397 po::options_description control("Scheduler");
1398 control.add_options()
1399 ("quiet,q", po_bool(), "")
1400 ("keep-alive", var<uint16_t>(uint16_t(300)), "Interval in seconds to ping (reconnect) the database server")
1401 ("schedule-database", var<string>(), "Database link as in\n\tuser:password@server[:port]/database[?compress=0|1].")
1402 ;
1403
1404 conf.AddOptions(control);
1405}
1406
1407void PrintUsage()
1408{
1409 cout <<
1410 "The scheduler program is able to schedule a new observation.\n"
1411 "\n"
1412 "Usage: scheduler [-c type] [OPTIONS]\n"
1413 " or: scheduler [OPTIONS]\n";
1414 cout << endl;
1415}
1416
1417void PrintHelp()
1418{
1419 Main::PrintHelp<StateMachineToO>();
1420}
1421
1422int main(int argc, const char* argv[])
1423{
1424 Configuration conf(argv[0]);
1425 conf.SetPrintUsage(PrintUsage);
1426 Main::SetupConfiguration(conf);
1427 SetupConfiguration(conf);
1428
1429 if (!conf.DoParse(argc, argv, PrintHelp))
1430 return 127;
1431
1432 if (!conf.Has("console"))
1433 return RunShell<LocalStream>(conf);
1434
1435 if (conf.Get<int>("console")==0)
1436 return RunShell<LocalShell>(conf);
1437 else
1438 return RunShell<LocalConsole>(conf);
1439
1440 return 0;
1441}
1442
Note: See TracBrowser for help on using the repository browser.