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

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