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

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