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

Last change on this file since 10555 was 10555, checked in by tbretz, 9 years ago
Implemented ENABLE_FTU and TOGGLE_FTU
File size: 39.6 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 << kRed << "Error 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    bool SetThreshold(int32_t patch, int32_t value)
560    {
561
562        if (patch>159)
563            return false;
564
565        if (value<0 || value>0xffff)
566            return false;
567
568        if (patch<0)
569        {
570            for (int i=0; i<160; i++)
571                fStaticData[i/4].fDAC[i%4] = value;
572        }
573        else
574            fStaticData[patch/4].fDAC[patch%4] = value;
575
576        // Maybe move to a "COMMIT" command?
577        CmdSendStatDat();
578
579        return true;
580    }
581
582    bool EnableFTU(int32_t board, bool enable)
583    {
584        if (board>39)
585            return false;
586
587        if (board<0)
588        {
589            if (enable)
590                fStaticData.EnableAllFTU();
591            else
592                fStaticData.DisableAllFTU();
593        }
594        else
595        {
596            if (enable)
597                fStaticData.EnableFTU(board);
598            else
599                fStaticData.DisableFTU(board);
600
601        }
602
603        // Maybe move to a "COMMIT" command?
604        CmdSendStatDat();
605
606        return true;
607    }
608
609    bool ToggleFTU(uint32_t board)
610    {
611        if (board>39)
612            return false;
613
614        fStaticData.ToggleFTU(board);
615
616        // Maybe move to a "COMMIT" command?
617        CmdSendStatDat();
618
619        return true;
620    }
621
622    int GetState() const { return IsConnected() ? fState : (int)FTM::kDisconnected; }
623};
624
625const uint16_t ConnectionFTM::kMaxAddr = 0xfff;
626
627// ------------------------------------------------------------------------
628
629#include "DimDescriptionService.h"
630
631class ConnectionDimFTM : public ConnectionFTM
632{
633private:
634
635    DimDescribedService fDimPassport;
636    DimDescribedService fDimTriggerCounter;
637    DimDescribedService fDimError;
638    DimDescribedService fDimFtuList;
639    DimDescribedService fDimStaticData;
640    DimDescribedService fDimDynamicData;
641
642    template<class T>
643        void Update(DimDescribedService &svc, const T &data) const
644    {
645        //cout << "Update: " << svc.getName() << " (" << sizeof(T) << ")" << endl;
646        svc.setData(const_cast<T*>(&data), sizeof(T));
647        svc.updateService();
648    }
649
650    virtual void UpdateFirstHeader()
651    {
652        ConnectionFTM::UpdateFirstHeader();
653
654        const DimPassport data(fHeader);
655        Update(fDimPassport, data);
656    }
657
658    virtual void UpdateHeader()
659    {
660        ConnectionFTM::UpdateHeader();
661
662        const DimTriggerCounter data(fHeader);
663        Update(fDimTriggerCounter, data);
664    }
665
666    virtual void UpdateFtuList()
667    {
668        ConnectionFTM::UpdateFtuList();
669
670        const DimFtuList data(fHeader, fFtuList);
671        Update(fDimFtuList, data);
672    }
673
674    virtual void UpdateStaticData()
675    {
676        ConnectionFTM::UpdateStaticData();
677
678        const DimStaticData data(fHeader, fStaticData);
679        Update(fDimStaticData, data);
680    }
681
682    virtual void UpdateDynamicData()
683    {
684        ConnectionFTM::UpdateDynamicData();
685
686        const DimDynamicData data(fHeader, fDynamicData);
687        Update(fDimDynamicData, data);
688    }
689
690    virtual void UpdateError()
691    {
692        ConnectionFTM::UpdateError();
693
694        const DimError data(fHeader, fError);
695        Update(fDimError, data);
696    }
697
698public:
699    ConnectionDimFTM(ba::io_service& ioservice, MessageImp &imp) :
700        ConnectionFTM(ioservice, imp),
701        fDimPassport      ("FTM_CONTROL/PASSPORT",        "X:1;S:1",      NULL, 0, ""),
702        fDimTriggerCounter("FTM_CONTROL/TRIGGER_COUNTER", "X:1;L:1",      NULL, 0, ""),
703        fDimError         ("FTM_CONTROL/ERROR",           "X:1;S:1;S:28", NULL, 0, ""),
704        fDimFtuList       ("FTM_CONTROL/FTU_LIST",        "X:1;X:1;S:1;C:4;X:40;C:40;C:40", NULL, 0, ""),
705        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, ""),
706        fDimDynamicData   ("FTM_CONTROL/DYNAMIC_DATA",    "X:1;X:1;F:4;I:160;I:40;S:40;S:40", NULL, 0, "")
707    {
708    }
709
710    // 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
711};
712
713// ------------------------------------------------------------------------
714
715template <class T, class S>
716class StateMachineFTM : public T, public ba::io_service, public ba::io_service::work
717{
718    int Wrap(boost::function<void()> f)
719    {
720        f();
721        return T::GetCurrentState();
722    }
723
724    boost::function<int(const EventImp &)> Wrapper(boost::function<void()> func)
725    {
726        return boost::bind(&StateMachineFTM::Wrap, this, func);
727    }
728
729private:
730    S fFTM;
731
732    enum states_t
733    {
734        kStateDisconnected = FTM::kDisconnected,
735        kStateConnected    = FTM::kConnected,
736        kStateIdle         = FTM::kIdle,
737        kStateTakingData   = FTM::kTakingData,
738
739        kCmdTest
740    };
741
742    bool CheckEventSize(size_t has, const char *name, size_t size)
743    {
744        if (has==size)
745            return true;
746
747        stringstream msg;
748        msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
749        T::Fatal(msg);
750        return false;
751    }
752
753    int SetRegister(const EventImp &evt)
754    {
755        if (!CheckEventSize(evt.GetSize(), "SetRegister", 8))
756            return T::kSM_FatalError;
757
758        const unsigned int *dat = reinterpret_cast<const unsigned int*>(evt.GetData());
759
760        if (dat[1]>uint16_t(-1))
761        {
762            stringstream msg;
763            msg << hex << "Value " << dat[1] << " out of range.";
764            T::Error(msg);
765            return T::GetCurrentState();
766        }
767
768
769        if (dat[0]>uint16_t(-1) || !fFTM.CmdSetRegister(dat[0], dat[1]))
770        {
771            stringstream msg;
772            msg << hex << "Address " << dat[0] << " out of range.";
773            T::Error(msg);
774        }
775
776        return T::GetCurrentState();
777    }
778
779    int GetRegister(const EventImp &evt)
780    {
781        if (!CheckEventSize(evt.GetSize(), "GetRegister", 4))
782            return T::kSM_FatalError;
783
784        const unsigned int addr = evt.GetInt();
785        if (addr>uint16_t(-1) || !fFTM.CmdGetRegister(addr))
786        {
787            stringstream msg;
788            msg << hex << "Address " << addr << " out of range.";
789            T::Error(msg);
790        }
791
792        return T::GetCurrentState();
793    }
794
795    int TakeNevents(const EventImp &evt)
796    {
797        if (!CheckEventSize(evt.GetSize(), "TakeNevents", 4))
798            return T::kSM_FatalError;
799
800        const unsigned int dat = evt.GetUInt();
801
802        /*
803        if (dat[1]>uint32_t(-1))
804        {
805            stringstream msg;
806            msg << hex << "Value " << dat[1] << " out of range.";
807            T::Error(msg);
808            return T::GetCurrentState();
809        }*/
810
811        fFTM.CmdTakeNevents(dat);
812
813        return T::GetCurrentState();
814    }
815
816    int DisableReports(const EventImp &evt)
817    {
818        if (!CheckEventSize(evt.GetSize(), "DisableReports", 1))
819            return T::kSM_FatalError;
820
821        fFTM.CmdDisableReports(evt.GetText()[0]!=0);
822
823        return T::GetCurrentState();
824    }
825
826    int SetVerbosity(const EventImp &evt)
827    {
828        if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
829            return T::kSM_FatalError;
830
831        fFTM.SetVerbose(evt.GetText()[0]!=0);
832
833        return T::GetCurrentState();
834    }
835
836    int LoadStaticData(const EventImp &evt)
837    {
838        if (fFTM.LoadStaticData(evt.GetString()))
839            return T::GetCurrentState();
840
841        stringstream msg;
842        msg << "Loading static data from file '" << evt.GetString() << "' failed ";
843
844        if (errno)
845            msg << "(" << strerror(errno) << ")";
846        else
847            msg << "(wrong size, expected " << sizeof(FTM::StaticData) << " bytes)";
848
849        T::Warn(msg);
850
851        return T::GetCurrentState();
852    }
853
854    int SaveStaticData(const EventImp &evt)
855    {
856        if (fFTM.SaveStaticData(evt.GetString()))
857            return T::GetCurrentState();
858
859        stringstream msg;
860        msg << "Writing static data to file '" << evt.GetString() << "' failed ";
861        msg << "(" << strerror(errno) << ")";
862
863        T::Warn(msg);
864
865        return T::GetCurrentState();
866    }
867
868    int SetThreshold(const EventImp &evt)
869    {
870        if (!CheckEventSize(evt.GetSize(), "SetThreshold", 8))
871            return T::kSM_FatalError;
872
873        const int32_t *data = reinterpret_cast<const int32_t*>(evt.GetData());
874
875        if (!fFTM.SetThreshold(data[0], data[1]))
876            T::Warn("SetThreshold - Maximum allowed patch number 159, valid value range 0-0xffff");
877
878        return T::GetCurrentState();
879    }
880
881    int EnableFTU(const EventImp &evt)
882    {
883        if (!CheckEventSize(evt.GetSize(), "EnableFTU", 5))
884            return T::kSM_FatalError;
885
886        const int32_t &board  = *reinterpret_cast<const int32_t*>(evt.GetText());
887        const int8_t  &enable = *reinterpret_cast<const int8_t*>(evt.GetText()+4);
888
889        if (!fFTM.EnableFTU(board, enable))
890            T::Warn("EnableFTU - Board number must be <40.");
891
892        return T::GetCurrentState();
893    }
894
895    int ToggleFTU(const EventImp &evt)
896    {
897        if (!CheckEventSize(evt.GetSize(), "ToggleFTU", 4))
898            return T::kSM_FatalError;
899
900        if (!fFTM.ToggleFTU(evt.GetInt()))
901            T::Warn("ToggleFTU - Allowed range of boards 0-39.");
902
903        return T::GetCurrentState();
904    }
905
906    int Disconnect()
907    {
908        // Close all connections
909        fFTM.PostClose(false);
910
911        /*
912         // Now wait until all connection have been closed and
913         // all pending handlers have been processed
914         poll();
915         */
916
917        return T::GetCurrentState();
918    }
919
920    int Reconnect(const EventImp &evt)
921    {
922        // Close all connections to supress the warning in SetEndpoint
923        fFTM.PostClose(false);
924
925        // Now wait until all connection have been closed and
926        // all pending handlers have been processed
927        poll();
928
929        if (evt.GetText()[0]!=0)
930            fFTM.SetEndpoint(evt.GetString());
931
932        // Now we can reopen the connection
933        fFTM.PostClose(true);
934
935        return T::GetCurrentState();
936    }
937
938    /*
939    int Transition(const Event &evt)
940    {
941        switch (evt.GetTargetState())
942        {
943        case kStateDisconnected:
944        case kStateConnected:
945        }
946
947        return T::kSM_FatalError;
948    }*/
949
950    int Execute()
951    {
952        // Dispatch (execute) at most one handler from the queue. In contrary
953        // to run_one(), it doesn't wait until a handler is available
954        // which can be dispatched, so poll_one() might return with 0
955        // handlers dispatched. The handlers are always dispatched/executed
956        // synchronously, i.e. within the call to poll_one()
957        poll_one();
958
959        return fFTM.GetState();
960    }
961
962public:
963    StateMachineFTM(ostream &out=cout) :
964        T(out, "FTM_CONTROL"), ba::io_service::work(static_cast<ba::io_service&>(*this)),
965        fFTM(*this, *this)
966    {
967        // ba::io_service::work is a kind of keep_alive for the loop.
968        // It prevents the io_service to go to stopped state, which
969        // would prevent any consecutive calls to run()
970        // or poll() to do nothing. reset() could also revoke to the
971        // previous state but this might introduce some overhead of
972        // deletion and creation of threads and more.
973
974        // State names
975        AddStateName(kStateDisconnected, "Disconnected",
976                     "FTM board not connected via ethernet.");
977
978        AddStateName(kStateConnected, "Connected",
979                     "Ethernet connection to FTM established (no state received yet).");
980
981        AddStateName(kStateIdle, "Idle",
982                     "Ethernet connection to FTM established, FTM in idle state.");
983
984        AddStateName(kStateTakingData, "TakingData",
985                     "Ethernet connection to FTM established, FTM is in taking data state.");
986
987        // FTM Commands
988        AddConfiguration("TOGGLE_LED", kStateIdle)
989            (Wrapper(boost::bind(&ConnectionFTM::CmdToggleLed, &fFTM)))
990            ("toggle led");
991
992        AddConfiguration("PING", kStateIdle)
993            (Wrapper(boost::bind(&ConnectionFTM::CmdPing, &fFTM)))
994            ("send ping");
995
996        AddConfiguration("REQUEST_DYNAMIC_DATA", kStateIdle)
997            (Wrapper(boost::bind(&ConnectionFTM::CmdReqDynDat, &fFTM)))
998            ("request transmission of dynamic data block");
999
1000        AddConfiguration("REQUEST_STATIC_DATA", kStateIdle)
1001            (Wrapper(boost::bind(&ConnectionFTM::CmdReqStatDat, &fFTM)))
1002            ("request transmission of static data from FTM to memory");
1003
1004        AddConfiguration("GET_REGISTER", "I", kStateIdle)
1005            (boost::bind(&StateMachineFTM::GetRegister, this, _1))
1006            ("read register from address addr"
1007            "|addr[short]:Address of register");
1008
1009        AddConfiguration("SET_REGISTER", "I:2", kStateIdle)
1010            (boost::bind(&StateMachineFTM::SetRegister, this, _1))
1011            ("set register to value"
1012            "|addr[short]:Address of register"
1013            "|val[short]:Value to be set");
1014
1015        AddConfiguration("START_RUN", kStateIdle)
1016            (Wrapper(boost::bind(&ConnectionFTM::CmdStartRun, &fFTM)))
1017            ("start a run (start distributing triggers)");
1018
1019        AddConfiguration("STOP_RUN", kStateTakingData)
1020            (Wrapper(boost::bind(&ConnectionFTM::CmdStopRun, &fFTM)))
1021            ("stop a run (stop distributing triggers)");
1022
1023        AddConfiguration("TAKE_N_EVENTS", "I", kStateIdle)
1024            (boost::bind(&StateMachineFTM::TakeNevents, this, _1))
1025            ("take n events (distribute n triggers)|number[int]:Number of events to be taken");
1026
1027        AddConfiguration("DISABLE_REPORTS", "B", kStateIdle)
1028            (boost::bind(&StateMachineFTM::DisableReports, this, _1))
1029            ("disable sending rate reports"
1030             "|status[bool]:disable or enable that the FTM sends rate reports (yes/no)");
1031
1032        AddConfiguration("SET_THRESHOLD", "I:2", kStateIdle)
1033            (boost::bind(&StateMachineFTM::SetThreshold, this, _1))
1034            ("Set the comparator threshold"
1035             "|Patch[idx]:Index of the patch (0-159), -1 for all"
1036             "|Threshold[counts]:Threshold to be set in binary counts");
1037
1038        AddConfiguration("ENABLE_FTU", "I:1;B:1", kStateIdle)
1039            (boost::bind(&StateMachineFTM::EnableFTU, this, _1))
1040            ("Enable or disable FTU"
1041             "|Board[idx]:Index of the board (0-39), -1 for all"
1042             "|Enable[bool]:Whether FTU should be enabled or disabled (yes/no)");
1043
1044        AddConfiguration("TOGGLE_FTU", "I:1", kStateIdle)
1045            (boost::bind(&StateMachineFTM::ToggleFTU, this, _1))
1046            ("Toggle status of FTU (this is mainly meant to be used in the GUI)"
1047             "|Board[idx]:Index of the board (0-39)");
1048
1049        T::AddConfiguration("SET_VERBOSE", "B")
1050            (boost::bind(&StateMachineFTM::SetVerbosity, this, _1))
1051            ("set verbosity state"
1052             "|verbosity[bool]:disable or enable verbosity for received data (yes/no)");
1053
1054        T::AddConfiguration("SAVE", "C", kStateIdle)
1055            (boost::bind(&StateMachineFTM::SaveStaticData, this, _1))
1056            ("Saves the static data (FTM configuration) from memory to a file"
1057             "|filename[string]:Filename (can include a path), .bin is automatically added");
1058
1059        T::AddConfiguration("LOAD", "C", kStateIdle)
1060            (boost::bind(&StateMachineFTM::LoadStaticData, this, _1))
1061            ("Loads the static data (FTM configuration) from a file into memory and sends it to the FTM"
1062             "|filename[string]:Filename (can include a path), .bin is automatically added");
1063
1064        // Conenction commands
1065        AddConfiguration("DISCONNECT", kStateConnected, kStateIdle)
1066            (boost::bind(&StateMachineFTM::Disconnect, this))
1067            ("disconnect from ethernet");
1068
1069        AddConfiguration("RECONNECT", "O", kStateDisconnected, kStateConnected, kStateIdle)
1070            (boost::bind(&StateMachineFTM::Reconnect, this, _1))
1071            ("(Re)connect ethernet connection to FTM, a new address can be given"
1072             "|[host][string]:new ethernet address in the form <host:port>");
1073
1074        // Other
1075        AddTransition(kCmdTest, "TEST", "O")
1076            (boost::bind(&StateMachineFTM::Test, this, _1))
1077            ("Just for test purpose, do not use");
1078
1079        fFTM.StartConnect();
1080
1081
1082        // ENABLE_FTU             idx bool
1083        // ---> EnableFtu(idx==-1, bool)
1084
1085        // ENABLE_TRIGGER         bool
1086        // ENABLE_EXT1            bool
1087        // ENABLE_EXT2            bool
1088        // ENABLE_TIM             bool
1089        // ENABLE_VETO            bool
1090        // ---> Enable(bit, bool)
1091
1092
1093        // SET_TRIGGER_SEQUENCE   val val val
1094        // ---> SetTriggerSequence(val, val, val)
1095
1096        // SET_TRIGGER_INTERVAL   val
1097        // SET_TRIGGER_DELAY      val
1098        // SET_TIME_MARKER_DELAY  val
1099        // SET_DEAD_TIME          val
1100        // ---> SetXYZ(val)
1101
1102        // SET_PRESCALING         idx val
1103        // ---> SetPrescaling(idx==-1, val)
1104    }
1105
1106        /// Just for test purpose, do not touch
1107    int Test(const Event &evt)
1108    {
1109        const Converter conv(T::Out(), evt.GetFormat(), false);
1110        T::Out() << kBlue << evt.GetName();
1111        T::Out() << " " << conv.GetString(evt.GetData(), evt.GetSize());
1112        T::Out() << endl;
1113
1114        return T::GetCurrentState();
1115    }
1116
1117    void SetEndpoint(const string &url)
1118    {
1119        fFTM.SetEndpoint(url);
1120    }
1121
1122    bool SetConfiguration(const Configuration &conf)
1123    {
1124        SetEndpoint(conf.Get<string>("addr"));
1125        return true;
1126    }
1127};
1128
1129// ------------------------------------------------------------------------
1130
1131void RunThread(StateMachineImp *io_service)
1132{
1133    // This is necessary so that the StateMachien Thread can signal the
1134    // Readline to exit
1135    io_service->Run();
1136    Readline::Stop();
1137}
1138
1139template<class S, class T>
1140int RunDim(Configuration &conf)
1141{
1142    WindowLog wout;
1143
1144    /*
1145    static Test shell(conf.GetName().c_str(), conf.Get<int>("console")!=1);
1146
1147    WindowLog &win  = shell.GetStreamIn();
1148    WindowLog &wout = shell.GetStreamOut();
1149    */
1150    cout << "Start" << endl;
1151
1152    if (conf.Has("log"))
1153        if (!wout.OpenLogFile(conf.Get<string>("log")))
1154            wout << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
1155    cout << "Start" << endl;
1156
1157    // Start io_service.Run to use the StateMachineImp::Run() loop
1158    // Start io_service.run to only use the commandHandler command detaching
1159    StateMachineFTM<S, T> io_service(wout);
1160    cout << "Start" << endl;
1161    if (!io_service.SetConfiguration(conf))
1162        return -1;
1163
1164    cout << "Start" << endl;
1165    io_service.Run();
1166
1167    /*
1168    shell.SetReceiver(io_service);
1169
1170    boost::thread t(boost::bind(RunThread, &io_service));
1171    // boost::thread t(boost::bind(&StateMachineFTM<S>::Run, &io_service));
1172
1173    shell.Run();                 // Run the shell
1174    io_service.Stop();           // Signal Loop-thread to stop
1175    // io_service.Close();       // Obsolete, done by the destructor
1176
1177    // Wait until the StateMachine has finished its thread
1178    // before returning and destroying the dim objects which might
1179    // still be in use.
1180    t.join();
1181    */
1182
1183    return 0;
1184}
1185
1186template<class T, class S, class R>
1187int RunShell(Configuration &conf)
1188{
1189    static T shell(conf.GetName().c_str(), conf.Get<int>("console")!=1);
1190
1191    WindowLog &win  = shell.GetStreamIn();
1192    WindowLog &wout = shell.GetStreamOut();
1193
1194    if (conf.Has("log"))
1195        if (!wout.OpenLogFile(conf.Get<string>("log")))
1196            win << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
1197
1198    StateMachineFTM<S, R> io_service(wout);
1199    if (!io_service.SetConfiguration(conf))
1200        return -1;
1201
1202    shell.SetReceiver(io_service);
1203
1204    boost::thread t(boost::bind(RunThread, &io_service));
1205    // boost::thread t(boost::bind(&StateMachineFTM<S>::Run, &io_service));
1206
1207    shell.Run();                 // Run the shell
1208    io_service.Stop();           // Signal Loop-thread to stop
1209    // io_service.Close();       // Obsolete, done by the destructor
1210
1211    // Wait until the StateMachine has finished its thread
1212    // before returning and destroying the dim objects which might
1213    // still be in use.
1214    t.join();
1215
1216    return 0;
1217}
1218
1219void SetupConfiguration(Configuration &conf)
1220{
1221    const string n = conf.GetName()+".log";
1222
1223    po::options_description config("Program options");
1224    config.add_options()
1225        ("dns",       var<string>("localhost"), "Dim nameserver host name (Overwites DIM_DNS_NODE environment variable)")
1226        ("log,l",     var<string>(n), "Write log-file")
1227        ("no-dim,d",  po_switch(),    "Disable dim services")
1228        ("console,c", var<int>(),     "Use console (0=shell, 1=simple buffered, X=simple unbuffered)")
1229        ;
1230
1231    po::options_description control("FTM control options");
1232    control.add_options()
1233        ("addr",      var<string>("localhost:5000"),  "Network address of FTM")
1234        ;
1235
1236    conf.AddEnv("dns", "DIM_DNS_NODE");
1237
1238    conf.AddOptions(config);
1239    conf.AddOptions(control);
1240}
1241
1242/*
1243 Extract usage clause(s) [if any] for SYNOPSIS.
1244 Translators: "Usage" and "or" here are patterns (regular expressions) which
1245 are used to match the usage synopsis in program output.  An example from cp
1246 (GNU coreutils) which contains both strings:
1247  Usage: cp [OPTION]... [-T] SOURCE DEST
1248    or:  cp [OPTION]... SOURCE... DIRECTORY
1249    or:  cp [OPTION]... -t DIRECTORY SOURCE...
1250 */
1251void PrintUsage()
1252{
1253    cout <<
1254        "The ftmctrl controls the FTM (FACT Trigger Master) board.\n"
1255        "\n"
1256        "The default is that the program is started without user intercation. "
1257        "All actions are supposed to arrive as DimCommands. Using the -c "
1258        "option, a local shell can be initialized. With h or help a short "
1259        "help message about the usuage can be brought to the screen.\n"
1260        "\n"
1261        "Usage: ftmctrl [-c type] [OPTIONS]\n"
1262        "  or:  ftmctrl [OPTIONS]\n"
1263        "\n"
1264        "Options:\n"
1265        "The following describes the available commandline options. "
1266        "For further details on how command line option are parsed "
1267        "and in which order which configuration sources are accessed "
1268        "please refer to the class reference of the Configuration class.";
1269    cout << endl;
1270
1271}
1272
1273void PrintHelp()
1274{
1275}
1276
1277/*
1278 The first line of the --version information is assumed to be in one
1279 of the following formats:
1280
1281   <version>
1282   <program> <version>
1283   {GNU,Free} <program> <version>
1284   <program> ({GNU,Free} <package>) <version>
1285   <program> - {GNU,Free} <package> <version>
1286
1287 and separated from any copyright/author details by a blank line.
1288
1289 Handle multi-line bug reporting sections of the form:
1290
1291   Report <program> bugs to <addr>
1292   GNU <package> home page: <url>
1293   ...
1294*/
1295void PrintVersion(const char *name)
1296{
1297    cout <<
1298        name << " - "PACKAGE_STRING"\n"
1299        "\n"
1300        "Written by Thomas Bretz et al.\n"
1301        "\n"
1302        "Report bugs to <"PACKAGE_BUGREPORT">\n"
1303        "Home page: "PACKAGE_URL"\n"
1304        "\n"
1305        "Copyright (C) 2011 by the FACT Collaboration.\n"
1306        "This is free software; see the source for copying conditions.\n"
1307        << endl;
1308}
1309
1310int main(int argc, const char* argv[])
1311{
1312    Configuration conf(argv[0]);
1313    conf.SetPrintUsage(PrintUsage);
1314    SetupConfiguration(conf);
1315
1316    po::variables_map vm;
1317    try
1318    {
1319        vm = conf.Parse(argc, argv);
1320    }
1321#if BOOST_VERSION > 104000
1322    catch (po::multiple_occurrences &e)
1323    {
1324        cout << "Error: " << e.what() << " of '" << e.get_option_name() << "' option." << endl;
1325        cout << endl;
1326        return -1;
1327    }
1328#endif
1329    catch (std::exception &e)
1330    {
1331        cout << "Error: " << e.what() << endl;
1332        cout << endl;
1333
1334        return -1;
1335    }
1336
1337    if (conf.HasPrint())
1338        return -1;
1339
1340    if (conf.HasVersion())
1341    {
1342        PrintVersion(argv[0]);
1343        return -1;
1344    }
1345
1346    if (conf.HasHelp())
1347    {
1348        PrintHelp();
1349        return -1;
1350    }
1351
1352    // To allow overwriting of DIM_DNS_NODE set 0 to 1
1353    setenv("DIM_DNS_NODE", conf.Get<string>("dns").c_str(), 1);
1354
1355    //try
1356    {
1357        // No console access at all
1358        if (!conf.Has("console"))
1359        {
1360            if (conf.Get<bool>("no-dim"))
1361                return RunDim<StateMachine, ConnectionFTM>(conf);
1362            else
1363                return RunDim<StateMachineDim, ConnectionDimFTM>(conf);
1364        }
1365        // Cosole access w/ and w/o Dim
1366        if (conf.Get<bool>("no-dim"))
1367        {
1368            if (conf.Get<int>("console")==0)
1369                return RunShell<LocalShell, StateMachine, ConnectionFTM>(conf);
1370            else
1371                return RunShell<LocalConsole, StateMachine, ConnectionFTM>(conf);
1372        }
1373        else
1374        {
1375            if (conf.Get<int>("console")==0)
1376                return RunShell<LocalShell, StateMachineDim, ConnectionDimFTM>(conf);
1377            else
1378                return RunShell<LocalConsole, StateMachineDim, ConnectionDimFTM>(conf);
1379        }
1380    }
1381    /*catch (std::exception& e)
1382    {
1383        cerr << "Exception: " << e.what() << endl;
1384        return -1;
1385    }*/
1386
1387    return 0;
1388}
Note: See TracBrowser for help on using the repository browser.