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

Last change on this file since 19615 was 19588, checked in by tbretz, 5 years ago
Added fRa,fDec,fErr,fName to FlareTriggers insert and renamed key on request of Daniela.
File size: 49.1 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 // `Reschedule`: Start time before end time of ToO (type==data)
754 // `Following`: Start time after end time of ToO (type==any)
755 // `Delete`: Start time between start and end time of ToO (type==data)
756
757 vector<string> list;
758
759 int32_t firstdata = -1; // First observation with type==data
760 int32_t reschedule = -1; // Last observation with: Start time before end time of ToO (type==data)
761 int32_t following = -1; // First observation with: Start time after end time of ToO (type==any)
762 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)
763 for (size_t i=0; i<resS.num_rows(); i++)
764 {
765 const mysqlpp::Row &row = resS[i];
766
767 Out() << setw(2) << i << " | ";
768 Out() << row["Reschedule"] << " | ";
769 Out() << row["Following"] << " | ";
770 Out() << row["fScheduleID"] << " | ";
771 Out() << row["fStart"] << " | ";
772 if (bool(row["Delete"]))
773 {
774 Out() << " < delete > | ";
775 list.push_back((string)row["fScheduleID"]);
776 }
777 else
778 Out() << row["fLastUpdate"] << " | ";
779 Out() << row["fMeasurementID"] << " | ";
780 Out() << row["fUser"] << " | ";
781 Out() << row["fData"] << " | " << setw(4);
782 Out() << row["fSourceKey"] << " | ";
783 Out() << row["fMeasurementTypeKey"] << '\n';
784
785 if (bool(row["Reschedule"]))
786 reschedule = i;
787
788 if (bool(row["Reschedule"]) && !row["Delete"])
789 last_resch = i;
790
791 if (following==-1 && bool(row["Following"]))
792 following = i;
793
794 const uint16_t type = row["fMeasurementTypeKey"];
795 if (firstdata==-1 && type==4)
796 firstdata = i;
797 }
798
799 // --------------------------------------------------------------------
800
801 if (firstdata==-1)
802 {
803 Warn("No data run found.");
804 return false;
805 }
806
807 // --------------------------------------------------------------------
808
809 if (reschedule>=0)
810 Out() << "\nLast data run before restart at " << restart.GetAsStr() << ": idx=" << reschedule << " / ID=" << resS[reschedule]["fScheduleID"] << endl;
811
812 // --------------------------------------------------------------------
813
814 const Time first((string)resS[firstdata]["fStart"]); // First data run of the night
815 const Time last( (string)resS[resS.num_rows()-1]["fStart"]); // Last scheduled observation of the night
816
817 // --------------------------------------------------------------------
818
819 if (restart<=first)
820 {
821 Warn("ToO ends before first data taking... skipped.");
822 return false;
823 }
824 if (schedtime>=last)
825 {
826 Warn("ToO starts after data taking... skipped.");
827 return false;
828 }
829
830 // --------------------------------------------------------------------
831
832 if (schedtime<first)
833 {
834 Info("ToO starts before first data taking... rescheduling start time.");
835 schedtime = first;
836 }
837 if (restart>last)
838 {
839 Info("ToO ends after data taking... rescheduling end time!");
840 restart = last;
841 }
842
843 // --------------------------------------------------------------------
844
845 if (restart<schedtime+boost::posix_time::minutes(required))
846 {
847 Warn("Could not schedule more than the required "+to_string(required)+" minutes... skipped.");
848 return false;
849 }
850
851 // --------------------------------------------------------------------
852
853 if (following>=0)
854 {
855 const Time follow((string)resS[following]["fStart"]);
856 if (follow<restart+boost::posix_time::minutes(skip_time))
857 {
858 Info("Following observation would be less than "+to_string(skip_time)+" min... skipping rescheduled source.");
859 reschedule = -1;
860 }
861 }
862
863 // ====================================================================
864
865 if (!list.empty())
866 {
867 Out() << "\nDelete entries:\n";
868 for (const auto &l : list)
869 Out() << l << "\n";
870 Out() << endl;
871 }
872
873 // ====================================================================
874
875 if (source.key<0)
876 source.key = AddSource(name, grb.ra, grb.dec, fDryRun);
877
878 Out() << Time()-stopwatch << endl;
879
880 // ====================================================================
881
882 Info("Source will be scheduled.");
883
884 Out() << "New entries:\n";
885
886 vector<string> insert;
887
888 const bool ongoing = last_resch>=0 && source.key==uint16_t(resS[last_resch]["fSourceKey"]);
889
890 if (ongoing)
891 Out() << " | | | < ongoing > | 0 | | | " << setw(4) << source.key << " | 4\n";
892 else
893 {
894 Out() << " | auto | " << schedtime.GetAsStr() << " | < start > | 0 | ToO | " << (is_pointing?" nodrs ":"nodrs,grb") << " | " << setw(4) << source.key << " | 4\n";
895 insert.emplace_back("('"+schedtime.GetAsStr()+"',0,'ToO','"+(is_pointing?"nodrs:true":"nodrs:true,grb:true")+"',"+to_string(source.key)+",4)");
896 }
897
898 // --------------------------------------------------------------------
899
900 if (reschedule>=0 && restart!=last)
901 {
902 Out() << " | auto | " << restart.GetAsStr() << " | < end > | 0 | ToO | NULL | " << setw(4) << resS[reschedule]["fSourceKey"] << " | 4\n";
903
904 const string fData = (string)resS[reschedule]["fData"];
905 const string fKey = (string)resS[reschedule]["fSourceKey"];
906
907 const string data = resS[reschedule]["fData"] ? "'"+fData+"'" : "NULL";
908 insert.emplace_back("('"+restart.GetAsStr()+"',0,'ToO',"+data+","+fKey+",4)");
909 }
910
911 // ====================================================================
912
913 Out() << Time()-stopwatch << endl;
914
915 // ====================================================================
916
917 const string queryD = string("DELETE FROM Schedule WHERE fScheduleID IN (")+boost::algorithm::join(list, ",")+")";
918
919 const string queryI =
920 "INSERT INTO Schedule\n (fStart,fMeasurementID,fUser,fData,fSourceKey,fMeasurementTypeKey)\n"
921 "VALUES\n "+string(boost::algorithm::join(insert, ",\n "));
922
923 if (fDryRun)
924 {
925 Out() << "\n";
926 if (!list.empty())
927 Out() << queryD << "\n\n";
928 if (!insert.empty())
929 Out() << queryI << "\n\n";
930 Out() << flush;
931 return false;
932 }
933
934 // Empty interrupt to stop data taking as early as possible
935 // Triggers also RELOAD_SOURCES
936 if (!ongoing)
937 Dim::SendCommandNB("DIM_CONTROL/INTERRUPT", "prepare");
938
939 // ---------------------------------------------------------
940
941 fConnection.query("LOCK TABLES Schedule WRITE");
942
943 // ---------------------------------------------------------
944 if (!list.empty())
945 {
946 const mysqlpp::SimpleResult res = fConnection.query(queryD).execute();
947 Info(to_string(res.rows())+" row(s) deleted from Schedule.");
948 }
949
950 // ---------------------------------------------------------
951
952 if (!insert.empty())
953 {
954 auto q = fConnection.query(queryI);
955 q.execute();
956 if (!q.info().empty())
957 Info(q.info());
958 }
959 // ---------------------------------------------------------
960
961 fConnection.query("UNLOCK TABLES");
962
963 // ---------------------------------------------------------
964
965 if (!ongoing)
966 Dim::SendCommand("DIM_CONTROL/INTERRUPT", "reschedule");
967
968 ostringstream out;
969 out << Time()-stopwatch;
970 Info(out);
971
972 // ---------------------------------------------------------
973
974 const string queryT =
975 "INSERT INTO ToOs\n"
976 " (fTypeID, fRightAscension, fDeclination, fSourceKEY) VALUES\n"
977 " ('"+to_string(grb.type)+"', "+to_string(grb.ra/15)+", "+to_string(grb.dec)+", "+to_string(source.key)+")";
978
979 if (!fDryRun)
980 {
981 auto q = fConnection.query(queryT);
982 q.execute();
983 if (!q.info().empty())
984 Info(q.info());
985 }
986 else
987 Out() << queryT << endl;
988
989 // ---------------------------------------------------------
990
991 return true;
992 }
993/*
994 bool Schedule(const string &name, const ToO::DataGRB &grb, const CheckVisibility &check, const bool &is_pointing)
995 {
996 try
997 {
998 return ScheduleImp(name, grb, check, is_pointing);
999 }
1000 catch (const exception &e)
1001 {
1002 Error(string("SQL query failed: ")+e.what());
1003 fConnection.disconnect();
1004 return false;
1005 }
1006 }
1007*/
1008 // ========================================================================
1009
1010
1011 // ========================================================================
1012
1013 bool CheckEventSize(size_t has, const char *name, size_t size)
1014 {
1015 if (has==size)
1016 return true;
1017
1018 ostringstream msg;
1019 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
1020 Fatal(msg);
1021 return false;
1022 }
1023
1024 bool CheckMinEventSize(size_t has, const char *name, size_t size)
1025 {
1026 if (has>size)
1027 return true;
1028
1029 ostringstream msg;
1030 msg << name << " - Received event has " << has << " bytes, but expected more.";
1031 Fatal(msg);
1032 return false;
1033 }
1034
1035 void EnterIntoAlertDB(const string &name, const ToO::DataGRB &grb, const CheckVisibility &check, const bool &is_pointing)
1036 {
1037 // Do not enter satellite pointing alerts (as there are too many)
1038 if (is_pointing)
1039 return;
1040
1041 // Check for general visibility of the source during the night
1042 // If required: For better performance, both (sun and object)
1043 // RstTime could be calculated only here
1044 const double sun_set = fmod(check.vis_sun.set, 1);
1045 const double sun_rise = fmod(check.vis_sun.rise, 1);
1046
1047 const double rise = fmod(check.vis_obj.rise, 1);
1048 const double trans = fmod(check.vis_obj.transit, 1);
1049 const double set = fmod(check.vis_obj.set, 1);
1050
1051 if ((rise <sun_set || rise >sun_rise) &&
1052 (trans<sun_set || trans>sun_rise) &&
1053 (set <sun_set || set >sun_rise) )
1054 return;
1055
1056 Info("Source "+name+" might be observable.");
1057
1058 // Make an entry in the alter databse to issue a call
1059 const string queryS =
1060 "INSERT FlareAlerts.FlareTriggers SET\n"
1061 " fTriggerInserted=Now(),\n"
1062 " fNight="+to_string(Time().NightAsInt())+",\n"
1063 " fPacketTypeKey="+to_string(grb.type)+",\n"
1064 " fRa="+to_string(grb.ra/15)+",\n"
1065 " fDec="+to_string(grb.dec)+",\n"
1066 " fErr="+to_string(grb.err)+",\n"
1067 " fName=\""+name+"\",\n"
1068 " fTriggerType=7";
1069
1070 const bool fDryRun = GetCurrentState()!=ToO::State::kArmed;
1071
1072 if (!fDryRun)
1073 {
1074 auto q = fConnection.query(queryS);
1075 q.execute();
1076 if (!q.info().empty())
1077 Info(q.info());
1078 }
1079 else
1080 Out() << queryS << endl;
1081 }
1082
1083 int ScheduleGCN(const EventImp &evt)
1084 {
1085 if (!CheckMinEventSize(evt.GetSize(), "ScheduleGCN", sizeof(ToO::DataGRB)))
1086 return kSM_FatalError;
1087
1088 const ToO::DataGRB &grb = evt.Ref<ToO::DataGRB>();
1089
1090 const auto it = GCN::PaketTypes.find(grb.type);
1091 if (it==GCN::PaketTypes.end())
1092 {
1093 Warn("Unknown Packet Type: "+to_string(grb.type));
1094 return GetCurrentState();
1095 }
1096
1097
1098 const string name = evt.Ptr<char>(sizeof(ToO::DataGRB));
1099
1100 const auto &paket = it->second;
1101
1102 ostringstream out;
1103 out << name << " [" << paket.name << ":" << grb.type << "]: RA=" << grb.ra/15. << "h DEC=" << grb.dec << "\u00b0 ERR=" << grb.err << "\u00b0";
1104
1105 Info(out);
1106 Info(paket.description);
1107
1108 // The []-operator automatically creates a default entry if not yet existing
1109 auto &conditions = fVisibilityCriteria[grb.type];
1110
1111 const CheckVisibility check(conditions, grb.ra, grb.dec);
1112
1113 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]));
1114 if (check.valid_sun)
1115 {
1116 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]));
1117 Info("Zenith angle: "+Tools::Form("%5.1f\u00b0 ", check.position.zd)+(check.valid_zd ?"OK ":"failed")+Tools::Form(" [zd < %5.1f\u00b0]", conditions[kZenithMax]));
1118 Info("Current: "+Tools::Form("%5.1f\u00b5A ", check.current) +(check.valid_current?"OK ":"failed")+Tools::Form(" [I < %5.1f\u00b5A]", conditions[kCurrentMax]));
1119 //Info(string("Rel. threshold: ")+(check.valid_threshold?"OK ":"failed")+" ["+Tools::Form("%5.1f", check.threshold)+"]");
1120 }
1121
1122/*
1123 * Swift: https://gcn.gsfc.nasa.gov/swift.html
1124 relevante Paket-Typen:
1125 60 BAT_Alert
1126 97 BAT_QL_Pos
1127 61 BAT_Pos
1128 62 BAT_Pos_Nack
1129 Es kann wohl vorkommen, dass ein Alert danach anders klassifiziert ist
1130 -> BAT_Trans (84) und es steht dass dann in der GRB notice eine catalog
1131 ID im comment steht. Da ist es mir noch nicht so ganz klar, woran man
1132 echte Alerts erkennt.
1133
1134 * Fermi: https://gcn.gsfc.nasa.gov/fermi.html
1135 relevante Paket-Typen:
1136 110 GBM_Alert P B B B&A
1137 111 GBM_Flt_Pos P B B B&A
1138 112 GBM_Gnd_Pos P B B B&A
1139 115 GBM_Final_Pos P B B B&A
1140 Bei GBM müssen wir usn überlegen wie wir es mit den Positionsupdates
1141 machen - das können auch mal ein paar Grad sein. siehe zB
1142 https://gcn.gsfc.nasa.gov/other/568833894.fermi
1143
1144 Wenn ich das richtig sehe, kommt bei GBM_Alert noch keine
1145 Position, sndern erst mit GBM_Flt_Pos (in dem Bsp kommt das 3 Sek
1146 später)
1147
1148 * INTEGRAL: https://gcn.gsfc.nasa.gov/integral.html
1149 wenn ich es richtig verstehe, sind die interessanten Alerts die WAKEUP
1150 (tw kommt ein Positionsupdate via OFFLINE oder REFINED). Es gibt auch
1151 noch WEAK, aber das sind scheinbar subthreshold alerts - würde ich
1152 jetzt erst mal weglassen. Im letzten Jahr gab es 8 WAKEUP alerts. 2017
1153 sogar nur 3, 2016 7, 2015 waren es einige mehr, aber das war V404_Cyg -
1154 in dem Fall steht dann aber ein Object-Name dabei - ansonsten steht as
1155 "Possbibly real GRB event" - zT steht auch, dass es coincident mit GBM
1156 events ist
1157
1158 * KONUS: https://gcn.gsfc.nasa.gov/konus.html
1159 Ist mir noch etwas unklar, was das genau ist - die schicken Lichtkurven
1160 von GRBs und Transients - muss man nochmal genauer schauen.
1161
1162 * MAXI: https://gcn.gsfc.nasa.gov/maxi.html
1163 wenn ich das richtig verstehe, sind das nicht nur GRBs -> müsste man
1164 nochmal genauer schauen bevor man da was implementiert
1165
1166 * AGILE: https://gcn.gsfc.nasa.gov/agile.html
1167 Wahrscheinlich eher nicht relevant, da steht was von 30min nach dem
1168 Burst kommt das 'Wakeup'. Die relevanten Paket-Typen wären 100
1169 (Wakeup_Pos), 101 (Ground_Pos) und 102 (Refined_Pos)
1170
1171 * AMON: https://gcn.gsfc.nasa.gov/amon.html
1172 sind bisher nur neutrinos - da macht MAGIC glaub ich schon automatic
1173 Follow-Up - müssen wir also kucken was Sinn macht
1174
1175 * Ligo-Virgo GW: https://gcn.gsfc.nasa.gov/lvc.html
1176 da ist natürlich die Error-Region groß - müsste man schauen, was man
1177 da will
1178
1179 Fazit: am relevantesten sind Fermi/GBM und Swift/BAT. Evtl könnte
1180 INTEGRAL noch Sinn machen und über AMON und LVC sollte man nachdenken,
1181 ob/was man haben will.
1182*/
1183
1184 const bool is_pointing = grb.type==51 || grb.type==83;
1185
1186 try
1187 {
1188 if (!check.visible)
1189 {
1190 Info("Source not observable... skipped.");
1191 EnterIntoAlertDB(name, grb, check, is_pointing);
1192
1193 return fConnection.connected() ? GetCurrentState() : ToO::State::kDisconnected;
1194 }
1195
1196 Info("Source is visible... scheduling.");
1197
1198 if (!ScheduleImp(name, grb, check, is_pointing))
1199 EnterIntoAlertDB(name, grb, check, is_pointing);
1200 }
1201 catch (const exception &e)
1202 {
1203 Error(string("SQL query failed: ")+e.what());
1204 fConnection.disconnect();
1205 }
1206
1207 return fConnection.connected() ? GetCurrentState() : ToO::State::kDisconnected;
1208 }
1209
1210 int AddNewSource(const EventImp &evt)
1211 {
1212 if (!CheckMinEventSize(evt.GetSize(), "AddNewSource", 2*sizeof(double)))
1213 return kSM_FatalError;
1214
1215 const double &ra = evt.Ptr<double>()[0];
1216 const double &dec = evt.Ptr<double>()[1];
1217 const string name = evt.Ptr<char>(sizeof(double)*2);
1218
1219 ostringstream out;
1220 out << "New Source: '" << name << "' RA=" << ra << "h DEC=" << dec << "\u00b0";
1221
1222 Info(out);
1223
1224 try
1225 {
1226 const Source source = GetSourceKey(name, ra*15, dec);
1227
1228 if (source.key>=0) // Not a known source
1229 Warn("Source already known with key="+to_string(source.key)+".");
1230 else
1231 /*source.key =*/ AddSource(name, ra*15, dec);
1232
1233 }
1234 catch (const exception &e)
1235 {
1236 Error(string("SQL query failed: ")+e.what());
1237 fConnection.disconnect();
1238 }
1239
1240 return fConnection.connected() ? GetCurrentState() : ToO::State::kDisconnected;
1241 }
1242
1243 int Enable()
1244 {
1245 return GetCurrentState()==ToO::State::kConnected ? ToO::State::kArmed : GetCurrentState();
1246 }
1247 int Disable()
1248 {
1249 return GetCurrentState()==ToO::State::kArmed ? ToO::State::kConnected : GetCurrentState();
1250 }
1251
1252 int Execute()
1253 {
1254 Time now;
1255 if (now>fKeepAliveDeadline)
1256 {
1257 static int ernum = 0;
1258 try
1259 {
1260 // Unfortunately, reconnecting [Takes about 1s] is
1261 // the only non-blocking way to ensure an open
1262 // connection. Ping would be nice but is blocking.
1263 fConnection = Database(fUri);
1264 ernum = 0;
1265 }
1266 catch (const mysqlpp::ConnectionFailed &e)
1267 {
1268 if (ernum!=e.errnum())
1269 Error(e.what());
1270 ernum = e.errnum();
1271 }
1272
1273 fKeepAliveDeadline += boost::posix_time::seconds(fKeepAliveInterval);
1274 }
1275
1276 if (!fConnection.connected())
1277 return ToO::State::kDisconnected;
1278
1279 if (fConnection.connected() && GetCurrentState()<=ToO::State::kDisconnected)
1280 return ToO::State::kArmed;
1281
1282 return GetCurrentState();
1283 }
1284
1285public:
1286 StateMachineToO(ostream &out=cout) : StateMachineDim(out, "SCHEDULER"),
1287 fConnection(""), fKeepAliveDeadline(Time())
1288 {
1289 AddStateName(ToO::State::kDisconnected, "Disconnected",
1290 "The Dim DNS is reachable, but the required subsystems are not available.");
1291
1292 AddStateName(ToO::State::kConnected, "Connected",
1293 "All needed subsystems are connected to their hardware, no action is performed.");
1294
1295 AddStateName(ToO::State::kArmed, "Armed",
1296 "All needed subsystems are connected to their hardware, scheduling in progress.");
1297
1298 AddEvent("GCN", "S:1;D:1;D:1;D:1;C")
1299 (bind(&StateMachineToO::ScheduleGCN, this, placeholders::_1))
1300 ("Schedule a ToO"
1301 "|type[int16]:Pakage or type ID (see HeadersToO.h)"
1302 "|ra[deg]:Right ascension"
1303 "|dec[deg]:Declination"
1304 "|err[deg]:Error radius"
1305 "|name[string]:Tentative or known source name");
1306
1307 AddEvent("ADD_SOURCE", "D:1;D:1;C")
1308 (bind(&StateMachineToO::AddNewSource, this, placeholders::_1))
1309 ("Add a new source to the databse if not yet available"
1310 "|ra[h]:Right ascension"
1311 "|dec[deg]:Declination"
1312 "|name[string]:Monitored source name");
1313
1314 AddEvent("START", "")
1315 (bind(&StateMachineToO::Enable, this/*, placeholders::_1*/))
1316 ("");
1317
1318 AddEvent("STOP", "")
1319 (bind(&StateMachineToO::Disable, this/*, placeholders::_1*/))
1320 ("");
1321 }
1322
1323 /*
1324 bool GetConfig(Configuration &conf, const string &name, const string &sub, uint16_t &rc)
1325 {
1326 if (conf.HasDef(name, sub))
1327 {
1328 rc = conf.GetDef<uint16_t>(name, sub);
1329 return true;
1330 }
1331
1332 Error("Neither "+name+"default nor "+name+sub+" found.");
1333 return false;
1334 }*/
1335
1336 void AddCriteria(const vector<uint16_t> &ids, const VisibilityCriterion &crit, const float &limit)
1337 {
1338 // The []-operator automatically creates a default entry if not yet existing
1339 for (auto id=ids.begin(); id!=ids.end(); id++)
1340 fVisibilityCriteria[*id][crit] = limit;
1341 }
1342
1343 int EvalOptions(Configuration &conf)
1344 {
1345 fVerbose = !conf.Get<bool>("quiet");
1346 fKeepAliveInterval = conf.Get<uint16_t>("keep-alive");
1347 fUri = conf.Get<string>("schedule-database");
1348
1349 const vector<uint16_t> GRBs =
1350 {
1351 60, 61, 62, 97, 110, 111, 112, 115, 53, 54, 55, 56, 100, 101, 102
1352 };
1353
1354 AddCriteria(GRBs, kCurrentMax, 30);
1355 AddCriteria(GRBs, kZenithMax, 45);
1356
1357 return -1;
1358 }
1359};
1360
1361
1362// ------------------------------------------------------------------------
1363
1364#include "Main.h"
1365
1366template<class T>
1367int RunShell(Configuration &conf)
1368{
1369 return Main::execute<T, StateMachineToO>(conf);
1370}
1371
1372void SetupConfiguration(Configuration &conf)
1373{
1374 po::options_description control("Scheduler");
1375 control.add_options()
1376 ("quiet,q", po_bool(), "")
1377 ("keep-alive", var<uint16_t>(uint16_t(300)), "Interval in seconds to ping (reconnect) the database server")
1378 ("schedule-database", var<string>(), "Database link as in\n\tuser:password@server[:port]/database[?compress=0|1].")
1379 ;
1380
1381 conf.AddOptions(control);
1382}
1383
1384void PrintUsage()
1385{
1386 cout <<
1387 "The scheduler program is able to schedule a new observation.\n"
1388 "\n"
1389 "Usage: scheduler [-c type] [OPTIONS]\n"
1390 " or: scheduler [OPTIONS]\n";
1391 cout << endl;
1392}
1393
1394void PrintHelp()
1395{
1396 Main::PrintHelp<StateMachineToO>();
1397}
1398
1399int main(int argc, const char* argv[])
1400{
1401 Configuration conf(argv[0]);
1402 conf.SetPrintUsage(PrintUsage);
1403 Main::SetupConfiguration(conf);
1404 SetupConfiguration(conf);
1405
1406 if (!conf.DoParse(argc, argv, PrintHelp))
1407 return 127;
1408
1409 if (!conf.Has("console"))
1410 return RunShell<LocalStream>(conf);
1411
1412 if (conf.Get<int>("console")==0)
1413 return RunShell<LocalShell>(conf);
1414 else
1415 return RunShell<LocalConsole>(conf);
1416
1417 return 0;
1418}
1419
Note: See TracBrowser for help on using the repository browser.