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

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