source: trunk/FACT++/src/biasctrl.cc@ 11338

Last change on this file since 11338 was 11313, checked in by tbretz, 13 years ago
File size: 21.6 KB
Line 
1#include <boost/bind.hpp>
2#include <boost/array.hpp>
3#if BOOST_VERSION < 104400
4#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 4))
5#undef BOOST_HAS_RVALUE_REFS
6#endif
7#endif
8#include <boost/thread.hpp>
9#include <boost/asio/error.hpp>
10#include <boost/asio/deadline_timer.hpp>
11
12#include "Dim.h"
13#include "Event.h"
14#include "Shell.h"
15#include "StateMachineDim.h"
16#include "ConnectionUSB.h"
17#include "Configuration.h"
18#include "Console.h"
19#include "Converter.h"
20
21#include "tools.h"
22
23#include "LocalControl.h"
24
25namespace ba = boost::asio;
26namespace bs = boost::system;
27namespace dummy = ba::placeholders;
28
29using namespace std;
30
31// ------------------------------------------------------------------------
32
33class ConnectionBias : public ConnectionUSB
34{
35 vector<char> fBuffer;
36
37 bool fIsVerbose;
38
39 enum
40 {
41 kNumBoards = 13,
42 kNumChannelsPerBoard = 32,
43 kNumChannels = kNumBoards*kNumChannelsPerBoard
44 };
45
46 enum Command_t
47 {
48 kCmdReset = 0,
49 kCmdRead = 1,
50 kCmdWrite = 3
51 };
52
53
54 // Resistance in Ohm for voltage correction
55#define RESISTOR float(1000)
56
57
58 vector<uint16_t> fVolt; // Voltage in DAC units
59 vector<uint16_t> fRefVolt;
60
61 vector<uint16_t> fCurrent; // Current in ADC units
62 vector<uint16_t> fRefCurrent;
63
64 vector<bool> fOC;
65 vector<bool> fPresent;
66
67 bool fResetHit;
68
69private:
70 void HandleReceivedData(const bs::error_code& err, size_t bytes_received, int /*type*/)
71 {
72 // Do not schedule a new read if the connection failed.
73 if (bytes_received==0 || err)
74 {
75 if (err==ba::error::eof)
76 Warn("Connection closed by remote host (BIAS).");
77
78 // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category))
79 // 125: Operation canceled
80 if (err && err!=ba::error::eof && // Connection closed by remote host
81 err!=ba::error::basic_errors::not_connected && // Connection closed by remote host
82 err!=ba::error::basic_errors::operation_aborted) // Connection closed by us
83 {
84 ostringstream str;
85 str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl;
86 Error(str);
87 }
88 PostClose(err!=ba::error::basic_errors::operation_aborted);
89 return;
90 }
91
92 if (bytes_received%3)
93 {
94 Error("Number of received bytes not a multiple of 3, can't read data.");
95 PostClose(true);
96 return;
97 }
98
99 if (fIsVerbose)
100 {
101 Out() << endl << kBold << "Data received:" << endl;
102 Out() << Converter::GetHex<uint8_t>(fBuffer, 32) << endl;
103 }
104
105 int wrapcnt = -1;
106
107 // === Check/update all wrap counter ===
108 for (unsigned int i=0; i<fBuffer.size(); i += 3)
109 {
110 const int old = wrapcnt;
111 wrapcnt = (fBuffer[i]>>4)&7;
112
113 if (wrapcnt==-1 || (old+1)%8 == wrapcnt)
114 continue;
115
116 Error("WrapCnt wrong");
117 // Error receiving proper answer!
118 return;
119 }
120
121 // Success with received answer
122
123 if (fBuffer.size()!=kNumChannels*3)
124 return;
125
126 /*
127 data[0] = (cmd<<5) | (board<<1) | (((channel&16)>>4) & 1);
128 data[1] = (channel<<4) | (cmdval>>8);
129 data[2] = val&0xff;
130 */
131
132 // ################## Read all channels status ##################
133
134 // Evaluate data returned from crate
135 for (int i=0; i<kNumChannels; i++)
136 {
137 fOC[i] = fBuffer[i*3]&0x80;
138 fCurrent[i] = fBuffer[i*3+1] | ((fBuffer[i*3]&0xf)<<8);
139 fPresent[i] = fBuffer[i*3+2]&0x70 ? false : true;
140
141 // FIXME FIXME FIXME
142 fResetHit = fBuffer[i*3+2] & 0x80;
143
144 if (i==2*kNumChannelsPerBoard+19)
145 fOC[i] = false;
146 }
147 }
148
149 void HandleTransmittedData(size_t n)
150 {
151 fBuffer.resize(n);
152 AsyncRead(ba::buffer(fBuffer));
153 AsyncWait(fInTimeout, 50, &ConnectionUSB::HandleReadTimeout);
154 }
155
156 // This is called when a connection was established
157 void ConnectionEstablished()
158 {
159 }
160
161 void HandleReadTimeout(const bs::error_code &error)
162 {
163 if (error==ba::error::basic_errors::operation_aborted)
164 return;
165
166 if (error)
167 {
168 ostringstream str;
169 str << "Read timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl;
170 Error(str);
171
172 PostClose();
173 return;
174
175 }
176
177 if (!is_open())
178 {
179 // For example: Here we could schedule a new accept if we
180 // would not want to allow two connections at the same time.
181 return;
182 }
183
184 // Check whether the deadline has passed. We compare the deadline
185 // against the current time since a new asynchronous operation
186 // may have moved the deadline before this actor had a chance
187 // to run.
188 if (fInTimeout.expires_at() > ba::deadline_timer::traits_type::now())
189 return;
190
191 Error("Timeout reading data from "+URL());
192
193 PostClose();
194 }
195
196
197 void SystemReset()
198 {
199 PostMessage(GetCmd(0, kCmdReset));
200 }
201
202 vector<char> GetCmd(uint16_t id, Command_t cmd, uint16_t dac=0)
203 {
204 const unsigned int board = id/kNumChannelsPerBoard;
205 const unsigned int channel = id%kNumChannelsPerBoard;
206
207 return GetCmd(board, channel, cmd, dac);
208 }
209
210 vector<char> GetCmd(uint16_t board, uint16_t channel, Command_t cmd, uint16_t dac=0)
211 {
212 vector<char> data(3);
213
214 /*
215 if (board>kNumBoards)
216 return;
217 if (channel>kNumChannelsPerBoard)
218 return;
219 if (dac>0xfff)
220 return;
221 */
222
223 data[0] = (cmd<<5) | (board<<1) | (((channel&16)>>4) & 1);
224 data[1] = (channel<<4) | (dac>>8);
225 data[2] = dac&0xff;
226
227 return data;
228 }
229
230 void ReadAllChannelsStatus()
231 {
232 vector<char> data;
233 data.reserve(kNumChannels*3);
234
235 // Prepare command to read all channels
236 for (int i=0; i<kNumChannels; i++)
237 {
238 const vector<char> cmd = GetCmd(i, kCmdRead);
239 data.insert(data.end(), cmd.begin(), cmd.end());
240 }
241
242 PostMessage(data);
243 }
244
245 void GlobalSetDac(uint16_t dac)
246 {
247 PostMessage(GetCmd(0, kCmdWrite, dac));
248 /*
249 // On success
250 if (fBuffer.size() == 3)
251 {
252 for (int i=0; i<MAX_NUM_BOARDS; i++)
253 for (int j=0; j<NUM_CHANNELS; j++)
254 {
255 DAC[i][j] = SetPoint;
256 Volt[i][j] = Voltage;
257 RefVolt[i][j] = Voltage;
258 }
259 }
260 */
261 }
262
263
264 void SetChannels(const map<uint16_t, uint16_t> &vals)
265 {
266 if (vals.empty())
267 return;
268
269 vector<char> data;
270
271 // Build and execute commands
272 for (map<uint16_t, uint16_t>::const_iterator it=vals.begin();
273 it!=vals.end(); it++)
274 {
275 //const uint16_t dac = it->second/90.0*0xfff;
276
277 // If DAC value unchanged, do not send command
278 if (fVolt[it->first] == it->second)
279 continue;
280
281 const vector<char> cmd = GetCmd(it->first, kCmdWrite, it->second);
282 data.insert(data.end(), cmd.begin(), cmd.end());
283 }
284
285 PostMessage(data);
286
287 /*
288 // On success
289 if (Data.size() == Buf.size())
290 {
291 for (map<unsigned int, double>::const_iterator it = V.begin(); it != V.end(); ++it) {
292 DAC[it->first/NUM_CHANNELS][it->first%NUM_CHANNELS] = (unsigned int) (it->second/90.0*0x0fff);
293 Volt[it->first/NUM_CHANNELS][it->first%NUM_CHANNELS] = it->second;
294 RefVolt[it->first/NUM_CHANNELS][it->first%NUM_CHANNELS] = it->second;
295 }
296 */
297 }
298
299 /*
300 // ***** Synchronize board *****
301 bool Crate::Synch()
302 {
303 //############################################################
304 int Trial = 0;
305 vector<unsigned char> Data;
306
307 while(++Trial <= 3) {
308 Data = Communicate(string(1, 0));
309 if (Data.size() == 3) return true;
310 }
311 return false;
312 //############################################################
313 }
314 */
315
316 void SetReferenceCurrent()
317 {
318 fRefCurrent = fCurrent;
319 }
320
321 void GlobalSet(double voltage)
322 {
323 if (voltage>90)
324 return;
325
326 GlobalSetDac(voltage/90.0*0xfff);
327 }
328
329 // ***** Correct voltages according to current *****
330 void AdaptVoltages()
331 {
332 map<uint16_t, uint16_t> values;
333
334 for (int i=0; i<kNumChannels; i++)
335 {
336 if (fRefVolt[i]==0)
337 continue;
338
339 // Calculate difference and convert ADC units to Amps
340 const double diffcur = (fRefCurrent[i]-fCurrent[i])*1.22;
341
342 // Calculate voltage difference
343 const double diffvolt = diffcur*RESISTOR/1e6;
344
345 // Calculate new vlaue by onverting voltage difference to DAC units
346 const int32_t dac = fRefVolt[i] + diffvolt/90.0*0xfff;
347
348 if (dac<0 || dac>0xfff)
349 continue;
350
351 values[i] = fRefVolt[i] + dac;
352 }
353
354 SetChannels(values);
355
356 /*
357 static int LastUpdate = 0;
358 if (time(NULL)-LastUpdate > 5)
359 {
360 LastUpdate = time(NULL);
361 UpdateDIM();
362 }*/
363 }
364
365public:
366 ConnectionBias(ba::io_service& ioservice, MessageImp &imp) : ConnectionUSB(ioservice, imp()),
367 fIsVerbose(true),
368 fVolt(kNumChannels),
369 fRefVolt(kNumChannels),
370 fCurrent(kNumChannels),
371 fRefCurrent(kNumChannels),
372 fOC(kNumChannels),
373 fPresent(kNumChannels)
374 {
375 SetLogStream(&imp);
376 }
377
378 void SetVerbose(bool b)
379 {
380 fIsVerbose = b;
381 }
382};
383
384// ------------------------------------------------------------------------
385
386#include "DimDescriptionService.h"
387
388class ConnectionDimBias : public ConnectionBias
389{
390private:
391
392 DimDescribedService fDimCurrent;
393
394 void Update(DimDescribedService &svc, vector<float> data, float time) const
395 {
396 data.insert(data.begin(), time);
397 svc.setData(data.data(), data.size()*sizeof(float));
398 svc.updateService();
399 }
400
401 void UpdateCur(float time, const vector<float> &curr)
402 {
403 Update(fDimCurrent, curr, time);
404 }
405
406public:
407 ConnectionDimBias(ba::io_service& ioservice, MessageImp &imp) :
408 ConnectionBias(ioservice, imp),
409 fDimCurrent("BIAS_CONTROL/CURRENT", "F:1;F:4", "")
410 {
411 }
412
413 // 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
414};
415
416// ------------------------------------------------------------------------
417
418template <class T, class S>
419class StateMachineBias : public T, public ba::io_service, public ba::io_service::work
420{
421 int Wrap(boost::function<void()> f)
422 {
423 f();
424 return T::GetCurrentState();
425 }
426
427 boost::function<int(const EventImp &)> Wrapper(boost::function<void()> func)
428 {
429 return boost::bind(&StateMachineBias::Wrap, this, func);
430 }
431
432private:
433 S fBias;
434
435 enum states_t
436 {
437 kStateDisconnected = 1,
438 kStateConnected = 2,
439 };
440
441 int Disconnect()
442 {
443 // Close all connections
444 fBias.PostClose(false);
445
446 /*
447 // Now wait until all connection have been closed and
448 // all pending handlers have been processed
449 poll();
450 */
451
452 return T::GetCurrentState();
453 }
454
455 int Reconnect(const EventImp &evt)
456 {
457 // Close all connections to supress the warning in SetEndpoint
458 fBias.PostClose(false);
459
460 // Now wait until all connection have been closed and
461 // all pending handlers have been processed
462 poll();
463
464 if (evt.GetBool())
465 fBias.SetEndpoint(evt.GetString());
466
467 // Now we can reopen the connection
468 fBias.PostClose(true);
469
470 return T::GetCurrentState();
471 }
472
473 int Execute()
474 {
475 // Dispatch (execute) at most one handler from the queue. In contrary
476 // to run_one(), it doesn't wait until a handler is available
477 // which can be dispatched, so poll_one() might return with 0
478 // handlers dispatched. The handlers are always dispatched/executed
479 // synchronously, i.e. within the call to poll_one()
480 poll_one();
481
482 return fBias.IsConnected() ? kStateConnected : kStateDisconnected;
483 }
484
485 bool CheckEventSize(size_t has, const char *name, size_t size)
486 {
487 if (has==size)
488 return true;
489
490 ostringstream msg;
491 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
492 T::Fatal(msg);
493 return false;
494 }
495
496 int SetVerbosity(const EventImp &evt)
497 {
498 if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
499 return T::kSM_FatalError;
500
501 fBias.SetVerbose(evt.GetBool());
502
503 return T::GetCurrentState();
504 }
505
506public:
507 StateMachineBias(ostream &out=cout) :
508 T(out, "BIAS_CONTROL"), ba::io_service::work(static_cast<ba::io_service&>(*this)),
509 fBias(*this, *this)
510 {
511 // ba::io_service::work is a kind of keep_alive for the loop.
512 // It prevents the io_service to go to stopped state, which
513 // would prevent any consecutive calls to run()
514 // or poll() to do nothing. reset() could also revoke to the
515 // previous state but this might introduce some overhead of
516 // deletion and creation of threads and more.
517
518 // State names
519 AddStateName(kStateDisconnected, "Disconnected",
520 "Bias-power supply not connected via USB.");
521
522 AddStateName(kStateConnected, "Connected",
523 "USB connection to bias-power supply established.");
524
525 // Verbosity commands
526 T::AddEvent("SET_VERBOSE", "B")
527 (boost::bind(&StateMachineBias::SetVerbosity, this, _1))
528 ("set verbosity state"
529 "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
530
531 // Conenction commands
532 AddEvent("DISCONNECT", kStateConnected)
533 (boost::bind(&StateMachineBias::Disconnect, this))
534 ("disconnect from ethernet");
535
536 AddEvent("RECONNECT", "O", kStateDisconnected, kStateConnected)
537 (boost::bind(&StateMachineBias::Reconnect, this, _1))
538 ("(Re)connect ethernet connection to FTM, a new address can be given"
539 "|[host][string]:new ethernet address in the form <host:port>");
540 }
541
542 void SetEndpoint(const string &url)
543 {
544 fBias.SetEndpoint(url);
545 }
546
547 int EvalConfiguration(const Configuration &conf)
548 {
549 SetEndpoint(conf.Get<string>("addr"));
550
551 fBias.SetVerbose(!conf.Get<bool>("quiet"));
552
553 fBias.Connect();
554
555 return -1;
556 }
557};
558
559// ------------------------------------------------------------------------
560
561void RunThread(StateMachineImp *io_service)
562{
563 // This is necessary so that the StateMachien Thread can signal the
564 // Readline to exit
565 io_service->Run();
566 Readline::Stop();
567}
568
569/*
570template<class S, class T>
571int RunDim(Configuration &conf)
572{
573 WindowLog wout;
574
575 ReadlineColor::PrintBootMsg(wout, conf.GetName(), false);
576
577
578 if (conf.Has("log"))
579 if (!wout.OpenLogFile(conf.Get<string>("log")))
580 wout << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
581
582 // Start io_service.Run to use the StateMachineImp::Run() loop
583 // Start io_service.run to only use the commandHandler command detaching
584 StateMachineBias<S, T> io_service(wout);
585 if (!io_service.EvalConfiguration(conf))
586 return -1;
587
588 io_service.Run();
589
590 return 0;
591}
592*/
593
594template<class T, class S, class R>
595int RunShell(Configuration &conf)
596{
597 static T shell(conf.GetName().c_str(), conf.Get<int>("console")!=1);
598
599 WindowLog &win = shell.GetStreamIn();
600 WindowLog &wout = shell.GetStreamOut();
601
602 if (conf.Has("log"))
603 if (!wout.OpenLogFile(conf.Get<string>("log")))
604 win << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
605
606 StateMachineBias<S, R> io_service(wout);
607 if (!io_service.EvalConfiguration(conf))
608 return -1;
609
610 shell.SetReceiver(io_service);
611
612 boost::thread t(boost::bind(RunThread, &io_service));
613 // boost::thread t(boost::bind(&StateMachineBias<S>::Run, &io_service));
614
615 if (conf.Has("cmd"))
616 {
617 const vector<string> v = conf.Get<vector<string>>("cmd");
618 for (vector<string>::const_iterator it=v.begin(); it!=v.end(); it++)
619 shell.ProcessLine(*it);
620 }
621
622 if (conf.Has("exec"))
623 {
624 const vector<string> v = conf.Get<vector<string>>("exec");
625 for (vector<string>::const_iterator it=v.begin(); it!=v.end(); it++)
626 shell.Execute(*it);
627 }
628
629 if (conf.Get<bool>("quit"))
630 shell.Stop();
631
632 shell.Run(); // Run the shell
633 io_service.Stop(); // Signal Loop-thread to stop
634 // io_service.Close(); // Obsolete, done by the destructor
635
636 // Wait until the StateMachine has finished its thread
637 // before returning and destroying the dim objects which might
638 // still be in use.
639 t.join();
640
641 return 0;
642}
643
644void SetupConfiguration(Configuration &conf)
645{
646 const string n = conf.GetName()+".log";
647
648 po::options_description config("Program options");
649 config.add_options()
650 ("dns", var<string>("localhost"), "Dim nameserver host name (Overwites DIM_DNS_NODE environment variable)")
651 ("log,l", var<string>(n), "Write log-file")
652 ("no-dim,d", po_bool(), "Disable dim services")
653 ("console,c", var<int>(), "Use console (0=shell, 1=simple buffered, X=simple unbuffered)")
654 ("cmd", vars<string>(), "Execute one or more commands at startup")
655 ("exec,e", vars<string>(), "Execute one or more scrips at startup")
656 ("quit", po_switch(), "Quit after startup");
657 ;
658
659 po::options_description control("FTM control options");
660 control.add_options()
661 ("addr,a", var<string>("ttysS0"), "Device address of USB port to bias-power supply")
662 ("quiet,q", po_bool(), "Disable printing contents of all received messages (except dynamic data) in clear text.")
663 ;
664
665 conf.AddEnv("dns", "DIM_DNS_NODE");
666
667 conf.AddOptions(config);
668 conf.AddOptions(control);
669}
670
671/*
672 Extract usage clause(s) [if any] for SYNOPSIS.
673 Translators: "Usage" and "or" here are patterns (regular expressions) which
674 are used to match the usage synopsis in program output. An example from cp
675 (GNU coreutils) which contains both strings:
676 Usage: cp [OPTION]... [-T] SOURCE DEST
677 or: cp [OPTION]... SOURCE... DIRECTORY
678 or: cp [OPTION]... -t DIRECTORY SOURCE...
679 */
680void PrintUsage()
681{
682 cout <<
683 "The biasctrl controls the bias-power supply boards.\n"
684 "\n"
685 "The default is that the program is started without user intercation. "
686 "All actions are supposed to arrive as DimCommands. Using the -c "
687 "option, a local shell can be initialized. With h or help a short "
688 "help message about the usuage can be brought to the screen.\n"
689 "\n"
690 "Usage: biasctrl [-c type] [OPTIONS]\n"
691 " or: biasctrl [OPTIONS]\n";
692 cout << endl;
693}
694
695void PrintHelp()
696{
697 /* Additional help text which is printed after the configuration
698 options goes here */
699
700 /*
701 cout << "bla bla bla" << endl << endl;
702 cout << endl;
703 cout << "Environment:" << endl;
704 cout << "environment" << endl;
705 cout << endl;
706 cout << "Examples:" << endl;
707 cout << "test exam" << endl;
708 cout << endl;
709 cout << "Files:" << endl;
710 cout << "files" << endl;
711 cout << endl;
712 */
713}
714
715int main(int argc, const char* argv[])
716{
717 Configuration conf(argv[0]);
718 conf.SetPrintUsage(PrintUsage);
719 SetupConfiguration(conf);
720
721 po::variables_map vm;
722 try
723 {
724 vm = conf.Parse(argc, argv);
725 }
726#if BOOST_VERSION > 104000
727 catch (po::multiple_occurrences &e)
728 {
729 cerr << "Program options invalid due to: " << e.what() << " of '" << e.get_option_name() << "'." << endl;
730 return -1;
731 }
732#endif
733 catch (exception& e)
734 {
735 cerr << "Program options invalid due to: " << e.what() << endl;
736 return -1;
737 }
738
739 if (conf.HasVersion() || conf.HasPrint())
740 return -1;
741
742 if (conf.HasHelp())
743 {
744 PrintHelp();
745 return -1;
746 }
747
748 Dim::Setup(conf.Get<string>("dns"));
749
750 //try
751 {
752 // No console access at all
753 if (!conf.Has("console"))
754 {
755 if (conf.Get<bool>("no-dim"))
756 return RunShell<LocalStream, StateMachine, ConnectionBias>(conf);
757 else
758 return RunShell<LocalStream, StateMachineDim, ConnectionDimBias>(conf);
759 }
760 // Cosole access w/ and w/o Dim
761 if (conf.Get<bool>("no-dim"))
762 {
763 if (conf.Get<int>("console")==0)
764 return RunShell<LocalShell, StateMachine, ConnectionBias>(conf);
765 else
766 return RunShell<LocalConsole, StateMachine, ConnectionBias>(conf);
767 }
768 else
769 {
770 if (conf.Get<int>("console")==0)
771 return RunShell<LocalShell, StateMachineDim, ConnectionDimBias>(conf);
772 else
773 return RunShell<LocalConsole, StateMachineDim, ConnectionDimBias>(conf);
774 }
775 }
776 /*catch (std::exception& e)
777 {
778 cerr << "Exception: " << e.what() << endl;
779 return -1;
780 }*/
781
782 return 0;
783}
Note: See TracBrowser for help on using the repository browser.