source: trunk/FACT++/src/gcn.cc@ 18960

Last change on this file since 18960 was 18780, checked in by Daniela Dorner, 8 years ago
fixed that AMON alerts caused the program to disconnect because of missing description
File size: 20.9 KB
Line 
1#include <functional>
2
3#include "Dim.h"
4#include "Event.h"
5#include "Shell.h"
6#include "StateMachineDim.h"
7#include "StateMachineAsio.h"
8#include "Connection.h"
9#include "LocalControl.h"
10#include "Configuration.h"
11#include "Console.h"
12#include "Converter.h"
13
14#include "tools.h"
15#include "../externals/nova.h"
16
17#include "HeadersGCN.h"
18
19#include <QtXml/QDomDocument>
20
21namespace ba = boost::asio;
22namespace bs = boost::system;
23namespace dummy = ba::placeholders;
24
25using namespace std;
26using namespace GCN;
27
28// ------------------------------------------------------------------------
29
30class ConnectionGCN : public Connection
31{
32private:
33 map<uint16_t, GCN::PaketType_t> fTypes;
34
35 vector<string> fEndPoints;
36 int fEndPoint;
37
38 bool fIsVerbose;
39 bool fDebugRx;
40
41 uint32_t fRxSize;
42 vector<char> fRxData;
43
44 Time fLastKeepAlive;
45
46 GCN::PaketType_t GetType(const QDomElement &what)
47 {
48 const QDomNodeList param = what.elementsByTagName("Param");
49 for (int i=0; i<param.count(); i++)
50 {
51 const QDomElement elem = param.at(i).toElement();
52 if (elem.attribute("name").toStdString()!="Packet_Type")
53 continue;
54
55 const uint16_t val = elem.attribute("value").toUInt();
56 const auto it = fTypes.find(val);
57 if (it!=fTypes.end())
58 return it->second;
59
60 Warn("Unknown paket type "+to_string(val)+".");
61 }
62
63 return { -1, "", "" };
64 }
65
66
67 int ProcessXml(const QDomElement &root)
68 {
69 if (root.isNull())
70 return -1;
71
72 const string role = root.attribute("role", "").toStdString();
73 const string name = root.tagName().toStdString();
74
75 // A full description can be found at http://voevent.dc3.com/schema/default.html
76
77 if (name=="trn:Transport")
78 {
79 if (role=="iamalive")
80 {
81 const QDomElement orig = root.firstChildElement("Origin");
82 const QDomElement time = root.firstChildElement("TimeStamp");
83 if (orig.isNull() || time.isNull())
84 return -1;
85
86 fLastKeepAlive = Time(time.text().toStdString());
87
88 if (fIsVerbose)
89 {
90 Out() << Time().GetAsStr() << " ----- " << name << " [" << role << "] -----" << endl;
91 Out() << " " << time.tagName().toStdString() << " = " << fLastKeepAlive.GetAsStr() << '\n';
92 Out() << " " << orig.tagName().toStdString() << " = " << orig.text().toStdString() << '\n';
93 Out() << endl;
94 }
95
96 return true;
97 }
98
99 return false;
100 }
101
102 ofstream fout("gcn.stream", ios::app);
103 fout << "------------------------------------------------------------------------------\n" << fRxData.data() << endl;
104
105 if (name=="voe:VOEvent")
106 {
107 // WHAT: http://gcn.gsfc.nasa.gov/tech_describe.html
108 const QDomElement who = root.firstChildElement("Who");
109 const QDomElement what = root.firstChildElement("What");
110 const QDomElement when = root.firstChildElement("WhereWhen");
111 //const QDomElement how = root.firstChildElement("How");
112 //const QDomElement why = root.firstChildElement("Why");
113 //const QDomElement cite = root.firstChildElement("Citations");
114 //const QDomElement desc = root.firstChildElement("Description");
115 //const QDomElement ref = root.firstChildElement("Reference");
116 if (who.isNull() || what.isNull() || when.isNull())
117 return -1;
118
119 const QDomElement date = who.firstChildElement("Date");
120 const QDomElement author = who.firstChildElement("Author");
121 const QDomElement sname = author.firstChildElement("shortName");
122 const QDomElement desc = what.firstChildElement("Description");
123
124 const QDomElement obsdat = when.firstChildElement("ObsDataLocation");
125 const QDomElement obsloc = obsdat.firstChildElement("ObservationLocation");
126 const QDomElement coord = obsloc.firstChildElement("AstroCoords");
127
128 const QDomElement time = coord.firstChildElement("Time").firstChildElement("TimeInstant").firstChildElement("ISOTime");
129 const QDomElement pos2d = coord.firstChildElement("Position2D");
130 const QDomElement name1 = pos2d.firstChildElement("Name1");
131 const QDomElement name2 = pos2d.firstChildElement("Name2");
132 const QDomElement val2 = pos2d.firstChildElement("Value2");
133 const QDomElement c1 = val2.firstChildElement("C1");
134 const QDomElement c2 = val2.firstChildElement("C2");
135 const QDomElement errad = pos2d.firstChildElement("Error2Radius");
136
137 if (date.isNull() || author.isNull() || sname.isNull() || //desc.isNull() ||
138 obsdat.isNull() || obsloc.isNull() || coord.isNull() || time.isNull() ||
139 pos2d.isNull() || name1.isNull() || name2.isNull() || val2.isNull() ||
140 c1.isNull() || c2.isNull() || errad.isNull())
141 return -1;
142
143 const GCN::PaketType_t ptype = GetType(what);
144
145 // 59/31: Konus LC / IPN raw [observation]
146 // 110: Fermi GBM (ART) [observation] (Initial) // Stop data taking
147 // 111: Fermi GBM (FLT) [observation] (after ~2s) // Start pointing/run
148 // 112: Fermi GBM (GND) [observation] (after 2-20s) // Refine pointing
149 // 115: Fermi GBM position [observation] (final ~hours)
150 //
151 // 51: Intergal pointdir [utility]
152 // 83: Swift pointdir [utility]
153 // 129: Fermi pointdir [utility]
154 //
155 // 2: Test coord ( 1) [test]
156 // 44: HETE test ( 41- 43) [test]
157 // 52: Integral SPIACS [test]
158 // 53: Integral Wakeup [test]
159 // 54: Integral refined [test]
160 // 55: Integral Offline [test]
161 // 56: Integral Weak [test]
162 // 82: BAT GRB pos test ( 61) [test]
163 // 109: AGILE GRB pos test (100-103) [test]
164 // 119: Fermi GRB pos test (111-113) [test]
165 // 124: Fermi LAT pos upd test (120-122) [test]
166 // 136: MAXI coord test ( 134) [test]
167 //
168 // Integral: RA=1.2343, Dec=2.3456
169 //
170
171 /*
172 54
173 ==
174 <Group name="Test_mpos" >
175 <Param name="Test_Notice" value="true" />
176 </Group>
177
178
179 82
180 ==
181 <Group name="Solution_Status" >
182 <Param name="Test_Submission" value="false" />
183 </Group>
184
185
186 115
187 ===
188 2013-07-20 19:04:13: TIME = 2013-07-20 02:46:40
189
190 <Group name="Trigger_ID" >
191 <Param name="Test_Submission" value="false" />
192 </Group>
193 */
194
195 const string unit = pos2d.attribute("unit").toStdString();
196
197 const double ra = c1.text().toDouble();
198 const double dec = c2.text().toDouble();
199 const double err = errad.text().toDouble();
200
201 const string n1 = name1.text().toStdString();
202 const string n2 = name2.text().toStdString();
203
204 Out() << Time(date.text().toStdString()).GetAsStr() << " ----- " << sname.text().toStdString() << " [" << role << "]\n";
205 if (!desc.isNull())
206 Out() << "[" << desc.text().toStdString() << "]\n";
207 Out() << ptype.name << "[" << ptype.type << "]: " << ptype.description << endl;
208 Out() << left;
209 Out() << " " << setw(5) << "TIME" << "= " << Time(time.text().toStdString()).GetAsStr() << '\n';
210 Out() << " " << setw(5) << n1 << "= " << ra << unit << '\n';
211 Out() << " " << setw(5) << n2 << "= " << dec << unit << '\n';
212 Out() << " " << setw(5) << "ERR" << "= " << err << unit << '\n';
213
214 if (n1=="RA" && n2=="Dec" && unit=="deg")
215 {
216 const double jd = Time().JD();
217
218 Nova::EquPosn equ;
219 equ.ra = ra;
220 equ.dec = dec;
221
222 const Nova::ZdAzPosn pos = Nova::GetHrzFromEqu(equ, jd);
223 const Nova::EquPosn moon = Nova::GetLunarEquCoords(jd);
224 const Nova::ZdAzPosn sun = Nova::GetHrzFromEqu(Nova::GetSolarEquCoords(jd), jd);
225
226 const double disk = Nova::GetLunarDisk(jd);
227 const double dist = Nova::GetAngularSeparation(equ, moon);
228
229 Out() << " " << setw(5) << "ZD" << "= " << pos.zd << "deg\n";
230 Out() << " " << setw(5) << "Az" << "= " << pos.az << "deg\n";
231
232 Out() << " " << setw(5) << "MOON" << "= " << int(disk*100) << "%\n";
233 Out() << " " << setw(5) << "DIST" << "= " << dist << "deg\n";
234
235 if (dist>10 && dist<170 && pos.zd<80 && sun.zd>108)
236 {
237 Out() << " visible ";
238 if (pos.zd<70)
239 Out() << '+';
240 if (pos.zd<60)
241 Out() << '+';
242 if (pos.zd<45)
243 Out() << '+';
244 Out() << '\n';
245 }
246 }
247
248 Out() << endl;
249
250 if (role=="observation")
251 {
252 return true;
253 }
254
255 if (role=="test")
256 {
257 return true;
258 }
259
260 if (role=="retraction")
261 {
262 return true;
263 }
264
265 if (role=="utility")
266 {
267 return true;
268 }
269
270 return false;
271 }
272
273 Out() << Time().GetAsStr() << " ----- " << name << " [" << role << "] -----" << endl;
274
275 return false;
276 }
277
278 void HandleReceivedData(const bs::error_code& err, size_t bytes_received, int type)
279 {
280 // Do not schedule a new read if the connection failed.
281 if (bytes_received==0 || err)
282 {
283 if (err==ba::error::eof)
284 Warn("Connection closed by remote host (GCN).");
285
286 // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category))
287 // 125: Operation canceled
288 if (err && err!=ba::error::eof && // Connection closed by remote host
289 err!=ba::error::basic_errors::not_connected && // Connection closed by remote host
290 err!=ba::error::basic_errors::operation_aborted) // Connection closed by us
291 {
292 ostringstream str;
293 str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl;
294 Error(str);
295 }
296 PostClose(err!=ba::error::basic_errors::operation_aborted);
297 return;
298 }
299
300 if (type==0)
301 {
302 fRxSize = ntohl(fRxSize);
303 fRxData.assign(fRxSize+1, 0);
304 ba::async_read(*this, ba::buffer(fRxData, fRxSize),
305 boost::bind(&ConnectionGCN::HandleReceivedData, this,
306 dummy::error, dummy::bytes_transferred, 1));
307 return;
308 }
309
310 if (fDebugRx)
311 {
312 Out() << "------------------------------------------------------\n";
313 Out() << fRxData.data() << '\n';
314 Out() << "------------------------------------------------------" << endl;
315 }
316
317 QDomDocument doc;
318 if (!doc.setContent(QString(fRxData.data()), false))
319 {
320 Warn("Parsing of xml failed [0].");
321 PostClose(false);
322 return;
323 }
324
325 if (fDebugRx)
326 Out() << "Parsed:\n-------\n" << doc.toString().toStdString() << endl;
327
328 const int rc = ProcessXml(doc.documentElement());
329 if (rc<0)
330 {
331 Warn("Parsing of xml failed [1].");
332 PostClose(false);
333 return;
334 }
335
336 if (!rc)
337 {
338 Out() << "------------------------------------------------------\n";
339 Out() << doc.toString().toStdString() << '\n';
340 Out() << "------------------------------------------------------" << endl;
341 }
342
343 StartRead();
344 }
345
346 void StartRead()
347 {
348 ba::async_read(*this, ba::buffer(&fRxSize, 4),
349 boost::bind(&ConnectionGCN::HandleReceivedData, this,
350 dummy::error, dummy::bytes_transferred, 0));
351 }
352
353 // This is called when a connection was established
354 void ConnectionEstablished()
355 {
356 StartRead();
357 }
358
359public:
360 ConnectionGCN(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()),
361 fIsVerbose(false), fDebugRx(false), fLastKeepAlive(Time::none)
362 {
363 SetLogStream(&imp);
364
365 for (auto it=GCN::kTypes; it->type>0; it++)
366 fTypes[it->type] = *it;
367 }
368
369 void SetVerbose(bool b)
370 {
371 fIsVerbose = b;
372 }
373
374 void SetDebugRx(bool b)
375 {
376 fDebugRx = b;
377 Connection::SetVerbose(b);
378 }
379
380 void SetEndPoints(const vector<string> &v)
381 {
382 fEndPoints = v;
383 fEndPoint = 0;
384 }
385
386 void StartConnect()
387 {
388 if (fEndPoints.size()>0)
389 SetEndpoint(fEndPoints[fEndPoint++%fEndPoints.size()]);
390 Connection::StartConnect();
391 }
392
393 bool IsValid()
394 {
395 return fLastKeepAlive.IsValid() ? Time()-fLastKeepAlive<boost::posix_time::minutes(2) : false;
396 }
397};
398
399// ------------------------------------------------------------------------
400
401#include "DimDescriptionService.h"
402
403class ConnectionDimGCN : public ConnectionGCN
404{
405private:
406
407public:
408 ConnectionDimGCN(ba::io_service& ioservice, MessageImp &imp) : ConnectionGCN(ioservice, imp)
409 {
410 }
411};
412
413// ------------------------------------------------------------------------
414
415template <class T, class S>
416class StateMachineGCN : public StateMachineAsio<T>
417{
418private:
419 S fGCN;
420
421 int Disconnect()
422 {
423 // Close all connections
424 fGCN.PostClose(false);
425
426 return T::GetCurrentState();
427 }
428
429 int Reconnect(const EventImp &evt)
430 {
431 // Close all connections to supress the warning in SetEndpoint
432 fGCN.PostClose(false);
433
434 // Now wait until all connection have been closed and
435 // all pending handlers have been processed
436 ba::io_service::poll();
437
438 if (evt.GetBool())
439 fGCN.SetEndpoint(evt.GetString());
440
441 // Now we can reopen the connection
442 fGCN.PostClose(true);
443
444 return T::GetCurrentState();
445 }
446
447 int Execute()
448 {
449 if (!fGCN.IsConnected())
450 return State::kDisconnected;
451
452 return fGCN.IsValid() ? State::kValid : State::kConnected;
453 }
454
455 bool CheckEventSize(size_t has, const char *name, size_t size)
456 {
457 if (has==size)
458 return true;
459
460 ostringstream msg;
461 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
462 T::Fatal(msg);
463 return false;
464 }
465
466 int SetVerbosity(const EventImp &evt)
467 {
468 if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
469 return T::kSM_FatalError;
470
471 fGCN.SetVerbose(evt.GetBool());
472
473 return T::GetCurrentState();
474 }
475
476 int SetDebugRx(const EventImp &evt)
477 {
478 if (!CheckEventSize(evt.GetSize(), "SetDebugRx", 1))
479 return T::kSM_FatalError;
480
481 fGCN.SetDebugRx(evt.GetBool());
482
483 return T::GetCurrentState();
484 }
485
486public:
487 StateMachineGCN(ostream &out=cout) :
488 StateMachineAsio<T>(out, "GCN"), fGCN(*this, *this)
489 {
490 // State names
491 T::AddStateName(State::kDisconnected, "Disconnected",
492 "No connection to GCN.");
493 T::AddStateName(State::kConnected, "Connected",
494 "Connection to GCN established.");
495 T::AddStateName(State::kValid, "Valid",
496 "Connection valid (keep alive received within past 2min)");
497
498 // Verbosity commands
499 T::AddEvent("SET_VERBOSE", "B:1")
500 (bind(&StateMachineGCN::SetVerbosity, this, placeholders::_1))
501 ("set verbosity state"
502 "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
503 T::AddEvent("SET_DEBUG_RX", "B:1")
504 (bind(&StateMachineGCN::SetDebugRx, this, placeholders::_1))
505 ("Set debux-rx state"
506 "|debug[bool]:dump received text and parsed text to console (yes/no)");
507
508
509 // Conenction commands
510 T::AddEvent("DISCONNECT", State::kConnected)
511 (bind(&StateMachineGCN::Disconnect, this))
512 ("disconnect from ethernet");
513 T::AddEvent("RECONNECT", "O", State::kDisconnected, State::kConnected)
514 (bind(&StateMachineGCN::Reconnect, this, placeholders::_1))
515 ("(Re)connect ethernet connection to FTM, a new address can be given"
516 "|[host][string]:new ethernet address in the form <host:port>");
517
518 fGCN.StartConnect();
519 }
520
521 void SetEndpoint(const string &url)
522 {
523 vector<string> v;
524 v.push_back(url);
525 fGCN.SetEndPoints(v);
526 }
527
528 vector<string> fEndPoints;
529
530 int EvalOptions(Configuration &conf)
531 {
532 fGCN.SetVerbose(!conf.Get<bool>("quiet"));
533 fGCN.SetEndPoints(conf.Vec<string>("addr"));
534
535 return -1;
536 }
537};
538
539// ------------------------------------------------------------------------
540
541#include "Main.h"
542
543template<class T, class S, class R>
544int RunShell(Configuration &conf)
545{
546 return Main::execute<T, StateMachineGCN<S, R>>(conf);
547}
548
549void SetupConfiguration(Configuration &conf)
550{
551 po::options_description control("FTM control options");
552 control.add_options()
553 ("no-dim", po_bool(), "Disable dim services")
554 ("addr,a", vars<string>(), "Network addresses of GCN server")
555 ("quiet,q", po_bool(true), "Disable printing contents of all received messages (except dynamic data) in clear text.")
556 ;
557
558 conf.AddOptions(control);
559}
560
561/*
562 Extract usage clause(s) [if any] for SYNOPSIS.
563 Translators: "Usage" and "or" here are patterns (regular expressions) which
564 are used to match the usage synopsis in program output. An example from cp
565 (GNU coreutils) which contains both strings:
566 Usage: cp [OPTION]... [-T] SOURCE DEST
567 or: cp [OPTION]... SOURCE... DIRECTORY
568 or: cp [OPTION]... -t DIRECTORY SOURCE...
569 */
570void PrintUsage()
571{
572 cout <<
573 "The gcn reads and evaluates alerts from the GCN network.\n"
574 "\n"
575 "The default is that the program is started without user intercation. "
576 "All actions are supposed to arrive as DimCommands. Using the -c "
577 "option, a local shell can be initialized. With h or help a short "
578 "help message about the usuage can be brought to the screen.\n"
579 "\n"
580 "Usage: gcn [-c type] [OPTIONS]\n"
581 " or: gcn [OPTIONS]\n";
582 cout << endl;
583}
584
585void PrintHelp()
586{
587 Main::PrintHelp<StateMachineGCN<StateMachine, ConnectionGCN>>();
588
589 /* Additional help text which is printed after the configuration
590 options goes here */
591
592 /*
593 cout << "bla bla bla" << endl << endl;
594 cout << endl;
595 cout << "Environment:" << endl;
596 cout << "environment" << endl;
597 cout << endl;
598 cout << "Examples:" << endl;
599 cout << "test exam" << endl;
600 cout << endl;
601 cout << "Files:" << endl;
602 cout << "files" << endl;
603 cout << endl;
604 */
605}
606
607int main(int argc, const char* argv[])
608{
609 Configuration conf(argv[0]);
610 conf.SetPrintUsage(PrintUsage);
611 Main::SetupConfiguration(conf);
612 SetupConfiguration(conf);
613
614 if (!conf.DoParse(argc, argv, PrintHelp))
615 return 127;
616
617 //try
618 {
619 // No console access at all
620 if (!conf.Has("console"))
621 {
622 if (conf.Get<bool>("no-dim"))
623 return RunShell<LocalStream, StateMachine, ConnectionGCN>(conf);
624 else
625 return RunShell<LocalStream, StateMachineDim, ConnectionDimGCN>(conf);
626 }
627 // Cosole access w/ and w/o Dim
628 if (conf.Get<bool>("no-dim"))
629 {
630 if (conf.Get<int>("console")==0)
631 return RunShell<LocalShell, StateMachine, ConnectionGCN>(conf);
632 else
633 return RunShell<LocalConsole, StateMachine, ConnectionGCN>(conf);
634 }
635 else
636 {
637 if (conf.Get<int>("console")==0)
638 return RunShell<LocalShell, StateMachineDim, ConnectionDimGCN>(conf);
639 else
640 return RunShell<LocalConsole, StateMachineDim, ConnectionDimGCN>(conf);
641 }
642 }
643 /*catch (std::exception& e)
644 {
645 cerr << "Exception: " << e.what() << endl;
646 return -1;
647 }*/
648
649 return 0;
650}
Note: See TracBrowser for help on using the repository browser.