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

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