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

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