source: trunk/FACT++/src/ftmctrl.cc @ 10655

Last change on this file since 10655 was 10655, checked in by tbretz, 9 years ago
Little changed to the command descriptions.
File size: 53.9 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/asio/error.hpp>
10#include <boost/asio/deadline_timer.hpp>
11
12#include "Dim.h"
13#include "Event.h"
14#include "Shell.h"
15#include "StateMachineDim.h"
16#include "Connection.h"
17#include "Configuration.h"
18#include "Timers.h"
19#include "Console.h"
20#include "Converter.h"
21
22#include "FACT.h"
23#include "tools.h"
24
25#include "LocalControl.h"
26#include "HeadersFTM.h"
27
28
29namespace ba = boost::asio;
30namespace bs = boost::system;
31
32using namespace std;
33
34// ------------------------------------------------------------------------
35
36class ConnectionFTM : public Connection
37{
38    vector<uint16_t> fBuffer;
39
40    bool fHasHeader;
41    int  fState;
42
43    bool fIsVerbose;
44    bool fIsDynamicOut;
45    bool fIsHexOutput;
46
47    // --verbose
48    // --hex-out
49    // --dynamic-out
50    // --load-file
51    // --leds
52    // --trigger-interval
53    // --physcis-coincidence
54    // --calib-coincidence
55    // --physcis-window
56    // --physcis-window
57    // --trigger-delay
58    // --time-marker-delay
59    // --dead-time
60    // --clock-conditioner-r0
61    // --clock-conditioner-r1
62    // --clock-conditioner-r8
63    // --clock-conditioner-r9
64    // --clock-conditioner-r11
65    // --clock-conditioner-r13
66    // --clock-conditioner-r14
67    // --clock-conditioner-r15
68    // ...
69
70protected:
71    map<uint16_t, int> fCounter;
72
73    FTM::Header      fHeader;
74    FTM::FtuList     fFtuList;
75    FTM::StaticData  fStaticData;
76    FTM::DynamicData fDynamicData;
77    FTM::Error       fError;
78
79    virtual void UpdateFirstHeader()
80    {
81        // FIXME: Message() ?
82        Out() << endl << kBold << "First header received:" << endl;
83        Out() << fHeader;
84        if (fIsHexOutput)
85            Out() << Converter::GetHex<uint16_t>(fHeader, 16) << endl;
86    }
87
88    virtual void UpdateHeader()
89    {
90        // emit service with trigger counter from header
91        if (!fIsVerbose)
92            return;
93
94        if (fHeader.fType==FTM::kDynamicData && !fIsDynamicOut)
95            return;
96
97        Out() << endl << kBold << "Header received:" << endl;
98        Out() << fHeader;
99        if (fIsHexOutput)
100            Out() << Converter::GetHex<uint16_t>(fHeader, 16) << endl;
101    }
102
103    virtual void UpdateFtuList()
104    {
105        if (!fIsVerbose)
106            return;
107
108        Out() << endl << kBold << "FtuList received:" << endl;
109        Out() << fFtuList;
110        if (fIsHexOutput)
111            Out() << Converter::GetHex<uint16_t>(fFtuList, 16) << endl;
112    }
113
114    virtual void UpdateStaticData()
115    {
116        if (!fIsVerbose)
117            return;
118
119        Out() << endl << kBold << "Static data received:" << endl;
120        Out() << fStaticData;
121        if (fIsHexOutput)
122            Out() << Converter::GetHex<uint16_t>(fStaticData, 16) << endl;
123    }
124
125    virtual void UpdateDynamicData()
126    {
127        if (!fIsDynamicOut)
128            return;
129
130        Out() << endl << kBold << "Dynamic data received:" << endl;
131        Out() << fDynamicData;
132        if (fIsHexOutput)
133            Out() << Converter::GetHex<uint16_t>(fDynamicData, 16) << endl;
134    }
135
136    virtual void UpdateError()
137    {
138        if (!fIsVerbose)
139            return;
140
141        Out() << endl << kRed << "Error received:" << endl;
142        Out() << fError;
143        if (fIsHexOutput)
144            Out() << Converter::GetHex<uint16_t>(fError, 16) << endl;
145    }
146
147    virtual void UpdateCounter()
148    {
149        if (!fIsVerbose)
150            return;
151
152        if (!fIsDynamicOut)
153            return;
154
155        Out() << "Received: ";
156        Out() << "H=" << fCounter[FTM::kHeader] << "  ";
157        Out() << "S=" << fCounter[FTM::kStaticData] << "  ";
158        Out() << "D=" << fCounter[FTM::kDynamicData] << "  ";
159        Out() << "F=" << fCounter[FTM::kFtuList] << "  ";
160        Out() << "E=" << fCounter[FTM::kErrorList] << "  ";
161        Out() << "R=" << fCounter[FTM::kRegister] << endl;
162    }
163
164    bool CheckConsistency()
165    {
166        bool warn1 = false;
167        if (fStaticData.IsEnabled(FTM::StaticData::kPedestal) != (fStaticData.GetSequencePed()  >0) ||
168            fStaticData.IsEnabled(FTM::StaticData::kLPint)    != (fStaticData.GetSequenceLPint()>0) ||
169            fStaticData.IsEnabled(FTM::StaticData::kLPext)    != (fStaticData.GetSequenceLPext()>0))
170        {
171            warn1 = true;
172            fStaticData.Enable(FTM::StaticData::kPedestal, fStaticData.GetSequencePed()>0);
173            fStaticData.Enable(FTM::StaticData::kLPint,    fStaticData.GetSequenceLPint()>0);
174            fStaticData.Enable(FTM::StaticData::kLPext,    fStaticData.GetSequenceLPext()>0);
175        }
176
177        bool warn2 = false;
178        const uint16_t ref = fStaticData[0].fPrescaling;
179        for (int i=1; i<40; i++)
180        {
181            if (fStaticData[i].fPrescaling != ref)
182            {
183                warn2 = true;
184                fStaticData[i].fPrescaling = ref;
185            }
186        }
187
188        if (warn1)
189            Warn("GeneralSettings not consistent with trigger sequence.");
190        if (warn2)
191            Warn("Prescaling not consistent for all boards.");
192
193        return !warn1 && !warn2;
194    }
195
196private:
197    void HandleReceivedData(const bs::error_code& err, size_t bytes_received, int /*type*/)
198    {
199        cout << "Data received " << err << " " << bytes_received << endl;
200
201        // Do not schedule a new read if the connection failed.
202        if (bytes_received==0 || err)
203        {
204            if (err==ba::error::eof)
205                Warn("Connection closed by remote host (FTM).");
206
207            // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category))
208            // 125: Operation canceled
209            if (err && err!=ba::error::eof &&                     // Connection closed by remote host
210                err!=ba::error::basic_errors::not_connected &&    // Connection closed by remote host
211                err!=ba::error::basic_errors::operation_aborted)  // Connection closed by us
212            {
213                stringstream str;
214                str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl;
215                Error(str);
216            }
217            PostClose(err!=ba::error::basic_errors::operation_aborted);
218            return;
219        }
220
221        // If we have not yet received a header we expect one now
222        // This could be moved to a HandleReceivedHeader function
223        if (!fHasHeader)
224        {
225            if (bytes_received!=sizeof(FTM::Header))
226            {
227                stringstream str;
228                str << "Excepted " << sizeof(FTM::Header) << " bytes (FTM::Header) but received " << bytes_received << ".";
229                Error(str);
230                PostClose(false);
231                return;
232            }
233
234            fHeader = fBuffer;
235
236            // Check the data integrity
237            if (fHeader.fDelimiter!=FTM::kDelimiterStart)
238            {
239                stringstream str;
240                str << "Invalid header received: start delimiter wrong, received " << hex << fHeader.fDelimiter << " expected " << FTM::kDelimiterStart << ".";
241                Error(str);
242                PostClose(false);
243                return;
244            }
245
246            fHasHeader = true;
247
248            // Convert FTM state into FtmCtrl state
249            switch (fHeader.fState)
250            {
251            case FTM::kFtmIdle:
252            case FTM::kFtmConfig:
253                fState = FTM::kIdle;
254                break;
255
256            case FTM::kFtmCalib:
257            case FTM::kFtmRunning:
258                fState = FTM::kTakingData;
259                break;
260            }
261
262            if (++fCounter[FTM::kHeader]==1)
263                UpdateFirstHeader();
264
265            UpdateCounter();
266            UpdateHeader();
267
268            // Start reading of data
269            switch (fHeader.fType)
270            {
271            case FTM::kStaticData:
272            case FTM::kDynamicData:
273            case FTM::kFtuList:
274            case FTM::kRegister:
275            case FTM::kErrorList:
276                // This is not very efficient because the space is reallocated
277                // maybe we can check if the capacity of the std::vector
278                // is ever decreased. If not, everythign is fine.
279                fBuffer.resize(fHeader.fDataSize);
280                AsyncRead(ba::buffer(fBuffer));
281                AsyncWait(fInTimeout, 50, &Connection::HandleReadTimeout);
282                return;
283
284            default:
285                stringstream str;
286                str << "Unknonw type " << fHeader.fType << " in received header." << endl;
287                Error(str);
288                PostClose(false);
289                return;
290            }
291
292            return;
293        }
294
295        // Check the data integrity (check end delimiter)
296        if (ntohs(fBuffer.back())!=FTM::kDelimiterEnd)
297        {
298            stringstream str;
299            str << "Invalid data received: end delimiter wrong, received ";
300            str << hex << ntohs(fBuffer.back()) << " expected " << FTM::kDelimiterEnd << ".";
301            Error(str);
302            PostClose(false);
303            return;
304        }
305
306        // Remove end delimiter
307        fBuffer.pop_back();
308
309        try
310        {
311            // If we have already received a header this is the data now
312            // This could be moved to a HandleReceivedData function
313
314            fCounter[fHeader.fType]++;
315            UpdateCounter();
316
317            cout << "TYPE=" << fHeader.fType << endl;
318
319            switch (fHeader.fType)
320            {
321            case FTM::kFtuList:
322                fFtuList = fBuffer;
323                UpdateFtuList();
324                break;
325
326            case FTM::kStaticData:
327                fStaticData = fBuffer;
328
329                if (fCounter[FTM::kStaticData]==1)
330                    if (!CheckConsistency())
331                    {
332                        CmdSendStatDat();
333                        break;
334                    }
335
336                UpdateStaticData();
337                break;
338
339            case FTM::kDynamicData:
340                fDynamicData = fBuffer;
341                UpdateDynamicData();
342                break;
343
344            case FTM::kRegister:
345                if (fIsVerbose)
346                {
347                    Out() << endl << kBold << "Register received: " << endl;
348                    Out() << "Addr:  " << ntohs(fBuffer[0]) << endl;
349                    Out() << "Value: " << ntohs(fBuffer[1]) << endl;
350                }
351                break;
352
353            case FTM::kErrorList:
354                fError = fBuffer;
355                UpdateError();
356                break;
357
358            default:
359                stringstream str;
360                str << "Unknonw type " << fHeader.fType << " in header." << endl;
361                Error(str);
362                PostClose(false);
363                return;
364            }
365        }
366        catch (const logic_error &e)
367        {
368            stringstream str;
369            str << "Exception converting buffer into data structure: " << e.what();
370            Error(str);
371            PostClose(false);
372            return;
373        }
374
375        fInTimeout.cancel();
376
377        fHeader.clear();
378        fHasHeader = false;
379        fBuffer.resize(sizeof(FTM::Header)/2);
380        AsyncRead(ba::buffer(fBuffer));
381    }
382
383    // This is called when a connection was established
384    void ConnectionEstablished()
385    {
386        fState = FTM::kConnected;
387        fCounter.clear();
388
389        fHeader.clear();
390        fHasHeader = false;
391        fBuffer.resize(sizeof(FTM::Header)/2);
392        AsyncRead(ba::buffer(fBuffer));
393
394        // Get a header and configdata!
395        CmdReqStatDat();
396
397        // get the DNA of the FTUs
398        CmdPing();
399    }
400
401    void HandleReadTimeout(const bs::error_code &error)
402    {
403        if (error && error!=ba::error::basic_errors::operation_aborted)
404        {
405            stringstream str;
406            str << "Read timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl;
407            Error(str);
408
409            PostClose();
410            return;
411
412        }
413
414        if (!is_open())
415        {
416            // For example: Here we could schedule a new accept if we
417            // would not want to allow two connections at the same time.
418            return;
419        }
420
421        // Check whether the deadline has passed. We compare the deadline
422        // against the current time since a new asynchronous operation
423        // may have moved the deadline before this actor had a chance
424        // to run.
425        if (fInTimeout.expires_at() > ba::deadline_timer::traits_type::now())
426            return;
427
428        Error("Timeout reading data from "+URL());
429
430        PostClose();
431    }
432
433
434    template<size_t N>
435    void PostCmd(boost::array<uint16_t, N> dat, uint16_t u1=0, uint16_t u2=0, uint16_t u3=0, uint16_t u4=0)
436    {
437        boost::array<uint16_t, 5> cmd = {{ '@', u1, u2, u3, u4 }};
438
439        stringstream msg;
440        msg << "Sending command:" << hex;
441        msg << " 0x" << setw(4) << setfill('0') << cmd[0];
442        msg << " 0x" << setw(4) << setfill('0') << u1;
443        msg << " 0x" << setw(4) << setfill('0') << u2;
444        msg << " 0x" << setw(4) << setfill('0') << u3;
445        msg << " 0x" << setw(4) << setfill('0') << u4;
446        msg << " (+" << dec << dat.size() << " words)";
447        Message(msg);
448
449        vector<uint16_t> out(cmd.size()+dat.size());
450
451        transform(cmd.begin(), cmd.end(), out.begin(), htons);
452        transform(dat.begin(), dat.end(), out.begin()+cmd.size(), htons);
453
454        PostMessage(out);
455    }
456
457    void PostCmd(vector<uint16_t> dat, uint16_t u1=0, uint16_t u2=0, uint16_t u3=0, uint16_t u4=0)
458    {
459        boost::array<uint16_t, 5> cmd = {{ '@', u1, u2, u3, u4 }};
460
461        stringstream msg;
462        msg << "Sending command:" << hex;
463        msg << " 0x" << setw(4) << setfill('0') << cmd[0];
464        msg << " 0x" << setw(4) << setfill('0') << u1;
465        msg << " 0x" << setw(4) << setfill('0') << u2;
466        msg << " 0x" << setw(4) << setfill('0') << u3;
467        msg << " 0x" << setw(4) << setfill('0') << u4;
468        msg << " (+" << dec << dat.size() << " words)";
469        Message(msg);
470
471        vector<uint16_t> out(cmd.size()+dat.size());
472
473        transform(cmd.begin(), cmd.end(), out.begin(), htons);
474        copy(dat.begin(), dat.end(), out.begin()+cmd.size());
475
476        PostMessage(out);
477    }
478
479    void PostCmd(uint16_t u1=0, uint16_t u2=0, uint16_t u3=0, uint16_t u4=0)
480    {
481        PostCmd(boost::array<uint16_t, 0>(), u1, u2, u3, u4);
482    }
483public:
484
485    static const uint16_t kMaxAddr;
486
487public:
488    ConnectionFTM(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()),
489        fIsVerbose(true), fIsDynamicOut(true), fIsHexOutput(true)
490    {
491        SetLogStream(&imp);
492    }
493
494    void CmdToggleLed()
495    {
496        PostCmd(FTM::kCmdToggleLed);
497    }
498
499    void CmdPing()
500    {
501        PostCmd(FTM::kCmdPing);
502    }
503
504    void CmdReqDynDat()
505    {
506        PostCmd(FTM::kCmdRead, FTM::kReadDynamicData);
507    }
508
509    void CmdReqStatDat()
510    {
511        PostCmd(FTM::kCmdRead, FTM::kReadStaticData);
512    }
513
514    void CmdSendStatDat()
515    {
516        PostCmd(fStaticData.HtoN(), FTM::kCmdWrite, FTM::kWriteStaticData);
517
518        // Request the changed configuration to ensure the
519        // change is distributed in the network
520        CmdReqStatDat();
521    }
522
523    void CmdStartRun()
524    {
525        PostCmd(FTM::kCmdStartRun, FTM::kStartRun);
526
527        // Update state information by requesting a new header
528        CmdGetRegister(0);
529    }
530
531    void CmdStopRun()
532    {
533        PostCmd(FTM::kCmdStopRun);
534
535        // Update state information by requesting a new header
536        CmdGetRegister(0);
537    }
538
539    void CmdTakeNevents(uint32_t n)
540    {
541        const boost::array<uint16_t, 2> data = {{ uint16_t(n>>16), uint16_t(n&0xffff) }};
542        PostCmd(data, FTM::kCmdStartRun, FTM::kTakeNevents);
543
544        // Update state information by requesting a new header
545        CmdGetRegister(0);
546    }
547
548    bool CmdSetRegister(uint16_t addr, uint16_t val)
549    {
550        if (addr>kMaxAddr)
551            return false;
552
553        const boost::array<uint16_t, 2> data = {{ addr, val }};
554        PostCmd(data, FTM::kCmdWrite, FTM::kWriteRegister);
555
556        // Request the changed configuration to ensure the
557        // change is distributed in the network
558        CmdReqStatDat();
559
560        return true;
561    }
562
563    bool CmdGetRegister(uint16_t addr)
564    {
565        if (addr>kMaxAddr)
566            return false;
567
568        const boost::array<uint16_t, 1> data = {{ addr }};
569        PostCmd(data, FTM::kCmdRead, FTM::kReadRegister);
570
571        return true;
572    }
573
574    bool CmdDisableReports(bool b)
575    {
576        PostCmd(FTM::kCmdDisableReports, b ? uint16_t(0) : uint16_t(1));
577        return true;
578    }
579
580    void SetVerbose(bool b)
581    {
582        fIsVerbose = b;
583    }
584
585    void SetHexOutput(bool b)
586    {
587        fIsHexOutput = b;
588    }
589
590    void SetDynamicOut(bool b)
591    {
592        fIsDynamicOut = b;
593    }
594
595    bool LoadStaticData(string name)
596    {
597        if (name.rfind(".bin")!=name.length()-5)
598            name += ".bin";
599
600        ifstream fin(name);
601        if (!fin)
602            return false;
603
604        FTM::StaticData data;
605
606        fin.read(reinterpret_cast<char*>(&data), sizeof(FTM::StaticData));
607
608        if (fin.gcount()<streamsize(sizeof(FTM::StaticData)))
609            return false;
610
611        if (fin.fail() || fin.eof())
612            return false;
613
614        if (fin.peek()!=-1)
615            return false;
616
617        fStaticData = data;
618
619        CmdSendStatDat();
620
621        return true;
622    }
623
624    bool SaveStaticData(string name) const
625    {
626        if (name.rfind(".bin")!=name.length()-5)
627            name += ".bin";
628
629        ofstream fout(name);
630        if (!fout)
631            return false;
632
633        fout.write(reinterpret_cast<const char*>(&fStaticData), sizeof(FTM::StaticData));
634
635        return !fout.bad();
636    }
637
638    bool SetThreshold(int32_t patch, int32_t value)
639    {
640        if (patch>159)
641            return false;
642
643        if (value<0 || value>0xffff)
644            return false;
645
646        if (patch<0)
647        {
648            bool ident = true;
649            for (int i=0; i<160; i++)
650                if (fStaticData[i/4].fDAC[patch%4] != value)
651                {
652                    ident = false;
653                    break;
654                }
655
656            if (ident)
657                return true;
658
659            for (int i=0; i<160; i++)
660                fStaticData[i/4].fDAC[i%4] = value;
661        }
662        else
663        {
664            if (fStaticData[patch/4].fDAC[patch%4] == value)
665                return true;
666
667            fStaticData[patch/4].fDAC[patch%4] = value;
668        }
669
670        // Maybe move to a "COMMIT" command?
671        CmdSendStatDat();
672
673        return true;
674    }
675
676    bool SetPrescaling(uint32_t value)
677    {
678        if (value>0xffff)
679            return false;
680
681
682        bool ident = true;
683        for (int i=0; i<40; i++)
684            if (fStaticData[i].fPrescaling != value)
685            {
686                ident = false;
687                break;
688            }
689
690        if (ident)
691            return true;
692
693        for (int i=0; i<40; i++)
694            fStaticData[i].fPrescaling = value;
695
696        // Maybe move to a "COMMIT" command?
697        CmdSendStatDat();
698
699        return true;
700    }
701
702    bool EnableFTU(int32_t board, bool enable)
703    {
704        if (board>39)
705            return false;
706
707        if (board<0)
708        {
709            if (enable)
710                fStaticData.EnableAllFTU();
711            else
712                fStaticData.DisableAllFTU();
713        }
714        else
715        {
716            if (enable)
717                fStaticData.EnableFTU(board);
718            else
719                fStaticData.DisableFTU(board);
720
721        }
722
723        // Maybe move to a "COMMIT" command?
724        CmdSendStatDat();
725
726        return true;
727    }
728
729    bool ToggleFTU(uint32_t board)
730    {
731        if (board>39)
732            return false;
733
734        fStaticData.ToggleFTU(board);
735
736        // Maybe move to a "COMMIT" command?
737        CmdSendStatDat();
738
739        return true;
740    }
741
742    bool SetVal(uint16_t *dest, uint32_t val, uint32_t max)
743    {
744        if (val>max)
745            return false;
746
747        if (*dest==val)
748            return true;
749
750        *dest = val;
751
752        CmdSendStatDat();
753
754        return true;
755    }
756
757    bool SetTriggerInterval(uint32_t val)
758    {
759        return SetVal(&fStaticData.fTriggerInterval, val,
760                      FTM::StaticData::kMaxTriggerInterval);
761    }
762
763    bool SetTriggerDelay(uint32_t val)
764    {
765        return SetVal(&fStaticData.fDelayTrigger, val,
766                      FTM::StaticData::kMaxDelayTrigger);
767    }
768
769    bool SetTimeMarkerDelay(uint32_t val)
770    {
771        return SetVal(&fStaticData.fDelayTimeMarker, val,
772                      FTM::StaticData::kMaxDelayTimeMarker);
773    }
774
775    bool SetDeadTime(uint32_t val)
776    {
777        return SetVal(&fStaticData.fDeadTime, val,
778                      FTM::StaticData::kMaxDeadTime);
779    }
780
781    void Enable(FTM::StaticData::GeneralSettings type, bool enable)
782    {
783        fStaticData.Enable(type, enable);
784    }
785
786    bool SetTriggerSeq(const uint8_t d[3])
787    {
788        const uint16_t oldset = fStaticData.fGeneralSettings;
789        const uint16_t oldseq = fStaticData.fTriggerSequence;
790
791        fStaticData.Enable(FTM::StaticData::kPedestal, d[0]>0);
792        fStaticData.Enable(FTM::StaticData::kLPext,    d[1]>0);
793        fStaticData.Enable(FTM::StaticData::kLPint,    d[2]>0);
794
795        if (d[0]>FTM::StaticData::kMaxSequence ||
796            d[1]>FTM::StaticData::kMaxSequence ||
797            d[2]>FTM::StaticData::kMaxSequence)
798            return false;
799
800        fStaticData.fTriggerSequence =
801            (d[0]<<10) | (d[1]<<5) || d[2];
802
803        if (oldseq!=fStaticData.fTriggerSequence || oldset!=fStaticData.fGeneralSettings)
804            CmdSendStatDat();
805
806        return true;
807    }
808
809    bool SetTriggerCoincidence(uint16_t n, uint16_t win)
810    {
811        if (n==0 || n>FTM::StaticData::kMaxCoincidence ||
812            win>FTM::StaticData::kMaxWindow)
813            return false;
814
815        if (==fStaticData.fCoincidencePhysics &&
816            win==fStaticData.fWindowPhysics)
817            return true;
818
819        fStaticData.fCoincidencePhysics = n;
820        fStaticData.fWindowPhysics      = win;
821
822        CmdSendStatDat();
823
824        return true;
825    }
826
827    bool SetCalibCoincidence(uint16_t n, uint16_t win)
828    {
829        if (n==0 || n>FTM::StaticData::kMaxCoincidence ||
830            win>FTM::StaticData::kMaxWindow)
831            return false;
832
833        if (==fStaticData.fCoincidenceCalib &&
834            win==fStaticData.fWindowCalib)
835            return true;
836
837        fStaticData.fCoincidenceCalib = n;
838        fStaticData.fWindowCalib      = win;
839
840        CmdSendStatDat();
841
842        return true;
843    }
844
845    int GetState() const { return IsConnected() ? fState : (int)FTM::kDisconnected; }
846};
847
848const uint16_t ConnectionFTM::kMaxAddr = 0xfff;
849
850// ------------------------------------------------------------------------
851
852#include "DimDescriptionService.h"
853
854class ConnectionDimFTM : public ConnectionFTM
855{
856private:
857
858    DimDescribedService fDimPassport;
859    DimDescribedService fDimTriggerCounter;
860    DimDescribedService fDimError;
861    DimDescribedService fDimFtuList;
862    DimDescribedService fDimStaticData;
863    DimDescribedService fDimDynamicData;
864    DimDescribedService fDimCounter;
865
866    template<class T>
867        void Update(DimDescribedService &svc, const T &data) const
868    {
869        //cout << "Update: " << svc.getName() << " (" << sizeof(T) << ")" << endl;
870        svc.setData(const_cast<T*>(&data), sizeof(T));
871        svc.updateService();
872    }
873
874    void UpdateFirstHeader()
875    {
876        ConnectionFTM::UpdateFirstHeader();
877
878        const FTM::DimPassport data(fHeader);
879        Update(fDimPassport, data);
880    }
881
882    void UpdateHeader()
883    {
884        ConnectionFTM::UpdateHeader();
885
886        const FTM::DimTriggerCounter data(fHeader);
887        Update(fDimTriggerCounter, data);
888    }
889
890    void UpdateFtuList()
891    {
892        ConnectionFTM::UpdateFtuList();
893
894        const FTM::DimFtuList data(fHeader, fFtuList);
895        Update(fDimFtuList, data);
896    }
897
898    void UpdateStaticData()
899    {
900        ConnectionFTM::UpdateStaticData();
901
902        const FTM::DimStaticData data(fHeader, fStaticData);
903        Update(fDimStaticData, data);
904    }
905
906    void UpdateDynamicData()
907    {
908        ConnectionFTM::UpdateDynamicData();
909
910        const FTM::DimDynamicData data(fHeader, fDynamicData);
911        Update(fDimDynamicData, data);
912    }
913
914    void UpdateError()
915    {
916        ConnectionFTM::UpdateError();
917
918        const FTM::DimError data(fHeader, fError);
919        Update(fDimError, data);
920    }
921
922    void UpdateCounter()
923    {
924        ConnectionFTM::UpdateCounter();
925
926        const uint32_t counter[6] =
927        {
928            fCounter[FTM::kHeader],
929            fCounter[FTM::kStaticData],
930            fCounter[FTM::kDynamicData],
931            fCounter[FTM::kFtuList],
932            fCounter[FTM::kErrorList],
933            fCounter[FTM::kRegister],
934        };
935
936        Update(fDimCounter, counter);
937    }
938
939public:
940    ConnectionDimFTM(ba::io_service& ioservice, MessageImp &imp) :
941        ConnectionFTM(ioservice, imp),
942        fDimPassport      ("FTM_CONTROL/PASSPORT",        "X:1;S:1", ""),
943        fDimTriggerCounter("FTM_CONTROL/TRIGGER_COUNTER", "X:1;S:1;S:1;S:2;S:1;S:1;S:21;S:1;S:1", ""),
944        fDimError         ("FTM_CONTROL/ERROR",           "X:1;S:1;S:28", ""),
945        fDimFtuList       ("FTM_CONTROL/FTU_LIST",        "X:1;X:1;S:1;C:4;X:40;C:40;C:40",  ""),
946        fDimStaticData    ("FTM_CONTROL/STATIC_DATA",     "X:1;S:1;S:1;X:1;S:1;S:3;S:1;S:1;S:1;S:1;S:1;S:1;I:1;S:8;S:80;S:160;S:40;S:40", ""),
947        fDimDynamicData   ("FTM_CONTROL/DYNAMIC_DATA",    "X:1;X:1;F:4;I:160;I:40;S:40;S:40", ""),
948        fDimCounter       ("FTM_CONTROL/COUNTER",         "I:6", "")
949    {
950    }
951
952    // 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
953};
954
955// ------------------------------------------------------------------------
956
957template <class T, class S>
958class StateMachineFTM : public T, public ba::io_service, public ba::io_service::work
959{
960    int Wrap(boost::function<void()> f)
961    {
962        f();
963        return T::GetCurrentState();
964    }
965
966    boost::function<int(const EventImp &)> Wrapper(boost::function<void()> func)
967    {
968        return boost::bind(&StateMachineFTM::Wrap, this, func);
969    }
970
971private:
972    S fFTM;
973
974    enum states_t
975    {
976        kStateDisconnected = FTM::kDisconnected,
977        kStateConnected    = FTM::kConnected,
978        kStateIdle         = FTM::kIdle,
979        kStateTakingData   = FTM::kTakingData,
980
981        kCmdTest
982    };
983
984    bool CheckEventSize(size_t has, const char *name, size_t size)
985    {
986        if (has==size)
987            return true;
988
989        stringstream msg;
990        msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
991        T::Fatal(msg);
992        return false;
993    }
994
995    int SetRegister(const EventImp &evt)
996    {
997        if (!CheckEventSize(evt.GetSize(), "SetRegister", 8))
998            return T::kSM_FatalError;
999
1000        const unsigned int *dat = reinterpret_cast<const unsigned int*>(evt.GetData());
1001
1002        if (dat[1]>uint16_t(-1))
1003        {
1004            stringstream msg;
1005            msg << hex << "Value " << dat[1] << " out of range.";
1006            T::Error(msg);
1007            return T::GetCurrentState();
1008        }
1009
1010
1011        if (dat[0]>uint16_t(-1) || !fFTM.CmdSetRegister(dat[0], dat[1]))
1012        {
1013            stringstream msg;
1014            msg << hex << "Address " << dat[0] << " out of range.";
1015            T::Error(msg);
1016        }
1017
1018        return T::GetCurrentState();
1019    }
1020
1021    int GetRegister(const EventImp &evt)
1022    {
1023        if (!CheckEventSize(evt.GetSize(), "GetRegister", 4))
1024            return T::kSM_FatalError;
1025
1026        const unsigned int addr = evt.GetInt();
1027        if (addr>uint16_t(-1) || !fFTM.CmdGetRegister(addr))
1028        {
1029            stringstream msg;
1030            msg << hex << "Address " << addr << " out of range.";
1031            T::Error(msg);
1032        }
1033
1034        return T::GetCurrentState();
1035    }
1036
1037    int TakeNevents(const EventImp &evt)
1038    {
1039        if (!CheckEventSize(evt.GetSize(), "TakeNevents", 4))
1040            return T::kSM_FatalError;
1041
1042        const unsigned int dat = evt.GetUInt();
1043
1044        /*
1045        if (dat[1]>uint32_t(-1))
1046        {
1047            stringstream msg;
1048            msg << hex << "Value " << dat[1] << " out of range.";
1049            T::Error(msg);
1050            return T::GetCurrentState();
1051        }*/
1052
1053        fFTM.CmdTakeNevents(dat);
1054
1055        return T::GetCurrentState();
1056    }
1057
1058    int DisableReports(const EventImp &evt)
1059    {
1060        if (!CheckEventSize(evt.GetSize(), "DisableReports", 1))
1061            return T::kSM_FatalError;
1062
1063        fFTM.CmdDisableReports(evt.GetText()[0]!=0);
1064
1065        return T::GetCurrentState();
1066    }
1067
1068    int SetVerbosity(const EventImp &evt)
1069    {
1070        if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
1071            return T::kSM_FatalError;
1072
1073        fFTM.SetVerbose(evt.GetText()[0]!=0);
1074
1075        return T::GetCurrentState();
1076    }
1077
1078    int SetHexOutput(const EventImp &evt)
1079    {
1080        if (!CheckEventSize(evt.GetSize(), "SetHexOutput", 1))
1081            return T::kSM_FatalError;
1082
1083        fFTM.SetHexOutput(evt.GetText()[0]!=0);
1084
1085        return T::GetCurrentState();
1086    }
1087
1088    int SetDynamicOut(const EventImp &evt)
1089    {
1090        if (!CheckEventSize(evt.GetSize(), "SetDynamicOut", 1))
1091            return T::kSM_FatalError;
1092
1093        fFTM.SetDynamicOut(evt.GetText()[0]!=0);
1094
1095        return T::GetCurrentState();
1096    }
1097
1098    int LoadStaticData(const EventImp &evt)
1099    {
1100        if (fFTM.LoadStaticData(evt.GetString()))
1101            return T::GetCurrentState();
1102
1103        stringstream msg;
1104        msg << "Loading static data from file '" << evt.GetString() << "' failed ";
1105
1106        if (errno)
1107            msg << "(" << strerror(errno) << ")";
1108        else
1109            msg << "(wrong size, expected " << sizeof(FTM::StaticData) << " bytes)";
1110
1111        T::Warn(msg);
1112
1113        return T::GetCurrentState();
1114    }
1115
1116    int SaveStaticData(const EventImp &evt)
1117    {
1118        if (fFTM.SaveStaticData(evt.GetString()))
1119            return T::GetCurrentState();
1120
1121        stringstream msg;
1122        msg << "Writing static data to file '" << evt.GetString() << "' failed ";
1123        msg << "(" << strerror(errno) << ")";
1124
1125        T::Warn(msg);
1126
1127        return T::GetCurrentState();
1128    }
1129
1130    int SetThreshold(const EventImp &evt)
1131    {
1132        if (!CheckEventSize(evt.GetSize(), "SetThreshold", 8))
1133            return T::kSM_FatalError;
1134
1135        const int32_t *data = reinterpret_cast<const int32_t*>(evt.GetData());
1136
1137        if (!fFTM.SetThreshold(data[0], data[1]))
1138            T::Warn("SetThreshold - Maximum allowed patch number 159, valid value range 0-0xffff");
1139
1140        return T::GetCurrentState();
1141    }
1142
1143    int EnableFTU(const EventImp &evt)
1144    {
1145        if (!CheckEventSize(evt.GetSize(), "EnableFTU", 5))
1146            return T::kSM_FatalError;
1147
1148        const int32_t &board  = *reinterpret_cast<const int32_t*>(evt.GetText());
1149        const int8_t  &enable = *reinterpret_cast<const int8_t*>(evt.GetText()+4);
1150
1151        if (!fFTM.EnableFTU(board, enable))
1152            T::Warn("EnableFTU - Board number must be <40.");
1153
1154        return T::GetCurrentState();
1155    }
1156
1157    int ToggleFTU(const EventImp &evt)
1158    {
1159        if (!CheckEventSize(evt.GetSize(), "ToggleFTU", 4))
1160            return T::kSM_FatalError;
1161
1162        if (!fFTM.ToggleFTU(evt.GetInt()))
1163            T::Warn("ToggleFTU - Allowed range of boards 0-39.");
1164
1165        return T::GetCurrentState();
1166    }
1167
1168    int SetTriggerInterval(const EventImp &evt)
1169    {
1170        if (!CheckEventSize(evt.GetSize(), "SetTriggerInterval", 4))
1171            return T::kSM_FatalError;
1172
1173        if (!fFTM.SetTriggerInterval(evt.GetInt()))
1174            T::Warn("SetTriggerInterval - Value out of range.");
1175
1176        return T::GetCurrentState();
1177    }
1178
1179    int SetTriggerDelay(const EventImp &evt)
1180    {
1181        if (!CheckEventSize(evt.GetSize(), "SetTriggerDelay", 4))
1182            return T::kSM_FatalError;
1183
1184        if (!fFTM.SetTriggerDelay(evt.GetInt()))
1185            T::Warn("SetTriggerDealy -  Value out of range.");
1186
1187        return T::GetCurrentState();
1188    }
1189
1190    int SetTimeMarkerDelay(const EventImp &evt)
1191    {
1192        if (!CheckEventSize(evt.GetSize(), "SetTimeMarkerDelay", 4))
1193            return T::kSM_FatalError;
1194
1195        if (!fFTM.SetTimeMarkerDelay(evt.GetInt()))
1196            T::Warn("SetTimeMarkerDelay -  Value out of range.");
1197
1198        return T::GetCurrentState();
1199    }
1200
1201    int SetPrescaling(const EventImp &evt)
1202    {
1203        if (!CheckEventSize(evt.GetSize(), "SetPrescaling", 4))
1204            return T::kSM_FatalError;
1205
1206        if (!fFTM.SetPrescaling(evt.GetInt()))
1207            T::Warn("SetPrescaling -  Value out of range.");
1208
1209        return T::GetCurrentState();
1210    }
1211
1212    int SetTriggerSeq(const EventImp &evt)
1213    {
1214        if (!CheckEventSize(evt.GetSize(), "SetTriggerSeq", 3))
1215            return T::kSM_FatalError;
1216
1217        const uint8_t *data = reinterpret_cast<const uint8_t*>(evt.GetData());
1218
1219        if (!fFTM.SetTriggerSeq(data))
1220            T::Warn("SetTriggerSeq -  Value out of range.");
1221
1222        return T::GetCurrentState();
1223    }
1224
1225    int SetDeadTime(const EventImp &evt)
1226    {
1227        if (!CheckEventSize(evt.GetSize(), "SetDeadTime", 4))
1228            return T::kSM_FatalError;
1229
1230        if (!fFTM.SetDeadTime(evt.GetInt()))
1231            T::Warn("SetDeadTime -  Value out of range.");
1232
1233        return T::GetCurrentState();
1234    }
1235
1236    int SetTriggerCoincidence(const EventImp &evt)
1237    {
1238        if (!CheckEventSize(evt.GetSize(), "SetTriggerCoincidence", 4))
1239            return T::kSM_FatalError;
1240
1241        const uint16_t *d = reinterpret_cast<const uint16_t*>(evt.GetText());;
1242
1243        if (!fFTM.SetTriggerCoincidence(d[0], d[1]))
1244            T::Warn("SetTriggerCoincidence -  Value out of range.");
1245
1246        return T::GetCurrentState();
1247    }
1248
1249    int SetCalibCoincidence(const EventImp &evt)
1250    {
1251        if (!CheckEventSize(evt.GetSize(), "SetCalibCoincidence", 4))
1252            return T::kSM_FatalError;
1253
1254        const uint16_t *d = reinterpret_cast<const uint16_t*>(evt.GetText());;
1255
1256        if (!fFTM.SetCalibCoincidence(d[0], d[1]))
1257            T::Warn("SetCalibCoincidence -  Value out of range.");
1258
1259        return T::GetCurrentState();
1260    }
1261
1262    int Enable(const EventImp &evt, FTM::StaticData::GeneralSettings type)
1263    {
1264        if (!CheckEventSize(evt.GetSize(), "Enable", 1))
1265            return T::kSM_FatalError;
1266
1267        fFTM.Enable(type, evt.GetText()[0]!=0);
1268
1269        return T::GetCurrentState();
1270    }
1271
1272    int Disconnect()
1273    {
1274        // Close all connections
1275        fFTM.PostClose(false);
1276
1277        /*
1278         // Now wait until all connection have been closed and
1279         // all pending handlers have been processed
1280         poll();
1281         */
1282
1283        return T::GetCurrentState();
1284    }
1285
1286    int Reconnect(const EventImp &evt)
1287    {
1288        // Close all connections to supress the warning in SetEndpoint
1289        fFTM.PostClose(false);
1290
1291        // Now wait until all connection have been closed and
1292        // all pending handlers have been processed
1293        poll();
1294
1295        if (evt.GetText()[0]!=0)
1296            fFTM.SetEndpoint(evt.GetString());
1297
1298        // Now we can reopen the connection
1299        fFTM.PostClose(true);
1300
1301        return T::GetCurrentState();
1302    }
1303
1304    /*
1305    int Transition(const Event &evt)
1306    {
1307        switch (evt.GetTargetState())
1308        {
1309        case kStateDisconnected:
1310        case kStateConnected:
1311        }
1312
1313        return T::kSM_FatalError;
1314    }*/
1315
1316    int Execute()
1317    {
1318        // Dispatch (execute) at most one handler from the queue. In contrary
1319        // to run_one(), it doesn't wait until a handler is available
1320        // which can be dispatched, so poll_one() might return with 0
1321        // handlers dispatched. The handlers are always dispatched/executed
1322        // synchronously, i.e. within the call to poll_one()
1323        poll_one();
1324
1325        return fFTM.GetState();
1326    }
1327
1328public:
1329    StateMachineFTM(ostream &out=cout) :
1330        T(out, "FTM_CONTROL"), ba::io_service::work(static_cast<ba::io_service&>(*this)),
1331        fFTM(*this, *this)
1332    {
1333        // ba::io_service::work is a kind of keep_alive for the loop.
1334        // It prevents the io_service to go to stopped state, which
1335        // would prevent any consecutive calls to run()
1336        // or poll() to do nothing. reset() could also revoke to the
1337        // previous state but this might introduce some overhead of
1338        // deletion and creation of threads and more.
1339
1340        // State names
1341        AddStateName(kStateDisconnected, "Disconnected",
1342                     "FTM board not connected via ethernet.");
1343
1344        AddStateName(kStateConnected, "Connected",
1345                     "Ethernet connection to FTM established (no state received yet).");
1346
1347        AddStateName(kStateIdle, "Idle",
1348                     "Ethernet connection to FTM established, FTM in idle state.");
1349
1350        AddStateName(kStateTakingData, "TakingData",
1351                     "Ethernet connection to FTM established, FTM is in taking data state.");
1352
1353        // FTM Commands
1354        AddConfiguration("TOGGLE_LED", kStateIdle)
1355            (Wrapper(boost::bind(&ConnectionFTM::CmdToggleLed, &fFTM)))
1356            ("toggle led");
1357
1358        AddConfiguration("PING", kStateIdle)
1359            (Wrapper(boost::bind(&ConnectionFTM::CmdPing, &fFTM)))
1360            ("send ping");
1361
1362        AddConfiguration("REQUEST_DYNAMIC_DATA", kStateIdle)
1363            (Wrapper(boost::bind(&ConnectionFTM::CmdReqDynDat, &fFTM)))
1364            ("request transmission of dynamic data block");
1365
1366        AddConfiguration("REQUEST_STATIC_DATA", kStateIdle)
1367            (Wrapper(boost::bind(&ConnectionFTM::CmdReqStatDat, &fFTM)))
1368            ("request transmission of static data from FTM to memory");
1369
1370        AddConfiguration("GET_REGISTER", "I", kStateIdle)
1371            (boost::bind(&StateMachineFTM::GetRegister, this, _1))
1372            ("read register from address addr"
1373            "|addr[short]:Address of register");
1374
1375        AddConfiguration("SET_REGISTER", "I:2", kStateIdle)
1376            (boost::bind(&StateMachineFTM::SetRegister, this, _1))
1377            ("set register to value"
1378            "|addr[short]:Address of register"
1379            "|val[short]:Value to be set");
1380
1381        AddConfiguration("START_RUN", kStateIdle)
1382            (Wrapper(boost::bind(&ConnectionFTM::CmdStartRun, &fFTM)))
1383            ("start a run (start distributing triggers)");
1384
1385        AddConfiguration("STOP_RUN", kStateTakingData)
1386            (Wrapper(boost::bind(&ConnectionFTM::CmdStopRun, &fFTM)))
1387            ("stop a run (stop distributing triggers)");
1388
1389        AddConfiguration("TAKE_N_EVENTS", "I", kStateIdle)
1390            (boost::bind(&StateMachineFTM::TakeNevents, this, _1))
1391            ("take n events (distribute n triggers)|number[int]:Number of events to be taken");
1392
1393        AddConfiguration("DISABLE_REPORTS", "B", kStateIdle)
1394            (boost::bind(&StateMachineFTM::DisableReports, this, _1))
1395            ("disable sending rate reports"
1396             "|status[bool]:disable or enable that the FTM sends rate reports (yes/no)");
1397
1398        AddConfiguration("SET_THRESHOLD", "I:2", kStateIdle)
1399            (boost::bind(&StateMachineFTM::SetThreshold, this, _1))
1400            ("Set the comparator threshold"
1401             "|Patch[idx]:Index of the patch (0-159), -1 for all"
1402             "|Threshold[counts]:Threshold to be set in binary counts");
1403
1404        AddConfiguration("SET_PRESCALING", "I:1", kStateIdle)
1405            (boost::bind(&StateMachineFTM::SetPrescaling, this, _1))
1406            (""
1407             "|[]:");
1408
1409        AddConfiguration("ENABLE_FTU", "I:1;B:1", kStateIdle)
1410            (boost::bind(&StateMachineFTM::EnableFTU, this, _1))
1411            ("Enable or disable FTU"
1412             "|Board[idx]:Index of the board (0-39), -1 for all"
1413             "|Enable[bool]:Whether FTU should be enabled or disabled (yes/no)");
1414
1415        AddConfiguration("TOGGLE_FTU", "I:1", kStateIdle)
1416            (boost::bind(&StateMachineFTM::ToggleFTU, this, _1))
1417            ("Toggle status of FTU (this is mainly meant to be used in the GUI)"
1418             "|Board[idx]:Index of the board (0-39)");
1419
1420        AddConfiguration("SET_TRIGGER_INTERVAL", "I:1", kStateIdle)
1421            (boost::bind(&StateMachineFTM::SetTriggerInterval, this, _1))
1422            ("Sets the trigger interval which is the distance between two consecutive artificial triggers."
1423             "|interval[int]:The applied trigger interval is: interval*4ns+8ns");
1424
1425        AddConfiguration("SET_TRIGGER_DELAY", "I:1", kStateIdle)
1426            (boost::bind(&StateMachineFTM::SetTriggerDelay, this, _1))
1427            (""
1428             "|delay[int]:The applied trigger delay is: delay*4ns+8ns");
1429
1430        AddConfiguration("SET_TIME_MARKER_DELAY", "I:1", kStateIdle)
1431            (boost::bind(&StateMachineFTM::SetTimeMarkerDelay, this, _1))
1432            (""
1433            "|delay[int]:The applied time marker delay is: delay*4ns+8ns");
1434
1435        AddConfiguration("SET_DEAD_TIME", "I:1", kStateIdle)
1436            (boost::bind(&StateMachineFTM::SetDeadTime, this, _1))
1437            (""
1438            "|dead_time[int]:The applied dead time is: dead_time*4ns+8ns");
1439
1440        AddConfiguration("ENABLE_TRIGGER", "B:1", kStateIdle)
1441            (boost::bind(&StateMachineFTM::Enable, this, _1, FTM::StaticData::kTrigger))
1442            ("Switch on the physics trigger"
1443             "|Enable[bool]:Enable physics trigger (yes/no)");
1444
1445        // FIXME: Switch on/off depending on sequence
1446        AddConfiguration("ENABLE_EXT1", "B:1", kStateIdle)
1447            (boost::bind(&StateMachineFTM::Enable, this, _1, FTM::StaticData::kExt1))
1448            ("Switch on the triggers through the first external line"
1449             "|Enable[bool]:Enable ext1 trigger (yes/no)");
1450
1451        // FIXME: Switch on/off depending on sequence
1452        AddConfiguration("ENABLE_EXT2", "B:1", kStateIdle)
1453            (boost::bind(&StateMachineFTM::Enable, this, _1, FTM::StaticData::kExt2))
1454            ("Switch on the triggers through the second external line"
1455             "|Enable[bool]:Enable ext2 trigger (yes/no)");
1456
1457        AddConfiguration("ENABLE_VETO", "B:1", kStateIdle)
1458            (boost::bind(&StateMachineFTM::Enable, this, _1, FTM::StaticData::kVeto))
1459            ("Enable veto line"
1460             "|Enable[bool]:Enable veto (yes/no)");
1461
1462        AddConfiguration("SET_TRIGGER_SEQUENCE", "C:3", kStateIdle)
1463            (boost::bind(&StateMachineFTM::SetTriggerSeq, this, _1))
1464            ("Setup the sequence of artificial triggers produced by the FTM"
1465             "|Ped[int]:number of pedestal triggers in a row"
1466             "|LPint[int]:number of triggers of the internal light pulser"
1467             "|LPext[int]:number of triggers of the external light pulser");
1468
1469        AddConfiguration("SET_TRIGGER_COINCIDENCE", "S:2", kStateIdle)
1470            (boost::bind(&StateMachineFTM::SetTriggerCoincidence, this, _1))
1471            ("Setup the coincidence condition for physcis triggers"
1472             "|N[int]:Number of requirered coincident triggers from sum-patches (1-40)");
1473
1474        AddConfiguration("SET_CALIBRATION_COINCIDENCE", "S:2", kStateIdle)
1475            (boost::bind(&StateMachineFTM::SetCalibCoincidence, this, _1))
1476            ("Setup the coincidence condition for artificial (calibration) triggers"
1477             "|N[int]:Number of requirered coincident triggers from sum-patches (1-40)");
1478
1479
1480        // Load/save static data block
1481        T::AddConfiguration("SAVE", "C", kStateIdle)
1482            (boost::bind(&StateMachineFTM::SaveStaticData, this, _1))
1483            ("Saves the static data (FTM configuration) from memory to a file"
1484             "|filename[string]:Filename (can include a path), .bin is automatically added");
1485
1486        T::AddConfiguration("LOAD", "C", kStateIdle)
1487            (boost::bind(&StateMachineFTM::LoadStaticData, this, _1))
1488            ("Loads the static data (FTM configuration) from a file into memory and sends it to the FTM"
1489             "|filename[string]:Filename (can include a path), .bin is automatically added");
1490
1491
1492
1493        // Verbosity commands
1494        T::AddConfiguration("SET_VERBOSE", "B")
1495            (boost::bind(&StateMachineFTM::SetVerbosity, this, _1))
1496            ("set verbosity state"
1497             "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
1498
1499        T::AddConfiguration("SET_HEX_OUTPUT", "B")
1500            (boost::bind(&StateMachineFTM::SetHexOutput, this, _1))
1501            ("enable or disable hex output for received data"
1502             "|hexout[bool]:disable or enable hex output for received data (yes/no)");
1503
1504        T::AddConfiguration("SET_DYNAMIC_OUTPUT", "B")
1505            (boost::bind(&StateMachineFTM::SetDynamicOut, this, _1))
1506            ("enable or disable output for received dynamic data (data is still broadcasted via Dim)"
1507             "|dynout[bool]:disable or enable output for dynamic data (yes/no)");
1508
1509
1510        // Conenction commands
1511        AddConfiguration("DISCONNECT", kStateConnected, kStateIdle)
1512            (boost::bind(&StateMachineFTM::Disconnect, this))
1513            ("disconnect from ethernet");
1514
1515        AddConfiguration("RECONNECT", "O", kStateDisconnected, kStateConnected, kStateIdle)
1516            (boost::bind(&StateMachineFTM::Reconnect, this, _1))
1517            ("(Re)connect ethernet connection to FTM, a new address can be given"
1518             "|[host][string]:new ethernet address in the form <host:port>");
1519
1520        // Other
1521        AddTransition(kCmdTest, "TEST", "O")
1522            (boost::bind(&StateMachineFTM::Test, this, _1))
1523            ("Just for test purpose, do not use");
1524
1525        fFTM.StartConnect();
1526    }
1527
1528        /// Just for test purpose, do not touch
1529    int Test(const Event &evt)
1530    {
1531        const Converter conv(T::Out(), evt.GetFormat(), false);
1532        T::Out() << kBlue << evt.GetName();
1533        T::Out() << " " << conv.GetString(evt.GetData(), evt.GetSize());
1534        T::Out() << endl;
1535
1536        return T::GetCurrentState();
1537    }
1538
1539    void SetEndpoint(const string &url)
1540    {
1541        fFTM.SetEndpoint(url);
1542    }
1543
1544    bool SetConfiguration(const Configuration &conf)
1545    {
1546        SetEndpoint(conf.Get<string>("addr"));
1547
1548        fFTM.SetVerbose(!conf.Get<bool>("quiet"));
1549        fFTM.SetHexOutput(conf.Get<bool>("hex-out"));
1550        fFTM.SetDynamicOut(conf.Get<bool>("dynamic-out"));
1551
1552        return true;
1553    }
1554};
1555
1556// ------------------------------------------------------------------------
1557
1558void RunThread(StateMachineImp *io_service)
1559{
1560    // This is necessary so that the StateMachien Thread can signal the
1561    // Readline to exit
1562    io_service->Run();
1563    Readline::Stop();
1564}
1565
1566template<class S, class T>
1567int RunDim(Configuration &conf)
1568{
1569    WindowLog wout;
1570
1571    /*
1572    static Test shell(conf.GetName().c_str(), conf.Get<int>("console")!=1);
1573
1574    WindowLog &win  = shell.GetStreamIn();
1575    WindowLog &wout = shell.GetStreamOut();
1576    */
1577
1578    if (conf.Has("log"))
1579        if (!wout.OpenLogFile(conf.Get<string>("log")))
1580            wout << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
1581
1582    // Start io_service.Run to use the StateMachineImp::Run() loop
1583    // Start io_service.run to only use the commandHandler command detaching
1584    StateMachineFTM<S, T> io_service(wout);
1585    if (!io_service.SetConfiguration(conf))
1586        return -1;
1587
1588    io_service.Run();
1589
1590    /*
1591    shell.SetReceiver(io_service);
1592
1593    boost::thread t(boost::bind(RunThread, &io_service));
1594    // boost::thread t(boost::bind(&StateMachineFTM<S>::Run, &io_service));
1595
1596    shell.Run();                 // Run the shell
1597    io_service.Stop();           // Signal Loop-thread to stop
1598    // io_service.Close();       // Obsolete, done by the destructor
1599
1600    // Wait until the StateMachine has finished its thread
1601    // before returning and destroying the dim objects which might
1602    // still be in use.
1603    t.join();
1604    */
1605
1606    return 0;
1607}
1608
1609template<class T, class S, class R>
1610int RunShell(Configuration &conf)
1611{
1612    static T shell(conf.GetName().c_str(), conf.Get<int>("console")!=1);
1613
1614    WindowLog &win  = shell.GetStreamIn();
1615    WindowLog &wout = shell.GetStreamOut();
1616
1617    if (conf.Has("log"))
1618        if (!wout.OpenLogFile(conf.Get<string>("log")))
1619            win << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
1620
1621    StateMachineFTM<S, R> io_service(wout);
1622    if (!io_service.SetConfiguration(conf))
1623        return -1;
1624
1625    shell.SetReceiver(io_service);
1626
1627    boost::thread t(boost::bind(RunThread, &io_service));
1628    // boost::thread t(boost::bind(&StateMachineFTM<S>::Run, &io_service));
1629
1630    shell.Run();                 // Run the shell
1631    io_service.Stop();           // Signal Loop-thread to stop
1632    // io_service.Close();       // Obsolete, done by the destructor
1633
1634    // Wait until the StateMachine has finished its thread
1635    // before returning and destroying the dim objects which might
1636    // still be in use.
1637    t.join();
1638
1639    return 0;
1640}
1641
1642void SetupConfiguration(Configuration &conf)
1643{
1644    const string n = conf.GetName()+".log";
1645
1646    po::options_description config("Program options");
1647    config.add_options()
1648        ("dns",       var<string>("localhost"), "Dim nameserver host name (Overwites DIM_DNS_NODE environment variable)")
1649        ("log,l",     var<string>(n), "Write log-file")
1650        ("no-dim,d",  po_switch(),    "Disable dim services")
1651        ("console,c", var<int>(),     "Use console (0=shell, 1=simple buffered, X=simple unbuffered)")
1652        ;
1653
1654    po::options_description control("FTM control options");
1655    control.add_options()
1656        ("addr,a",      var<string>("localhost:5000"),  "Network address of FTM")
1657        ("quiet,q",     po_switch(), "Disable printing contents of all received messages (except dynamic data) in clear text.")
1658        ("hex-out",     po_switch(), "Enable printing contents of all printed messages also as hex data.")
1659        ("dynamic-out", po_switch(), "Enable printing received dynamic data.")
1660        ;
1661
1662    conf.AddEnv("dns", "DIM_DNS_NODE");
1663
1664    conf.AddOptions(config);
1665    conf.AddOptions(control);
1666}
1667
1668/*
1669 Extract usage clause(s) [if any] for SYNOPSIS.
1670 Translators: "Usage" and "or" here are patterns (regular expressions) which
1671 are used to match the usage synopsis in program output.  An example from cp
1672 (GNU coreutils) which contains both strings:
1673  Usage: cp [OPTION]... [-T] SOURCE DEST
1674    or:  cp [OPTION]... SOURCE... DIRECTORY
1675    or:  cp [OPTION]... -t DIRECTORY SOURCE...
1676 */
1677void PrintUsage()
1678{
1679    cout <<
1680        "The ftmctrl controls the FTM (FACT Trigger Master) board.\n"
1681        "\n"
1682        "The default is that the program is started without user intercation. "
1683        "All actions are supposed to arrive as DimCommands. Using the -c "
1684        "option, a local shell can be initialized. With h or help a short "
1685        "help message about the usuage can be brought to the screen.\n"
1686        "\n"
1687        "Usage: ftmctrl [-c type] [OPTIONS]\n"
1688        "  or:  ftmctrl [OPTIONS]\n";
1689    cout << endl;
1690}
1691
1692void PrintHelp()
1693{
1694    /* Additional help text which is printed after the configuration
1695     options goes here */
1696
1697    /*
1698     cout << "bla bla bla" << endl << endl;
1699     cout << endl;
1700     cout << "Environment:" << endl;
1701     cout << "environment" << endl;
1702     cout << endl;
1703     cout << "Examples:" << endl;
1704     cout << "test exam" << endl;
1705     cout << endl;
1706     cout << "Files:" << endl;
1707     cout << "files" << endl;
1708     cout << endl;
1709     */
1710}
1711
1712/*
1713string GetLocalIp()
1714{
1715    const char *kDnsIp = getenv("DIM_DNS_NODE");
1716
1717    struct addrinfo hints, *servinfo, *p;
1718
1719    memset(&hints, 0, sizeof hints);
1720    hints.ai_family   = AF_INET; //AF_UNSPEC; // use AF_INET6 to force IPv6
1721    hints.ai_socktype = SOCK_STREAM;
1722
1723    int rv;
1724    if ((rv = getaddrinfo(kDnsIp, NULL, &hints, &servinfo)) != 0)
1725    {
1726        cout << "WARNING - getaddrinfo: " << gai_strerror(rv) << endl;
1727        return kDnsIp;
1728    }
1729
1730    // loop through all the results and connect to the first we can
1731    for (p=servinfo; p; p=p->ai_next)
1732    {
1733        const int sock = socket(AF_INET, SOCK_DGRAM, 0);
1734        if (sock==-1)
1735            continue;
1736
1737        if (connect(sock, p->ai_addr, p->ai_addrlen)==-1)
1738        {
1739            cout << "WARNING - connect: " << strerror(errno) << endl;
1740            close(sock);
1741            continue;
1742        }
1743
1744        sockaddr_in name;
1745        socklen_t namelen = sizeof(name);
1746        if (getsockname(sock, (sockaddr*)&name, &namelen)==-1)
1747        {
1748            cout << "WARNING - getsockname: " << strerror(errno) << endl;
1749            close(sock);
1750            continue;
1751        }
1752
1753        char buffer[16];
1754        if (!inet_ntop(AF_INET, &name.sin_addr, buffer, 16))
1755        {
1756            cout << "WARNING - inet_ntop: " << strerror(errno) << endl;
1757            close(sock);
1758            continue;
1759        }
1760
1761        close(sock);
1762
1763        freeaddrinfo(servinfo); // all done with this structure
1764
1765        cout << "DIM_HOST_NODE=" << buffer << endl;
1766        return buffer;
1767    }
1768
1769    freeaddrinfo(servinfo); // all done with this structure
1770    return kDnsIp;
1771}
1772*/
1773
1774int main(int argc, const char* argv[])
1775{
1776    Configuration conf(argv[0]);
1777    conf.SetPrintUsage(PrintUsage);
1778    SetupConfiguration(conf);
1779
1780    po::variables_map vm;
1781    try
1782    {
1783        vm = conf.Parse(argc, argv);
1784    }
1785#if BOOST_VERSION > 104000
1786    catch (po::multiple_occurrences &e)
1787    {
1788        cout << "Error: " << e.what() << " of '" << e.get_option_name() << "' option." << endl;
1789        cout << endl;
1790        return -1;
1791    }
1792#endif
1793    catch (std::exception &e)
1794    {
1795        cout << "Error: " << e.what() << endl;
1796        cout << endl;
1797
1798        return -1;
1799    }
1800
1801    if (conf.HasPrint())
1802        return -1;
1803
1804    if (conf.HasVersion())
1805    {
1806        FACT::PrintVersion(argv[0]);
1807        return -1;
1808    }
1809
1810    if (conf.HasHelp())
1811    {
1812        PrintHelp();
1813        return -1;
1814    }
1815
1816    Dim::Setup(conf.Get<string>("dns"));
1817
1818    //try
1819    {
1820        // No console access at all
1821        if (!conf.Has("console"))
1822        {
1823            if (conf.Get<bool>("no-dim"))
1824                return RunDim<StateMachine, ConnectionFTM>(conf);
1825            else
1826                return RunDim<StateMachineDim, ConnectionDimFTM>(conf);
1827        }
1828        // Cosole access w/ and w/o Dim
1829        if (conf.Get<bool>("no-dim"))
1830        {
1831            if (conf.Get<int>("console")==0)
1832                return RunShell<LocalShell, StateMachine, ConnectionFTM>(conf);
1833            else
1834                return RunShell<LocalConsole, StateMachine, ConnectionFTM>(conf);
1835        }
1836        else
1837        {
1838            if (conf.Get<int>("console")==0)
1839                return RunShell<LocalShell, StateMachineDim, ConnectionDimFTM>(conf);
1840            else
1841                return RunShell<LocalConsole, StateMachineDim, ConnectionDimFTM>(conf);
1842        }
1843    }
1844    /*catch (std::exception& e)
1845    {
1846        cerr << "Exception: " << e.what() << endl;
1847        return -1;
1848    }*/
1849
1850    return 0;
1851}
Note: See TracBrowser for help on using the repository browser.