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

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