source: trunk/FACT++/src/fadctrl.cc@ 10839

Last change on this file since 10839 was 10835, checked in by tbretz, 14 years ago
Added --max-mem program option; updated allowed states for CONNECT and DISCONNECT; added the offline state; propagate IP address to event-builder; moved EventBuilderWrapper to new file.
File size: 43.2 KB
Line 
1#include <boost/bind.hpp>
2#include <boost/array.hpp>
3#if BOOST_VERSION < 104400
4#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 4))
5#undef BOOST_HAS_RVALUE_REFS
6#endif
7#endif
8#include <boost/thread.hpp>
9//#include <boost/foreach.hpp>
10#include <boost/asio/error.hpp>
11#include <boost/asio/deadline_timer.hpp>
12#include <boost/date_time/posix_time/posix_time_types.hpp>
13
14#include "Dim.h"
15#include "Event.h"
16#include "Shell.h"
17#include "StateMachineDim.h"
18#include "Connection.h"
19#include "Configuration.h"
20#include "Console.h"
21#include "Converter.h"
22#include "LocalControl.h"
23#include "HeadersFAD.h"
24
25#include "tools.h"
26
27namespace ba = boost::asio;
28namespace bs = boost::system;
29
30using ba::ip::tcp;
31
32using namespace std;
33
34// ------------------------------------------------------------------------
35
36class ConnectionFAD : public Connection
37{
38 vector<uint16_t> fBuffer;
39
40protected:
41 FAD::EventHeader fEventHeader;
42 FAD::ChannelHeader fChannelHeader[FAD::kNumChannels];
43
44private:
45 bool fIsVerbose;
46 bool fIsHexOutput;
47 bool fIsDataOutput;
48
49 uint64_t fCounter;
50
51protected:
52 virtual void UpdateFirstHeader()
53 {
54 }
55
56 virtual void UpdateEventHeader()
57 {
58 // emit service with trigger counter from header
59 if (!fIsVerbose)
60 return;
61
62 Out() << endl << kBold << "Header received (N=" << dec << fCounter << "):" << endl;
63 Out() << fEventHeader;
64 if (fIsHexOutput)
65 Out() << Converter::GetHex<uint16_t>(fEventHeader, 16) << endl;
66 }
67
68 virtual void UpdateChannelHeader(int i)
69 {
70 // emit service with trigger counter from header
71 if (!fIsVerbose)
72 return;
73
74 Out() << fChannelHeader[i];
75 if (fIsHexOutput)
76 Out() << Converter::GetHex<uint16_t>(fChannelHeader, 16) << endl;
77 }
78
79 virtual void UpdateData(const uint16_t *data, size_t sz)
80 {
81 // emit service with trigger counter from header
82 if (fIsVerbose && fIsDataOutput)
83 Out() << Converter::GetHex<uint16_t>(data, sz, 16, true) << endl;
84 }
85
86private:
87 enum
88 {
89 kReadHeader = 1,
90 kReadData = 2,
91 };
92
93 void HandleReceivedData(const bs::error_code& err, size_t bytes_received, int type)
94 {
95 // Do not schedule a new read if the connection failed.
96 if (bytes_received==0 || err)
97 {
98 if (err==ba::error::eof)
99 Warn("Connection closed by remote host (FAD).");
100
101 // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category))
102 // 125: Operation canceled
103 if (err && err!=ba::error::eof && // Connection closed by remote host
104 err!=ba::error::basic_errors::not_connected && // Connection closed by remote host
105 err!=ba::error::basic_errors::operation_aborted) // Connection closed by us
106 {
107 ostringstream str;
108 str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl;
109 Error(str);
110 }
111 PostClose(err!=ba::error::basic_errors::operation_aborted);
112 return;
113 }
114
115 // FIXME FIXME FIXME. The data block could have the same size!!!!!
116 // !!!!!!!!!!!!!!!!!!!
117 if (type==kReadHeader)
118 {
119 if (bytes_received!=sizeof(FAD::EventHeader))
120 {
121 ostringstream str;
122 str << "Bytes received (" << bytes_received << " don't match header size " << sizeof(FAD::EventHeader);
123 Error(str);
124 PostClose(false);
125 return;
126 }
127
128 fEventHeader = fBuffer;
129
130 if (fEventHeader.fStartDelimiter!=FAD::kDelimiterStart)
131 {
132 ostringstream str;
133 str << "Invalid header received: start delimiter wrong, received ";
134 str << hex << fEventHeader.fStartDelimiter << ", expected " << FAD::kDelimiterStart << ".";
135 Error(str);
136 PostClose(false);
137 return;
138 }
139
140 if (fCounter==0)
141 UpdateFirstHeader();
142
143 UpdateEventHeader();
144
145 fCounter++;
146
147 fBuffer.resize(fEventHeader.fPackageLength-sizeof(FAD::EventHeader)/2);
148 AsyncRead(ba::buffer(fBuffer), kReadData);
149 AsyncWait(fInTimeout, 50, &Connection::HandleReadTimeout);
150
151 return;
152 }
153
154 fInTimeout.cancel();
155
156 if (ntohs(fBuffer.back())!=FAD::kDelimiterEnd)
157 {
158 ostringstream str;
159 str << "Invalid data received: end delimiter wrong, received ";
160 str << hex << ntohs(fBuffer.back()) << ", expected " << FAD::kDelimiterEnd << ".";
161 Error(str);
162 PostClose(false);
163 return;
164 }
165
166 /*
167 uint8_t *ptr = reinterpret_cast<uint8_t*>(fBuffer.data());
168 for (unsigned int i=0; i<FAD::kNumChannels; i++)
169 {
170 if (ptr+sizeof(FAD::ChannelHeader)/2 > reinterpret_cast<uint8_t*>(fBuffer.data())+fBuffer.size()*2)
171 {
172 Error("WRONG SIZE1");
173 break;
174 }
175
176 // FIXME: Size consistency check!!!!
177 fChannelHeader[i] = vector<uint16_t>((uint16_t*)ptr, (uint16_t*)ptr+sizeof(FAD::ChannelHeader)/2);
178 ptr += sizeof(FAD::ChannelHeader);
179
180 // FIXME CHECK: Event Size vs ROI
181
182 UpdateChannelHeader(i);
183
184 if (ptr+fChannelHeader[i].fRegionOfInterest*2 > reinterpret_cast<uint8_t*>(fBuffer.data())+fBuffer.size()*2)
185 {
186 Error("WRONG SIZE2");
187 break;
188 }
189
190 uint16_t *data = reinterpret_cast<uint16_t*>(ptr);
191 for (uint16_t *d=data; d<data+fChannelHeader[i].fRegionOfInterest; d++)
192 {
193 const bool sign = *d & 0x2000;
194 const bool overflow = *d & 0x1000;
195
196 if (sign)
197 *d |= 0xf000; // no overflow, nagative
198 else
199 *d &= 0x07ff; // no overlow, positive
200
201 // max = [-2047;2048]
202
203 if (overflow)
204 {
205 if (sign)
206 *d = 0xF800; // overflow, negative
207 else
208 *d = 0x0800; // overflow, positive
209 }
210 }
211
212 UpdateData(data, fChannelHeader[i].fRegionOfInterest*2);
213 ptr += fChannelHeader[i].fRegionOfInterest*2;
214 }*/
215
216 fBuffer.resize(sizeof(FAD::EventHeader)/2);
217 AsyncRead(ba::buffer(fBuffer), kReadHeader);
218 }
219
220 void HandleReadTimeout(const bs::error_code &error)
221 {
222 if (error==ba::error::basic_errors::operation_aborted)
223 return;
224
225 if (error)
226 {
227 ostringstream str;
228 str << "Read timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl;
229 Error(str);
230
231 PostClose();
232 return;
233
234 }
235
236 if (!is_open())
237 {
238 // For example: Here we could schedule a new accept if we
239 // would not want to allow two connections at the same time.
240 return;
241 }
242
243 // Check whether the deadline has passed. We compare the deadline
244 // against the current time since a new asynchronous operation
245 // may have moved the deadline before this actor had a chance
246 // to run.
247 if (fInTimeout.expires_at() > ba::deadline_timer::traits_type::now())
248 return;
249
250 Error("Timeout reading data from "+URL());
251 PostClose();
252 }
253
254 // This is called when a connection was established
255 void ConnectionEstablished()
256 {
257 fEventHeader.clear();
258 for (unsigned int i=0; i<FAD::kNumChannels; i++)
259 fChannelHeader[i].clear();
260
261 fCounter = 0;
262
263 fBuffer.resize(sizeof(FAD::EventHeader)/2);
264 AsyncRead(ba::buffer(fBuffer), kReadHeader);
265
266// for (int i=0; i<36; i++)
267// CmdSetRoi(i, 100);
268
269 Cmd(FAD::kCmdTriggerLine, true);
270 Cmd(FAD::kCmdSingleTrigger);
271 }
272
273 void PostCmd(std::vector<uint16_t> cmd)
274 {
275 ostringstream msg;
276 msg << "Sending command:" << hex;
277 msg << " 0x" << setw(4) << setfill('0') << cmd[0];
278 msg << " (+ " << cmd.size()-1 << " bytes data)";
279 Message(msg);
280
281 transform(cmd.begin(), cmd.end(), cmd.begin(), htons);
282
283 PostMessage(cmd);
284 }
285
286 void PostCmd(uint16_t cmd)
287 {
288 ostringstream msg;
289 msg << "Sending command:" << hex;
290 msg << " 0x" << setw(4) << setfill('0') << cmd;
291 Message(msg);
292
293 cmd = htons(cmd);
294 PostMessage(&cmd, sizeof(uint16_t));
295 }
296
297 void PostCmd(uint16_t cmd, uint16_t data)
298 {
299 ostringstream msg;
300 msg << "Sending command:" << hex;
301 msg << " 0x" << setw(4) << setfill('0') << cmd;
302 msg << " 0x" << setw(4) << setfill('0') << data;
303 Message(msg);
304
305 const uint16_t d[2] = { htons(cmd), htons(data) };
306 PostMessage(d, sizeof(d));
307 }
308
309public:
310 ConnectionFAD(ba::io_service& ioservice, MessageImp &imp) :
311 Connection(ioservice, imp()),
312 fIsVerbose(true), fIsHexOutput(false), fIsDataOutput(false), fCounter(0)
313 {
314 // Maximum possible needed space:
315 // The full header, all channels with all DRS bins
316 // Two trailing shorts
317 fBuffer.reserve(sizeof(FAD::EventHeader) + FAD::kNumChannels*(sizeof(FAD::ChannelHeader) + FAD::kMaxBins*sizeof(uint16_t)) + 2*sizeof(uint16_t));
318
319 SetLogStream(&imp);
320 }
321
322 void Cmd(FAD::Enable cmd, bool on=true)
323 {
324 PostCmd(cmd + (on ? 0 : 0x100));
325 }
326
327 // ------------------------------
328
329 // IMPLEMENT: Abs/Rel
330 void CmdPhaseShift(int16_t val)
331 {
332 vector<uint16_t> cmd(abs(val)+2, FAD::kCmdPhaseApply);
333 cmd[0] = FAD::kCmdPhaseReset;
334 cmd[1] = val<0 ? FAD::kCmdPhaseDecrease : FAD::kCmdPhaseIncrease;
335 PostCmd(cmd);
336 }
337
338 bool CmdSetTriggerRate(int32_t val)
339 {
340 if (val<0 || val>0xffff)
341 return false;
342
343 PostCmd(FAD::kCmdWriteRate, val);//uint8_t(1000./val/12.5));
344 //PostCmd(kCmdContTriggerRate, uint8_t(80/val));
345
346 return true;
347 }
348
349 void CmdSetRegister(uint8_t addr, uint16_t val)
350 {
351 // Allowed addr: [0, MAX_ADDR]
352 // Allowed value: [0, MAX_VAL]
353 PostCmd(FAD::kCmdWrite + addr, val);
354 }
355
356 bool CmdSetDacValue(uint8_t addr, uint16_t val)
357 {
358 if (addr>FAD::kMaxDacAddr) // NDAC
359 return false;
360
361 PostCmd(FAD::kCmdWriteDac + addr, val);
362 return true;
363 }
364
365 bool CmdSetRoi(int8_t addr, uint16_t val)
366 {
367 if (addr>FAD::kMaxRoiAddr)
368 return false;
369
370 if (val>FAD::kMaxRoiValue)
371 return false;
372
373 if (addr<0)
374 for (int i=0; i<=FAD::kMaxRoiAddr; i++)
375 PostCmd(FAD::kCmdWriteRoi + i, val);
376 else
377 PostCmd(FAD::kCmdWriteRoi + addr, val);
378
379 return true;
380 }
381
382 bool CmdSetRoi(uint16_t val) { return CmdSetRoi(-1, val); }
383
384 void AmplitudeCalibration()
385 {
386 // ------------- case baseline -----------------
387
388 CmdSetRoi(-1, FAD::kMaxBins);
389
390 CmdSetDacValue(1, 0);
391 CmdSetDacValue(2, 0);
392 CmdSetDacValue(3, 0);
393
394 // Take N events
395
396 /*
397 // ====== Part B: Baseline calibration =====
398
399 // Loop over all channels(ch) and time-slices (t)
400 T0 = TriggerCell[chip]
401 Sum[ch][(t+T0) % kMaxBins] += Data[ch][t];
402 // FIXME: Determine median instead of average
403
404 Baseline[ch][slice] = MEDIAN( sum[ch][slice] )
405 */
406
407 // --------------- case gain -------------------
408
409 // Set new DAC values and start accumulation
410 CmdSetDacValue(1, 50000);
411 CmdSetDacValue(2, 50000);
412 CmdSetDacValue(3, 50000);
413
414 // Take N events
415
416 /*
417 // ====== Part C: Gain calibration =====
418
419 T0 = TriggerCell[chip]
420 Sum[ch][(t+T0) % kMaxBins] += Data[ch][t];
421 // FIXME: Determine median instead of average
422
423 Gain[ch][slice] = MEDIAN( sum[ch][slice] ) - Baseline[ch][slice]
424 */
425
426 // --------------- secondary ------------------
427
428 // FIXME: Can most probably be done together with the baseline calibration
429 // FIXME: Why does the secondary baseline not influence the baseline?
430
431 CmdSetDacValue(1, 0);
432 CmdSetDacValue(2, 0);
433 CmdSetDacValue(3, 0);
434
435 // Take N events
436
437 /*
438 // ====== Part D: Secondary calibration =====
439
440 T0 = TriggerCell[chip]
441 Sum[ch][t] = Data[ch][t] - Baseline[ch][(i-T0) % kMaxBins];
442
443 // Determine secondary baseline if integration finished
444 SecondaryBaseline[ch][t] = MEDIAN( Sum[ch][t] )
445 */
446 }
447
448 void SetVerbose(bool b)
449 {
450 fIsVerbose = b;
451 }
452
453 void SetHexOutput(bool b)
454 {
455 fIsHexOutput = b;
456 }
457
458 void SetDataOutput(bool b)
459 {
460 fIsDataOutput = b;
461 }
462
463};
464
465// ------------------------------------------------------------------------
466/*
467#include "DimDescriptionService.h"
468
469class ConnectionDimFAD : public ConnectionFAD
470{
471private:
472
473 DimDescribedService fDimPassport;
474 DimDescribedService fDimTemperatures;
475 DimDescribedService fDimSetup;
476 DimDescribedService fDimEventHeader;
477
478 template<class T>
479 void Update(DimDescribedService &svc, const T &data) const
480 {
481 //cout << "Update: " << svc.getName() << " (" << sizeof(T) << ")" << endl;
482 svc.setData(const_cast<T*>(&data), sizeof(T));
483 svc.updateService();
484 }
485
486 void UpdateFirstHeader()
487 {
488 ConnectionFAD::UpdateFirstHeader();
489
490 const FAD::DimPassport data(fEventHeader);
491 Update(fDimPassport, data);
492 }
493
494 void UpdateEventHeader()
495 {
496 ConnectionFAD::UpdateEventHeader();
497
498 const FAD::DimTemperatures data0(fEventHeader);
499 const FAD::DimSetup data1(fEventHeader);
500 const FAD::DimEventHeader data2(fEventHeader);
501
502 Update(fDimTemperatures, data0);
503 Update(fDimSetup, data1);
504 Update(fDimEventHeader, data2);
505 }
506
507public:
508 ConnectionDimFAD(ba::io_service& ioservice, MessageImp &imp) :
509 ConnectionFAD(ioservice, imp),
510 fDimPassport ("FAD_CONTROL/PASSPORT", "I:1;S:2;X:1", ""),
511 fDimTemperatures("FAD_CONTROL/TEMPERATURES", "I:1;F:4", ""),
512 fDimSetup ("FAD_CONTROL/SETUP", "I:2;S:12", ""),
513 fDimEventHeader ("FAD_CONTROL/EVENT_HEADER", "C", "")
514 {
515 }
516
517 // A B [C] [D] E [F] G H [I] J K [L] M N O P Q R [S] T U V W [X] Y Z
518};
519*/
520// ------------------------------------------------------------------------
521
522#include "EventBuilderWrapper.h"
523
524// ------------------------------------------------------------------------
525
526template <class T>
527class StateMachineFAD : public T, public EventBuilderWrapper, public ba::io_service, public ba::io_service::work
528{
529private:
530 typedef pair<string, ConnectionFAD*> Connection;
531 typedef pair<const uint8_t, Connection> Board;
532 typedef map<uint8_t, Connection> BoardList;
533
534 BoardList fBoards;
535
536 bool fIsVerbose;
537 bool fIsHexOutput;
538 bool fIsDataOutput;
539
540 bool CheckEventSize(size_t has, const char *name, size_t size)
541 {
542 if (has==size)
543 return true;
544
545 ostringstream msg;
546 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
547 T::Fatal(msg);
548 return false;
549 }
550
551 int Cmd(FAD::Enable command)
552 {
553 for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++)
554 i->second.second->Cmd(command);
555
556 return T::GetCurrentState();
557 }
558
559 int CmdEnable(const EventImp &evt, FAD::Enable command)
560 {
561 if (!CheckEventSize(evt.GetSize(), "CmdEnable", 1))
562 return T::kSM_FatalError;
563
564 for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++)
565 i->second.second->Cmd(command, evt.GetBool());
566
567 return T::GetCurrentState();
568 }
569
570 bool Check(const uint32_t *dat, uint32_t maxaddr, uint32_t maxval)
571 {
572 if (dat[0]>FAD::kMaxRegAddr)
573 {
574 ostringstream msg;
575 msg << hex << "Address " << dat[0] << " out of range, max=" << maxaddr << ".";
576 T::Error(msg);
577 return false;
578 }
579
580 if (dat[1]>FAD::kMaxRegValue)
581 {
582 ostringstream msg;
583 msg << hex << "Value " << dat[1] << " out of range, max=" << maxval << ".";
584 T::Error(msg);
585 return false;
586 }
587
588 return true;
589 }
590
591 int SetRegister(const EventImp &evt)
592 {
593 if (!CheckEventSize(evt.GetSize(), "SetRegister", 8))
594 return T::kSM_FatalError;
595
596 const uint32_t *dat = reinterpret_cast<const uint32_t*>(evt.GetData());
597
598 if (!Check(dat, FAD::kMaxRegAddr, FAD::kMaxRegValue))
599 return T::GetCurrentState();
600
601 for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++)
602 i->second.second->CmdSetRegister(dat[0], dat[1]);
603
604 return T::GetCurrentState();
605 }
606
607 int SetRoi(const EventImp &evt)
608 {
609 if (!CheckEventSize(evt.GetSize(), "SetRoi", 8))
610 return T::kSM_FatalError;
611
612 // ---- was uint32_t
613 const int32_t *dat = reinterpret_cast<const int32_t*>(evt.GetData());
614
615 // ---- -1 for all
616 //if (!Check(dat, FAD::kMaxRoiAddr, FAD::kMaxRoiValue))
617 // return T::GetCurrentState();
618
619 for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++)
620 i->second.second->CmdSetRoi(dat[0], dat[1]);
621
622 return T::GetCurrentState();
623 }
624
625 int SetDac(const EventImp &evt)
626 {
627 if (!CheckEventSize(evt.GetSize(), "SetDac", 8))
628 return T::kSM_FatalError;
629
630 const uint32_t *dat = reinterpret_cast<const uint32_t*>(evt.GetData());
631
632 if (!Check(dat, FAD::kMaxDacAddr, FAD::kMaxDacValue))
633 return T::GetCurrentState();
634
635 for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++)
636 i->second.second->CmdSetDacValue(dat[0], dat[1]);
637
638 return T::GetCurrentState();
639 }
640
641 int Trigger(int n)
642 {
643 for (int nn=0; nn<n; nn++)
644 for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++)
645 i->second.second->Cmd(FAD::kCmdSingleTrigger);
646
647 return T::GetCurrentState();
648 }
649
650 int SendTriggers(const EventImp &evt)
651 {
652 if (!CheckEventSize(evt.GetSize(), "SendTriggers", 4))
653 return T::kSM_FatalError;
654
655 Trigger(evt.GetUInt());
656
657 return T::GetCurrentState();
658 }
659
660 int StartRun(const EventImp &evt, bool start)
661 {
662 if (!CheckEventSize(evt.GetSize(), "StartRun", 0))
663 return T::kSM_FatalError;
664
665 for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++)
666 i->second.second->Cmd(FAD::kCmdRun, start);
667
668 return T::GetCurrentState();
669 }
670
671 int PhaseShift(const EventImp &evt)
672 {
673 if (!CheckEventSize(evt.GetSize(), "PhaseShift", 2))
674 return T::kSM_FatalError;
675
676 for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++)
677 i->second.second->CmdPhaseShift(evt.GetShort());
678
679 return T::GetCurrentState();
680 }
681
682 int SetTriggerRate(const EventImp &evt)
683 {
684 if (!CheckEventSize(evt.GetSize(), "SetTriggerRate", 4))
685 return T::kSM_FatalError;
686
687 if (evt.GetUShort()>0xff)
688 {
689 ostringstream msg;
690 msg << hex << "Value " << evt.GetUShort() << " out of range, max=" << 0xff << "(?)";
691 T::Error(msg);
692 return false;
693 }
694
695 for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++)
696 i->second.second->CmdSetTriggerRate(evt.GetUInt());
697
698 return T::GetCurrentState();
699 }
700
701 int Test(const EventImp &evt)
702 {
703 if (!CheckEventSize(evt.GetSize(), "Test", 2))
704 return T::kSM_FatalError;
705
706
707 SetMode(evt.GetShort());
708
709 return T::GetCurrentState();
710 }
711
712
713 int SetVerbosity(const EventImp &evt)
714 {
715 if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
716 return T::kSM_FatalError;
717
718 for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++)
719 i->second.second->SetVerbose(evt.GetText()[0]!=0);
720
721 return T::GetCurrentState();
722 }
723
724 int SetHexOutput(const EventImp &evt)
725 {
726 if (!CheckEventSize(evt.GetSize(), "SetHexOutput", 1))
727 return T::kSM_FatalError;
728
729 for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++)
730 i->second.second->SetHexOutput(evt.GetText()[0]!=0);
731
732 return T::GetCurrentState();
733 }
734
735 int SetDataOutput(const EventImp &evt)
736 {
737 if (!CheckEventSize(evt.GetSize(), "SetDataOutput", 1))
738 return T::kSM_FatalError;
739
740 for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++)
741 i->second.second->SetDataOutput(evt.GetText()[0]!=0);
742
743 return T::GetCurrentState();
744 }
745
746 const BoardList::iterator GetSlot(int slot)
747 {
748 const BoardList::iterator i = fBoards.find(slot);
749 if (i!=fBoards.end())
750 return i;
751
752 ostringstream str;
753 str << "Slot " << slot << " not found.";
754 T::Warn(str.str());
755 return fBoards.end();
756 }
757
758 int AddAddress(const EventImp &evt)
759 {
760 const string addr = Tools::Trim(evt.GetText());
761
762 for (BoardList::const_iterator i=fBoards.begin(); i!=fBoards.end(); i++)
763 {
764 if (i->second.first==addr)
765 {
766 T::Warn("Address "+addr+" already known.... ignored.");
767 return T::GetCurrentState();
768 }
769 }
770
771 AddEndpoint(addr);
772
773 return T::GetCurrentState();
774 }
775
776 int RemoveSlot(const EventImp &evt)
777 {
778 if (!CheckEventSize(evt.GetSize(), "RemoveSlot", 2))
779 return T::kSM_FatalError;
780
781 const int16_t slot = evt.GetShort();
782
783 const BoardList::iterator v = GetSlot(slot);
784 if (v!=fBoards.end())
785 {
786 delete v->second.second;
787 fBoards.erase(v);
788 }
789
790 return T::GetCurrentState();
791 }
792
793 int ListSlots()
794 {
795 for (BoardList::const_iterator i=fBoards.begin(); i!=fBoards.end(); i++)
796 {
797 ostringstream str;
798 str << "Slot " << setw(2) << (int)i->first << ": " << i->second.first;
799
800 const ConnectionFAD *c = i->second.second;
801
802 if (c->IsConnecting())
803 str << " (0:connecting, ";
804 else
805 {
806 if (c->IsClosed())
807 str << " (0:disconnected, ";
808 if (c->IsConnected())
809 str << " (0:connected, ";
810 }
811
812 switch (fStatus2[i->first])
813 {
814 case 0: str << "1-7:disconnected)"; break;
815 case 1: str << "1-7:connecting [" << GetNumConnected(i->first) << "])"; break;
816 case 2: str << "1-7:connected)"; break;
817 }
818
819 T::Out() << str.str() << endl;
820 }
821
822 return T::GetCurrentState();
823 }
824
825 void EnableSlot(BoardList::iterator i, bool enable=true)
826 {
827 if (i==fBoards.end())
828 return;
829
830 ConnectionFAD* &ptr = i->second.second;
831
832 if (!enable)
833 ptr->PostClose(false);
834 else
835 {
836 ptr->SetEndpoint(i->second.first);
837 ptr->StartConnect();
838 }
839 }
840
841 void EnableAll(bool enable=true)
842 {
843 for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++)
844 EnableSlot(i, enable);
845 }
846
847 /*
848 int Enable(const EventImp &evt)
849 {
850 if (!CheckEventSize(evt.GetSize(), "Enable", 3))
851 return T::kSM_FatalError;
852
853 const int16_t slot = evt.GetShort();
854 const bool enable = evt.GetText()[2]>0;
855
856 if (slot<0)
857 {
858 EnableAll(enable);
859 return T::GetCurrentState();
860 }
861
862 EnableSlot(GetSlot(slot), enable);
863
864 return T::GetCurrentState();
865 }*/
866
867 int Disconnect()
868 {
869 Exit();
870 EnableAll(false);
871 return T::GetCurrentState();
872 }
873
874 int Connect()
875 {
876 vector<string> addr;
877 for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++)
878 addr.push_back(i->second.first);
879
880 Start(addr);
881 EnableAll(true);
882
883 return T::GetCurrentState();
884 }
885
886 /*
887 int Reconnect(const EventImp &evt)
888 {
889 if (!CheckEventSize(evt.GetSize(), "Reconnect", 2))
890 return T::kSM_FatalError;
891
892 const int16_t slot = evt.GetShort();
893
894 if (slot<0)
895 {
896 // Close all connections to supress the warning in SetEndpoint
897 for (BoardList::const_iterator i=fBoards.begin(); i!=fBoards.end(); i++)
898 i->second.second->PostClose(false);
899
900 // Now wait until all connection have been closed and
901 // all pending handlers have been processed
902 poll();
903
904 // Now we can reopen the connection
905 for (BoardList::const_iterator i=fBoards.begin(); i!=fBoards.end(); i++)
906 i->second.second->PostClose(true);
907
908 return T::GetCurrentState();
909 }
910
911 const BoardList::const_iterator v = GetSlot(slot);
912 if (v==fBoards.end())
913 return T::GetCurrentState();
914
915 // Close all connections to supress the warning in SetEndpoint
916 v->second.second->PostClose(false);
917
918 // Now wait until all connection have been closed and
919 // all pending handlers have been processed
920 poll();
921
922 // Now we can reopen the connection
923 v->second.second->PostClose(true);
924
925 return T::GetCurrentState();
926 }*/
927
928 virtual void UpdateConnectionStatus()
929 {
930 //cout << "Connection Status changed prop to Dim." << endl;
931 }
932
933 vector<char> fStatus1;
934 vector<char> fStatus2;
935
936 int Execute()
937 {
938 // Dispatch (execute) at most one handler from the queue. In contrary
939 // to run_one(), it doesn't wait until a handler is available
940 // which can be dispatched, so poll_one() might return with 0
941 // handlers dispatched. The handlers are always dispatched/executed
942 // synchronously, i.e. within the call to poll_one()
943 poll_one();
944
945 // ===== Evaluate connection status =====
946
947 uint16_t nconnecting1 = 0;
948 uint16_t nconnecting2 = 0;
949 uint16_t nconnected1 = 0;
950 uint16_t nconnected2 = 0;
951
952 vector<char> stat1(40);
953 vector<char> stat2(40);
954 for (BoardList::const_iterator i=fBoards.begin(); i!=fBoards.end(); i++)
955 {
956 const ConnectionFAD &c = *i->second.second;
957
958 const int &idx = i->first;
959
960 // ----- Command socket -----
961 if (c.IsConnecting())
962 {
963 stat1[idx] = 1;
964 nconnecting1++;
965 }
966 if (c.IsConnected())
967 {
968 stat1[idx] = 2;
969 nconnected1++;
970 }
971
972 // ----- Event builder -----
973 if (!IsConnected(idx) && !IsDisconnected(idx))
974 {
975 stat2[idx] = 1;
976 nconnecting2++;
977 }
978
979 if (IsConnected(idx))
980 {
981 stat2[idx] = 2;
982 nconnected2++;
983 }
984 }
985
986 // ===== Send connection status via dim =====
987
988 if (fStatus1!=stat1 || fStatus2!=stat2)
989 {
990 fStatus1 = stat1;
991 fStatus2 = stat2;
992 UpdateConnectionStatus();
993 }
994
995 // ===== Return connection status =====
996
997 // fadctrl: Always connecting if not disabled
998 // event builder:
999
1000 if (nconnected1==fBoards.size() && nconnected2==fBoards.size())
1001 return FAD::kConnected;
1002
1003 if (nconnected1==0 && nconnected2==0)
1004 return IsThreadRunning() ? FAD::kOffline : FAD::kDisconnected;
1005
1006 // FIXME: Evaluate event builder status
1007 return FAD::kConnecting;
1008 }
1009
1010 void AddEndpoint(const string &addr)
1011 {
1012 if (fBoards.size()==40)
1013 {
1014 T::Warn("Not more than 40 slots allowed.");
1015 return;
1016 }
1017
1018 int i=0;
1019 while (1)
1020 {
1021 const BoardList::const_iterator v = fBoards.find(i);
1022 if (v==fBoards.end())
1023 break;
1024 i++;
1025 }
1026
1027 fBoards[i] = make_pair(addr, new ConnectionFAD(*this, *this));
1028 fBoards[i].second->SetVerbose(fIsVerbose);
1029 fBoards[i].second->SetHexOutput(fIsHexOutput);
1030 fBoards[i].second->SetDataOutput(fIsDataOutput);
1031 }
1032
1033
1034
1035public:
1036 StateMachineFAD(ostream &out=cout) :
1037 T(out, "FAD_CONTROL"), EventBuilderWrapper(static_cast<MessageImp&>(*this)), ba::io_service::work(static_cast<ba::io_service&>(*this)),
1038 fStatus1(40), fStatus2(40)
1039 {
1040 // ba::io_service::work is a kind of keep_alive for the loop.
1041 // It prevents the io_service to go to stopped state, which
1042 // would prevent any consecutive calls to run()
1043 // or poll() to do nothing. reset() could also revoke to the
1044 // previous state but this might introduce some overhead of
1045 // deletion and creation of threads and more.
1046
1047 // State names
1048 T::AddStateName(FAD::kDisconnected, "Offline",
1049 "All enabled FAD boards are disconnected and the event-builer thread is not running.");
1050
1051 T::AddStateName(FAD::kDisconnected, "Disconnected",
1052 "All enabled FAD boards are disconnected, but the event-builder thread is running.");
1053
1054 T::AddStateName(FAD::kConnected, "Connected",
1055 "All enabled FAD boards are connected..");
1056
1057 T::AddStateName(FAD::kConnecting, "Connecting",
1058 "Only some enabled FAD boards are connected.");
1059
1060 // FAD Commands
1061 T::AddEvent("ENABLE_SRCLK", "B:1")
1062 (boost::bind(&StateMachineFAD::CmdEnable, this, _1, FAD::kCmdSrclk))
1063 ("Set SRCLK");
1064 T::AddEvent("ENABLE_SCLK", "B:1")
1065 (boost::bind(&StateMachineFAD::CmdEnable, this, _1, FAD::kCmdSclk))
1066 ("Set SCLK");
1067 T::AddEvent("ENABLE_DRS", "B:1")
1068 (boost::bind(&StateMachineFAD::CmdEnable, this, _1, FAD::kCmdDrsEnable))
1069 ("Switch Domino wave");
1070 T::AddEvent("ENABLE_DWRITE", "B:1")
1071 (boost::bind(&StateMachineFAD::CmdEnable, this, _1, FAD::kCmdDwrite))
1072 ("Set Dwrite (possibly high / always low)");
1073 T::AddEvent("SET_DEBUG_MODE", "B:1")
1074 (boost::bind(&StateMachineFAD::CmdEnable, this, _1, FAD::kCmdSocket))
1075 ("Set debug mode (yes: dump events through command socket, no=dump events through other sockets)");
1076 T::AddEvent("ENABLE_TRIGGER_LINE", "B:1")
1077 (boost::bind(&StateMachineFAD::CmdEnable, this, _1, FAD::kCmdTriggerLine))
1078 ("Incoming triggers can be accepted/will not be accepted");
1079 T::AddEvent("SET_TRIGGER_RATE", "I:1")
1080 (boost::bind(&StateMachineFAD::SetTriggerRate, this, _1))
1081 ("Enable continous trigger");
1082 T::AddEvent("SEND_SINGLE_TRIGGER")
1083 (boost::bind(&StateMachineFAD::Trigger, this, 1))
1084 ("Issue software triggers");
1085 T::AddEvent("SEND_N_TRIGGERS", "I")
1086 (boost::bind(&StateMachineFAD::SendTriggers, this, _1))
1087 ("Issue software triggers");
1088 T::AddEvent("START", "")
1089 (boost::bind(&StateMachineFAD::StartRun, this, _1, true))
1090 ("Set FAD DAQ mode. when started, no configurations must be send.");
1091 T::AddEvent("STOP")
1092 (boost::bind(&StateMachineFAD::StartRun, this, _1, false))
1093 ("");
1094 T::AddEvent("PHASE_SHIFT", "S:1")
1095 (boost::bind(&StateMachineFAD::PhaseShift, this, _1))
1096 ("Adjust ADC phase (in 'steps')");
1097
1098 T::AddEvent("CONTINOUS_TRIGGER_ON")
1099 (boost::bind(&StateMachineFAD::Cmd, this, FAD::kCmdContTriggerOn))
1100 ("");
1101 T::AddEvent("CONTINOUS_TRIGGER_OFF")
1102 (boost::bind(&StateMachineFAD::Cmd, this, FAD::kCmdContTriggerOff))
1103 ("");
1104
1105 T::AddEvent("RESET_TRIGGER_ID")
1106 (boost::bind(&StateMachineFAD::Cmd, this, FAD::kCmdResetTriggerId))
1107 ("");
1108
1109 T::AddEvent("SET_REGISTER", "I:2")
1110 (boost::bind(&StateMachineFAD::SetRegister, this, _1))
1111 ("set register to value"
1112 "|addr[short]:Address of register"
1113 "|val[short]:Value to be set");
1114
1115 // FIXME: Maybe add a mask which channels should be set?
1116 T::AddEvent("SET_REGION_OF_INTEREST", "I:2")
1117 (boost::bind(&StateMachineFAD::SetRoi, this, _1))
1118 ("Set region-of-interest to value"
1119 "|addr[short]:Address of register"
1120 "|val[short]:Value to be set");
1121
1122 // FIXME: Maybe add a mask which channels should be set?
1123 T::AddEvent("SET_DAC_VALUE", "I:2")
1124 (boost::bind(&StateMachineFAD::SetDac, this, _1))
1125 ("Set DAC numbers in range to value"
1126 "|addr[short]:Address of register"
1127 "|val[short]:Value to be set");
1128
1129 // Verbosity commands
1130 T::AddEvent("SET_VERBOSE", "B")
1131 (boost::bind(&StateMachineFAD::SetVerbosity, this, _1))
1132 ("set verbosity state"
1133 "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
1134
1135 T::AddEvent("SET_HEX_OUTPUT", "B")
1136 (boost::bind(&StateMachineFAD::SetHexOutput, this, _1))
1137 ("enable or disable hex output for received data"
1138 "|hexout[bool]:disable or enable hex output for received data (yes/no)");
1139
1140 T::AddEvent("SET_DATA_OUTPUT", "B")
1141 (boost::bind(&StateMachineFAD::SetDataOutput, this, _1))
1142 ("");
1143
1144 // Conenction commands
1145 /*
1146 T::AddEvent("ENABLE", "S:1;B:1", FAD::kDisconnected)
1147 (boost::bind(&StateMachineFAD::Enable, this, _1))
1148 ("");*/
1149
1150 T::AddEvent("CONNECT", FAD::kOffline)
1151 (boost::bind(&StateMachineFAD::Connect, this))
1152 ("");
1153
1154 T::AddEvent("DISCONNECT", FAD::kDisconnected, FAD::kConnecting, FAD::kConnected)
1155 (boost::bind(&StateMachineFAD::Disconnect, this))
1156 ("");
1157
1158 T::AddEvent("TEST", "S:1")
1159 (boost::bind(&StateMachineFAD::Test, this, _1))
1160 ("");
1161
1162 T::AddEvent("ADD_ADDRESS", "C", FAD::kOffline)
1163 (boost::bind(&StateMachineFAD::AddAddress, this, _1))
1164 ("Add the address of a DRS4 board to the first free slot"
1165 "|IP[string]:address in the format <address:port>");
1166 T::AddEvent("REMOVE_SLOT", "S:1", FAD::kOffline)
1167 (boost::bind(&StateMachineFAD::RemoveSlot, this, _1))
1168 ("Remove the Iaddress in slot n. For a list see LIST"
1169 "|slot[int]:Remove the address in slot n from the list");
1170 T::AddEvent("LIST_SLOTS")
1171 (boost::bind(&StateMachineFAD::ListSlots, this))
1172 ("Print a list of all available board addressesa and whether they are enabled");
1173 }
1174
1175 ~StateMachineFAD()
1176 {
1177 for (BoardList::const_iterator i=fBoards.begin(); i!=fBoards.end(); i++)
1178 delete i->second.second;
1179 fBoards.clear();
1180 }
1181
1182 bool SetConfiguration(const Configuration &conf)
1183 {
1184 fIsVerbose = !conf.Get<bool>("quiet");
1185 fIsHexOutput = conf.Get<bool>("hex-out");
1186 fIsDataOutput = conf.Get<bool>("data-out");
1187
1188 SetMaxMemory(conf.Get<unsigned int>("max-mem"));
1189
1190 if (!(conf.Has("base-addr") ^ conf.Has("addr")))
1191 {
1192 T::Out() << kRed << "SetConfiguration - Only --base-addr or --addr allowed." << endl;
1193 return false;
1194 }
1195
1196 if (conf.Has("base-addr"))
1197 {
1198 const string base = conf.Get<string>("base-addr");
1199
1200 const size_t p0 = base.find_first_of(':');
1201 const size_t p1 = base.find_last_of(':');
1202
1203 if (p0==string::npos || p0!=p1)
1204 {
1205 T::Out() << kRed << "SetConfiguration - Wrong format of argument --base-addr ('host:port' expected)" << endl;
1206 return false;
1207 }
1208
1209 tcp::resolver resolver(get_io_service());
1210
1211 boost::system::error_code ec;
1212
1213 const tcp::resolver::query query(base.substr(0, p0), base.substr(p0+1));
1214 const tcp::resolver::iterator iterator = resolver.resolve(query, ec);
1215
1216 if (ec)
1217 {
1218 T::Out() << " " << ec.message() << " (" << ec << ")";
1219 return false;
1220 }
1221
1222 const tcp::endpoint endpoint = *iterator;
1223
1224 const ba::ip::address_v4::bytes_type ip = endpoint.address().to_v4().to_bytes();
1225
1226 if (ip[2]>250 || ip[3]>244)
1227 {
1228 T::Out() << kRed << "SetConfiguration - IP address given by --base-addr out-of-range." << endl;
1229 return false;
1230 }
1231
1232 for (int crate=0; crate<2; crate++)
1233 for (int board=0; board<10; board++)
1234 {
1235 //if (crate==0 && board==2)
1236 // continue;
1237
1238 ostringstream str;
1239 str << (int)ip[0] << "." << (int)ip[1] << ".";
1240 str << (int)(ip[2]+crate) << "." << (int)(ip[3]+board) << ":";
1241 str << endpoint.port();
1242
1243 AddEndpoint(str.str());
1244 }
1245 }
1246
1247 if (conf.Has("addr"))
1248 {
1249 const vector<string> addrs = conf.Get<vector<string>>("addr");
1250 for (vector<string>::const_iterator i=addrs.begin(); i<addrs.end(); i++)
1251 AddEndpoint(*i);
1252 }
1253
1254 Connect();
1255
1256 return true;
1257 }
1258
1259};
1260
1261// ------------------------------------------------------------------------
1262
1263
1264void RunThread(StateMachineImp *io_service)
1265{
1266 // This is necessary so that the StateMachien Thread can signal the
1267 // Readline to exit
1268 io_service->Run();
1269 Readline::Stop();
1270}
1271
1272template<class S>
1273int RunDim(Configuration &conf)
1274{
1275 /*
1276 initscr(); // Start curses mode
1277 cbreak(); // Line buffering disabled, Pass on
1278 intrflush(stdscr, FALSE);
1279 start_color(); // Initialize ncurses colors
1280 use_default_colors(); // Assign terminal default colors to -1
1281 for (int i=1; i<8; i++)
1282 init_pair(i, i, -1); // -1: def background
1283 scrollok(stdscr, true);
1284 */
1285
1286 WindowLog wout;
1287
1288 //log.SetWindow(stdscr);
1289 if (conf.Has("log"))
1290 if (!wout.OpenLogFile(conf.Get<string>("log")))
1291 wout << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
1292
1293 // Start io_service.Run to use the StateMachineImp::Run() loop
1294 // Start io_service.run to only use the commandHandler command detaching
1295 StateMachineFAD<S> io_service(wout);
1296 if (!io_service.SetConfiguration(conf))
1297 return -1;
1298
1299 io_service.Run();
1300
1301 return 0;
1302}
1303
1304template<class T, class S>
1305int RunShell(Configuration &conf)
1306{
1307 static T shell(conf.GetName().c_str(), conf.Get<int>("console")!=1);
1308
1309 WindowLog &win = shell.GetStreamIn();
1310 WindowLog &wout = shell.GetStreamOut();
1311
1312 if (conf.Has("log"))
1313 if (!wout.OpenLogFile(conf.Get<string>("log")))
1314 win << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
1315
1316 StateMachineFAD<S> io_service(wout);
1317 if (!io_service.SetConfiguration(conf))
1318 return -1;
1319
1320 shell.SetReceiver(io_service);
1321
1322 boost::thread t(boost::bind(RunThread, &io_service));
1323 //boost::thread t(boost::bind(&StateMachineFAD<S>::Run, &io_service));
1324
1325 shell.Run(); // Run the shell
1326 io_service.Stop(); // Signal Loop-thread to stop
1327
1328 // Wait until the StateMachine has finished its thread
1329 // before returning and destroying the dim objects which might
1330 // still be in use.
1331 t.join();
1332
1333 return 0;
1334}
1335
1336void SetupConfiguration(Configuration &conf)
1337{
1338 const string n = conf.GetName()+".log";
1339
1340 po::options_description config("Program options");
1341 config.add_options()
1342 ("dns", var<string>("localhost"), "Dim nameserver host name (Overwites DIM_DNS_NODE environment variable)")
1343 ("log,l", var<string>(n), "Write log-file")
1344 ("no-dim,d", po_switch(), "Disable dim services")
1345 ("console,c", var<int>(), "Use console (0=shell, 1=simple buffered, X=simple unbuffered)")
1346 ;
1347
1348 po::options_description control("FAD control options");
1349 control.add_options()
1350// ("addr,a", var<string>("localhost:5000"), "Network address of FTM")
1351 ("quiet,q", po_bool(), "Disable printing contents of all received messages in clear text.")
1352 ("hex-out", po_bool(), "Enable printing contents of all printed messages also as hex data.")
1353 ("data-out", po_bool(), "Enable printing received event data.")
1354 ("addr", vars<string>(), "Network address of FAD")
1355 ("base-addr", var<string>(), "Base address of all FAD")
1356 ("max-mem,m", var<unsigned int>(100), "Maximum memory the event builder thread is allowed to consume for its event buffer")
1357 ;
1358
1359 conf.AddEnv("dns", "DIM_DNS_NODE");
1360
1361 conf.AddOptions(config);
1362 conf.AddOptions(control);
1363}
1364
1365void PrintUsage()
1366{
1367 cout <<
1368 "The fadctrl controls the FAD boards.\n"
1369 "\n"
1370 "The default is that the program is started without user intercation. "
1371 "All actions are supposed to arrive as DimCommands. Using the -c "
1372 "option, a local shell can be initialized. With h or help a short "
1373 "help message about the usuage can be brought to the screen.\n"
1374 "\n"
1375 "Usage: fadctrl [-c type] [OPTIONS]\n"
1376 " or: fadctrl [OPTIONS]\n";
1377 cout << endl;
1378}
1379
1380void PrintHelp()
1381{
1382 /* Additional help text which is printed after the configuration
1383 options goes here */
1384}
1385
1386int main(int argc, const char* argv[])
1387{
1388 Configuration conf(argv[0]);
1389 conf.SetPrintUsage(PrintUsage);
1390 SetupConfiguration(conf);
1391
1392 po::variables_map vm;
1393 try
1394 {
1395 vm = conf.Parse(argc, argv);
1396 }
1397#if BOOST_VERSION > 104000
1398 catch (po::multiple_occurrences &e)
1399 {
1400 cerr << "Program options invalid due to: " << e.what() << " of '" << e.get_option_name() << "'." << endl;
1401 return -1;
1402 }
1403#endif
1404 catch (exception& e)
1405 {
1406 cerr << "Program options invalid due to: " << e.what() << endl;
1407 return -1;
1408 }
1409
1410 if (conf.HasVersion() || conf.HasPrint())
1411 return -1;
1412
1413 if (conf.HasHelp())
1414 {
1415 PrintHelp();
1416 return -1;
1417 }
1418
1419 Dim::Setup(conf.Get<string>("dns"));
1420
1421// try
1422 {
1423 // No console access at all
1424 if (!conf.Has("console"))
1425 {
1426 if (conf.Get<bool>("no-dim"))
1427 return RunDim<StateMachine>(conf);
1428 else
1429 return RunDim<StateMachineDim>(conf);
1430 }
1431 // Cosole access w/ and w/o Dim
1432 if (conf.Get<bool>("no-dim"))
1433 {
1434 if (conf.Get<int>("console")==0)
1435 return RunShell<LocalShell, StateMachine>(conf);
1436 else
1437 return RunShell<LocalConsole, StateMachine>(conf);
1438 }
1439 else
1440 {
1441 if (conf.Get<int>("console")==0)
1442 return RunShell<LocalShell, StateMachineDim>(conf);
1443 else
1444 return RunShell<LocalConsole, StateMachineDim>(conf);
1445 }
1446 }
1447/* catch (std::exception& e)
1448 {
1449 cerr << "Exception: " << e.what() << endl;
1450 return -1;
1451 }*/
1452
1453 return 0;
1454}
Note: See TracBrowser for help on using the repository browser.