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

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