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

Last change on this file since 19466 was 19438, checked in by tbretz, 6 years ago
This is a new version which is supposed to be able schedule ToOs
File size: 38.2 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
24/*
25void SetupConfiguration(Configuration &conf);
26{
27 po::options_description control("Makeschedule");
28 control.add_options()
29 ("uri", var<string>(), "Database link as in\n\tuser:password@server[:port]/database[?compress=0|1].")
30 ;
31
32 // po::positional_options_description p;
33 // p.add("date", 1); // The first positional options
34
35 conf.AddOptions(control);
36 //conf.SetArgumentPositions(p);
37}
38
39void PrintUsage();
40{
41 cout <<
42 "makeschedule - Creates an automatic schedule\n"
43 "\n"
44 "Usage: makeschedule [yyyy-mm-dd]\n";
45 cout << endl;
46}
47
48void PrintHelp();
49{
50 cout << endl;
51}*/
52
53struct CheckVisibility
54{
55 // Input
56 float moon_min = 10;
57 float moon_max = 170;
58 float sun_max = -12;
59 float zd_max = 75;
60 float current_max = 110;
61
62 // Output
63 Nova::SolarObjects solarobj;
64 Nova::ZdAzPosn position;
65
66 double moon_dist = -1;
67 double current = -1;
68
69 bool valid_zd = false;
70 bool valid_current = false;
71 bool valid_sun = false;
72 bool valid_moon = false;
73
74 bool visible = false;
75
76 void calc(const Nova::EquPosn &equ, const double &jd)
77 {
78 solarobj = Nova::SolarObjects(jd);
79 position = Nova::GetHrzFromEqu(equ, jd);
80 moon_dist = Nova::GetAngularSeparation(equ, solarobj.fMoonEqu);
81
82 current = FACT::PredictI(solarobj, equ);
83
84 valid_moon = moon_dist>moon_min && moon_dist<moon_max;
85 valid_zd = position.zd<zd_max;
86 valid_sun = solarobj.fSunHrz.alt<sun_max;
87 valid_current = current<current_max;
88
89 visible = valid_moon && valid_zd && valid_sun && valid_current;
90 }
91
92 CheckVisibility(const Nova::EquPosn &equ, const double &jd)
93 {
94 calc(equ, jd);
95 }
96
97 CheckVisibility(const Nova::EquPosn &equ)
98 {
99 calc(equ, Time().JD());
100 }
101
102 CheckVisibility(const double &ra, const double &dec, const double &jd)
103 {
104 Nova::EquPosn equ;
105 equ.ra = ra;
106 equ.dec = dec;
107 calc(equ, jd);
108 }
109
110 CheckVisibility(const double &ra, const double &dec)
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 string fUri;
414 Database fConnection;
415 uint16_t fVerbose;
416 bool fDryRun;
417
418 Time fKeepAliveDeadline;
419 uint16_t fKeepAliveInterval;
420
421 bool ScheduleImp(const string &name, const double &ra, const double &dec)
422 {
423 fDryRun = true;
424
425 Time stopwatch;
426
427 // This is just a desperate try which will most likely fail
428 if (!fConnection.connected())
429 fConnection.reconnect();
430
431 /*
432 +-----------------+---------------------+------+-----+---------+----------------+
433 | Field | Type | Null | Key | Default | Extra |
434 +-----------------+---------------------+------+-----+---------+----------------+
435 | fSourceKEY | int(11) | NO | PRI | NULL | auto_increment |
436 | fSourceName | varchar(30) | NO | | NULL | |
437 | fRightAscension | double(10,6) | NO | | NULL | |
438 | fDeclination | double(10,6) | NO | | NULL | |
439 | fEpochKEY | tinyint(4) | YES | | NULL | |
440 | fFlux | float | YES | | NULL | |
441 | fSlope | float | YES | | NULL | |
442 | fSourceTypeKey | int(11) | NO | MUL | NULL | |
443 | fWobbleOffset | float | NO | | 0.6 | |
444 | fWobbleAngle0 | int(11) | NO | | 90 | |
445 | fWobbleAngle1 | int(11) | NO | | -90 | |
446 | fMagnitude | float(4,2) | YES | | NULL | |
447 | fIsToO | tinyint(3) unsigned | NO | | 0 | |
448 +-----------------+---------------------+------+-----+---------+----------------+
449 */
450
451 const double min_dist = 0.1;
452 const double wobble_offset = 0.6;
453 const double camera_radius = 2.3;
454 const double magnitude_max = 4.5;
455
456 double wobble_angle = 0;
457
458 /*
459 Mag Cnt
460 -2 1
461 -1 3
462 0 11
463 1 33
464 2 121
465 3 321
466 4 871
467 5 1364
468 6 404
469 7 12
470 */
471
472 // The wobble position lay in the plane whihc is normal the to the plane source-center-star
473 // and goes through
474
475 const string query0 =
476 "SELECT fSourceKEY, fRightAscension, fDeclination, fMagnitude,\n"
477 " ADIST(fDeclination, fRightAscension*15, "+to_string(dec)+", "+to_string(ra)+") AS Dist\n"
478 " FROM Source\n"
479 " WHERE (fSourceTypeKey=2 OR fSourceTypeKey=3) AND fMagnitude<"+to_string(magnitude_max)+"\n"
480 " HAVING Dist<"+to_string(camera_radius+wobble_offset)+"\n"
481 " ORDER BY fMagnitude ASC";
482
483 Out() << query0 << endl;
484
485 const mysqlpp::StoreQueryResult res0 = fConnection.query(query0).store();
486
487 Out() << Time()-stopwatch << endl;
488
489 Info("Found "+to_string(res0.num_rows())+" stars in the camera field-of-view with magnitude less than "+to_string(magnitude_max));
490
491 for (size_t i=0; i<::min<size_t>(10, res0.num_rows()); i++)
492 {
493 const mysqlpp::Row &row = res0[i];
494
495 // TVector3 souce, star;
496 // source.SetMagThetaPhi(1, rad(90-dec), rad(ra));
497 // star.SetMagThetaPhi( 1, rad(90-dec), rad(ra));
498 //
499 // star.RotateZ( -source.Phi());
500 // source.RotateZ(-source.Phi());
501 //
502 // star.RotateY( 180-source.Theta());
503 // source.RotateY(180-source.Theta());
504 //
505 // rho = star.Phi();
506
507 const double rad = M_PI/180;
508
509 const double dec0 = rad * dec;
510 const double ra0 = rad * ra;
511
512 const double dec1 = rad * row["fDeclination"];
513 const double ra1 = rad * row["fRightAscension"] * 15;
514
515 const double s2 = sin(ra0-ra1);
516 const double c2 = cos(ra0-ra1);
517
518 const double s0 = cos(dec0);
519 const double c0 = sin(dec0);
520
521 const double c1 = cos(dec1);
522 const double s1 = sin(dec1);
523
524 const double k0 = -s2*c1;
525 const double k1 = s0*s1 - c0*c2*c1;
526
527 const double rho = atan(k0/k1) / rad; // atan2(k0, k1)/rad
528
529 if (i==0)
530 wobble_angle = rho;
531
532 Info(" "+Tools::Form("Mag=%5.2f Dist=%5.1f Phi=%6.1f", (double)row["fMagnitude"], (double)row["Dist"], rho));
533 }
534
535 if (res0.num_rows())
536 {
537 Info("The brightest star (M="+string(res0[0]["fMagnitude"])+") at distance is visible at a wobble angle of "+Tools::Form("%.2f", wobble_angle)+"\u00b0");
538 Info("Wobble angles determined as "+Tools::Form("%.2f", wobble_angle-90)+"\u00b0 and "+Tools::Form("%.2f", wobble_angle+90)+"\u000b");
539 }
540 else
541 {
542 Info("Using default wobble angles.");
543 }
544
545 const string query1 =
546 "SELECT fSourceKey, fSourceName\n"
547 " FROM Source\n"
548 " WHERE ADIST(fDeclination, fRightAscension*15, "+to_string(dec)+", "+to_string(ra)+")<"+to_string(min_dist)+"\n"
549 " ORDER BY fSourceKey ASC";
550
551 const mysqlpp::StoreQueryResult res1 = fConnection.query(query1).store();
552
553 if (res1.num_rows())
554 {
555 Warn("The following sources in the source table have a distance less than "+to_string(min_dist)+"\u00b0");
556
557 for (size_t i=0; i<res1.num_rows(); i++)
558 {
559 const mysqlpp::Row &row = res1[i];
560 Warn(" "+string(row["fSourceName"])+" ["+to_string(uint32_t(row["fSourceKey"]))+"]");
561 }
562 }
563
564 Out() << Time()-stopwatch << endl;
565
566 int32_t source_key = -1;
567
568 const string query2 =
569 "SELECT fSourceKey FROM Source WHERE fSourceName='"+name+"'";
570
571 const mysqlpp::StoreQueryResult res2 = fConnection.query(query2).store();
572
573 if (res2.num_rows())
574 {
575 source_key = res2[0]["fSourceKey"];
576 Info("A source with the same name (key="+to_string(source_key)+") was found in the Source table.");
577 }
578
579 if (source_key<0)
580 {
581 const string query =
582 "INSERT INTO Source\n"
583 " (fSourceName, fRightAscension, fDeclination, fWobbleAngle0, fWobbleAngle1, fSourceTypeKey, fIsToO) VALUES\n"
584 " ('"+name+"', "+to_string(ra/15.)+", "+to_string(dec)+", "+to_string(wobble_angle-90)+", "+to_string(wobble_angle+90)+", 1, 1)";
585
586 if (!fDryRun)
587 {
588 auto q = fConnection.query(query);
589 q.execute();
590 Info(q.info());
591
592 source_key = q.insert_id();
593
594 Info(string(fDryRun?"[dry-run] ":"")+"The new source got the key "+to_string(source_key));
595 }
596 else
597 Out() << query << endl;
598
599 }
600
601 Out() << Time()-stopwatch << endl;
602
603 /*
604 +---------------------+--------------+------+-----+-------------------+-----------------------------+
605 | Field | Type | Null | Key | Default | Extra |
606 +---------------------+--------------+------+-----+-------------------+-----------------------------+
607 | fScheduleID | int(11) | NO | PRI | NULL | auto_increment |
608 | fStart | datetime | NO | MUL | NULL | |
609 | fLastUpdate | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
610 | fMeasurementID | int(11) | NO | | NULL | |
611 | fUser | varchar(32) | NO | | NULL | |
612 | fData | varchar(256) | YES | | NULL | |
613 | fSourceKey | smallint(6) | YES | | NULL | |
614 | fMeasurementTypeKey | tinyint(4) | NO | | NULL | |
615 +---------------------+--------------+------+-----+-------------------+-----------------------------+
616 */
617
618 const uint16_t contingency = 1; // The contingency to delete earlier entries from the Table
619 const uint16_t duration = 60; // The target duration of the ToO
620 const uint16_t required = 20; // If the ToO would be less than required, skip_it
621 const uint16_t skip_time = 20; // If following observation would be less than skip_time, skip it
622
623 if (duration<required)
624 {
625 Error("Requested duration is smaller than required time.");
626 return false;
627 }
628
629 const Time now;
630
631 const Time sunset = now.GetPrevSunSet();
632 const Time sunrise = now.GetNextSunRise();
633
634 if (sunset>sunrise || sunrise>sunset+boost::posix_time::hours(24))
635 {
636 Error("Unable to schedule a ToO during daytime.");
637 return false;
638 }
639
640 // Safety margin
641 Time schedtime = now-boost::posix_time::seconds(contingency);
642 Time restart = now+boost::posix_time::minutes(duration);
643
644 Out() << '\n';
645 Out() << "Contingency: " << contingency << " s\n";
646 Out() << "Start of ToO: " << now.GetAsStr() << '\n';
647 Out() << "End of ToO: " << restart.GetAsStr() << '\n';
648 Out() << "Duration: " << duration << " min\n\n";
649
650 Out() << "Schedule:\n";
651
652 const string queryS =
653 "SELECT *,\n"
654 " (fMeasurementTypeKey=4 AND fStart<='"+restart.GetAsStr()+"') AS `Reschedule`,\n"
655 " (fStart>'"+restart.GetAsStr()+"') AS `Following`,\n"
656 " (fMeasurementTypeKey=4 AND fStart BETWEEN '"+schedtime.GetAsStr()+"' AND '"+restart.GetAsStr()+"') AS `Delete`\n"
657 " FROM Schedule\n"
658 " WHERE fStart BETWEEN '"+sunset.GetAsStr()+"' AND '"+sunrise.GetAsStr()+"'\n"
659 " AND fMeasurementID=0\n"
660 " ORDER BY fStart ASC, fMeasurementID ASC";
661
662 const mysqlpp::StoreQueryResult resS = fConnection.query(queryS).store();
663
664 if (resS.num_rows()<2)
665 {
666 Error("Available schedule is too short to be evaluated.");
667 return false;
668 }
669
670 vector<string> list;
671
672 int32_t firstdata = -1;
673 int32_t reschedule = -1;
674 int32_t following = -1;
675 for (size_t i=0; i<resS.num_rows(); i++)
676 {
677 const mysqlpp::Row &row = resS[i];
678
679 Out() << setw(2) << i << " | ";
680 Out() << row["Reschedule"] << " | ";
681 Out() << row["Following"] << " | ";
682 Out() << row["fScheduleID"] << " | ";
683 Out() << row["fStart"] << " | ";
684 if (bool(row["Delete"]))
685 {
686 Out() << " < delete > | ";
687 list.push_back((string)row["fScheduleID"]);
688 }
689 else
690 Out() << row["fLastUpdate"] << " | ";
691 Out() << row["fMeasurementID"] << " | ";
692 Out() << row["fUser"] << " | ";
693 Out() << row["fData"] << " | " << setw(4);
694 Out() << row["fSourceKey"] << " | ";
695 Out() << row["fMeasurementTypeKey"] << '\n';
696
697 if (bool(row["Reschedule"]))
698 reschedule = i;
699
700 if (following==-1 && bool(row["Following"]))
701 following = i;
702
703 const uint16_t type = row["fMeasurementTypeKey"];
704 if (firstdata==-1 && type==4)
705 firstdata = i;
706 }
707
708 // --------------------------------------------------------------------
709
710 if (firstdata==-1)
711 {
712 Warn("No data run found.");
713 return false;
714 }
715
716 // --------------------------------------------------------------------
717
718 if (reschedule>=0)
719 Out() << "\nLast data run before restart at " << restart.GetAsStr() << ": idx=" << reschedule << " / ID=" << resS[reschedule]["fScheduleID"] << endl;
720
721 // --------------------------------------------------------------------
722
723 const Time first((string)resS[firstdata]["fStart"]); // First data run of the night
724 const Time last( (string)resS[resS.num_rows()-1]["fStart"]); // Last scheduled observation of the night
725
726 // --------------------------------------------------------------------
727
728 if (restart<=first)
729 {
730 Warn("ToO ends before first data taking... skipped.");
731 return false;
732 }
733 if (schedtime>=last)
734 {
735 Warn("ToO starts after data taking... skipped.");
736 return false;
737 }
738
739 // --------------------------------------------------------------------
740
741 if (schedtime<first)
742 {
743 Info("ToO starts before first data taking... rescheduling start time.");
744 schedtime = first;
745 }
746 if (restart>last)
747 {
748 Info("ToO ends after data taking... rescheduling end time!");
749 restart = last;
750 }
751
752 // --------------------------------------------------------------------
753
754 if (restart<schedtime+boost::posix_time::minutes(required))
755 {
756 Warn("Could not schedule more than the required "+to_string(required)+" minutes... skipped.");
757 return false;
758 }
759
760 // --------------------------------------------------------------------
761
762 if (following>=0)
763 {
764 const Time follow((string)resS[following]["fStart"]);
765 if (follow<restart+boost::posix_time::minutes(skip_time))
766 {
767 Info("Following observation would be less than "+to_string(skip_time)+" min... skipping rescheduled source.");
768 reschedule = -1;
769 }
770 }
771
772 // ====================================================================
773
774 if (!list.empty())
775 {
776 Out() << "\nDelete entries:\n";
777 for (const auto &l : list)
778 Out() << l << "\n";
779 Out() << endl;
780 }
781
782 // ====================================================================
783
784 Info("Source will be scheduled.");
785
786 vector<string> insert;
787
788 Out() << "New entries:\n";
789 Out() << " | auto | " << schedtime.GetAsStr() << " | < start > | 0 | ToO | nodrs,grb | " << setw(4) << source_key << " | 4\n";
790
791 insert.emplace_back("('"+schedtime.GetAsStr()+"',0,'ToO','nodrs:true,grb:true',"+to_string(source_key)+",4)");
792
793 // --------------------------------------------------------------------
794
795 if (reschedule>=0 && restart!=last)
796 {
797 Out() << " | auto | " << restart.GetAsStr() << " | < end > | 0 | ToO | NULL | " << setw(4) << resS[reschedule]["fSourceKey"] << " | 4\n";
798
799 const string fData = (string)resS[reschedule]["fData"];
800 const string fKey = (string)resS[reschedule]["fSourceKey"];
801
802 const string data = resS[reschedule]["fData"] ? "'"+fData+"'" : "NULL";
803 insert.emplace_back("('"+restart.GetAsStr()+"',0,'ToO',"+data+","+fKey+",4)");
804 }
805
806 // ====================================================================
807
808 Out() << Time()-stopwatch << endl;
809
810 // ====================================================================
811
812 const string queryD = string("DELETE FROM Schedule WHERE fScheduleID IN (")+boost::algorithm::join(list, ",")+")";
813
814 const string queryI =
815 "INSERT INTO Schedule\n (fStart,fMeasurementID,fUser,fData,fSoureKey,fMeasurementTypeKey)\n"
816 "VALUES\n "+string(boost::algorithm::join(insert, ",\n "));
817
818 if (fDryRun)
819 {
820 Out() << "\n";
821 if (!list.empty())
822 Out() << queryD << "\n\n";
823 if (!insert.empty())
824 Out() << queryI << "\n\n";
825 Out() << flush;
826 return false;
827 }
828
829 // Empty interrupt to stop data taking as early as possible
830 Dim::SendCommandNB("DIM_CONTROL/INTERRUPT");
831
832 // Reload sources as early as possible
833 Dim::SendCommandNB("DRIVE_CONTROL/RELOAD_SOURCES");
834
835 // Start pointing procedure as early as possible
836 //Dim::SendCommand("DRIVE_CONTROL/TRACK_WOBBLE", wobble, obs[sub].source);
837
838 // ---------------------------------------------------------
839
840 fConnection.query("LOCK TABLES Schedule WRITE");
841
842 // ---------------------------------------------------------
843 /*
844 if (!list.empty())
845 {
846 const mysqlpp::SimpleResult res = fConnection.query(queryD).execute();
847 Info(to_string(res.rows())+" row(s) deleted from Schedule.");
848 }
849
850 // ---------------------------------------------------------
851
852 if (!insert.empty())
853 {
854 auto q = fConnection.query(queryI);
855 q.execute();
856 Info(q.info());
857 }
858 */
859 // ---------------------------------------------------------
860
861 fConnection.query("UNLOCK TABLES");
862
863 // ---------------------------------------------------------
864
865 Dim::SendCommand("DIM_CONTROL/INTERRUPT", "reschedule");
866
867 ostringstream out;
868 out << Time()-stopwatch;
869 Info(out);
870
871 return true;
872 }
873
874 bool Schedule(const string &name, const double &ra, const double &dec)
875 {
876 try
877 {
878 return ScheduleImp(name, ra, dec);
879 }
880 catch (const exception &e)
881 {
882 Error(string("SQL query failed: ")+e.what());
883 fConnection.disconnect();
884 return false;
885 }
886 }
887
888 // ========================================================================
889
890
891 // ========================================================================
892
893 bool CheckEventSize(size_t has, const char *name, size_t size)
894 {
895 if (has==size)
896 return true;
897
898 ostringstream msg;
899 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
900 Fatal(msg);
901 return false;
902 }
903
904 int ScheduleGCN(const EventImp &evt)
905 {
906 if (!CheckEventSize(evt.GetSize(), "ScheduleGCN", 2+4+3*8))
907 return kSM_FatalError;
908
909 const ToO::DataGRB &grb = evt.Ref<ToO::DataGRB>();
910
911 const GCN::PaketType_t *ptr=GCN::kTypes;
912 for (; ptr->type>=0 && ptr->type!=grb.type; ptr++);
913 if (ptr->type==-1)
914 {
915 Warn("Unknown Packet Type: "+to_string(ptr->type));
916 return GetCurrentState();
917 }
918
919 ostringstream out;
920 out << "Received: '" << ptr->name << "' ID=" << grb.type << " NUM=" << grb.trigid << " RA=" << grb.ra/15. << "h DEC=" << grb.dec << "\u00b0 ERR=" << grb.err;
921
922 Info(out);
923 Info(ptr->description);
924
925 const CheckVisibility check(grb.ra, grb.dec);
926
927 Info(string("Source is")+(check.visible?" ":" NOT ")+"visible.");
928
929 Info(string("Sun altitude: ")+(check.valid_sun ?"OK ":"failed")+" ["+Tools::Form("%5.1f", check.solarobj.fSunHrz.alt)+"\u00b0]");
930 Info(string("Moon distance: ")+(check.valid_moon ?"OK ":"failed")+" ["+Tools::Form("%5.1f", check.moon_dist)+"\u00b0]");
931 Info(string("Zenith angle: ")+(check.valid_zd ?"OK ":"failed")+" ["+Tools::Form("%5.1f", check.position.zd)+"\u00b0]");
932 Info(string("Current: ")+(check.valid_current?"OK ":"failed")+" ["+Tools::Form("%5.1f", check.current)+" \u00b5A]");
933
934 const string name = ptr->instrument+"#"+to_string(grb.trigid);
935 Info("Source name set to '"+name+"'");
936
937 /*
938 * Swift: https://gcn.gsfc.nasa.gov/swift.html
939 relevante Paket-Typen:
940 60 BAT_Alert
941 97 BAT_QL_Pos
942 61 BAT_Pos
943 62 BAT_Pos_Nack
944 Es kann wohl vorkommen, dass ein Alert danach anders klassifiziert ist
945 -> BAT_Trans (84) und es steht dass dann in der GRB notice eine catalog
946 ID im comment steht. Da ist es mir noch nicht so ganz klar, woran man
947 echte Alerts erkennt.
948
949 * Fermi: https://gcn.gsfc.nasa.gov/fermi.html
950 relevante Paket-Typen:
951 110 GBM_Alert P B B B&A
952 111 GBM_Flt_Pos P B B B&A
953 112 GBM_Gnd_Pos P B B B&A
954 115 GBM_Final_Pos P B B B&A
955 Bei GBM müssen wir usn überlegen wie wir es mit den Positionsupdates
956 machen - das können auch mal ein paar Grad sein. siehe zB
957 https://gcn.gsfc.nasa.gov/other/568833894.fermi
958
959 Wenn ich das richtig sehe, kommt bei GBM_Alert noch keine
960 Position, sndern erst mit GBM_Flt_Pos (in dem Bsp kommt das 3 Sek
961 später).
962
963 * INTEGRAL: https://gcn.gsfc.nasa.gov/integral.html
964 wenn ich es richtig verstehe, sind die interessanten Alerts die WAKEUP
965 (tw kommt ein Positionsupdate via OFFLINE oder REFINED). Es gibt auch
966 noch WEAK, aber das sind scheinbar subthreshold alerts - würde ich
967 jetzt erst mal weglassen. Im letzten Jahr gab es 8 WAKEUP alerts. 2017
968 sogar nur 3, 2016 7, 2015 waren es einige mehr, aber das war V404_Cyg -
969 in dem Fall steht dann aber ein Object-Name dabei - ansonsten steht as
970 "Possbibly real GRB event" - zT steht auch, dass es coincident mit GBM
971 events ist
972
973 * KONUS: https://gcn.gsfc.nasa.gov/konus.html
974 Ist mir noch etwas unklar, was das genau ist - die schicken Lichtkurven
975 von GRBs und Transients - muss man nochmal genauer schauen.
976
977 * MAXI: https://gcn.gsfc.nasa.gov/maxi.html
978 wenn ich das richtig verstehe, sind das nicht nur GRBs -> müsste man
979 nochmal genauer schauen bevor man da was implementiert
980
981 * AGILE: https://gcn.gsfc.nasa.gov/agile.html
982 Wahrscheinlich eher nicht relevant, da steht was von 30min nach dem
983 Burst kommt das 'Wakeup'. Die relevanten Paket-Typen wären 100
984 (Wakeup_Pos), 101 (Ground_Pos) und 102 (Refined_Pos)
985
986 * AMON: https://gcn.gsfc.nasa.gov/amon.html
987 sind bisher nur neutrinos - da macht MAGIC glaub ich schon automatic
988 Follow-Up - müssen wir also kucken was Sinn macht
989
990 * Ligo-Virgo GW: https://gcn.gsfc.nasa.gov/lvc.html
991 da ist natürlich die Error-Region groß - müsste man schauen, was man
992 da will
993
994 Fazit: am relevantesten sind Fermi/GBM und Swift/BAT. Evtl könnte
995 INTEGRAL noch Sinn machen und über AMON und LVC sollte man nachdenken,
996 ob/was man haben will.
997*/
998
999 Schedule(name, grb.ra, grb.dec);
1000
1001 return fConnection.connected() ? ToO::State::kConnected : ToO::State::kDisconnected;
1002 //return GetCurrentState();
1003 }
1004
1005 int Execute()
1006 {
1007 Time now;
1008 if (now>fKeepAliveDeadline)
1009 {
1010 static int ernum = 0;
1011 try
1012 {
1013 // Unfortunately, reconnecting [Takes about 1s] is
1014 // the only non-blocking way to ensure an open
1015 // connection. Ping would be nice but is blocking.
1016 fConnection = Database(fUri);
1017 ernum = 0;
1018 }
1019 catch (const mysqlpp::ConnectionFailed &e)
1020 {
1021 if (ernum!=e.errnum())
1022 Error(e.what());
1023 ernum = e.errnum();
1024 }
1025
1026 fKeepAliveDeadline += boost::posix_time::seconds(fKeepAliveInterval);
1027 }
1028
1029 return fConnection.connected() ? ToO::State::kConnected : ToO::State::kDisconnected;
1030 }
1031
1032public:
1033 StateMachineToO(ostream &out=cout) : StateMachineDim(out, "SCHEDULER"),
1034 fConnection(""), fKeepAliveDeadline(Time())
1035 {
1036 AddStateName(ToO::State::kDisconnected, "Disconnected",
1037 "The Dim DNS is reachable, but the required subsystems are not available.");
1038
1039 AddStateName(ToO::State::kConnected, "Connected",
1040 "All needed subsystems are connected to their hardware, no action is performed.");
1041
1042 AddEvent("GCN", "S:1;I:1;D:1;D:1;D:1")
1043 (bind(&StateMachineToO::ScheduleGCN, this, placeholders::_1))
1044 (""
1045 "|type[int16]:TypeID (see HeaderGCN.h)"
1046 "|num[uint32]:Right ascension"
1047 "|ra[double]:Right ascension"
1048 "|dec[double]:Declination"
1049 "|err[double]:Declination");
1050 }
1051
1052 /*
1053 bool GetConfig(Configuration &conf, const string &name, const string &sub, uint16_t &rc)
1054 {
1055 if (conf.HasDef(name, sub))
1056 {
1057 rc = conf.GetDef<uint16_t>(name, sub);
1058 return true;
1059 }
1060
1061 Error("Neither "+name+"default nor "+name+sub+" found.");
1062 return false;
1063 }*/
1064
1065 int EvalOptions(Configuration &conf)
1066 {
1067 fVerbose = !conf.Get<bool>("quiet");
1068 fDryRun = conf.Get<bool>("dry-run");
1069 fKeepAliveInterval = conf.Get<uint16_t>("keep-alive");
1070 fUri = conf.Get<string>("schedule-database");
1071 return -1;
1072 }
1073};
1074
1075
1076// ------------------------------------------------------------------------
1077
1078#include "Main.h"
1079
1080template<class T>
1081int RunShell(Configuration &conf)
1082{
1083 return Main::execute<T, StateMachineToO>(conf);
1084}
1085
1086void SetupConfiguration(Configuration &conf)
1087{
1088 po::options_description control("Scheduler");
1089 control.add_options()
1090 ("quiet,q", po_bool(), "")
1091 ("dry-run", po_bool(), "Switch off any alteration of the database")
1092 ("keep-alive", var<uint16_t>(uint16_t(300)), "Interval in seconds to ping (reconnect) the database server")
1093 ("schedule-database", var<string>(), "Database link as in\n\tuser:password@server[:port]/database[?compress=0|1].")
1094 ;
1095
1096 conf.AddOptions(control);
1097}
1098
1099void PrintUsage()
1100{
1101 cout <<
1102 "The scheduler program is able to schedule a new observation.\n"
1103 "\n"
1104 "Usage: scheduler [-c type] [OPTIONS]\n"
1105 " or: scheduler [OPTIONS]\n";
1106 cout << endl;
1107}
1108
1109void PrintHelp()
1110{
1111 Main::PrintHelp<StateMachineToO>();
1112}
1113
1114int main(int argc, const char* argv[])
1115{
1116 Configuration conf(argv[0]);
1117 conf.SetPrintUsage(PrintUsage);
1118 Main::SetupConfiguration(conf);
1119 SetupConfiguration(conf);
1120
1121 if (!conf.DoParse(argc, argv, PrintHelp))
1122 return 127;
1123
1124 if (!conf.Has("console"))
1125 return RunShell<LocalStream>(conf);
1126
1127 if (conf.Get<int>("console")==0)
1128 return RunShell<LocalShell>(conf);
1129 else
1130 return RunShell<LocalConsole>(conf);
1131
1132 return 0;
1133}
1134
Note: See TracBrowser for help on using the repository browser.