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

Last change on this file since 10856 was 10844, checked in by tbretz, 14 years ago
Print thread status is ListSlots()
File size: 43.4 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:not connected)"; 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 T::Out() << "Thread :";
823 if (!IsThreadRunning())
824 T::Out() << " not";
825 T::Out() << " running" << endl;
826
827 // FIXME: Output state
828
829 return T::GetCurrentState();
830 }
831
832 void EnableSlot(BoardList::iterator i, bool enable=true)
833 {
834 if (i==fBoards.end())
835 return;
836
837 ConnectionFAD* &ptr = i->second.second;
838
839 if (!enable)
840 ptr->PostClose(false);
841 else
842 {
843 ptr->SetEndpoint(i->second.first);
844 ptr->StartConnect();
845 }
846 }
847
848 void EnableAll(bool enable=true)
849 {
850 for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++)
851 EnableSlot(i, enable);
852 }
853
854 /*
855 int Enable(const EventImp &evt)
856 {
857 if (!CheckEventSize(evt.GetSize(), "Enable", 3))
858 return T::kSM_FatalError;
859
860 const int16_t slot = evt.GetShort();
861 const bool enable = evt.GetText()[2]>0;
862
863 if (slot<0)
864 {
865 EnableAll(enable);
866 return T::GetCurrentState();
867 }
868
869 EnableSlot(GetSlot(slot), enable);
870
871 return T::GetCurrentState();
872 }*/
873
874 int Disconnect()
875 {
876 Exit();
877 EnableAll(false);
878 return T::GetCurrentState();
879 }
880
881 int Connect()
882 {
883 vector<string> addr;
884 for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++)
885 addr.push_back(i->second.first);
886
887 Start(addr);
888 EnableAll(true);
889
890 return T::GetCurrentState();
891 }
892
893 /*
894 int Reconnect(const EventImp &evt)
895 {
896 if (!CheckEventSize(evt.GetSize(), "Reconnect", 2))
897 return T::kSM_FatalError;
898
899 const int16_t slot = evt.GetShort();
900
901 if (slot<0)
902 {
903 // Close all connections to supress the warning in SetEndpoint
904 for (BoardList::const_iterator i=fBoards.begin(); i!=fBoards.end(); i++)
905 i->second.second->PostClose(false);
906
907 // Now wait until all connection have been closed and
908 // all pending handlers have been processed
909 poll();
910
911 // Now we can reopen the connection
912 for (BoardList::const_iterator i=fBoards.begin(); i!=fBoards.end(); i++)
913 i->second.second->PostClose(true);
914
915 return T::GetCurrentState();
916 }
917
918 const BoardList::const_iterator v = GetSlot(slot);
919 if (v==fBoards.end())
920 return T::GetCurrentState();
921
922 // Close all connections to supress the warning in SetEndpoint
923 v->second.second->PostClose(false);
924
925 // Now wait until all connection have been closed and
926 // all pending handlers have been processed
927 poll();
928
929 // Now we can reopen the connection
930 v->second.second->PostClose(true);
931
932 return T::GetCurrentState();
933 }*/
934
935 virtual void UpdateConnectionStatus()
936 {
937 //cout << "Connection Status changed prop to Dim." << endl;
938 }
939
940 vector<char> fStatus1;
941 vector<char> fStatus2;
942
943 int Execute()
944 {
945 // Dispatch (execute) at most one handler from the queue. In contrary
946 // to run_one(), it doesn't wait until a handler is available
947 // which can be dispatched, so poll_one() might return with 0
948 // handlers dispatched. The handlers are always dispatched/executed
949 // synchronously, i.e. within the call to poll_one()
950 poll_one();
951
952 // ===== Evaluate connection status =====
953
954 uint16_t nconnecting1 = 0;
955 uint16_t nconnecting2 = 0;
956 uint16_t nconnected1 = 0;
957 uint16_t nconnected2 = 0;
958
959 vector<char> stat1(40);
960 vector<char> stat2(40);
961 for (BoardList::const_iterator i=fBoards.begin(); i!=fBoards.end(); i++)
962 {
963 const ConnectionFAD &c = *i->second.second;
964
965 const int &idx = i->first;
966
967 // ----- Command socket -----
968 if (c.IsConnecting())
969 {
970 stat1[idx] = 1;
971 nconnecting1++;
972 }
973 if (c.IsConnected())
974 {
975 stat1[idx] = 2;
976 nconnected1++;
977 }
978
979 // ----- Event builder -----
980 if (!IsConnected(idx) && !IsDisconnected(idx))
981 {
982 stat2[idx] = 1;
983 nconnecting2++;
984 }
985
986 if (IsConnected(idx))
987 {
988 stat2[idx] = 2;
989 nconnected2++;
990 }
991 }
992
993 // ===== Send connection status via dim =====
994
995 if (fStatus1!=stat1 || fStatus2!=stat2)
996 {
997 fStatus1 = stat1;
998 fStatus2 = stat2;
999 UpdateConnectionStatus();
1000 }
1001
1002 // ===== Return connection status =====
1003
1004 // fadctrl: Always connecting if not disabled
1005 // event builder:
1006
1007 if (nconnected1==fBoards.size() && nconnected2==fBoards.size())
1008 return FAD::kConnected;
1009
1010 if (nconnected1==0 && nconnected2==0)
1011 return IsThreadRunning() ? FAD::kDisconnected : FAD::kOffline;
1012
1013 // FIXME: Evaluate event builder status
1014 return FAD::kConnecting;
1015 }
1016
1017 void AddEndpoint(const string &addr)
1018 {
1019 if (fBoards.size()==40)
1020 {
1021 T::Warn("Not more than 40 slots allowed.");
1022 return;
1023 }
1024
1025 int i=0;
1026 while (1)
1027 {
1028 const BoardList::const_iterator v = fBoards.find(i);
1029 if (v==fBoards.end())
1030 break;
1031 i++;
1032 }
1033
1034 fBoards[i] = make_pair(addr, new ConnectionFAD(*this, *this));
1035 fBoards[i].second->SetVerbose(fIsVerbose);
1036 fBoards[i].second->SetHexOutput(fIsHexOutput);
1037 fBoards[i].second->SetDataOutput(fIsDataOutput);
1038 }
1039
1040
1041
1042public:
1043 StateMachineFAD(ostream &out=cout) :
1044 T(out, "FAD_CONTROL"), EventBuilderWrapper(static_cast<MessageImp&>(*this)), ba::io_service::work(static_cast<ba::io_service&>(*this)),
1045 fStatus1(40), fStatus2(40)
1046 {
1047 // ba::io_service::work is a kind of keep_alive for the loop.
1048 // It prevents the io_service to go to stopped state, which
1049 // would prevent any consecutive calls to run()
1050 // or poll() to do nothing. reset() could also revoke to the
1051 // previous state but this might introduce some overhead of
1052 // deletion and creation of threads and more.
1053
1054 // State names
1055 T::AddStateName(FAD::kOffline, "Offline",
1056 "All enabled FAD boards are disconnected and the event-builer thread is not running.");
1057
1058 T::AddStateName(FAD::kDisconnected, "Disconnected",
1059 "All enabled FAD boards are disconnected, but the event-builder thread is running.");
1060
1061 T::AddStateName(FAD::kConnected, "Connected",
1062 "All enabled FAD boards are connected..");
1063
1064 T::AddStateName(FAD::kConnecting, "Connecting",
1065 "Only some enabled FAD boards are connected.");
1066
1067 // FAD Commands
1068 T::AddEvent("ENABLE_SRCLK", "B:1")
1069 (boost::bind(&StateMachineFAD::CmdEnable, this, _1, FAD::kCmdSrclk))
1070 ("Set SRCLK");
1071 T::AddEvent("ENABLE_SCLK", "B:1")
1072 (boost::bind(&StateMachineFAD::CmdEnable, this, _1, FAD::kCmdSclk))
1073 ("Set SCLK");
1074 T::AddEvent("ENABLE_DRS", "B:1")
1075 (boost::bind(&StateMachineFAD::CmdEnable, this, _1, FAD::kCmdDrsEnable))
1076 ("Switch Domino wave");
1077 T::AddEvent("ENABLE_DWRITE", "B:1")
1078 (boost::bind(&StateMachineFAD::CmdEnable, this, _1, FAD::kCmdDwrite))
1079 ("Set Dwrite (possibly high / always low)");
1080 T::AddEvent("SET_DEBUG_MODE", "B:1")
1081 (boost::bind(&StateMachineFAD::CmdEnable, this, _1, FAD::kCmdSocket))
1082 ("Set debug mode (yes: dump events through command socket, no=dump events through other sockets)");
1083 T::AddEvent("ENABLE_TRIGGER_LINE", "B:1")
1084 (boost::bind(&StateMachineFAD::CmdEnable, this, _1, FAD::kCmdTriggerLine))
1085 ("Incoming triggers can be accepted/will not be accepted");
1086 T::AddEvent("SET_TRIGGER_RATE", "I:1")
1087 (boost::bind(&StateMachineFAD::SetTriggerRate, this, _1))
1088 ("Enable continous trigger");
1089 T::AddEvent("SEND_SINGLE_TRIGGER")
1090 (boost::bind(&StateMachineFAD::Trigger, this, 1))
1091 ("Issue software triggers");
1092 T::AddEvent("SEND_N_TRIGGERS", "I")
1093 (boost::bind(&StateMachineFAD::SendTriggers, this, _1))
1094 ("Issue software triggers");
1095 T::AddEvent("START", "")
1096 (boost::bind(&StateMachineFAD::StartRun, this, _1, true))
1097 ("Set FAD DAQ mode. when started, no configurations must be send.");
1098 T::AddEvent("STOP")
1099 (boost::bind(&StateMachineFAD::StartRun, this, _1, false))
1100 ("");
1101 T::AddEvent("PHASE_SHIFT", "S:1")
1102 (boost::bind(&StateMachineFAD::PhaseShift, this, _1))
1103 ("Adjust ADC phase (in 'steps')");
1104
1105 T::AddEvent("CONTINOUS_TRIGGER_ON")
1106 (boost::bind(&StateMachineFAD::Cmd, this, FAD::kCmdContTriggerOn))
1107 ("");
1108 T::AddEvent("CONTINOUS_TRIGGER_OFF")
1109 (boost::bind(&StateMachineFAD::Cmd, this, FAD::kCmdContTriggerOff))
1110 ("");
1111
1112 T::AddEvent("RESET_TRIGGER_ID")
1113 (boost::bind(&StateMachineFAD::Cmd, this, FAD::kCmdResetTriggerId))
1114 ("");
1115
1116 T::AddEvent("SET_REGISTER", "I:2")
1117 (boost::bind(&StateMachineFAD::SetRegister, this, _1))
1118 ("set register 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_REGION_OF_INTEREST", "I:2")
1124 (boost::bind(&StateMachineFAD::SetRoi, this, _1))
1125 ("Set region-of-interest to value"
1126 "|addr[short]:Address of register"
1127 "|val[short]:Value to be set");
1128
1129 // FIXME: Maybe add a mask which channels should be set?
1130 T::AddEvent("SET_DAC_VALUE", "I:2")
1131 (boost::bind(&StateMachineFAD::SetDac, this, _1))
1132 ("Set DAC numbers in range to value"
1133 "|addr[short]:Address of register"
1134 "|val[short]:Value to be set");
1135
1136 // Verbosity commands
1137 T::AddEvent("SET_VERBOSE", "B")
1138 (boost::bind(&StateMachineFAD::SetVerbosity, this, _1))
1139 ("set verbosity state"
1140 "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
1141
1142 T::AddEvent("SET_HEX_OUTPUT", "B")
1143 (boost::bind(&StateMachineFAD::SetHexOutput, this, _1))
1144 ("enable or disable hex output for received data"
1145 "|hexout[bool]:disable or enable hex output for received data (yes/no)");
1146
1147 T::AddEvent("SET_DATA_OUTPUT", "B")
1148 (boost::bind(&StateMachineFAD::SetDataOutput, this, _1))
1149 ("");
1150
1151 // Conenction commands
1152 /*
1153 T::AddEvent("ENABLE", "S:1;B:1", FAD::kDisconnected)
1154 (boost::bind(&StateMachineFAD::Enable, this, _1))
1155 ("");*/
1156
1157 T::AddEvent("CONNECT", FAD::kOffline)
1158 (boost::bind(&StateMachineFAD::Connect, this))
1159 ("");
1160
1161 T::AddEvent("DISCONNECT", FAD::kDisconnected, FAD::kConnecting, FAD::kConnected)
1162 (boost::bind(&StateMachineFAD::Disconnect, this))
1163 ("");
1164
1165 T::AddEvent("TEST", "S:1")
1166 (boost::bind(&StateMachineFAD::Test, this, _1))
1167 ("");
1168
1169 T::AddEvent("ADD_ADDRESS", "C", FAD::kOffline)
1170 (boost::bind(&StateMachineFAD::AddAddress, this, _1))
1171 ("Add the address of a DRS4 board to the first free slot"
1172 "|IP[string]:address in the format <address:port>");
1173 T::AddEvent("REMOVE_SLOT", "S:1", FAD::kOffline)
1174 (boost::bind(&StateMachineFAD::RemoveSlot, this, _1))
1175 ("Remove the Iaddress in slot n. For a list see LIST"
1176 "|slot[int]:Remove the address in slot n from the list");
1177 T::AddEvent("LIST_SLOTS")
1178 (boost::bind(&StateMachineFAD::ListSlots, this))
1179 ("Print a list of all available board addressesa and whether they are enabled");
1180 }
1181
1182 ~StateMachineFAD()
1183 {
1184 for (BoardList::const_iterator i=fBoards.begin(); i!=fBoards.end(); i++)
1185 delete i->second.second;
1186 fBoards.clear();
1187 }
1188
1189 bool SetConfiguration(const Configuration &conf)
1190 {
1191 fIsVerbose = !conf.Get<bool>("quiet");
1192 fIsHexOutput = conf.Get<bool>("hex-out");
1193 fIsDataOutput = conf.Get<bool>("data-out");
1194
1195 SetMaxMemory(conf.Get<unsigned int>("max-mem"));
1196
1197 if (!(conf.Has("base-addr") ^ conf.Has("addr")))
1198 {
1199 T::Out() << kRed << "SetConfiguration - Only --base-addr or --addr allowed." << endl;
1200 return false;
1201 }
1202
1203 if (conf.Has("base-addr"))
1204 {
1205 const string base = conf.Get<string>("base-addr");
1206
1207 const size_t p0 = base.find_first_of(':');
1208 const size_t p1 = base.find_last_of(':');
1209
1210 if (p0==string::npos || p0!=p1)
1211 {
1212 T::Out() << kRed << "SetConfiguration - Wrong format of argument --base-addr ('host:port' expected)" << endl;
1213 return false;
1214 }
1215
1216 tcp::resolver resolver(get_io_service());
1217
1218 boost::system::error_code ec;
1219
1220 const tcp::resolver::query query(base.substr(0, p0), base.substr(p0+1));
1221 const tcp::resolver::iterator iterator = resolver.resolve(query, ec);
1222
1223 if (ec)
1224 {
1225 T::Out() << " " << ec.message() << " (" << ec << ")";
1226 return false;
1227 }
1228
1229 const tcp::endpoint endpoint = *iterator;
1230
1231 const ba::ip::address_v4::bytes_type ip = endpoint.address().to_v4().to_bytes();
1232
1233 if (ip[2]>250 || ip[3]>244)
1234 {
1235 T::Out() << kRed << "SetConfiguration - IP address given by --base-addr out-of-range." << endl;
1236 return false;
1237 }
1238
1239 for (int crate=0; crate<2; crate++)
1240 for (int board=0; board<10; board++)
1241 {
1242 //if (crate==0 && board==2)
1243 // continue;
1244
1245 ostringstream str;
1246 str << (int)ip[0] << "." << (int)ip[1] << ".";
1247 str << (int)(ip[2]+crate) << "." << (int)(ip[3]+board) << ":";
1248 str << endpoint.port();
1249
1250 AddEndpoint(str.str());
1251 }
1252 }
1253
1254 if (conf.Has("addr"))
1255 {
1256 const vector<string> addrs = conf.Get<vector<string>>("addr");
1257 for (vector<string>::const_iterator i=addrs.begin(); i<addrs.end(); i++)
1258 AddEndpoint(*i);
1259 }
1260
1261 Connect();
1262
1263 return true;
1264 }
1265
1266};
1267
1268// ------------------------------------------------------------------------
1269
1270
1271void RunThread(StateMachineImp *io_service)
1272{
1273 // This is necessary so that the StateMachien Thread can signal the
1274 // Readline to exit
1275 io_service->Run();
1276 Readline::Stop();
1277}
1278
1279template<class S>
1280int RunDim(Configuration &conf)
1281{
1282 /*
1283 initscr(); // Start curses mode
1284 cbreak(); // Line buffering disabled, Pass on
1285 intrflush(stdscr, FALSE);
1286 start_color(); // Initialize ncurses colors
1287 use_default_colors(); // Assign terminal default colors to -1
1288 for (int i=1; i<8; i++)
1289 init_pair(i, i, -1); // -1: def background
1290 scrollok(stdscr, true);
1291 */
1292
1293 WindowLog wout;
1294
1295 //log.SetWindow(stdscr);
1296 if (conf.Has("log"))
1297 if (!wout.OpenLogFile(conf.Get<string>("log")))
1298 wout << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
1299
1300 // Start io_service.Run to use the StateMachineImp::Run() loop
1301 // Start io_service.run to only use the commandHandler command detaching
1302 StateMachineFAD<S> io_service(wout);
1303 if (!io_service.SetConfiguration(conf))
1304 return -1;
1305
1306 io_service.Run();
1307
1308 return 0;
1309}
1310
1311template<class T, class S>
1312int RunShell(Configuration &conf)
1313{
1314 static T shell(conf.GetName().c_str(), conf.Get<int>("console")!=1);
1315
1316 WindowLog &win = shell.GetStreamIn();
1317 WindowLog &wout = shell.GetStreamOut();
1318
1319 if (conf.Has("log"))
1320 if (!wout.OpenLogFile(conf.Get<string>("log")))
1321 win << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
1322
1323 StateMachineFAD<S> io_service(wout);
1324 if (!io_service.SetConfiguration(conf))
1325 return -1;
1326
1327 shell.SetReceiver(io_service);
1328
1329 boost::thread t(boost::bind(RunThread, &io_service));
1330 //boost::thread t(boost::bind(&StateMachineFAD<S>::Run, &io_service));
1331
1332 shell.Run(); // Run the shell
1333 io_service.Stop(); // Signal Loop-thread to stop
1334
1335 // Wait until the StateMachine has finished its thread
1336 // before returning and destroying the dim objects which might
1337 // still be in use.
1338 t.join();
1339
1340 return 0;
1341}
1342
1343void SetupConfiguration(Configuration &conf)
1344{
1345 const string n = conf.GetName()+".log";
1346
1347 po::options_description config("Program options");
1348 config.add_options()
1349 ("dns", var<string>("localhost"), "Dim nameserver host name (Overwites DIM_DNS_NODE environment variable)")
1350 ("log,l", var<string>(n), "Write log-file")
1351 ("no-dim,d", po_switch(), "Disable dim services")
1352 ("console,c", var<int>(), "Use console (0=shell, 1=simple buffered, X=simple unbuffered)")
1353 ;
1354
1355 po::options_description control("FAD control options");
1356 control.add_options()
1357// ("addr,a", var<string>("localhost:5000"), "Network address of FTM")
1358 ("quiet,q", po_bool(), "Disable printing contents of all received messages in clear text.")
1359 ("hex-out", po_bool(), "Enable printing contents of all printed messages also as hex data.")
1360 ("data-out", po_bool(), "Enable printing received event data.")
1361 ("addr", vars<string>(), "Network address of FAD")
1362 ("base-addr", var<string>(), "Base address of all FAD")
1363 ("max-mem,m", var<unsigned int>(100), "Maximum memory the event builder thread is allowed to consume for its event buffer")
1364 ;
1365
1366 conf.AddEnv("dns", "DIM_DNS_NODE");
1367
1368 conf.AddOptions(config);
1369 conf.AddOptions(control);
1370}
1371
1372void PrintUsage()
1373{
1374 cout <<
1375 "The fadctrl controls the FAD boards.\n"
1376 "\n"
1377 "The default is that the program is started without user intercation. "
1378 "All actions are supposed to arrive as DimCommands. Using the -c "
1379 "option, a local shell can be initialized. With h or help a short "
1380 "help message about the usuage can be brought to the screen.\n"
1381 "\n"
1382 "Usage: fadctrl [-c type] [OPTIONS]\n"
1383 " or: fadctrl [OPTIONS]\n";
1384 cout << endl;
1385}
1386
1387void PrintHelp()
1388{
1389 /* Additional help text which is printed after the configuration
1390 options goes here */
1391}
1392
1393int main(int argc, const char* argv[])
1394{
1395 Configuration conf(argv[0]);
1396 conf.SetPrintUsage(PrintUsage);
1397 SetupConfiguration(conf);
1398
1399 po::variables_map vm;
1400 try
1401 {
1402 vm = conf.Parse(argc, argv);
1403 }
1404#if BOOST_VERSION > 104000
1405 catch (po::multiple_occurrences &e)
1406 {
1407 cerr << "Program options invalid due to: " << e.what() << " of '" << e.get_option_name() << "'." << endl;
1408 return -1;
1409 }
1410#endif
1411 catch (exception& e)
1412 {
1413 cerr << "Program options invalid due to: " << e.what() << endl;
1414 return -1;
1415 }
1416
1417 if (conf.HasVersion() || conf.HasPrint())
1418 return -1;
1419
1420 if (conf.HasHelp())
1421 {
1422 PrintHelp();
1423 return -1;
1424 }
1425
1426 Dim::Setup(conf.Get<string>("dns"));
1427
1428// try
1429 {
1430 // No console access at all
1431 if (!conf.Has("console"))
1432 {
1433 if (conf.Get<bool>("no-dim"))
1434 return RunDim<StateMachine>(conf);
1435 else
1436 return RunDim<StateMachineDim>(conf);
1437 }
1438 // Cosole access w/ and w/o Dim
1439 if (conf.Get<bool>("no-dim"))
1440 {
1441 if (conf.Get<int>("console")==0)
1442 return RunShell<LocalShell, StateMachine>(conf);
1443 else
1444 return RunShell<LocalConsole, StateMachine>(conf);
1445 }
1446 else
1447 {
1448 if (conf.Get<int>("console")==0)
1449 return RunShell<LocalShell, StateMachineDim>(conf);
1450 else
1451 return RunShell<LocalConsole, StateMachineDim>(conf);
1452 }
1453 }
1454/* catch (std::exception& e)
1455 {
1456 cerr << "Exception: " << e.what() << endl;
1457 return -1;
1458 }*/
1459
1460 return 0;
1461}
Note: See TracBrowser for help on using the repository browser.