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

Last change on this file since 19506 was 19479, checked in by tbretz, 6 years ago
Only enter a potential new source key if an observation is really scheduled.
File size: 39.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
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 if (source_key<0)
787 {
788 const string query =
789 "INSERT INTO Source\n"
790 " (fSourceName, fRightAscension, fDeclination, fWobbleAngle0, fWobbleAngle1, fSourceTypeKey, fIsToO) VALUES\n"
791 " ('"+name+"', "+to_string(ra/15.)+", "+to_string(dec)+", "+to_string(wobble_angle-90)+", "+to_string(wobble_angle+90)+", 1, 1)";
792
793 if (!fDryRun)
794 {
795 auto q = fConnection.query(query);
796 q.execute();
797 Info(q.info());
798
799 source_key = q.insert_id();
800
801 Info(string(fDryRun?"[dry-run] ":"")+"The new source got the key "+to_string(source_key));
802 }
803 else
804 Out() << query << endl;
805
806 }
807
808 Out() << Time()-stopwatch << endl;
809
810 // ====================================================================
811
812 vector<string> insert;
813
814 Out() << "New entries:\n";
815 Out() << " | auto | " << schedtime.GetAsStr() << " | < start > | 0 | ToO | nodrs,grb | " << setw(4) << source_key << " | 4\n";
816
817 insert.emplace_back("('"+schedtime.GetAsStr()+"',0,'ToO','nodrs:true,grb:true',"+to_string(source_key)+",4)");
818
819 // --------------------------------------------------------------------
820
821 if (reschedule>=0 && restart!=last)
822 {
823 Out() << " | auto | " << restart.GetAsStr() << " | < end > | 0 | ToO | NULL | " << setw(4) << resS[reschedule]["fSourceKey"] << " | 4\n";
824
825 const string fData = (string)resS[reschedule]["fData"];
826 const string fKey = (string)resS[reschedule]["fSourceKey"];
827
828 const string data = resS[reschedule]["fData"] ? "'"+fData+"'" : "NULL";
829 insert.emplace_back("('"+restart.GetAsStr()+"',0,'ToO',"+data+","+fKey+",4)");
830 }
831
832 // ====================================================================
833
834 Out() << Time()-stopwatch << endl;
835
836 // ====================================================================
837
838 const string queryD = string("DELETE FROM Schedule WHERE fScheduleID IN (")+boost::algorithm::join(list, ",")+")";
839
840 const string queryI =
841 "INSERT INTO Schedule\n (fStart,fMeasurementID,fUser,fData,fSoureKey,fMeasurementTypeKey)\n"
842 "VALUES\n "+string(boost::algorithm::join(insert, ",\n "));
843
844 if (fDryRun)
845 {
846 Out() << "\n";
847 if (!list.empty())
848 Out() << queryD << "\n\n";
849 if (!insert.empty())
850 Out() << queryI << "\n\n";
851 Out() << flush;
852 return false;
853 }
854
855 // Empty interrupt to stop data taking as early as possible
856 Dim::SendCommandNB("DIM_CONTROL/INTERRUPT");
857
858 // Reload sources as early as possible
859 Dim::SendCommandNB("DRIVE_CONTROL/RELOAD_SOURCES");
860
861 // Start pointing procedure as early as possible
862 //Dim::SendCommand("DRIVE_CONTROL/TRACK_WOBBLE", wobble, obs[sub].source);
863
864 // ---------------------------------------------------------
865
866 fConnection.query("LOCK TABLES Schedule WRITE");
867
868 // ---------------------------------------------------------
869 /*
870 if (!list.empty())
871 {
872 const mysqlpp::SimpleResult res = fConnection.query(queryD).execute();
873 Info(to_string(res.rows())+" row(s) deleted from Schedule.");
874 }
875
876 // ---------------------------------------------------------
877
878 if (!insert.empty())
879 {
880 auto q = fConnection.query(queryI);
881 q.execute();
882 Info(q.info());
883 }
884 */
885 // ---------------------------------------------------------
886
887 fConnection.query("UNLOCK TABLES");
888
889 // ---------------------------------------------------------
890
891 Dim::SendCommand("DIM_CONTROL/INTERRUPT", "reschedule");
892
893 ostringstream out;
894 out << Time()-stopwatch;
895 Info(out);
896
897 return true;
898 }
899
900 bool Schedule(const string &name, const double &ra, const double &dec)
901 {
902 try
903 {
904 return ScheduleImp(name, ra, dec);
905 }
906 catch (const exception &e)
907 {
908 Error(string("SQL query failed: ")+e.what());
909 fConnection.disconnect();
910 return false;
911 }
912 }
913
914 // ========================================================================
915
916
917 // ========================================================================
918
919 bool CheckEventSize(size_t has, const char *name, size_t size)
920 {
921 if (has==size)
922 return true;
923
924 ostringstream msg;
925 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
926 Fatal(msg);
927 return false;
928 }
929
930 int ScheduleGCN(const EventImp &evt)
931 {
932 if (!CheckEventSize(evt.GetSize(), "ScheduleGCN", 2+4+3*8))
933 return kSM_FatalError;
934
935 const ToO::DataGRB &grb = evt.Ref<ToO::DataGRB>();
936
937 const GCN::PaketType_t *ptr=GCN::kTypes;
938 for (; ptr->type>=0 && ptr->type!=grb.type; ptr++);
939 if (ptr->type==-1)
940 {
941 Warn("Unknown Packet Type: "+to_string(ptr->type));
942 return GetCurrentState();
943 }
944
945 ostringstream out;
946 out << "Received: '" << ptr->name << "' ID=" << grb.type << " NUM=" << grb.trigid << " RA=" << grb.ra/15. << "h DEC=" << grb.dec << "\u00b0 ERR=" << grb.err;
947
948 Info(out);
949 Info(ptr->description);
950
951 const CheckVisibility check(grb.ra, grb.dec);
952
953 Info(string("Source is")+(check.visible?" ":" NOT ")+"visible.");
954
955 Info(string("Sun altitude: ")+(check.valid_sun ?"OK ":"failed")+" ["+Tools::Form("%5.1f", check.solarobj.fSunHrz.alt)+"\u00b0]");
956 Info(string("Moon distance: ")+(check.valid_moon ?"OK ":"failed")+" ["+Tools::Form("%5.1f", check.moon_dist)+"\u00b0]");
957 Info(string("Zenith angle: ")+(check.valid_zd ?"OK ":"failed")+" ["+Tools::Form("%5.1f", check.position.zd)+"\u00b0]");
958 Info(string("Current: ")+(check.valid_current?"OK ":"failed")+" ["+Tools::Form("%5.1f", check.current)+" \u00b5A]");
959
960 const string name = ptr->instrument+"#"+to_string(grb.trigid);
961 Info("Source name set to '"+name+"'");
962
963 /*
964 * Swift: https://gcn.gsfc.nasa.gov/swift.html
965 relevante Paket-Typen:
966 60 BAT_Alert
967 97 BAT_QL_Pos
968 61 BAT_Pos
969 62 BAT_Pos_Nack
970 Es kann wohl vorkommen, dass ein Alert danach anders klassifiziert ist
971 -> BAT_Trans (84) und es steht dass dann in der GRB notice eine catalog
972 ID im comment steht. Da ist es mir noch nicht so ganz klar, woran man
973 echte Alerts erkennt.
974
975 * Fermi: https://gcn.gsfc.nasa.gov/fermi.html
976 relevante Paket-Typen:
977 110 GBM_Alert P B B B&A
978 111 GBM_Flt_Pos P B B B&A
979 112 GBM_Gnd_Pos P B B B&A
980 115 GBM_Final_Pos P B B B&A
981 Bei GBM müssen wir usn überlegen wie wir es mit den Positionsupdates
982 machen - das können auch mal ein paar Grad sein. siehe zB
983 https://gcn.gsfc.nasa.gov/other/568833894.fermi
984
985 Wenn ich das richtig sehe, kommt bei GBM_Alert noch keine
986 Position, sndern erst mit GBM_Flt_Pos (in dem Bsp kommt das 3 Sek
987 später)
988
989 * INTEGRAL: https://gcn.gsfc.nasa.gov/integral.html
990 wenn ich es richtig verstehe, sind die interessanten Alerts die WAKEUP
991 (tw kommt ein Positionsupdate via OFFLINE oder REFINED). Es gibt auch
992 noch WEAK, aber das sind scheinbar subthreshold alerts - würde ich
993 jetzt erst mal weglassen. Im letzten Jahr gab es 8 WAKEUP alerts. 2017
994 sogar nur 3, 2016 7, 2015 waren es einige mehr, aber das war V404_Cyg -
995 in dem Fall steht dann aber ein Object-Name dabei - ansonsten steht as
996 "Possbibly real GRB event" - zT steht auch, dass es coincident mit GBM
997 events ist
998
999 * KONUS: https://gcn.gsfc.nasa.gov/konus.html
1000 Ist mir noch etwas unklar, was das genau ist - die schicken Lichtkurven
1001 von GRBs und Transients - muss man nochmal genauer schauen.
1002
1003 * MAXI: https://gcn.gsfc.nasa.gov/maxi.html
1004 wenn ich das richtig verstehe, sind das nicht nur GRBs -> müsste man
1005 nochmal genauer schauen bevor man da was implementiert
1006
1007 * AGILE: https://gcn.gsfc.nasa.gov/agile.html
1008 Wahrscheinlich eher nicht relevant, da steht was von 30min nach dem
1009 Burst kommt das 'Wakeup'. Die relevanten Paket-Typen wären 100
1010 (Wakeup_Pos), 101 (Ground_Pos) und 102 (Refined_Pos)
1011
1012 * AMON: https://gcn.gsfc.nasa.gov/amon.html
1013 sind bisher nur neutrinos - da macht MAGIC glaub ich schon automatic
1014 Follow-Up - müssen wir also kucken was Sinn macht
1015
1016 * Ligo-Virgo GW: https://gcn.gsfc.nasa.gov/lvc.html
1017 da ist natürlich die Error-Region groß - müsste man schauen, was man
1018 da will
1019
1020 Fazit: am relevantesten sind Fermi/GBM und Swift/BAT. Evtl könnte
1021 INTEGRAL noch Sinn machen und über AMON und LVC sollte man nachdenken,
1022 ob/was man haben will.
1023*/
1024
1025 Schedule(name, grb.ra, grb.dec);
1026
1027 return fConnection.connected() ? ToO::State::kConnected : ToO::State::kDisconnected;
1028 //return GetCurrentState();
1029 }
1030
1031 int Execute()
1032 {
1033 Time now;
1034 if (now>fKeepAliveDeadline)
1035 {
1036 static int ernum = 0;
1037 try
1038 {
1039 // Unfortunately, reconnecting [Takes about 1s] is
1040 // the only non-blocking way to ensure an open
1041 // connection. Ping would be nice but is blocking.
1042 fConnection = Database(fUri);
1043 ernum = 0;
1044 }
1045 catch (const mysqlpp::ConnectionFailed &e)
1046 {
1047 if (ernum!=e.errnum())
1048 Error(e.what());
1049 ernum = e.errnum();
1050 }
1051
1052 fKeepAliveDeadline += boost::posix_time::seconds(fKeepAliveInterval);
1053 }
1054
1055 return fConnection.connected() ? ToO::State::kConnected : ToO::State::kDisconnected;
1056 }
1057
1058public:
1059 StateMachineToO(ostream &out=cout) : StateMachineDim(out, "SCHEDULER"),
1060 fConnection(""), fKeepAliveDeadline(Time())
1061 {
1062 AddStateName(ToO::State::kDisconnected, "Disconnected",
1063 "The Dim DNS is reachable, but the required subsystems are not available.");
1064
1065 AddStateName(ToO::State::kConnected, "Connected",
1066 "All needed subsystems are connected to their hardware, no action is performed.");
1067
1068 AddEvent("GCN", "S:1;I:1;D:1;D:1;D:1")
1069 (bind(&StateMachineToO::ScheduleGCN, this, placeholders::_1))
1070 (""
1071 "|type[int16]:TypeID (see HeaderGCN.h)"
1072 "|num[uint32]:Right ascension"
1073 "|ra[deg]:Right ascension"
1074 "|dec[deg]:Declination"
1075 "|err[deg]:Error radius");
1076 }
1077
1078 /*
1079 bool GetConfig(Configuration &conf, const string &name, const string &sub, uint16_t &rc)
1080 {
1081 if (conf.HasDef(name, sub))
1082 {
1083 rc = conf.GetDef<uint16_t>(name, sub);
1084 return true;
1085 }
1086
1087 Error("Neither "+name+"default nor "+name+sub+" found.");
1088 return false;
1089 }*/
1090
1091 int EvalOptions(Configuration &conf)
1092 {
1093 fVerbose = !conf.Get<bool>("quiet");
1094 fDryRun = conf.Get<bool>("dry-run");
1095 fKeepAliveInterval = conf.Get<uint16_t>("keep-alive");
1096 fUri = conf.Get<string>("schedule-database");
1097 return -1;
1098 }
1099};
1100
1101
1102// ------------------------------------------------------------------------
1103
1104#include "Main.h"
1105
1106template<class T>
1107int RunShell(Configuration &conf)
1108{
1109 return Main::execute<T, StateMachineToO>(conf);
1110}
1111
1112void SetupConfiguration(Configuration &conf)
1113{
1114 po::options_description control("Scheduler");
1115 control.add_options()
1116 ("quiet,q", po_bool(), "")
1117 ("dry-run", po_bool(), "Switch off any alteration of the database")
1118 ("keep-alive", var<uint16_t>(uint16_t(300)), "Interval in seconds to ping (reconnect) the database server")
1119 ("schedule-database", var<string>(), "Database link as in\n\tuser:password@server[:port]/database[?compress=0|1].")
1120 ;
1121
1122 conf.AddOptions(control);
1123}
1124
1125void PrintUsage()
1126{
1127 cout <<
1128 "The scheduler program is able to schedule a new observation.\n"
1129 "\n"
1130 "Usage: scheduler [-c type] [OPTIONS]\n"
1131 " or: scheduler [OPTIONS]\n";
1132 cout << endl;
1133}
1134
1135void PrintHelp()
1136{
1137 Main::PrintHelp<StateMachineToO>();
1138}
1139
1140int main(int argc, const char* argv[])
1141{
1142 Configuration conf(argv[0]);
1143 conf.SetPrintUsage(PrintUsage);
1144 Main::SetupConfiguration(conf);
1145 SetupConfiguration(conf);
1146
1147 if (!conf.DoParse(argc, argv, PrintHelp))
1148 return 127;
1149
1150 if (!conf.Has("console"))
1151 return RunShell<LocalStream>(conf);
1152
1153 if (conf.Get<int>("console")==0)
1154 return RunShell<LocalShell>(conf);
1155 else
1156 return RunShell<LocalConsole>(conf);
1157
1158 return 0;
1159}
1160
Note: See TracBrowser for help on using the repository browser.