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

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