Changeset 19438
- Timestamp:
- 02/05/19 16:08:39 (6 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/FACT++/src/scheduler.cc
r19125 r19438 1 #include <vector> 2 3 #include <boost/regex.hpp> 4 5 #include <mysql++/mysql++.h> 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" 6 13 7 14 #include "Dim.h" 15 #include "tools.h" 8 16 #include "Time.h" 9 #include "Event.h"10 #include "Connection.h"11 #include "LocalControl.h"12 17 #include "Configuration.h" 13 #include "StateMachineDim.h"14 15 #include "tools.h"16 18 17 19 using namespace std; 18 using namespace boost::gregorian; 19 using namespace boost::posix_time; 20 21 // things to be done/checked/changed 22 // * --schedule-database should be required 23 // * move definition of config parameters to AutoScheduler class 24 // + read in from config 25 // * in some (all?) loops iterator over vector can be replaced by counter 26 27 // other things to do 28 // 29 // define what to transmit as info/warn/error 30 31 32 // config parameters: 33 // mintime 34 // runtimec 35 // runtimep 36 // repostime 37 38 // missing: 39 // 40 // calculate time for std obs 41 // calculate sun set/rise 42 // 43 // return errors and other otherput from sendcommand to webinterface 44 45 // in which cases should the scheduler go in error state? 46 // when db is unavailable 47 // does one also need a 'set scheduler to ready' function then? 48 // do we want any error state at all? 49 50 51 // ========================================================================= 52 53 template <class T> 54 class AutoScheduler : public T 20 using namespace Nova; 21 22 // ----------------------------------------------------------------------- 23 24 /* 25 void SetupConfiguration(Configuration &conf); 55 26 { 56 bool fNextIsPreview; 57 public: 58 enum states_t 59 { 60 kSM_Scheduling=1, 61 kSM_Comitting, 62 }; 63 64 struct ObservationParameters 65 { 66 int obskey; 67 int obsmode; 68 int obstype; 69 int splitflag; 70 int telsetup; 71 float fluxweight; 72 float slope; 73 float flux; 74 float ra; 75 float dec; 76 ptime start; 77 ptime stop; 78 time_duration duration_db; 79 string sourcename; 80 int sourcekey; 81 }; 82 83 struct FixedObs 84 { 85 int obskey; 86 int sourcekey; 87 string sourcename; 88 int obsmode; 89 int obstype; 90 int telsetup; 91 float ra; 92 float dec; 93 ptime start; 94 ptime stop; 95 }; 96 97 // will need other types of obs 98 // FloatingObs (duration < stop-start + splitflag no) 99 // FloatingSplittedObs (duration < stop-start + splitflag yes) 100 // FixedSlot, i.e. just block a time slot 101 102 struct StdObs 103 { 104 int obskey_std; 105 int sourcekey_std; 106 string sourcename_std; 107 int obsmode_std; 108 int obstype_std; 109 int telsetup_std; 110 float fluxweight_std; 111 float slope_std; 112 float flux_std; 113 float ra_std; 114 float dec_std; 115 ptime obsstdstart; 116 ptime obsstdstop; 117 }; 118 119 struct ScheduledObs 120 { 121 int obskey_obs; 122 int sourcekey_obs; 123 string sourcename_obs; 124 int obsmode_obs; 125 int obstype_obs; 126 int telsetup_obs; 127 ptime obsstart; 128 ptime obsstop; 129 }; 130 131 struct ScheduledRun 132 { 133 //int runnumber; // to be seen, if runnumber is needed 134 int obskey_run; 135 int runtype; 136 int sourcekey_run; 137 string sourcename_run;//for convenience 138 int obsmode_run; 139 int obstype_run; 140 int telsetup_run; 141 ptime runstart; 142 ptime runstop; 143 }; 144 145 string fDatabase; 146 string fDBName; 147 int fDurationCalRun; //unit: minutes 148 int fDurationPedRun; //unit: minutes 149 int fDurationRepos; //unit: minutes 150 151 int Schedule() 152 { 153 bool error = false; 154 155 time_duration runtimec(0, fDurationCalRun, 0); 156 time_duration runtimep(0, fDurationPedRun, 0); 157 time_duration repostime(0, fDurationRepos, 0); 158 time_duration mintime(1, 0, 0); 159 160 const ptime startsched(microsec_clock::local_time()); 161 const ptime stopsched=startsched+years(1); 162 163 ostringstream str; 164 str << "Scheduling for the period from " << startsched << " to " << stopsched; 165 T::Message(str); 166 167 static const boost::regex expr("([[:word:].-]+):(.+)@([[:word:].-]+)(:([[:digit:]]+))?/([[:word:].-]+)"); 168 // 2: user 169 // 4: pass 170 // 5: server 171 // 7: port 172 // 9: db 173 174 boost::smatch what; 175 if (!boost::regex_match(fDatabase, what, expr, boost::match_extra)) 176 { 177 ostringstream msg; 178 msg << "Regex to parse database '" << fDatabase << "' empty."; 179 T::Error(msg); 180 return T::kSM_Error; 181 } 182 183 if (what.size()!=7) 184 { 185 ostringstream msg; 186 msg << "Parsing database name failed: '" << fDatabase << "'"; 187 T::Error(msg); 188 return T::kSM_Error; 189 } 190 191 const string user = what[1]; 192 const string passwd = what[2]; 193 const string server = what[3]; 194 const string db = fDBName.empty() ? what[6] : fDBName; 195 const int port = stoi(what[5]); 196 197 ostringstream dbnamemsg; 198 dbnamemsg << "Scheduling started -> using database " << db << "."; 199 T::Message(dbnamemsg); 200 201 str.str(""); 202 str << "Connecting to '"; 203 if (!user.empty()) 204 str << user << "@"; 205 str << server; 206 if (port) 207 str << ":" << port; 208 if (!db.empty()) 209 str << "/" << db; 210 str << "'"; 211 T::Info(str); 212 213 mysqlpp::Connection conn(db.c_str(), server.c_str(), user.c_str(), passwd.c_str(), port); 214 /* throws exceptions 215 if (!conn.connected()) 216 { 217 ostringstream msg; 218 msg << "MySQL connection error: " << conn.error(); 219 T::Error(msg); 220 return T::kSM_Error; 221 }*/ 222 223 // get observation parameters from DB 224 // maybe order by priority? 225 const mysqlpp::StoreQueryResult res = 226 conn.query("SELECT fObservationKEY, fStartTime, fStopTime, fDuration, fSourceName, fSourceKEY, fSplitFlag, fFluxWeight, fSlope, fFlux, fRightAscension, fDeclination, fObservationModeKEY, fObservationTypeKEY , fTelescopeSetupKEY FROM ObservationParameters LEFT JOIN Source USING(fSourceKEY) ORDER BY fStartTime").store(); 227 // FIXME: Maybe we have to check for a successfull 228 // query but an empty result 229 /* thorws exceptions? 230 if (!res) 231 { 232 ostringstream msg; 233 msg << "MySQL query failed: " << query.error(); 234 T::Error(msg); 235 return T::kSM_Error; 236 }*/ 237 238 str.str(""); 239 str << "Found " << res.num_rows() << " Observation Parameter sets."; 240 T::Debug(str); 241 242 ObservationParameters olist[res.num_rows()]; 243 vector<FixedObs> obsfixedlist; 244 vector<StdObs> obsstdlist; 245 vector<ScheduledObs> obslist; 246 vector<ScheduledRun> runlist; 247 248 // loop over observation parameters from DB 249 // fill these parameters into FixedObs and StdObs 250 int counter=0; 251 int counter2=0; 252 int counter3=0; 253 cout << "Obs: <obskey> <sourcename>(<sourcekey>, <fluxweight>) from <starttime> to <stoptime>" << endl; 254 for (vector<mysqlpp::Row>::const_iterator v=res.begin(); v<res.end(); v++) 255 { 256 cout << " Obs: " << (*v)[0].c_str() << " " << (*v)[4].c_str() << "(" << (*v)[5].c_str() << flush; 257 cout << ", " << (*v)[7].c_str() << ")" << flush; 258 cout << " from " << (*v)[1].c_str() << " to " << (*v)[2].c_str() << endl; 259 260 //0: obskey 261 //1: startime 262 //2: stoptime 263 //3: duration 264 //4: sourcename 265 //5: sourcekey 266 //6: splitflag 267 //7: fluxweight 268 //8: slope 269 //9: flux 270 //10: ra 271 //11: dec 272 //12: obsmode 273 //13: obstype 274 //14: telsetup 275 stringstream t1; 276 stringstream t2; 277 stringstream t3; 278 t1 << (*v)[1].c_str(); 279 t2 << (*v)[2].c_str(); 280 t3 << (*v)[3].c_str(); 281 282 //boost::posix_time::time_duration mintime(0,conf.Get<int>("mintime"), 0); 283 t1 >> Time::sql >> olist[counter].start; 284 t2 >> Time::sql >> olist[counter].stop; 285 t3 >> olist[counter].duration_db; 286 const time_period period(olist[counter].start, olist[counter].stop); 287 288 olist[counter].sourcename=(*v)[4].c_str(); 289 olist[counter].sourcekey=(*v)[5]; 290 291 if (!(*v)[0].is_null()) 292 olist[counter].obskey=(*v)[0]; 293 if (!(*v)[12].is_null()) 294 olist[counter].obsmode=(*v)[12]; 295 if (!(*v)[13].is_null()) 296 olist[counter].obstype=(*v)[13]; 297 if (!(*v)[14].is_null()) 298 olist[counter].telsetup=(*v)[14]; 299 if (!(*v)[6].is_null()) 300 olist[counter].splitflag=(*v)[6]; 301 if (!(*v)[7].is_null()) 302 olist[counter].fluxweight=(*v)[7]; 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 39 void 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 48 void PrintHelp(); 49 { 50 cout << endl; 51 }*/ 52 53 struct 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 /* 129 int 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 410 class StateMachineToO : public StateMachineDim 411 { 412 private: 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 } 303 596 else 304 olist[counter].fluxweight=0;//set fluxweight to 0 for check below 305 if (!(*v)[8].is_null()) 306 olist[counter].slope=(*v)[8]; 307 if (!(*v)[9].is_null()) 308 olist[counter].flux=(*v)[9]; 309 if (!(*v)[10].is_null()) 310 olist[counter].ra=(*v)[10]; 311 if (!(*v)[11].is_null()) 312 olist[counter].dec=(*v)[11]; 313 314 // time_duration cannot be used, as only up to 99 hours are handeled 315 // const time_duration duration = period.length(); 316 317 /* 318 if (olist[counter].stoptime < olist[counter].starttime+mintime) 319 cout << " ====> WARN: Observation too short. " << endl; 320 321 if (olist[counter].starttime.is_not_a_date_time()) 322 cout << " WARN: starttime not a date_time. " << endl; 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 } 323 689 else 324 cout << " start: " << Time::sql << olist[counter].starttime << endl; 325 if (olist[counter].stoptime.is_not_a_date_time()) 326 cout << " WARN: stoptime not a date_time. " << endl; 327 else 328 cout << " stop: " << Time::sql << olist[counter].stoptime << endl; 329 if (!(olist[counter].starttime.is_not_a_date_time() || olist[counter].stoptime.is_not_a_date_time())) 330 cout << " diff: " << period << endl; 331 if (olist[counter].stoptime < olist[counter].starttime) 332 cout << " ====> WARN: stop time (" << olist[counter].stoptime << ") < start time (" << olist[counter].starttime << "). " << endl; 333 cout << "diff: " << duration << flush; 334 cout << "dur_db: " << olist[counter].duration_db << endl; 335 */ 336 337 // always filled: obstype 338 // 339 // fixed observations: 340 // filled: starttime, stoptime 341 // not filled: fluxweight 342 // maybe filled: obsmode, telsetup, source (not filled for FixedSlotObs) 343 // maybe filled: duration (filled for FloatingObs and FloatingSplittedObs) 344 // maybe filled: splitflag (filled for FloatingSplittedObs) 345 // 346 // std observations: 347 // filled: fluxweight, telsetup, obsmore, source 348 // not filled: starttime, stoptime, duration 349 350 // fixed observations 351 if (!(olist[counter].stop.is_not_a_date_time() 352 && olist[counter].start.is_not_a_date_time()) 353 && olist[counter].fluxweight==0 354 ) 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)) 355 766 { 356 obsfixedlist.resize(counter2+1); 357 obsfixedlist[counter2].start=olist[counter].start; 358 obsfixedlist[counter2].stop=olist[counter].stop; 359 obsfixedlist[counter2].sourcename=olist[counter].sourcename; 360 obsfixedlist[counter2].obskey=olist[counter].obskey; 361 obsfixedlist[counter2].obstype=olist[counter].obstype; 362 obsfixedlist[counter2].obsmode=olist[counter].obsmode; 363 obsfixedlist[counter2].telsetup=olist[counter].telsetup; 364 obsfixedlist[counter2].sourcekey=olist[counter].sourcekey; 365 obsfixedlist[counter2].ra=olist[counter].ra; 366 obsfixedlist[counter2].dec=olist[counter].dec; 367 counter2++; 767 Info("Following observation would be less than "+to_string(skip_time)+" min... skipping rescheduled source."); 768 reschedule = -1; 368 769 } 369 370 // std obs 371 if (olist[counter].stop.is_not_a_date_time() 372 && olist[counter].start.is_not_a_date_time() 373 && olist[counter].fluxweight>0 374 ) 375 { 376 obsstdlist.resize(counter3+1); 377 obsstdlist[counter3].sourcename_std=olist[counter].sourcename; 378 obsstdlist[counter3].obskey_std=olist[counter].obskey; 379 obsstdlist[counter3].obsmode_std=olist[counter].obsmode; 380 obsstdlist[counter3].obstype_std=olist[counter].obstype; 381 obsstdlist[counter3].telsetup_std=olist[counter].telsetup; 382 obsstdlist[counter3].sourcekey_std=olist[counter].sourcekey; 383 obsstdlist[counter3].fluxweight_std=olist[counter].fluxweight; 384 obsstdlist[counter3].flux_std=olist[counter].flux; 385 obsstdlist[counter3].slope_std=olist[counter].slope; 386 obsstdlist[counter3].ra_std=olist[counter].ra; 387 obsstdlist[counter3].dec_std=olist[counter].dec; 388 counter3++; 389 } 390 391 counter++; 392 } 393 ostringstream fixedobsmsg; 394 fixedobsmsg << obsfixedlist.size() << " fixed observations found. "; 395 T::Message(fixedobsmsg); 396 cout << obsfixedlist.size() << " fixed observations found. " << endl; 397 398 ostringstream stdobsmsg; 399 stdobsmsg << obsstdlist.size() << " standard observations found. "; 400 T::Message(stdobsmsg); 401 cout << obsstdlist.size() << " standard observations found. " << endl; 402 403 // loop to add the fixed observations to the ScheduledObs list 404 // performed checks: 405 // * overlap of fixed observations: the overlap is split half-half 406 // * check for scheduling time range: only take into account fixed obs within the range 407 // missing checks and evaluation 408 // * check for mintime (pb with overlap checks) 409 // * check for sun 410 // * check for moon 411 counter2=0; 412 int skipcounter=0; 413 ptime finalobsfixedstart; 414 ptime finalobsfixedstop; 415 time_duration delta0(0,0,0); 416 417 cout << "Fixed Observations: " << endl; 418 for (struct vector<FixedObs>::const_iterator vobs=obsfixedlist.begin(); vobs!=obsfixedlist.end(); vobs++) 419 { 420 if (obsfixedlist[counter2].start < startsched 421 || obsfixedlist[counter2].stop > stopsched) 422 { 423 ostringstream skipfixedobsmsg; 424 skipfixedobsmsg << "Skip 1 fixed observation (obskey "; 425 skipfixedobsmsg << obsfixedlist[counter2].obskey; 426 skipfixedobsmsg << ") as it is out of scheduling time range."; 427 T::Message(skipfixedobsmsg); 428 429 counter2++; 430 skipcounter++; 431 continue; 432 } 433 counter3=0; 434 435 time_duration delta1=delta0; 436 time_duration delta2=delta0; 437 438 finalobsfixedstart=obsfixedlist[counter2].start; 439 finalobsfixedstop=obsfixedlist[counter2].stop; 440 441 for (struct vector<FixedObs>::const_iterator vobs5=obsfixedlist.begin(); vobs5!=obsfixedlist.end(); vobs5++) 442 { 443 if (vobs5->start < obsfixedlist[counter2].stop 444 && obsfixedlist[counter2].stop <= vobs5->stop 445 && obsfixedlist[counter2].start <= vobs5->start 446 && counter2!=counter3) 447 { 448 delta1=(obsfixedlist[counter2].stop-vobs5->start)/2; 449 finalobsfixedstop=obsfixedlist[counter2].stop-delta1; 450 451 ostringstream warndelta1; 452 warndelta1 << "Overlap between two fixed observations ("; 453 warndelta1 << obsfixedlist[counter2].obskey << " "; 454 warndelta1 << vobs5->obskey << "). The stoptime of "; 455 warndelta1 << obsfixedlist[counter2].obskey << " has been changed."; 456 T::Warn(warndelta1); 457 } 458 if (vobs5->start <= obsfixedlist[counter2].start 459 && obsfixedlist[counter2].start < vobs5->stop 460 && obsfixedlist[counter2].stop >= vobs5->stop 461 && counter2!=counter3) 462 { 463 delta2=(vobs5->stop-obsfixedlist[counter2].start)/2; 464 finalobsfixedstart=obsfixedlist[counter2].start+delta2; 465 466 ostringstream warndelta2; 467 warndelta2 << "Overlap between two fixed observations ("; 468 warndelta2 << obsfixedlist[counter2].obskey << " "; 469 warndelta2 << vobs5->obskey << "). The starttime of "; 470 warndelta2 << obsfixedlist[counter2].obskey << " has been changed."; 471 472 T::Warn(warndelta2); 473 } 474 counter3++; 475 } 476 477 const int num=counter2-skipcounter; 478 obslist.resize(num+1); 479 obslist[num].obsstart=finalobsfixedstart; 480 obslist[num].obsstop=finalobsfixedstop; 481 obslist[num].sourcename_obs=obsfixedlist[counter2].sourcename; 482 obslist[num].obsmode_obs=obsfixedlist[counter2].obsmode; 483 obslist[num].obstype_obs=obsfixedlist[counter2].obstype; 484 obslist[num].telsetup_obs=obsfixedlist[counter2].telsetup; 485 obslist[num].sourcekey_obs=obsfixedlist[counter2].sourcekey; 486 obslist[num].obskey_obs=obsfixedlist[counter2].obskey; 487 counter2++; 488 489 cout << " " << vobs->sourcename << " " << vobs->start; 490 cout << " - " << vobs->stop << endl; 491 } 492 ostringstream obsmsg; 493 obsmsg << "Added " << obslist.size() << " fixed observations to ScheduledObs. "; 494 T::Message(obsmsg); 495 cout << "Added " << obslist.size() << " fixed observations to ScheduledObs. " << endl; 496 497 for (int i=0; i<(int)obsstdlist.size(); i++) 498 { 499 for (int j=0; j<(int)obsstdlist.size(); j++) 500 { 501 if (obsstdlist[i].sourcekey_std == obsstdlist[j].sourcekey_std && i!=j) 502 { 503 cout << "One double sourcekey in std observations: " << obsstdlist[j].sourcekey_std << endl; 504 ostringstream errdoublestd; 505 errdoublestd << "One double sourcekey in std observations: " << obsstdlist[j].sourcekey_std << " (" << obsstdlist[j].sourcename_std << ")."; 506 T::Error(errdoublestd); 507 T::Message("Scheduling stopped."); 508 return error ? T::kSM_Error : T::kSM_Ready; 509 } 510 } 511 } 512 513 // loop over nights 514 // calculate sunset and sunrise 515 // check if there is already scheduled obs in that night 516 // 517 518 // in this loop the standard observations shall be 519 // checked, evaluated 520 // the observation times shall be calculated 521 // and the observations added to the ScheduledObs list 522 cout << "Standard Observations: " << endl; 523 for (struct vector<StdObs>::const_iterator vobs2=obsstdlist.begin(); vobs2!=obsstdlist.end(); vobs2++) 524 { 525 cout << " " << vobs2->sourcename_std << endl; 526 } 527 528 // in this loop the ScheduledRuns are filled 529 // (only data runs -> no runtype yet) 530 // might be merged with next loop 531 counter2=0; 532 for (struct vector<ScheduledObs>::const_iterator vobs3=obslist.begin(); vobs3!=obslist.end(); vobs3++) 533 { 534 runlist.resize(counter2+1); 535 runlist[counter2].runstart=obslist[counter2].obsstart; 536 runlist[counter2].runstop=obslist[counter2].obsstop; 537 runlist[counter2].sourcename_run=obslist[counter2].sourcename_obs; 538 runlist[counter2].obsmode_run=obslist[counter2].obsmode_obs; 539 runlist[counter2].obstype_run=obslist[counter2].obstype_obs; 540 runlist[counter2].telsetup_run=obslist[counter2].telsetup_obs; 541 runlist[counter2].sourcekey_run=obslist[counter2].sourcekey_obs; 542 runlist[counter2].obskey_run=obslist[counter2].obskey_obs; 543 counter2++; 544 //cout << (*vobs3).sourcename_obs << endl; 545 } 546 547 //delete old scheduled runs from the DB 548 const mysqlpp::SimpleResult res0 = 549 conn.query("DELETE FROM ScheduledRun").execute(); 550 // FIXME: Maybe we have to check for a successfull 551 // query but an empty result 552 /* throws exceptions 553 if (!res0) 554 { 555 ostringstream msg; 556 msg << "MySQL query failed: " << query0.error(); 557 T::Error(msg); 558 return T::kSM_Error; 559 }*/ 560 561 // in this loop the ScheduledRuns are inserted to the DB 562 // before the runtimes are adapted according to 563 // duration of P-Run, C-Run and repositioning 564 counter3=0; 565 int insertcount=0; 566 ptime finalstarttime; 567 ptime finalstoptime; 568 for (struct vector<ScheduledRun>::const_iterator vobs4=runlist.begin(); vobs4!=runlist.end(); vobs4++) 569 { 570 for (int i=2; i<5; i++) 571 { 572 switch(i) 573 { 574 case 2: 575 finalstarttime=runlist[counter3].runstart+repostime+runtimec+runtimep; 576 finalstoptime=runlist[counter3].runstop; 577 break; 578 case 3: 579 finalstarttime=runlist[counter3].runstart+repostime; 580 finalstoptime=runlist[counter3].runstart+runtimep+repostime; 581 break; 582 case 4: 583 finalstarttime=runlist[counter3].runstart+runtimep+repostime; 584 finalstoptime=runlist[counter3].runstart+repostime+runtimep+runtimec; 585 break; 586 } 587 ostringstream q1; 588 //cout << (*vobs4).sourcename_run << endl; 589 q1 << "INSERT ScheduledRun set fStartTime='" << Time::sql << finalstarttime; 590 q1 << "', fStopTime='" << Time::sql << finalstoptime; 591 q1 << "', fSourceKEY='" << (*vobs4).sourcekey_run; 592 q1 << "', fObservationKEY='" << (*vobs4).obskey_run; 593 q1 << "', fRunTypeKEY='" << i; 594 q1 << "', fTelescopeSetupKEY='" << (*vobs4).telsetup_run; 595 q1 << "', fObservationTypeKEY='" << (*vobs4).obstype_run; 596 q1 << "', fObservationModeKEY='" << (*vobs4).obsmode_run; 597 q1 << "'"; 598 599 //cout << "executing query: " << q1.str() << endl; 600 601 const mysqlpp::SimpleResult res1 = conn.query(q1.str()).execute(); 602 // FIXME: Maybe we have to check for a successfull 603 // query but an empty result 604 /* throws exceptions 605 if (!res1) 606 { 607 ostringstream msg; 608 msg << "MySQL query failed: " << query1.error(); 609 T::Error(str); 610 return T::kSM_Error; 611 }*/ 612 insertcount++; 613 } 614 counter3++; 615 } 616 ostringstream insertmsg; 617 insertmsg << "Inserted " << insertcount << " runs into the DB."; 618 T::Message(insertmsg); 619 //usleep(3000000); 620 T::Message("Scheduling done."); 621 622 return error; 623 } 624 625 /* 626 // commit probably done by webinterface 627 int Commit() 628 { 629 ostringstream str; 630 str << "Comitting preview (id=" << fSessionId << ")"; 631 T::Message(str); 632 633 usleep(3000000); 634 T::Message("Comitted."); 635 636 fSessionId = -1; 637 638 bool error = false; 639 return error ? T::kSM_Error : T::kSM_Ready; 640 } 641 */ 642 643 AutoScheduler(ostream &out=cout) : T(out, "SCHEDULER"), fNextIsPreview(true), fDBName("") 644 { 645 AddStateName(kSM_Scheduling, "Scheduling", "Scheduling in progress."); 646 647 AddEvent(kSM_Scheduling, "SCHEDULE", "C", T::kSM_Ready) 648 ("FIXME FIXME FIXME (explanation for the command)" 649 "|database[string]:FIXME FIXME FIMXE (meaning and format)"); 650 651 AddEvent(T::kSM_Ready, "RESET", T::kSM_Error) 652 ("Reset command to get out of the error state"); 653 654 //AddEvent(kSM_Comitting, "COMMIT", T::kSM_Ready); 655 656 T::PrintListOfEvents(); 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(); 657 1003 } 658 1004 659 1005 int Execute() 660 1006 { 661 switch (T::GetCurrentState()) 662 { 663 case kSM_Scheduling: 1007 Time now; 1008 if (now>fKeepAliveDeadline) 1009 { 1010 static int ernum = 0; 664 1011 try 665 1012 { 666 return Schedule() ? T::kSM_Error : T::kSM_Ready; 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; 667 1018 } 668 catch (const mysqlpp:: Exception&e)1019 catch (const mysqlpp::ConnectionFailed &e) 669 1020 { 670 T::Error(string("MySQL: ")+e.what()); 671 return T::kSM_Error; 1021 if (ernum!=e.errnum()) 1022 Error(e.what()); 1023 ernum = e.errnum(); 672 1024 } 673 1025 674 // This does an autmatic reset (FOR TESTING ONLY) 675 case T::kSM_Error: 676 return T::kSM_Ready; 677 } 678 return T::GetCurrentState(); 679 } 680 681 int Transition(const Event &evt) 682 { 683 switch (evt.GetTargetState()) 684 { 685 case kSM_Scheduling: 686 if (evt.GetSize()>0) 687 fDBName = evt.GetText(); 688 break; 689 } 690 691 return evt.GetTargetState(); 692 } 1026 fKeepAliveDeadline += boost::posix_time::seconds(fKeepAliveInterval); 1027 } 1028 1029 return fConnection.connected() ? ToO::State::kConnected : ToO::State::kDisconnected; 1030 } 1031 1032 public: 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 }*/ 693 1064 694 1065 int EvalOptions(Configuration &conf) 695 1066 { 696 fDatabase = conf.Get<string>("schedule-database"); 697 fDurationCalRun = conf.Get<int>("duration-cal-run"); 698 fDurationPedRun = conf.Get<int>("duration-ped-run"); 699 fDurationRepos = conf.Get<int>("duration-repos"); 700 701 if (!conf.Has("schedule")) 702 return -1; 703 704 fDBName = conf.Get<string>("schedule"); 705 return Schedule(); 706 } 707 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 } 708 1073 }; 709 1074 710 1075 711 1076 // ------------------------------------------------------------------------ 1077 712 1078 #include "Main.h" 713 1079 714 template<class T , class S>1080 template<class T> 715 1081 int RunShell(Configuration &conf) 716 1082 { 717 return Main::execute<T, AutoScheduler<S>>(conf);1083 return Main::execute<T, StateMachineToO>(conf); 718 1084 } 719 1085 720 1086 void SetupConfiguration(Configuration &conf) 721 1087 { 722 po::options_description control("Scheduler options");1088 po::options_description control("Scheduler"); 723 1089 control.add_options() 724 ("no-dim", po_switch(), "Disable dim services") 725 ("schedule-database", var<string>() 726 #if BOOST_VERSION >= 104200 727 ->required() 728 #endif 729 , "Database link as in\n\tuser:password@server[:port]/database.") 730 ("schedule", var<string>(), "") 731 ("mintime", var<int>(), "minimum observation time") 732 ("duration-cal-run", var<int>() 733 #if BOOST_VERSION >= 104200 734 ->required() 735 #endif 736 , "duration of calibration run [min]") 737 ("duration-ped-run", var<int>() 738 #if BOOST_VERSION >= 104200 739 ->required() 740 #endif 741 , "duration of pedestal run [min]") 742 ("duration-repos", var<int>() 743 #if BOOST_VERSION >= 104200 744 ->required() 745 #endif 746 , "duration of repositioning [min]") 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].") 747 1094 ; 748 1095 749 po::positional_options_description p;750 p.add("schedule", 1); // The first positional options751 752 1096 conf.AddOptions(control); 753 conf.SetArgumentPositions(p);754 1097 } 755 1098 … … 757 1100 { 758 1101 cout << 759 "The scheduler ... TEXT MISSING\n"1102 "The scheduler program is able to schedule a new observation.\n" 760 1103 "\n" 761 "The default is that the program is started without user intercation. " 762 "All actions are supposed to arrive as DimCommands. Using the -c " 763 "option, a local shell can be initialized. With h or help a short " 764 "help message about the usuage can be brought to the screen.\n" 765 "\n" 766 "Usage: scheduler [-c type] [OPTIONS] <schedule-database>\n" 767 " or: scheduler [OPTIONS] <schedule-database>\n"; 1104 "Usage: scheduler [-c type] [OPTIONS]\n" 1105 " or: scheduler [OPTIONS]\n"; 768 1106 cout << endl; 769 1107 } … … 771 1109 void PrintHelp() 772 1110 { 773 /* Additional help text which is printed after the configuration 774 options goes here */ 1111 Main::PrintHelp<StateMachineToO>(); 775 1112 } 776 1113 … … 782 1119 SetupConfiguration(conf); 783 1120 784 po::variables_map vm; 785 try 786 { 787 vm = conf.Parse(argc, argv); 788 } 789 #if BOOST_VERSION > 104000 790 catch (po::multiple_occurrences &e) 791 { 792 cerr << "Program options invalid due to: " << e.what() << " of '" << e.get_option_name() << "'." << endl; 793 return -1; 794 } 795 #endif 796 catch (exception& e) 797 { 798 cerr << "Program options invalid due to: " << e.what() << endl; 799 return -1; 800 } 801 802 // try 803 { 804 // No console access at all 805 if (!conf.Has("console")) 806 { 807 if (conf.Get<bool>("no-dim")) 808 return RunShell<LocalStream, StateMachine>(conf); 809 else 810 return RunShell<LocalStream, StateMachineDim>(conf); 811 } 812 // Cosole access w/ and w/o Dim 813 if (conf.Get<bool>("no-dim")) 814 { 815 if (conf.Get<int>("console")==0) 816 return RunShell<LocalShell, StateMachine>(conf); 817 else 818 return RunShell<LocalConsole, StateMachine>(conf); 819 } 820 else 821 { 822 if (conf.Get<int>("console")==0) 823 return RunShell<LocalShell, StateMachineDim>(conf); 824 else 825 return RunShell<LocalConsole, StateMachineDim>(conf); 826 } 827 } 828 /* catch (std::exception& e) 829 { 830 std::cerr << "Exception: " << e.what() << "\n"; 831 }*/ 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); 832 1131 833 1132 return 0; 834 1133 } 1134
Note:
See TracChangeset
for help on using the changeset viewer.