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

Last change on this file since 10517 was 10517, checked in by tbretz, 9 years ago
Added first version (not documented at all) of the fake-ftm and the ftmctrl
File size: 37.1 KB
Line 
1#include <boost/bind.hpp>
2#include <boost/array.hpp>
3#include <boost/thread.hpp>
4#include <boost/asio/error.hpp>
5#include <boost/asio/deadline_timer.hpp>
6
7#include "Event.h"
8#include "Shell.h"
9#include "StateMachineDim.h"
10#include "Connection.h"
11#include "Configuration.h"
12#include "Timers.h"
13#include "Console.h"
14#include "Converter.h"
15
16#include "tools.h"
17
18#include "LocalControl.h"
19#include "HeadersFTM.h"
20
21namespace ba = boost::asio;
22namespace bs = boost::system;
23
24using namespace std;
25using namespace FTM;
26
27// ------------------------------------------------------------------------
28
29class ConnectionFTM : public Connection
30{
31    vector<uint16_t> fBuffer;
32
33    bool fHasHeader;
34    int  fState;
35
36    bool fIsVerbose;
37    bool fIsDynamicOut;
38    bool fIsHexOutput;
39
40    // --verbose
41    // --hex-out
42    // --dynamic-out
43    // --load-file
44    // --leds
45    // --trigger-interval
46    // --physcis-coincidence
47    // --calib-coincidence
48    // --physcis-window
49    // --physcis-window
50    // --trigger-delay
51    // --time-marker-delay
52    // --dead-time
53    // --clock-conditioner-r0
54    // --clock-conditioner-r1
55    // --clock-conditioner-r8
56    // --clock-conditioner-r9
57    // --clock-conditioner-r11
58    // --clock-conditioner-r13
59    // --clock-conditioner-r14
60    // --clock-conditioner-r15
61    // ...
62
63    map<uint16_t, int> fCounter;
64
65protected:
66    FTM::Header      fHeader;
67    FTM::FtuList     fFtuList;
68    FTM::StaticData  fStaticData;
69    FTM::DynamicData fDynamicData;
70    FTM::Error       fError;
71
72    virtual void UpdateFirstHeader()
73    {
74        // FIXME: Message() ?
75        Out() << endl << kBold << "First header received:" << endl;
76        Out() << fHeader;
77        if (fIsHexOutput)
78            Out() << Converter::GetHex<uint16_t>(fHeader, sizeof(fHeader), 16) << endl;
79    }
80
81    virtual void UpdateHeader()
82    {
83        // emit service with trigger counter from header
84        if (!fIsVerbose)
85            return;
86
87        if (fHeader.fType==kDynamicData && !fIsDynamicOut)
88            return;
89
90        Out() << endl << kBold << "Header received:" << endl;
91        Out() << fHeader;
92        if (fIsHexOutput)
93            Out() << Converter::GetHex<uint16_t>(fHeader, sizeof(fHeader), 16) << endl;
94    }
95
96    virtual void UpdateFtuList()
97    {
98        if (!fIsVerbose)
99            return;
100
101        Out() << endl << kBold << "FtuList received:" << endl;
102        Out() << fFtuList;
103        if (fIsHexOutput)
104            Out() << Converter::GetHex<uint16_t>(fFtuList, 16) << endl;
105    }
106
107    virtual void UpdateStaticData()
108    {
109        if (!fIsVerbose)
110            return;
111
112        Out() << endl << kBold << "Static data received:" << endl;
113        Out() << fStaticData;
114        if (fIsHexOutput)
115            Out() << Converter::GetHex<uint16_t>(fStaticData, 16) << endl;
116    }
117
118    virtual void UpdateDynamicData()
119    {
120        if (!fIsDynamicOut)
121            return;
122
123        Out() << endl << kBold << "Dynamic data received:" << endl;
124        Out() << fDynamicData;
125        if (fIsHexOutput)
126            Out() << Converter::GetHex<uint16_t>(fDynamicData, 16) << endl;
127    }
128
129    virtual void UpdateError()
130    {
131        if (!fIsVerbose)
132            return;
133
134        Out() << endl << kBold << "Error list received:" << endl;
135        Out() << fError;
136        if (fIsHexOutput)
137            Out() << Converter::GetHex<uint16_t>(fError, 16) << endl;
138    }
139
140private:
141    void HandleReceivedData(const bs::error_code& err, size_t bytes_received, int /*type*/)
142    {
143        // Do not schedule a new read if the connection failed.
144        if (bytes_received==0 || err)
145        {
146            if (err==ba::error::eof)
147                Warn("Connection closed by remote host (FTM).");
148
149            // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category))
150            // 125: Operation canceled
151            if (err && err!=ba::error::eof &&                     // Connection closed by remote host
152                err!=ba::error::basic_errors::not_connected &&    // Connection closed by remote host
153                err!=ba::error::basic_errors::operation_aborted)  // Connection closed by us
154            {
155                stringstream str;
156                str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl;
157                Error(str);
158            }
159            PostClose(err!=ba::error::basic_errors::operation_aborted);
160            return;
161        }
162
163        // If we have not yet received a header we expect one now
164        // This could be moved to a HandleReceivedHeader function
165        if (!fHasHeader)
166        {
167            if (bytes_received!=sizeof(FTM::Header))
168            {
169                stringstream str;
170                str << "Excepted " << sizeof(FTM::Header) << " bytes (FTM::Header) but received " << bytes_received << ".";
171                Error(str);
172                PostClose(false);
173                return;
174            }
175
176            fHeader = fBuffer;
177
178            // Check the data integrity
179            if (fHeader.fDelimiter!=kDelimiterStart)
180            {
181                stringstream str;
182                str << "Invalid header received: start delimiter wrong, received " << hex << fHeader.fDelimiter << " expected " << kDelimiterStart << ".";
183                Error(str);
184                PostClose(false);
185                return;
186            }
187
188            fHasHeader = true;
189
190            // Convert FTM state into FtmCtrl state
191            switch (fHeader.fState)
192            {
193            case FTM::kFtmIdle:
194            case FTM::kFtmConfig:
195                fState = FTM::kIdle;
196                break;
197
198            case FTM::kFtmCalib:
199            case FTM::kFtmRunning:
200                fState = FTM::kTakingData;
201                break;
202            }
203
204            if (++fCounter[kHeader]==1)
205                UpdateFirstHeader();
206
207            UpdateHeader();
208
209            // Start reading of data
210            switch (fHeader.fType)
211            {
212            case kStaticData:
213            case kDynamicData:
214            case kFtuList:
215            case kRegister:
216            case kErrorList:
217                // This is not very efficient because the space is reallocated
218                // maybe we can check if the capacity of the std::vector
219                // is ever decreased. If not, everythign is fine.
220                fBuffer.resize(fHeader.fDataSize);
221                AsyncRead(ba::buffer(fBuffer));
222                AsyncWait(fInTimeout, 50, &Connection::HandleReadTimeout);
223                return;
224
225            default:
226                stringstream str;
227                str << "Unknonw type " << fHeader.fType << " in received header." << endl;
228                Error(str);
229                PostClose(false);
230                return;
231            }
232
233            return;
234        }
235
236        // Check the data integrity (check end delimiter)
237        if (ntohs(fBuffer.back())!=FTM::kDelimiterEnd)
238        {
239            stringstream str;
240            str << "Invalid data received: end delimiter wrong, received ";
241            str << hex << ntohs(fBuffer.back()) << " expected " << kDelimiterEnd << ".";
242            Error(str);
243            PostClose(false);
244            return;
245        }
246
247        // Remove end delimiter
248        fBuffer.pop_back();
249
250        try
251        {
252            // If we have already received a header this is the data now
253            // This could be moved to a HandleReceivedData function
254
255            fCounter[fHeader.fType]++;
256
257            switch (fHeader.fType)
258            {
259            case kFtuList:
260                fFtuList = fBuffer;
261                UpdateFtuList();
262                break;
263
264            case kStaticData:
265                fStaticData = fBuffer;
266                UpdateStaticData();
267            break;
268
269            case kDynamicData:
270                fDynamicData = fBuffer;
271                UpdateDynamicData();
272                break;
273
274            case kRegister:
275                if (fIsVerbose)
276                {
277                    Out() << endl << kBold << "Register received: " << endl;
278                    Out() << "Value: " << ntohs(fBuffer[0]) << endl;
279                }
280                break;
281
282            case kErrorList:
283                fError = fBuffer;
284                UpdateError();
285                break;
286
287            default:
288                stringstream str;
289                str << "Unknonw type " << fHeader.fType << " in header." << endl;
290                Error(str);
291                PostClose(false);
292                return;
293            }
294        }
295        catch (const logic_error &e)
296        {
297            stringstream str;
298            str << "Exception converting buffer into data structure: " << e.what();
299            Error(str);
300            PostClose(false);
301            return;
302        }
303
304        fInTimeout.cancel();
305
306        fHeader.clear();
307        fHasHeader = false;
308        fBuffer.resize(sizeof(FTM::Header)/2);
309        AsyncRead(ba::buffer(fBuffer));
310    }
311
312    // This is called when a connection was established
313    void ConnectionEstablished()
314    {
315        fState = FTM::kConnected;
316        fCounter.clear();
317
318        fHeader.clear();
319        fHasHeader = false;
320        fBuffer.resize(sizeof(FTM::Header)/2);
321        AsyncRead(ba::buffer(fBuffer));
322
323        // Get a header and configdata!
324        CmdReqStatDat();
325
326        // get the DNA of the FTUs
327        CmdPing();
328    }
329
330    void HandleReadTimeout(const bs::error_code &error)
331    {
332        if (error && error!=ba::error::basic_errors::operation_aborted)
333        {
334            stringstream str;
335            str << "Read timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl;
336            Error(str);
337
338            PostClose();
339            return;
340
341        }
342
343        if (!is_open())
344        {
345            // For example: Here we could schedule a new accept if we
346            // would not want to allow two connections at the same time.
347            return;
348        }
349
350        // Check whether the deadline has passed. We compare the deadline
351        // against the current time since a new asynchronous operation
352        // may have moved the deadline before this actor had a chance
353        // to run.
354        if (fInTimeout.expires_at() > ba::deadline_timer::traits_type::now())
355            return;
356
357        Error("Timeout reading data from "+URL());
358
359        PostClose();
360    }
361
362
363    template<size_t N>
364    void PostCmd(boost::array<uint16_t, N> dat, uint16_t u1=0, uint16_t u2=0, uint16_t u3=0, uint16_t u4=0)
365    {
366        boost::array<uint16_t, 5> cmd = {{ '@', u1, u2, u3, u4 }};
367
368        stringstream msg;
369        msg << "Sending command:" << hex;
370        msg << " 0x" << setw(4) << setfill('0') << cmd[0];
371        msg << " 0x" << setw(4) << setfill('0') << u1;
372        msg << " 0x" << setw(4) << setfill('0') << u2;
373        msg << " 0x" << setw(4) << setfill('0') << u3;
374        msg << " 0x" << setw(4) << setfill('0') << u4;
375        msg << " (+" << dec << dat.size() << " words)";
376        Message(msg);
377
378        vector<uint16_t> out(cmd.size()+dat.size());
379
380        transform(cmd.begin(), cmd.end(), out.begin(), htons);
381        transform(dat.begin(), dat.end(), out.begin()+cmd.size(), htons);
382
383        PostMessage(out);
384    }
385
386    void PostCmd(vector<uint16_t> dat, uint16_t u1=0, uint16_t u2=0, uint16_t u3=0, uint16_t u4=0)
387    {
388        boost::array<uint16_t, 5> cmd = {{ '@', u1, u2, u3, u4 }};
389
390        stringstream msg;
391        msg << "Sending command:" << hex;
392        msg << " 0x" << setw(4) << setfill('0') << cmd[0];
393        msg << " 0x" << setw(4) << setfill('0') << u1;
394        msg << " 0x" << setw(4) << setfill('0') << u2;
395        msg << " 0x" << setw(4) << setfill('0') << u3;
396        msg << " 0x" << setw(4) << setfill('0') << u4;
397        msg << " (+" << dec << dat.size() << " words)";
398        Message(msg);
399
400        vector<uint16_t> out(cmd.size()+dat.size());
401
402        transform(cmd.begin(), cmd.end(), out.begin(), htons);
403        copy(dat.begin(), dat.end(), out.begin()+cmd.size());
404
405        PostMessage(out);
406    }
407
408    void PostCmd(uint16_t u1=0, uint16_t u2=0, uint16_t u3=0, uint16_t u4=0)
409    {
410        PostCmd(boost::array<uint16_t, 0>(), u1, u2, u3, u4);
411    }
412public:
413
414    static const uint16_t kMaxAddr;
415
416public:
417    ConnectionFTM(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()),
418        fIsVerbose(true), fIsDynamicOut(false), fIsHexOutput(false)
419    {
420        cout << "xFTM" << endl;
421        SetLogStream(&imp);
422        cout << "check" << endl;
423    }
424
425    void CmdToggleLed()
426    {
427        PostCmd(kCmdToggleLed);
428    }
429
430    void CmdPing()
431    {
432        PostCmd(kCmdPing);
433    }
434
435    void CmdReqDynDat()
436    {
437        PostCmd(kCmdRead, kReadDynamicData);
438    }
439
440    void CmdReqStatDat()
441    {
442        PostCmd(kCmdRead, kReadStaticData);
443    }
444
445    void CmdSendStatDat()
446    {
447        PostCmd(fStaticData.HtoN(), kCmdWrite, kWriteStaticData);
448
449        // Request the changed configuration to ensure the
450        // change is distributed in the network
451        CmdReqStatDat();
452    }
453
454    void CmdStartRun()
455    {
456        PostCmd(kCmdStartRun, kStartRun);
457
458        // Update state information by requesting a new header
459        CmdGetRegister(0);
460    }
461
462    void CmdStopRun()
463    {
464        PostCmd(kCmdStopRun);
465
466        // Update state information by requesting a new header
467        CmdGetRegister(0);
468    }
469
470    void CmdTakeNevents(uint32_t n)
471    {
472        const boost::array<uint16_t, 2> data = {{ uint16_t(n>>16), uint16_t(n&0xffff) }};
473        PostCmd(data, kCmdStartRun, kTakeNevents);
474
475        // Update state information by requesting a new header
476        CmdGetRegister(0);
477    }
478
479    bool CmdSetRegister(uint16_t addr, uint16_t val)
480    {
481        if (addr>kMaxAddr)
482            return false;
483
484        const boost::array<uint16_t, 2> data = {{ addr, val }};
485        PostCmd(data, kCmdWrite, kWriteRegister);
486
487        // Request the changed configuration to ensure the
488        // change is distributed in the network
489        CmdReqStatDat();
490
491        return true;
492    }
493
494    bool CmdGetRegister(uint16_t addr)
495    {
496        if (addr>kMaxAddr)
497            return false;
498
499        const boost::array<uint16_t, 1> data = {{ addr }};
500        PostCmd(data, kCmdRead, kReadRegister);
501
502        return true;
503    }
504
505    bool CmdDisableReports(bool b)
506    {
507        PostCmd(kCmdDisableReports, b ? uint16_t(0) : uint16_t(1));
508        return true;
509    }
510
511    void SetVerbose(bool b)
512    {
513        fIsVerbose = b;
514    }
515
516    bool LoadStaticData(string name)
517    {
518        if (name.rfind(".bin")!=name.length()-5)
519            name += ".bin";
520
521        ifstream fin(name);
522        if (!fin)
523            return false;
524
525        FTM::StaticData data;
526
527        fin.read(reinterpret_cast<char*>(&data), sizeof(FTM::StaticData));
528
529        if (fin.gcount()<streamsize(sizeof(FTM::StaticData)))
530            return false;
531
532        if (fin.fail() || fin.eof())
533            return false;
534
535        if (fin.peek()!=-1)
536            return false;
537
538        fStaticData = data;
539
540        CmdSendStatDat();
541
542        return true;
543    }
544
545    bool SaveStaticData(string name) const
546    {
547        if (name.rfind(".bin")!=name.length()-5)
548            name += ".bin";
549
550        ofstream fout(name);
551        if (!fout)
552            return false;
553
554        fout.write(reinterpret_cast<const char*>(&fStaticData), sizeof(FTM::StaticData));
555
556        return !fout.bad();
557    }
558
559    int GetState() const { return IsConnected() ? fState : (int)FTM::kDisconnected; }
560};
561
562const uint16_t ConnectionFTM::kMaxAddr = 0xfff;
563
564// ------------------------------------------------------------------------
565
566#include "DimDescriptionService.h"
567
568class ConnectionDimFTM : public ConnectionFTM
569{
570private:
571
572    DimDescribedService fDimPassport;
573    DimDescribedService fDimTriggerCounter;
574    DimDescribedService fDimError;
575    DimDescribedService fDimFtuList;
576    DimDescribedService fDimStaticData;
577    DimDescribedService fDimDynamicData;
578
579    template<class T>
580        void Update(DimDescribedService &svc, const T &data) const
581    {
582        //cout << "Update: " << svc.getName() << " (" << sizeof(T) << ")" << endl;
583        svc.setData(const_cast<T*>(&data), sizeof(T));
584        svc.updateService();
585    }
586
587    virtual void UpdateFirstHeader()
588    {
589        ConnectionFTM::UpdateFirstHeader();
590
591        const DimPassport data(fHeader);
592        Update(fDimPassport, data);
593    }
594
595    virtual void UpdateHeader()
596    {
597        ConnectionFTM::UpdateHeader();
598
599        const DimTriggerCounter data(fHeader);
600        Update(fDimTriggerCounter, data);
601    }
602
603    virtual void UpdateFtuList()
604    {
605        ConnectionFTM::UpdateFtuList();
606
607        const DimFtuList data(fHeader, fFtuList);
608        Update(fDimFtuList, data);
609    }
610
611    virtual void UpdateStaticData()
612    {
613        ConnectionFTM::UpdateStaticData();
614
615        const DimStaticData data(fHeader, fStaticData);
616        Update(fDimStaticData, data);
617    }
618
619    virtual void UpdateDynamicData()
620    {
621        ConnectionFTM::UpdateDynamicData();
622
623        const DimDynamicData data(fHeader, fDynamicData);
624        Update(fDimDynamicData, data);
625    }
626
627    virtual void UpdateError()
628    {
629        ConnectionFTM::UpdateError();
630
631        const DimError data(fHeader, fError);
632        Update(fDimError, data);
633    }
634
635public:
636    ConnectionDimFTM(ba::io_service& ioservice, MessageImp &imp) :
637        ConnectionFTM(ioservice, imp),
638        fDimPassport      ("FTM_CONTROL/PASSPORT",        "X:1;S:1",      NULL, 0, ""),
639        fDimTriggerCounter("FTM_CONTROL/TRIGGER_COUNTER", "X:1;L:1",      NULL, 0, ""),
640        fDimError         ("FTM_CONTROL/ERROR",           "X:1;S:1;S:28", NULL, 0, ""),
641        fDimFtuList       ("FTM_CONTROL/FTU_LIST",        "X:1;X:1;S:1;C:4;X:40;C:40;C:40", NULL, 0, ""),
642        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", NULL, 0, ""),
643        fDimDynamicData   ("FTM_CONTROL/DYNAMIC_DATA",    "X:1;X:1;F:4;I:160;I:40;S:40;S:40", NULL, 0, "")
644    {
645    }
646
647    // 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
648};
649
650// ------------------------------------------------------------------------
651
652template <class T, class S>
653class StateMachineFTM : public T, public ba::io_service, public ba::io_service::work
654{
655    int Wrap(boost::function<void()> f)
656    {
657        f();
658        return T::GetCurrentState();
659    }
660
661    boost::function<int(const EventImp &)> Wrapper(boost::function<void()> func)
662    {
663        return boost::bind(&StateMachineFTM::Wrap, this, func);
664    }
665
666private:
667    S fFTM;
668
669    enum states_t
670    {
671        kStateDisconnected = FTM::kDisconnected,
672        kStateConnected    = FTM::kConnected,
673        kStateIdle         = FTM::kIdle,
674        kStateTakingData   = FTM::kTakingData,
675/*
676        kCmdToggleLed,
677        kCmdPing,
678        kCmdReqDynData,
679        kCmdReqStatData,
680        kCmdReqRegister,
681        kCmdSetRegister,
682  */
683        kCmdTest
684    };
685
686    int SetRegister(const Event &evt)
687    {
688        if (evt.GetSize()!=8)
689        {
690            stringstream msg;
691            msg << "SetRegister - Received event has " << evt.GetSize() << " bytes, but expected 8.";
692            T::Fatal(msg);
693
694            return T::kSM_FatalError;
695        }
696
697        const unsigned int *dat = reinterpret_cast<const unsigned int*>(evt.GetData());
698
699        if (dat[1]>uint16_t(-1))
700        {
701            stringstream msg;
702            msg << hex << "Value " << dat[1] << " out of range.";
703            T::Error(msg);
704            return T::GetCurrentState();
705        }
706
707
708        if (dat[0]>uint16_t(-1) || !fFTM.CmdSetRegister(dat[0], dat[1]))
709        {
710            stringstream msg;
711            msg << hex << "Address " << dat[0] << " out of range.";
712            T::Error(msg);
713        }
714
715        return T::GetCurrentState();
716    }
717
718    int GetRegister(const Event &evt)
719    {
720        if (evt.GetSize()!=4)
721        {
722            stringstream msg;
723            msg << "GetRegister - Received event has " << evt.GetSize() << "bytes,  but expected 2.";
724            T::Fatal(msg);
725            return T::kSM_FatalError;
726        }
727
728        const unsigned int addr = evt.GetInt();
729        if (addr>uint16_t(-1) || !fFTM.CmdGetRegister(addr))
730        {
731            stringstream msg;
732            msg << hex << "Address " << addr << " out of range.";
733            T::Error(msg);
734        }
735
736        return T::GetCurrentState();
737    }
738
739    int TakeNevents(const Event &evt)
740    {
741        if (evt.GetSize()!=4)
742        {
743            stringstream msg;
744            msg << "TakeNevents - Received event has " << evt.GetSize() << " bytes, but expected 4.";
745            T::Fatal(msg);
746
747            return T::kSM_FatalError;
748        }
749
750        const unsigned int dat = evt.GetUInt();
751
752        /*
753        if (dat[1]>uint32_t(-1))
754        {
755            stringstream msg;
756            msg << hex << "Value " << dat[1] << " out of range.";
757            T::Error(msg);
758            return T::GetCurrentState();
759        }*/
760
761        fFTM.CmdTakeNevents(dat);
762
763        return T::GetCurrentState();
764    }
765
766    int DisableReports(const Event &evt)
767    {
768        if (evt.GetSize()!=1)
769        {
770            stringstream msg;
771            msg << "DisableReports - Received event has " << evt.GetSize() << " bytes, but expected 1.";
772            T::Fatal(msg);
773
774            return T::kSM_FatalError;
775        }
776
777        fFTM.CmdDisableReports(evt.GetText()[0]!=0);
778
779        return T::GetCurrentState();
780    }
781
782    int SetVerbosity(const Event &evt)
783    {
784        if (evt.GetSize()!=1)
785        {
786            stringstream msg;
787            msg << "SetVerbosity - Received event has " << evt.GetSize() << " bytes, but expected 1.";
788            T::Fatal(msg);
789
790            return T::kSM_FatalError;
791        }
792
793        fFTM.SetVerbose(evt.GetText()[0]!=0);
794
795        return T::GetCurrentState();
796    }
797
798    int LoadStaticData(const Event &evt)
799    {
800        if (fFTM.LoadStaticData(evt.GetString()))
801            return T::GetCurrentState();
802
803        stringstream msg;
804        msg << "Loading static data from file '" << evt.GetString() << "' failed ";
805
806        if (errno)
807            msg << "(" << strerror(errno) << ")";
808        else
809            msg << "(wrong size, expected " << sizeof(FTM::StaticData) << " bytes)";
810
811        T::Warn(msg);
812
813        return T::GetCurrentState();
814    }
815
816    int SaveStaticData(const Event &evt)
817    {
818        if (fFTM.SaveStaticData(evt.GetString()))
819            return T::GetCurrentState();
820
821        stringstream msg;
822        msg << "Writing static data to file '" << evt.GetString() << "' failed ";
823        msg << "(" << strerror(errno) << ")";
824
825        T::Warn(msg);
826
827        return T::GetCurrentState();
828    }
829
830
831    int Disconnect()
832    {
833        // Close all connections
834        fFTM.PostClose(false);
835
836        /*
837         // Now wait until all connection have been closed and
838         // all pending handlers have been processed
839         poll();
840         */
841
842        return T::GetCurrentState();
843    }
844
845    int Reconnect(const Event &evt)
846    {
847        // Close all connections to supress the warning in SetEndpoint
848        fFTM.PostClose(false);
849
850        // Now wait until all connection have been closed and
851        // all pending handlers have been processed
852        poll();
853
854        if (evt.GetText()[0]!=0)
855            fFTM.SetEndpoint(evt.GetString());
856
857        // Now we can reopen the connection
858        fFTM.PostClose(true);
859
860        return T::GetCurrentState();
861    }
862
863    /*
864    int Transition(const Event &evt)
865    {
866        switch (evt.GetTargetState())
867        {
868        case kStateDisconnected:
869        case kStateConnected:
870        }
871
872        return T::kSM_FatalError;
873    }*/
874
875    int Execute()
876    {
877        // Dispatch (execute) at most one handler from the queue. In contrary
878        // to run_one(), it doesn't wait until a handler is available
879        // which can be dispatched, so poll_one() might return with 0
880        // handlers dispatched. The handlers are always dispatched/executed
881        // synchronously, i.e. within the call to poll_one()
882        poll_one();
883
884        return fFTM.GetState();
885    }
886
887public:
888    StateMachineFTM(ostream &out=cout) :
889        T(out, "FTM_CONTROL"), ba::io_service::work(static_cast<ba::io_service&>(*this)),
890        fFTM(*this, *this)
891    {
892        cout << "FTM" << endl;
893
894        // ba::io_service::work is a kind of keep_alive for the loop.
895        // It prevents the io_service to go to stopped state, which
896        // would prevent any consecutive calls to run()
897        // or poll() to do nothing. reset() could also revoke to the
898        // previous state but this might introduce some overhead of
899        // deletion and creation of threads and more.
900
901        // State names
902        AddStateName(kStateDisconnected, "Disconnected",
903                     "FTM board not connected via ethernet.");
904
905        AddStateName(kStateConnected, "Connected",
906                     "Ethernet connection to FTM established (no state received yet).");
907
908        AddStateName(kStateIdle, "Idle",
909                     "Ethernet connection to FTM established, FTM in idle state.");
910
911        AddStateName(kStateTakingData, "TakingData",
912                     "Ethernet connection to FTM established, FTM is in taking data state.");
913
914        // FTM Commands
915        AddConfiguration("TOGGLE_LED", kStateIdle)
916            (Wrapper(boost::bind(&ConnectionFTM::CmdToggleLed, &fFTM)))
917            ("toggle led");
918
919        AddConfiguration("PING", kStateIdle)
920            (Wrapper(boost::bind(&ConnectionFTM::CmdPing, &fFTM)))
921            ("send ping");
922
923        AddConfiguration("REQUEST_DYNAMIC_DATA", kStateIdle)
924            (Wrapper(boost::bind(&ConnectionFTM::CmdReqDynDat, &fFTM)))
925            ("request transmission of dynamic data block");
926
927        AddConfiguration("REQUEST_STATIC_DATA", kStateIdle)
928            (Wrapper(boost::bind(&ConnectionFTM::CmdReqStatDat, &fFTM)))
929            ("request transmission of static data from FTM to memory");
930
931        AddConfiguration("GET_REGISTER", "I", kStateIdle)
932            (boost::bind(&StateMachineFTM::GetRegister, this, _1))
933            ("read register from address addr"
934            "|addr[short]:Address of register");
935
936        AddConfiguration("SET_REGISTER", "I:2", kStateIdle)
937            (boost::bind(&StateMachineFTM::SetRegister, this, _1))
938            ("set register to value"
939            "|addr[short]:Address of register"
940            "|val[short]:Value to be set");
941
942        AddConfiguration("START_RUN", kStateIdle)
943            (Wrapper(boost::bind(&ConnectionFTM::CmdStartRun, &fFTM)))
944            ("start a run (start distributing triggers)");
945
946        AddConfiguration("STOP_RUN", kStateTakingData)
947            (Wrapper(boost::bind(&ConnectionFTM::CmdStopRun, &fFTM)))
948            ("stop a run (stop distributing triggers)");
949
950        AddConfiguration("TAKE_N_EVENTS", "I", kStateIdle)
951            (boost::bind(&StateMachineFTM::TakeNevents, this, _1))
952            ("take n events (distribute n triggers)|number[int]:Number of events to be taken");
953
954        AddConfiguration("DISABLE_REPORTS", "B", kStateIdle)
955            (boost::bind(&StateMachineFTM::DisableReports, this, _1))
956            ("disable sending rate reports"
957             "|status[bool]:disable or enable that the FTM sends rate reports (yes/no)");
958
959        T::AddConfiguration("SET_VERBOSE", "B")
960            (boost::bind(&StateMachineFTM::SetVerbosity, this, _1))
961            ("set verbosity state"
962             "|verbosity[bool]:disable or enable verbosity for received data (yes/no)");
963
964        T::AddConfiguration("SAVE", "C", kStateIdle)
965            (boost::bind(&StateMachineFTM::SaveStaticData, this, _1))
966            ("Saves the static data (FTM configuration) from memory to a file"
967             "|filename[string]:Filename (can include a path), .bin is automatically added");
968
969        T::AddConfiguration("LOAD", "C", kStateIdle)
970            (boost::bind(&StateMachineFTM::LoadStaticData, this, _1))
971            ("Loads the static data (FTM configuration) from a file into memory and sends it to the FTM"
972             "|filename[string]:Filename (can include a path), .bin is automatically added");
973
974        // Conenction commands
975        AddConfiguration("DISCONNECT", kStateConnected, kStateIdle)
976            (boost::bind(&StateMachineFTM::Disconnect, this))
977            ("disconnect from ethernet");
978
979        AddConfiguration("RECONNECT", "O", kStateDisconnected, kStateConnected, kStateIdle)
980            (boost::bind(&StateMachineFTM::Reconnect, this, _1))
981            ("(Re)connect ethernet connection to FTM, a new address can be given"
982             "|[host][string]:new ethernet address in the form <host:port>");
983
984        // Other
985        AddTransition(kCmdTest, "TEST", "O")
986            (boost::bind(&StateMachineFTM::Test, this, _1))
987            ("Just for test purpose, do not use");
988
989        fFTM.StartConnect();
990
991
992        // RESET_THRESHOLD        val
993        // --> SetThreshold(-1, val)
994
995        // SET_THRESHOLD          idx val
996        // ---> SetThreshold(idx, val)
997
998
999        // ENABLE_FTU             idx bool  (-1 for all)
1000        // ---> EnableFtu(idx, bool)
1001
1002
1003        // ENABLE_TRIGGER         bool
1004        // ENABLE_EXT1            bool
1005        // ENABLE_EXT2            bool
1006        // ENABLE_TIM             bool
1007        // ENABLE_VETO            bool
1008        // ---> Enable(bit, bool)
1009
1010
1011        // SET_TRIGGER_SEQUENCE   val val val
1012        // ---> SetTriggerSequence(val, val, val)
1013
1014
1015        // SET_TRIGGER_INTERVAL   val
1016        // SET_TRIGGER_DELAY      val
1017        // SET_TIME_MARKER_DELAY  val
1018        // SET_DEAD_TIME          val
1019        // ---> SetXYZ(val)
1020
1021
1022        // SET_PRESCALING         idx val
1023        // ---> SetPrescaling(-1, val)
1024
1025        // RESET_PRESCALING       val
1026        // ---> SetPrescaling(idx, val)
1027    }
1028
1029        /// Just for test purpose, do not touch
1030    int Test(const Event &evt)
1031    {
1032        const Converter conv(T::Out(), evt.GetFormat(), false);
1033        T::Out() << kBlue << evt.GetName();
1034        T::Out() << " " << conv.GetString(evt.GetData(), evt.GetSize());
1035        T::Out() << endl;
1036
1037        return T::GetCurrentState();
1038    }
1039
1040    void SetEndpoint(const string &url)
1041    {
1042        fFTM.SetEndpoint(url);
1043    }
1044
1045    bool SetConfiguration(const Configuration &conf)
1046    {
1047        SetEndpoint(conf.Get<string>("addr"));
1048        return true;
1049    }
1050};
1051
1052// ------------------------------------------------------------------------
1053
1054void RunThread(StateMachineImp *io_service)
1055{
1056    // This is necessary so that the StateMachien Thread can signal the
1057    // Readline to exit
1058    io_service->Run();
1059    Readline::Stop();
1060}
1061
1062template<class S, class T>
1063int RunDim(Configuration &conf)
1064{
1065    WindowLog wout;
1066
1067    /*
1068    static Test shell(conf.GetName().c_str(), conf.Get<int>("console")!=1);
1069
1070    WindowLog &win  = shell.GetStreamIn();
1071    WindowLog &wout = shell.GetStreamOut();
1072    */
1073    cout << "Start" << endl;
1074
1075    if (conf.Has("log"))
1076        if (!wout.OpenLogFile(conf.Get<string>("log")))
1077            wout << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
1078    cout << "Start" << endl;
1079
1080    // Start io_service.Run to use the StateMachineImp::Run() loop
1081    // Start io_service.run to only use the commandHandler command detaching
1082    StateMachineFTM<S, T> io_service(wout);
1083    cout << "Start" << endl;
1084    if (!io_service.SetConfiguration(conf))
1085        return -1;
1086
1087    cout << "Start" << endl;
1088    io_service.Run();
1089
1090    /*
1091    shell.SetReceiver(io_service);
1092
1093    boost::thread t(boost::bind(RunThread, &io_service));
1094    // boost::thread t(boost::bind(&StateMachineFTM<S>::Run, &io_service));
1095
1096    shell.Run();                 // Run the shell
1097    io_service.Stop();           // Signal Loop-thread to stop
1098    // io_service.Close();       // Obsolete, done by the destructor
1099
1100    // Wait until the StateMachine has finished its thread
1101    // before returning and destroying the dim objects which might
1102    // still be in use.
1103    t.join();
1104    */
1105
1106    return 0;
1107}
1108
1109template<class T, class S, class R>
1110int RunShell(Configuration &conf)
1111{
1112    static T shell(conf.GetName().c_str(), conf.Get<int>("console")!=1);
1113
1114    WindowLog &win  = shell.GetStreamIn();
1115    WindowLog &wout = shell.GetStreamOut();
1116
1117    if (conf.Has("log"))
1118        if (!wout.OpenLogFile(conf.Get<string>("log")))
1119            win << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
1120
1121    StateMachineFTM<S, R> io_service(wout);
1122    if (!io_service.SetConfiguration(conf))
1123        return -1;
1124
1125    shell.SetReceiver(io_service);
1126
1127    boost::thread t(boost::bind(RunThread, &io_service));
1128    // boost::thread t(boost::bind(&StateMachineFTM<S>::Run, &io_service));
1129
1130    shell.Run();                 // Run the shell
1131    io_service.Stop();           // Signal Loop-thread to stop
1132    // io_service.Close();       // Obsolete, done by the destructor
1133
1134    // Wait until the StateMachine has finished its thread
1135    // before returning and destroying the dim objects which might
1136    // still be in use.
1137    t.join();
1138
1139    return 0;
1140}
1141
1142void SetupConfiguration(Configuration &conf)
1143{
1144    const string n = conf.GetName()+".log";
1145
1146    po::options_description config("Program options");
1147    config.add_options()
1148        ("dns",       var<string>("localhost"), "Dim nameserver host name (Overwites DIM_DNS_NODE environment variable)")
1149        ("log,l",     var<string>(n), "Write log-file")
1150        ("no-dim,d",  po_switch(),    "Disable dim services")
1151        ("console,c", var<int>(),     "Use console (0=shell, 1=simple buffered, X=simple unbuffered)")
1152        ;
1153
1154    po::options_description control("FTM control options");
1155    control.add_options()
1156        ("addr",      var<string>("localhost:5000"),  "Network address of FTM")
1157        ;
1158
1159    conf.AddEnv("dns", "DIM_DNS_NODE");
1160
1161    conf.AddOptions(config);
1162    conf.AddOptions(control);
1163}
1164
1165/*
1166 Extract usage clause(s) [if any] for SYNOPSIS.
1167 Translators: "Usage" and "or" here are patterns (regular expressions) which
1168 are used to match the usage synopsis in program output.  An example from cp
1169 (GNU coreutils) which contains both strings:
1170  Usage: cp [OPTION]... [-T] SOURCE DEST
1171    or:  cp [OPTION]... SOURCE... DIRECTORY
1172    or:  cp [OPTION]... -t DIRECTORY SOURCE...
1173 */
1174void PrintUsage()
1175{
1176    cout <<
1177        "The ftmctrl controls the FTM (FACT Trigger Master) board.\n"
1178        "\n"
1179        "The default is that the program is started without user intercation. "
1180        "All actions are supposed to arrive as DimCommands. Using the -c "
1181        "option, a local shell can be initialized. With h or help a short "
1182        "help message about the usuage can be brought to the screen.\n"
1183        "\n"
1184        "Usage: ftmctrl [-c type] [OPTIONS]\n"
1185        "  or:  ftmctrl [OPTIONS]\n"
1186        "\n"
1187        "Options:\n"
1188        "The following describes the available commandline options. "
1189        "For further details on how command line option are parsed "
1190        "and in which order which configuration sources are accessed "
1191        "please refer to the class reference of the Configuration class.";
1192    cout << endl;
1193
1194}
1195
1196void PrintHelp()
1197{
1198}
1199
1200/*
1201 The first line of the --version information is assumed to be in one
1202 of the following formats:
1203
1204   <version>
1205   <program> <version>
1206   {GNU,Free} <program> <version>
1207   <program> ({GNU,Free} <package>) <version>
1208   <program> - {GNU,Free} <package> <version>
1209
1210 and separated from any copyright/author details by a blank line.
1211
1212 Handle multi-line bug reporting sections of the form:
1213
1214   Report <program> bugs to <addr>
1215   GNU <package> home page: <url>
1216   ...
1217*/
1218void PrintVersion(const char *name)
1219{
1220    cout <<
1221        name << " - "PACKAGE_STRING"\n"
1222        "\n"
1223        "Written by Thomas Bretz et al.\n"
1224        "\n"
1225        "Report bugs to <"PACKAGE_BUGREPORT">\n"
1226        "Home page: "PACKAGE_URL"\n"
1227        "\n"
1228        "Copyright (C) 2011 by the FACT Collaboration.\n"
1229        "This is free software; see the source for copying conditions.\n"
1230        << endl;
1231}
1232
1233int main(int argc, const char* argv[])
1234{
1235    Configuration conf(argv[0]);
1236    conf.SetPrintUsage(PrintUsage);
1237    SetupConfiguration(conf);
1238
1239    po::variables_map vm;
1240    try
1241    {
1242        vm = conf.Parse(argc, argv);
1243    }
1244#if BOOST_VERSION > 104000
1245    catch (po::multiple_occurrences &e)
1246    {
1247        cout << "Error: " << e.what() << " of '" << e.get_option_name() << "' option." << endl;
1248        cout << endl;
1249        return -1;
1250    }
1251#endif
1252    catch (std::exception &e)
1253    {
1254        cout << "Error: " << e.what() << endl;
1255        cout << endl;
1256
1257        return -1;
1258    }
1259
1260    if (conf.HasPrint())
1261        return -1;
1262
1263    if (conf.HasVersion())
1264    {
1265        PrintVersion(argv[0]);
1266        return -1;
1267    }
1268
1269    if (conf.HasHelp())
1270    {
1271        PrintHelp();
1272        return -1;
1273    }
1274
1275    // To allow overwriting of DIM_DNS_NODE set 0 to 1
1276    setenv("DIM_DNS_NODE", conf.Get<string>("dns").c_str(), 1);
1277
1278    //try
1279    {
1280        // No console access at all
1281        if (!conf.Has("console"))
1282        {
1283            if (conf.Get<bool>("no-dim"))
1284                return RunDim<StateMachine, ConnectionFTM>(conf);
1285            else
1286                return RunDim<StateMachineDim, ConnectionDimFTM>(conf);
1287        }
1288        // Cosole access w/ and w/o Dim
1289        if (conf.Get<bool>("no-dim"))
1290        {
1291            if (conf.Get<int>("console")==0)
1292                return RunShell<LocalShell, StateMachine, ConnectionFTM>(conf);
1293            else
1294                return RunShell<LocalConsole, StateMachine, ConnectionFTM>(conf);
1295        }
1296        else
1297        {
1298            if (conf.Get<int>("console")==0)
1299                return RunShell<LocalShell, StateMachineDim, ConnectionDimFTM>(conf);
1300            else
1301                return RunShell<LocalConsole, StateMachineDim, ConnectionDimFTM>(conf);
1302        }
1303    }
1304    /*catch (std::exception& e)
1305    {
1306        cerr << "Exception: " << e.what() << endl;
1307        return -1;
1308    }*/
1309
1310    return 0;
1311}
Note: See TracBrowser for help on using the repository browser.