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

Last change on this file since 10596 was 10587, checked in by tbretz, 14 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.