source: trunk/FACT++/src/mcp.cc@ 17168

Last change on this file since 17168 was 16784, checked in by tbretz, 11 years ago
Adapted the state checks according to the recent change in the ftmctrl.
File size: 22.7 KB
Line 
1#include "Dim.h"
2#include "Event.h"
3#include "Shell.h"
4#include "StateMachineDim.h"
5#include "Connection.h"
6#include "Configuration.h"
7#include "Console.h"
8
9#include "tools.h"
10
11#include "LocalControl.h"
12
13#include "HeadersFTM.h"
14#include "HeadersFAD.h"
15#include "HeadersMCP.h"
16#include "HeadersRateControl.h"
17
18namespace ba = boost::asio;
19namespace bs = boost::system;
20namespace dummy = ba::placeholders;
21
22using namespace std;
23
24// ------------------------------------------------------------------------
25
26#include "DimDescriptionService.h"
27#include "DimState.h"
28
29// ------------------------------------------------------------------------
30
31class StateMachineMCP : public StateMachineDim
32{
33private:
34 vector<bool> fFadConnected;
35 vector<bool> fFadNeedsReset;
36
37 vector<bool> fFadCratesForReset;
38 vector<bool> fFadBoardsForConnection;
39
40 uint16_t fNumConnectedFtu;
41 uint16_t fNumConnectedFad;
42
43 uint16_t fNumReset;
44
45 DimVersion fDim;
46 DimDescribedState fDimFTM;
47 DimDescribedState fDimFAD;
48 DimDescribedState fDimLog;
49 DimDescribedState fDimRC;
50
51 DimDescribedService fService;
52
53 Time fFadTimeout;
54
55 int HandleFadConnections(const EventImp &d)
56 {
57 if (d.GetSize()!=41)
58 return GetCurrentState();
59
60 const uint8_t *ptr = d.Ptr<uint8_t>();
61
62 fNumConnectedFad = 0;
63 fFadConnected.assign(40, false);
64
65 vector<bool> reset(4);
66
67 for (int i=0; i<40; i++)
68 {
69 const uint8_t stat1 = ptr[i]&3;
70 const uint8_t stat2 = ptr[i]>>3;
71
72 // disconnected: ignore
73 if (stat1==0 && stat2==0)
74 continue;
75
76 fFadConnected[i] = true;
77
78 if (stat1>=2 && stat2==8)
79 fNumConnectedFad++;
80
81 // Does not need reset
82 if (stat1>2 && stat2==8)
83 continue;
84
85 // Not configured (stat1==2?kLedGreen:kLedGreenCheck)
86 // Connection problem (stat1==1&&stat2==1?kLedRed:kLedOrange)
87 reset[i/10] = true;
88 }
89 return GetCurrentState();
90 }
91
92 int HandleFtmStaticData(const EventImp &d)
93 {
94 if (d.GetSize()!=sizeof(FTM::DimStaticData))
95 return GetCurrentState();
96
97 const FTM::DimStaticData &sdata = d.Ref<FTM::DimStaticData>();
98
99 fNumConnectedFtu = 0;
100 for (int i=0; i<40; i++)
101 {
102 if (sdata.IsActive(i))
103 fNumConnectedFtu++;
104 }
105 return GetCurrentState();
106 }
107
108 int Print() const
109 {
110 Out() << fDim << endl;
111 Out() << fDimFTM << endl;
112 Out() << fDimFAD << endl;
113 Out() << fDimLog << endl;
114 Out() << fDimRC << endl;
115
116 return GetCurrentState();
117 }
118
119 int GetReady()
120 {
121 return GetCurrentState();
122 }
123
124 int StopRun()
125 {
126 if (fDimFTM.state()==FTM::State::kTriggerOn)
127 {
128 Message("Stopping FTM");
129 Dim::SendCommandNB("FTM_CONTROL/STOP_TRIGGER");
130 }
131
132 // FIXME: Do step 2 only when FTM is stopped
133 if (fDimFAD.state()==FAD::State::kConnected || fDimFAD.state()==FAD::State::kRunInProgress)
134 {
135 //Dim::SendCommand("FAD_CONTROL/ENABLE_TRIGGER_LINE", bool(false));
136 Message("Stopping FAD");
137 Dim::SendCommandNB("FAD_CONTROL/ENABLE_CONTINOUS_TRIGGER", bool(false));
138 if (fDimFAD.state()==FAD::State::kRunInProgress)
139 Dim::SendCommandNB("FAD_CONTROL/CLOSE_OPEN_FILES");
140 }
141
142 return GetCurrentState();
143 }
144
145 int Reset()
146 {
147 if (GetCurrentState()<MCP::State::kConfiguring1 ||
148 GetCurrentState()>MCP::State::kConfigured)
149 return GetCurrentState();
150
151 fRunType = "";
152 Message("Reseting configuration states of FAD and FTM");
153
154 Dim::SendCommandNB("FTM_CONTROL/RESET_CONFIGURE");
155 Dim::SendCommandNB("FAD_CONTROL/RESET_CONFIGURE");
156 Dim::SendCommandNB("RATE_CONTROL/STOP");
157
158 Update(MCP::State::kIdle);
159 return MCP::State::kIdle;
160 /*
161 // FIMXE: Handle error states!
162 if (fDimLog.state()>=20)//kSM_NightlyOpen
163 Dim::SendCommand("DATA_LOGGER/STOP");
164
165 if (fDimLog.state()==0)
166 Dim::SendCommand("DATA_LOGGER/WAIT_FOR_RUN_NUMBER");
167
168 if (fDimFAD.state()==FAD::State::kConnected)
169 {
170 Dim::SendCommand("FAD_CONTROL/ENABLE_TRIGGER_LINE", bool(false));
171 Dim::SendCommand("FAD_CONTROL/ENABLE_CONTINOUS_TRIGGER", bool(false));
172 }
173
174 if (fDimFTM.state()==FTM::State::kTakingData)
175 Dim::SendCommand("FTM_CONTROL/STOP");
176
177 return GetCurrentState(); */
178 }
179
180 int64_t fMaxTime;
181 int64_t fNumEvents;
182 string fRunType;
183
184 int StartRun(const EventImp &evt)
185 {
186 if (!fDimFTM.online())
187 {
188 Error("No connection to ftmcontrol (see PRINT).");
189 return GetCurrentState();
190 }
191 if (!fDimFAD.online())
192 {
193 Warn("No connection to fadcontrol (see PRINT).");
194 return GetCurrentState();
195 }
196 if (!fDimLog.online())
197 {
198 Warn("No connection to datalogger (see PRINT).");
199 return GetCurrentState();
200 }
201 if (!fDimRC.online())
202 {
203 Warn("No connection to ratecontrol (see PRINT).");
204 return GetCurrentState();
205 }
206
207 fMaxTime = evt.Get<int64_t>();
208 fNumEvents = evt.Get<int64_t>(8);
209 fRunType = evt.Ptr<char>(16);
210
211 fNumReset = 0;
212
213 ostringstream str;
214 str << "Starting configuration '" << fRunType << "' for new run";
215 if (fNumEvents>0 || fMaxTime>0)
216 str << " [";
217 if (fNumEvents>0)
218 str << fNumEvents << " events";
219 if (fNumEvents>0 && fMaxTime>0)
220 str << " / ";
221 if (fMaxTime>0)
222 str << fMaxTime << "s";
223 if (fNumEvents>0 || fMaxTime>0)
224 str << "]";
225 Message(str);
226
227 // Strictly speaking, it is not necessary, but
228 // stopping the ratecontrol before we configure
229 // the FTM ensures that no threshold setting commands
230 // interfere with the configuration of the FTM.
231 if (fDimRC.state()!=RateControl::State::kConnected)
232 {
233 Dim::SendCommandNB("RATE_CONTROL/STOP");
234 Message("Stopping ratecontrol");
235 }
236
237 if (fDimLog.state()<30/*kSM_WaitForRun*/)
238 {
239 Dim::SendCommandNB("DATA_LOGGER/START_RUN_LOGGING");
240 Message("Starting datalogger");
241 }
242
243 Update(MCP::State::kConfiguring1);
244 return MCP::State::kConfiguring1;
245 }
246
247 struct Value
248 {
249 uint64_t time;
250 uint64_t nevts;
251 char type[];
252 };
253
254 Value *GetBuffer()
255 {
256 const size_t len = sizeof(Value)+fRunType.length()+1;
257
258 char *buf = new char[len];
259
260 Value *val = reinterpret_cast<Value*>(buf);
261
262 val->time = fMaxTime;
263 val->nevts = fNumEvents;
264
265 strcpy(val->type, fRunType.c_str());
266
267 return val;
268 }
269
270 void Update(int newstate)
271 {
272 Value *buf = GetBuffer();
273 fService.setQuality(newstate);
274 fService.setData(buf, sizeof(Value)+fRunType.length()+1);
275 fService.Update();
276 delete buf;
277 }
278
279 void ConfigureFAD()
280 {
281 Value *buf = GetBuffer();
282
283 Dim::SendCommandNB("FAD_CONTROL/CONFIGURE", buf, sizeof(Value)+fRunType.length()+1);
284 Message("Configuring FAD");
285
286 delete buf;
287 }
288
289 int HandleStateChange()
290 {
291 if (!fDim.online())
292 return MCP::State::kDimNetworkNA;
293
294 if (fDimFTM.state() >= FTM::State::kConnected &&
295 fDimFAD.state() >= FAD::State::kConnected &&
296 fDimLog.state() >= kSM_Ready)
297 return GetCurrentState()<=MCP::State::kIdle ? MCP::State::kIdle : GetCurrentState();
298
299 if (fDimFTM.state() >-2 &&
300 fDimFAD.state() >-2 &&
301 fDimLog.state() >-2 &&
302 fDimRC.state() >-2)
303 return MCP::State::kConnected;
304
305 if (fDimFTM.state() >-2 ||
306 fDimFAD.state() >-2 ||
307 fDimLog.state() >-2 ||
308 fDimRC.state() >-2)
309 return MCP::State::kConnecting;
310
311 return MCP::State::kDisconnected;
312 }
313
314 int Execute()
315 {
316 // ========================================================
317
318 if (GetCurrentState()==MCP::State::kConfiguring1)
319 {
320 if (fDimRC.state()!=RateControl::State::kConnected)
321 return MCP::State::kConfiguring1;
322
323 Dim::SendCommandNB("FTM_CONTROL/CONFIGURE", fRunType);
324 Message("Configuring Trigger (FTM)");
325
326 Update(MCP::State::kConfiguring2);
327 return MCP::State::kConfiguring2;
328 }
329
330 // --------------------------------------------------------
331
332 if (GetCurrentState()==MCP::State::kConfiguring2)
333 {
334 if (fDimFTM.state() != FTM::State::kConfigured1 ||
335 fDimLog.state()<30 || fDimLog.state()>0xff ||
336 fDimRC.state()!=RateControl::State::kConnected)
337 return MCP::State::kConfiguring2;
338
339 // For calibration, ratecontrol will globally set all threshold
340 // to make sure that does not interfer with the configuration,
341 // it is only done when the ftm reports Configured
342 Dim::SendCommandNB("RATE_CONTROL/CALIBRATE_RUN", fRunType);
343 Message("Starting Rate Control");
344
345 ConfigureFAD();
346
347 fFadTimeout = Time();
348
349 Update(MCP::State::kConfiguring3);
350 return MCP::State::kConfiguring3;
351 }
352
353 // --------------------------------------------------------
354
355 if (GetCurrentState()==MCP::State::kConfiguring3)
356 {
357 /*
358 // If everything is configured but the FADs
359 // we run into a timeout and some FAD need to be reset
360 // then we start an automatic crate reset
361 if (fDimFTM.state() == FTM::State::kConfigured &&
362 fDimFAD.state() != FAD::State::kConfigured &&
363 //fDimRC.state() > RateControl::State::kSettingGlobalThreshold &&
364 fFadTimeout+boost::posix_time::seconds(15)<Time() &&
365 count(fFadNeedsReset.begin(), fFadNeedsReset.end(), true)>0)
366 {
367 Update(MCP::State::kCrateReset0);
368 return MCP::State::kCrateReset0;
369 }
370 */
371 // If something is not yet properly configured: keep state
372 if (fDimFTM.state() != FTM::State::kConfigured1 ||
373 fDimFAD.state() != FAD::State::kConfigured ||
374 fDimRC.state() <= RateControl::State::kSettingGlobalThreshold)
375 return MCP::State::kConfiguring3;
376
377 // Note that before the trigger is started, the ratecontrol
378 // must not be InProgress. In rare cases there is interference.
379 Dim::SendCommandNB("FTM_CONTROL/START_TRIGGER");
380 Message("Starting Trigger (FTM)");
381
382 Update(MCP::State::kConfigured);
383 return MCP::State::kConfigured;
384 }
385
386 // --------------------------------------------------------
387
388 if (GetCurrentState()==MCP::State::kConfigured)
389 {
390 if (fDimFTM.state() != FTM::State::kTriggerOn)
391 return MCP::State::kConfigured;
392
393 Update(MCP::State::kTriggerOn);
394 return MCP::State::kTriggerOn;
395 }
396
397 // --------------------------------------------------------
398
399 if (GetCurrentState()==MCP::State::kTriggerOn)
400 {
401 if (fDimFTM.state() != FTM::State::kTriggerOn)
402 {
403 Update(MCP::State::kIdle);
404 return MCP::State::kIdle;
405 }
406
407 if (fDimFAD.state() != FAD::State::kRunInProgress)
408 return MCP::State::kTriggerOn;
409
410 Update(MCP::State::kTakingData);
411 return MCP::State::kTakingData;
412 }
413
414 // --------------------------------------------------------
415
416 if (GetCurrentState()==MCP::State::kTakingData)
417 {
418 if (/*fDimFTM.state()==FTM::State::kTriggerOn &&*/
419 fDimFAD.state()==FAD::State::kRunInProgress)
420 return MCP::State::kTakingData;
421
422 Update(MCP::State::kIdle);
423 return MCP::State::kIdle;
424 }
425
426 // ========================================================
427 /*
428 if (GetCurrentState()==MCP::State::kCrateReset0)
429 {
430 static const struct Data { int32_t id; char on; } __attribute__((__packed__)) d = { -1, 0 };
431
432 Dim::SendCommandNB("FTM_CONTROL/ENABLE_FTU", &d, sizeof(Data));
433
434 fFadCratesForReset = fFadNeedsReset;
435 fFadBoardsForConnection = fFadConnected;
436
437 for (int c=0; c<4; c++)
438 if (fFadNeedsReset[c])
439 for (int b=0; b<10; b++)
440 Dim::SendCommandNB("FAD_CONTROL/DISCONNECT", uint16_t(c*10+b));
441
442 fNumReset++;
443
444 Update(MCP::State::kCrateReset1);
445 return MCP::State::kCrateReset1;
446 }
447
448 // --------------------------------------------------------
449
450 if (GetCurrentState()==MCP::State::kCrateReset1)
451 {
452 if (fNumConnectedFtu>0 || count(fFadNeedsReset.begin(), fFadNeedsReset.end(), true)>0)
453 return MCP::State::kCrateReset1;
454
455 for (int i=0; i<4; i++)
456 if (fFadCratesForReset[i])
457 Dim::SendCommandNB("FAD_CONTROL/RESET_CRATE", uint16_t(i));
458
459 fFadTimeout = Time();
460
461 Update(MCP::State::kCrateReset2);
462 return MCP::State::kCrateReset2;
463 }
464
465 // --------------------------------------------------------
466
467 if (GetCurrentState()==MCP::State::kCrateReset2)
468 {
469 if (fFadTimeout+boost::posix_time::seconds(45)>Time())
470 return MCP::State::kCrateReset2;
471
472 static const struct Data { int32_t id; char on; } __attribute__((__packed__)) d = { -1, 1 };
473
474 Dim::SendCommandNB("FTM_CONTROL/ENABLE_FTU", &d, sizeof(Data));
475
476 for (int c=0; c<4; c++)
477 if (fFadCratesForReset[c])
478 for (int b=0; b<10; b++)
479 if (fFadBoardsForConnection[c*10+b])
480 Dim::SendCommandNB("FAD_CONTROL/CONNECT", uint16_t(c*10+b));
481
482 Update(MCP::State::kCrateReset3);
483 return MCP::State::kCrateReset3;
484 }
485
486 // --------------------------------------------------------
487
488 if (GetCurrentState()==MCP::State::kCrateReset3)
489 {
490 if (fNumConnectedFtu<40 || fFadBoardsForConnection!=fFadConnected)
491 return MCP::State::kCrateReset3;
492
493 if (count(fFadNeedsReset.begin(), fFadNeedsReset.end(), true)>0 && fNumReset<6)
494 {
495 Update(MCP::State::kCrateReset0);
496 return MCP::State::kCrateReset0;
497 }
498
499 // restart configuration
500 Update(MCP::State::kConfiguring1);
501 return MCP::State::kConfiguring1;
502 }
503 */
504 // ========================================================
505
506 return GetCurrentState();
507 }
508
509public:
510 StateMachineMCP(ostream &out=cout) : StateMachineDim(out, "MCP"),
511 fFadNeedsReset(4), fNumConnectedFtu(40),
512 fDimFTM("FTM_CONTROL"),
513 fDimFAD("FAD_CONTROL"),
514 fDimLog("DATA_LOGGER"),
515 fDimRC("RATE_CONTROL"),
516 fService("MCP/CONFIGURATION", "X:1;X:1;C", "Run configuration information"
517 "|MaxTime[s]:Maximum time before the run gets stopped"
518 "|MaxEvents[num]:Maximum number of events before the run gets stopped"
519 "|Name[text]:Name of the chosen configuration")
520 {
521 // ba::io_service::work is a kind of keep_alive for the loop.
522 // It prevents the io_service to go to stopped state, which
523 // would prevent any consecutive calls to run()
524 // or poll() to do nothing. reset() could also revoke to the
525 // previous state but this might introduce some overhead of
526 // deletion and creation of threads and more.
527
528 fDim.Subscribe(*this);
529 fDimFTM.Subscribe(*this);
530 fDimFAD.Subscribe(*this);
531 fDimLog.Subscribe(*this);
532 fDimRC.Subscribe(*this);
533
534 fDim.SetCallback(bind(&StateMachineMCP::HandleStateChange, this));
535 fDimFTM.SetCallback(bind(&StateMachineMCP::HandleStateChange, this));
536 fDimFAD.SetCallback(bind(&StateMachineMCP::HandleStateChange, this));
537 fDimLog.SetCallback(bind(&StateMachineMCP::HandleStateChange, this));
538 fDimRC.SetCallback(bind(&StateMachineMCP::HandleStateChange, this));
539
540 Subscribe("FAD_CONTROL/CONNECTIONS")
541 (bind(&StateMachineMCP::HandleFadConnections, this, placeholders::_1));
542 Subscribe("FTM_CONTROL/STATIC_DATA")
543 (bind(&StateMachineMCP::HandleFtmStaticData, this, placeholders::_1));
544
545 // State names
546 AddStateName(MCP::State::kDimNetworkNA, "DimNetworkNotAvailable",
547 "DIM dns server not available.");
548 AddStateName(MCP::State::kDisconnected, "Disconnected",
549 "Neither ftmctrl, fadctrl, datalogger nor rate control online.");
550 AddStateName(MCP::State::kConnecting, "Connecting",
551 "Either ftmctrl, fadctrl, datalogger or rate control not online.");
552 AddStateName(MCP::State::kConnected, "Connected",
553 "All needed subsystems online.");
554 AddStateName(MCP::State::kIdle, "Idle",
555 "Waiting for next configuration command");
556 AddStateName(MCP::State::kConfiguring1, "Configuring1",
557 "Starting configuration procedure, checking datalogger/ratecontrol state");
558 AddStateName(MCP::State::kConfiguring2, "Configuring2",
559 "Starting ratecontrol, waiting for FTM to get configured and Datalogger to get ready");
560 AddStateName(MCP::State::kConfiguring3, "Configuring3",
561 "Waiting for FADs and ratecontrol to get ready");
562 /*
563 AddStateName(MCP::State::kCrateReset0, "CrateReset0",
564 "Disabling FTUs, disconnecting FADs");
565 AddStateName(MCP::State::kCrateReset1, "CrateReset1",
566 "Waiting for FTUs to be disabled and for FADs to be disconnected");
567 AddStateName(MCP::State::kCrateReset2, "CrateReset2",
568 "Waiting 45s");
569 AddStateName(MCP::State::kCrateReset3, "CrateReset3",
570 "Waiting for FTUs to be enabled and for FADs to be re-connected");
571 */
572 AddStateName(MCP::State::kConfigured, "Configured",
573 "Everything is configured, trigger will be switched on now");
574 AddStateName(MCP::State::kTriggerOn, "TriggerOn",
575 "The trigger is switched on, waiting for FAD to receive data");
576 AddStateName(MCP::State::kTakingData, "TakingData",
577 "The trigger is switched on, FADs are sending data");
578
579
580 AddEvent("START", "X:2;C")//, MCP::State::kIdle)
581 (bind(&StateMachineMCP::StartRun, this, placeholders::_1))
582 ("Start the configuration and data taking for a run-type of a pre-defined setup"
583 "|TimeMax[s]:Maximum number of seconds before the run will be closed automatically"
584 "|NumMax[count]:Maximum number events before the run will be closed automatically"
585 "|Name[text]:Name of the configuration to be used for taking data");
586
587 AddEvent("STOP")
588 (bind(&StateMachineMCP::StopRun, this))
589 ("Stops the trigger (either disables the FTM trigger or the internal DRS trigger)");
590
591 AddEvent("RESET")
592 (bind(&StateMachineMCP::Reset, this))
593 ("If a configuration blockes because a system cannot configure itself properly, "
594 "this command can be called to leave the configuration procedure. The command "
595 "is also propagated to FTM and FAD");
596
597 AddEvent("PRINT")
598 (bind(&StateMachineMCP::Print, this))
599 ("Print the states and connection status of all systems connected to the MCP.");
600 }
601
602 int EvalOptions(Configuration &)
603 {
604 return -1;
605 }
606};
607
608// ------------------------------------------------------------------------
609
610#include "Main.h"
611
612template<class T>
613int RunShell(Configuration &conf)
614{
615 return Main::execute<T, StateMachineMCP>(conf);
616}
617
618/*
619 Extract usage clause(s) [if any] for SYNOPSIS.
620 Translators: "Usage" and "or" here are patterns (regular expressions) which
621 are used to match the usage synopsis in program output. An example from cp
622 (GNU coreutils) which contains both strings:
623 Usage: cp [OPTION]... [-T] SOURCE DEST
624 or: cp [OPTION]... SOURCE... DIRECTORY
625 or: cp [OPTION]... -t DIRECTORY SOURCE...
626 */
627void PrintUsage()
628{
629 cout <<
630 "The ftmctrl controls the FSC (FACT Slow Control) board.\n"
631 "\n"
632 "The default is that the program is started without user intercation. "
633 "All actions are supposed to arrive as DimCommands. Using the -c "
634 "option, a local shell can be initialized. With h or help a short "
635 "help message about the usuage can be brought to the screen.\n"
636 "\n"
637 "Usage: fscctrl [-c type] [OPTIONS]\n"
638 " or: fscctrl [OPTIONS]\n";
639 cout << endl;
640}
641
642void PrintHelp()
643{
644 Main::PrintHelp<StateMachineMCP>();
645
646 /* Additional help text which is printed after the configuration
647 options goes here */
648
649 /*
650 cout << "bla bla bla" << endl << endl;
651 cout << endl;
652 cout << "Environment:" << endl;
653 cout << "environment" << endl;
654 cout << endl;
655 cout << "Examples:" << endl;
656 cout << "test exam" << endl;
657 cout << endl;
658 cout << "Files:" << endl;
659 cout << "files" << endl;
660 cout << endl;
661 */
662}
663
664int main(int argc, const char* argv[])
665{
666 Configuration conf(argv[0]);
667 conf.SetPrintUsage(PrintUsage);
668 Main::SetupConfiguration(conf);
669
670 if (!conf.DoParse(argc, argv, PrintHelp))
671 return 127;
672
673 //try
674 {
675 // No console access at all
676 if (!conf.Has("console"))
677 {
678// if (conf.Get<bool>("no-dim"))
679// return RunShell<LocalStream, StateMachine, ConnectionFSC>(conf);
680// else
681 return RunShell<LocalStream>(conf);
682 }
683 // Cosole access w/ and w/o Dim
684/* if (conf.Get<bool>("no-dim"))
685 {
686 if (conf.Get<int>("console")==0)
687 return RunShell<LocalShell, StateMachine, ConnectionFSC>(conf);
688 else
689 return RunShell<LocalConsole, StateMachine, ConnectionFSC>(conf);
690 }
691 else
692*/ {
693 if (conf.Get<int>("console")==0)
694 return RunShell<LocalShell>(conf);
695 else
696 return RunShell<LocalConsole>(conf);
697 }
698 }
699 /*catch (std::exception& e)
700 {
701 cerr << "Exception: " << e.what() << endl;
702 return -1;
703 }*/
704
705 return 0;
706}
Note: See TracBrowser for help on using the repository browser.