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

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