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

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