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

Last change on this file since 19712 was 19658, checked in by tbretz, 5 years ago
Same as before, but nicer and easier to read.
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 };
429
430 Source GetSourceKey(const string &name, const double &ra, const double &dec)
431 {
432 const double min_dist = 0.1;
433 const double check_dist = 2.0;
434
435 // ----------------------- Check if source with same name exists -----------------------
436
437 const string query2 =
438 "SELECT fSourceKey, fIsToO FROM Source WHERE fSourceName='"+name+"'";
439
440 const mysqlpp::StoreQueryResult res2 = fConnection.query(query2).store();
441
442 if (res2.num_rows())
443 {
444 Source source;
445 source.key = res2[0]["fSourceKey"];
446 source.isToO = res2[0]["fIsToO"];
447
448 Info("A source with the same name (key="+to_string(source.key)+") was found in the Source table.");
449
450 return source;
451 }
452
453 // ----------------- Check if source with similar coordinates exists -------------------
454
455 const string query1 =
456 "SELECT\n"
457 " fSourceKey,\n"
458 " fSourceName,\n"
459 " ADIST(fDeclination, fRightAscension*15, "+to_string(dec)+", "+to_string(ra)+") AS Dist,\n"
460 " fIsToO\n"
461 "FROM Source\n"
462 "WHERE fSourceTypeKEY=1\n"
463 "HAVING Dist<"+to_string(check_dist)+"\n"
464 "ORDER BY Dist ASC";
465
466 const mysqlpp::StoreQueryResult res1 = fConnection.query(query1).store();
467
468 if (res1.num_rows())
469 {
470 Info("Found "+to_string(res1.num_rows())+" sources with a distance less than "+Tools::Form("%.2f", check_dist)+"\u00b0");
471
472 for (size_t i=0; i<res1.num_rows(); i++)
473 {
474 const mysqlpp::Row &row = res1[i];
475 Info(" "+string(row["fSourceName"])+" ["+string(row["fSourceKey"])+"]: D="+Tools::Form("%.2f", double(row["Dist"]))+"\u00b0");
476 }
477
478 const mysqlpp::Row &row = res1[0];
479 if (double(row["Dist"])<min_dist)
480 {
481 Source source;
482 source.key = res1[0]["fSourceKey"];
483 source.isToO = res1[0]["fIsToO"];
484
485 Warn("Sources closer than "+Tools::Form("%.2f", check_dist)+"\u00b0 detected.");
486 Warn("Overwriting source key with: "+string(row["fSourceName"])+" ["+to_string(source.key)+"]: D="+Tools::Form("%.2f", double(row["Dist"]))+"\u00b0");
487
488 return source;
489 }
490 }
491
492 return Source();
493 }
494
495 float GetWobbleAngle(const double &ra, const double &dec)
496 {
497 const double wobble_offset = 0.6;
498 const double camera_radius = 2.3;
499 const double magnitude_max = 4.5;
500
501 /*
502 Mag Cnt
503 -2 1
504 -1 3
505 0 11
506 1 33
507 2 121
508 3 321
509 4 871
510 5 1364
511 6 404
512 7 12
513 */
514
515 // The wobble position lay in the plane which is normal the to the plane source-center-star
516 // and goes through
517
518 double wobble_angle = 0;
519
520 const string query0 =
521 "SELECT fSourceKEY, fRightAscension, fDeclination, fMagnitude,\n"
522 " ADIST(fDeclination, fRightAscension*15, "+to_string(dec)+", "+to_string(ra)+") AS Dist\n"
523 " FROM Source\n"
524 " WHERE (fSourceTypeKey=2 OR fSourceTypeKey=3) AND fMagnitude<"+to_string(magnitude_max)+"\n"
525 " HAVING Dist<"+to_string(camera_radius+wobble_offset)+"\n"
526 " ORDER BY fMagnitude ASC";
527
528 // Out() << query0 << endl;
529
530 const mysqlpp::StoreQueryResult res0 = fConnection.query(query0).store();
531
532 Info("Found "+to_string(res0.num_rows())+" stars in the camera field-of-view with magnitude less than "+to_string(magnitude_max));
533
534 for (size_t i=0; i<::min<size_t>(10, res0.num_rows()); i++)
535 {
536 const mysqlpp::Row &row = res0[i];
537
538 // TVector3 souce, star;
539 // source.SetMagThetaPhi(1, rad(90-dec), rad(ra));
540 // star.SetMagThetaPhi( 1, rad(90-dec), rad(ra));
541 //
542 // star.RotateZ( -source.Phi());
543 // source.RotateZ(-source.Phi());
544 //
545 // star.RotateY( 180-source.Theta());
546 // source.RotateY(180-source.Theta());
547 //
548 // rho = star.Phi();
549
550 const double rad = M_PI/180;
551
552 const double dec0 = rad * dec;
553 const double ra0 = rad * ra;
554
555 const double dec1 = rad * row["fDeclination"];
556 const double ra1 = rad * row["fRightAscension"] * 15;
557
558 const double s2 = sin(ra0-ra1);
559 const double c2 = cos(ra0-ra1);
560
561 const double s0 = cos(dec0);
562 const double c0 = sin(dec0);
563
564 const double c1 = cos(dec1);
565 const double s1 = sin(dec1);
566
567 const double k0 = -s2*c1;
568 const double k1 = s0*s1 - c0*c2*c1;
569
570 const double rho = atan(k0/k1) / rad; // atan2(k0, k1)/rad
571
572 if (i==0)
573 wobble_angle = rho;
574
575 Info(" "+Tools::Form("Mag=%5.2f Dist=%5.1f Phi=%6.1f", (double)row["fMagnitude"], (double)row["Dist"], rho));
576 }
577
578 if (res0.num_rows())
579 {
580 Info("The brightest star (M="+string(res0[0]["fMagnitude"])+") at distance is visible at a wobble angle of "+Tools::Form("%.2f", wobble_angle)+"\u00b0");
581 Info("Wobble angles determined as "+Tools::Form("%.2f", wobble_angle-90)+"\u00b0 and "+Tools::Form("%.2f", wobble_angle+90)+"\u000b");
582 }
583 else
584 {
585 Info("Using default wobble angles.");
586 }
587
588 return wobble_angle;
589 }
590
591 uint32_t AddSource(const string &name, const double &ra, const double &dec, const bool fDryRun=false)
592 {
593 const double wobble_angle = GetWobbleAngle(ra, dec);
594
595 const string query =
596 "INSERT INTO Source\n"
597 " (fSourceName, fRightAscension, fDeclination, fWobbleAngle0, fWobbleAngle1, fSourceTypeKey, fIsToO) VALUES\n"
598 " ('"+name+"', "+to_string(ra/15.)+", "+to_string(dec)+", "+to_string(wobble_angle-90)+", "+to_string(wobble_angle+90)+", 1, 1)";
599
600 if (fDryRun)
601 {
602 Out() << query << endl;
603 return -1;
604 }
605
606 auto q = fConnection.query(query);
607 q.execute();
608 if (!q.info().empty())
609 Info(q.info());
610
611 const uint32_t source_key = q.insert_id();
612
613 Info(string(fDryRun?"[dry-run] ":"")+"The new source got the key "+to_string(source_key));
614
615 return source_key;
616 }
617
618 bool ScheduleImp(const string &name, const ToO::DataGRB &grb, const CheckVisibility &check, const bool &is_pointing)
619 {
620 const bool fDryRun = GetCurrentState()!=ToO::State::kArmed;
621
622 if (fDryRun)
623 Warn("Scheduler not armed!");
624
625 Time stopwatch;
626
627 // This is just a desperate try which will most likely fail
628 if (!fConnection.connected())
629 fConnection.reconnect();
630
631 /*
632 +-----------------+---------------------+------+-----+---------+----------------+
633 | Field | Type | Null | Key | Default | Extra |
634 +-----------------+---------------------+------+-----+---------+----------------+
635 | fSourceKEY | int(11) | NO | PRI | NULL | auto_increment |
636 | fSourceName | varchar(30) | NO | | NULL | |
637 | fRightAscension | double(10,6) | NO | | NULL | |
638 | fDeclination | double(10,6) | NO | | NULL | |
639 | fEpochKEY | tinyint(4) | YES | | NULL | |
640 | fFlux | float | YES | | NULL | |
641 | fSlope | float | YES | | NULL | |
642 | fSourceTypeKey | int(11) | NO | MUL | NULL | |
643 | fWobbleOffset | float | NO | | 0.6 | |
644 | fWobbleAngle0 | int(11) | NO | | 90 | |
645 | fWobbleAngle1 | int(11) | NO | | -90 | |
646 | fMagnitude | float(4,2) | YES | | NULL | |
647 | fIsToO | tinyint(3) unsigned | NO | | 0 | |
648 +-----------------+---------------------+------+-----+---------+----------------+
649 */
650
651 Source source = GetSourceKey(name, grb.ra, grb.dec);
652
653 Out() << Time()-stopwatch << endl;
654
655 // This is not a real alert but a pointing information of a satellite
656 if (is_pointing)
657 {
658 // We only observe known sources in paralell with satellites...
659 if (source.key<0)
660 {
661 Warn("Observation of only known sources requested but no known source found.");
662 return false;
663 }
664
665 // ... and only if they are 'standard' sources and not ToO sources.
666 if (source.isToO)
667 {
668 Warn("Known source with ToO flag... skipping.");
669 return false;
670 }
671
672 // For non prioritized sources we require a threshold <3
673 // and for prioritized sources a threshold <10
674 const set<uint16_t> prioritized_sources = { 1, 2, 7 };
675
676 const bool prio = prioritized_sources.find(source.key)!=prioritized_sources.end();
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 && !prio) || !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.