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

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