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

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