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

Last change on this file since 19438 was 19429, checked in by tbretz, 6 years ago
Output the broken XML
File size: 21.4 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 Out() << "------------------------------------------------------\n";
322 Out() << fRxData.data() << '\n';
323 Out() << "------------------------------------------------------" << endl;
324 PostClose(false);
325 return;
326 }
327
328 if (fDebugRx)
329 Out() << "Parsed:\n-------\n" << doc.toString().toStdString() << endl;
330
331 const int rc = ProcessXml(doc.documentElement());
332 if (rc<0)
333 {
334 Warn("Parsing of xml failed [1].");
335 Out() << "------------------------------------------------------\n";
336 Out() << fRxData.data() << '\n';
337 Out() << "------------------------------------------------------" << endl;
338 PostClose(false);
339 return;
340 }
341
342 if (!rc)
343 {
344 Out() << "------------------------------------------------------\n";
345 Out() << doc.toString().toStdString() << '\n';
346 Out() << "------------------------------------------------------" << endl;
347 }
348
349 StartRead();
350 }
351
352 void StartRead()
353 {
354 ba::async_read(*this, ba::buffer(&fRxSize, 4),
355 boost::bind(&ConnectionGCN::HandleReceivedData, this,
356 dummy::error, dummy::bytes_transferred, 0));
357 }
358
359 // This is called when a connection was established
360 void ConnectionEstablished()
361 {
362 StartRead();
363 }
364
365public:
366 ConnectionGCN(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()),
367 fIsVerbose(false), fDebugRx(false), fLastKeepAlive(Time::none)
368 {
369 SetLogStream(&imp);
370
371 for (auto it=GCN::kTypes; it->type>0; it++)
372 fTypes[it->type] = *it;
373 }
374
375 void SetVerbose(bool b)
376 {
377 fIsVerbose = b;
378 }
379
380 void SetDebugRx(bool b)
381 {
382 fDebugRx = b;
383 Connection::SetVerbose(b);
384 }
385
386 void SetEndPoints(const vector<string> &v)
387 {
388 fEndPoints = v;
389 fEndPoint = 0;
390 }
391
392 void StartConnect()
393 {
394 if (fEndPoints.size()>0)
395 SetEndpoint(fEndPoints[fEndPoint++%fEndPoints.size()]);
396 Connection::StartConnect();
397 }
398
399 bool IsValid()
400 {
401 return fLastKeepAlive.IsValid() ? Time()-fLastKeepAlive<boost::posix_time::minutes(2) : false;
402 }
403};
404
405// ------------------------------------------------------------------------
406
407#include "DimDescriptionService.h"
408
409class ConnectionDimGCN : public ConnectionGCN
410{
411private:
412
413public:
414 ConnectionDimGCN(ba::io_service& ioservice, MessageImp &imp) : ConnectionGCN(ioservice, imp)
415 {
416 }
417};
418
419// ------------------------------------------------------------------------
420
421template <class T, class S>
422class StateMachineGCN : public StateMachineAsio<T>
423{
424private:
425 S fGCN;
426
427 int Disconnect()
428 {
429 // Close all connections
430 fGCN.PostClose(false);
431
432 return T::GetCurrentState();
433 }
434
435 int Reconnect(const EventImp &evt)
436 {
437 // Close all connections to supress the warning in SetEndpoint
438 fGCN.PostClose(false);
439
440 // Now wait until all connection have been closed and
441 // all pending handlers have been processed
442 ba::io_service::poll();
443
444 if (evt.GetBool())
445 fGCN.SetEndpoint(evt.GetString());
446
447 // Now we can reopen the connection
448 fGCN.PostClose(true);
449
450 return T::GetCurrentState();
451 }
452
453 int Execute()
454 {
455 if (!fGCN.IsConnected())
456 return State::kDisconnected;
457
458 return fGCN.IsValid() ? State::kValid : State::kConnected;
459 }
460
461 bool CheckEventSize(size_t has, const char *name, size_t size)
462 {
463 if (has==size)
464 return true;
465
466 ostringstream msg;
467 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
468 T::Fatal(msg);
469 return false;
470 }
471
472 int SetVerbosity(const EventImp &evt)
473 {
474 if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
475 return T::kSM_FatalError;
476
477 fGCN.SetVerbose(evt.GetBool());
478
479 return T::GetCurrentState();
480 }
481
482 int SetDebugRx(const EventImp &evt)
483 {
484 if (!CheckEventSize(evt.GetSize(), "SetDebugRx", 1))
485 return T::kSM_FatalError;
486
487 fGCN.SetDebugRx(evt.GetBool());
488
489 return T::GetCurrentState();
490 }
491
492public:
493 StateMachineGCN(ostream &out=cout) :
494 StateMachineAsio<T>(out, "GCN"), fGCN(*this, *this)
495 {
496 // State names
497 T::AddStateName(State::kDisconnected, "Disconnected",
498 "No connection to GCN.");
499 T::AddStateName(State::kConnected, "Connected",
500 "Connection to GCN established.");
501 T::AddStateName(State::kValid, "Valid",
502 "Connection valid (keep alive received within past 2min)");
503
504 // Verbosity commands
505 T::AddEvent("SET_VERBOSE", "B:1")
506 (bind(&StateMachineGCN::SetVerbosity, this, placeholders::_1))
507 ("set verbosity state"
508 "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
509 T::AddEvent("SET_DEBUG_RX", "B:1")
510 (bind(&StateMachineGCN::SetDebugRx, this, placeholders::_1))
511 ("Set debux-rx state"
512 "|debug[bool]:dump received text and parsed text to console (yes/no)");
513
514
515 // Conenction commands
516 T::AddEvent("DISCONNECT", State::kConnected)
517 (bind(&StateMachineGCN::Disconnect, this))
518 ("disconnect from ethernet");
519 T::AddEvent("RECONNECT", "O", State::kDisconnected, State::kConnected)
520 (bind(&StateMachineGCN::Reconnect, this, placeholders::_1))
521 ("(Re)connect ethernet connection to FTM, a new address can be given"
522 "|[host][string]:new ethernet address in the form <host:port>");
523
524 fGCN.StartConnect();
525 }
526
527 void SetEndpoint(const string &url)
528 {
529 vector<string> v;
530 v.push_back(url);
531 fGCN.SetEndPoints(v);
532 }
533
534 vector<string> fEndPoints;
535
536 int EvalOptions(Configuration &conf)
537 {
538 fGCN.SetVerbose(!conf.Get<bool>("quiet"));
539 fGCN.SetEndPoints(conf.Vec<string>("addr"));
540
541 return -1;
542 }
543};
544
545// ------------------------------------------------------------------------
546
547#include "Main.h"
548
549template<class T, class S, class R>
550int RunShell(Configuration &conf)
551{
552 return Main::execute<T, StateMachineGCN<S, R>>(conf);
553}
554
555void SetupConfiguration(Configuration &conf)
556{
557 po::options_description control("FTM control options");
558 control.add_options()
559 ("no-dim", po_bool(), "Disable dim services")
560 ("addr,a", vars<string>(), "Network addresses of GCN server")
561 ("quiet,q", po_bool(true), "Disable printing contents of all received messages (except dynamic data) in clear text.")
562 ;
563
564 conf.AddOptions(control);
565}
566
567/*
568 Extract usage clause(s) [if any] for SYNOPSIS.
569 Translators: "Usage" and "or" here are patterns (regular expressions) which
570 are used to match the usage synopsis in program output. An example from cp
571 (GNU coreutils) which contains both strings:
572 Usage: cp [OPTION]... [-T] SOURCE DEST
573 or: cp [OPTION]... SOURCE... DIRECTORY
574 or: cp [OPTION]... -t DIRECTORY SOURCE...
575 */
576void PrintUsage()
577{
578 cout <<
579 "The gcn reads and evaluates alerts from the GCN network.\n"
580 "\n"
581 "The default is that the program is started without user intercation. "
582 "All actions are supposed to arrive as DimCommands. Using the -c "
583 "option, a local shell can be initialized. With h or help a short "
584 "help message about the usuage can be brought to the screen.\n"
585 "\n"
586 "Usage: gcn [-c type] [OPTIONS]\n"
587 " or: gcn [OPTIONS]\n";
588 cout << endl;
589}
590
591void PrintHelp()
592{
593 Main::PrintHelp<StateMachineGCN<StateMachine, ConnectionGCN>>();
594
595 /* Additional help text which is printed after the configuration
596 options goes here */
597
598 /*
599 cout << "bla bla bla" << endl << endl;
600 cout << endl;
601 cout << "Environment:" << endl;
602 cout << "environment" << endl;
603 cout << endl;
604 cout << "Examples:" << endl;
605 cout << "test exam" << endl;
606 cout << endl;
607 cout << "Files:" << endl;
608 cout << "files" << endl;
609 cout << endl;
610 */
611}
612
613int main(int argc, const char* argv[])
614{
615 Configuration conf(argv[0]);
616 conf.SetPrintUsage(PrintUsage);
617 Main::SetupConfiguration(conf);
618 SetupConfiguration(conf);
619
620 if (!conf.DoParse(argc, argv, PrintHelp))
621 return 127;
622
623 //try
624 {
625 // No console access at all
626 if (!conf.Has("console"))
627 {
628 if (conf.Get<bool>("no-dim"))
629 return RunShell<LocalStream, StateMachine, ConnectionGCN>(conf);
630 else
631 return RunShell<LocalStream, StateMachineDim, ConnectionDimGCN>(conf);
632 }
633 // Cosole access w/ and w/o Dim
634 if (conf.Get<bool>("no-dim"))
635 {
636 if (conf.Get<int>("console")==0)
637 return RunShell<LocalShell, StateMachine, ConnectionGCN>(conf);
638 else
639 return RunShell<LocalConsole, StateMachine, ConnectionGCN>(conf);
640 }
641 else
642 {
643 if (conf.Get<int>("console")==0)
644 return RunShell<LocalShell, StateMachineDim, ConnectionDimGCN>(conf);
645 else
646 return RunShell<LocalConsole, StateMachineDim, ConnectionDimGCN>(conf);
647 }
648 }
649 /*catch (std::exception& e)
650 {
651 cerr << "Exception: " << e.what() << endl;
652 return -1;
653 }*/
654
655 return 0;
656}
Note: See TracBrowser for help on using the repository browser.