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

Last change on this file since 19125 was 18964, checked in by tbretz, 7 years ago
Moved path to Makefile.
File size: 74.0 KB
Line 
1#include <functional>
2
3#include <boost/bind.hpp>
4
5#include "Dim.h"
6#include "Event.h"
7#include "Shell.h"
8#include "StateMachineDim.h"
9#include "StateMachineAsio.h"
10#include "ConnectionUSB.h"
11#include "Configuration.h"
12#include "Console.h"
13#include "PixelMap.h"
14
15#include "tools.h"
16
17#include "LocalControl.h"
18#include "HeadersBIAS.h"
19
20namespace ba = boost::asio;
21namespace bs = boost::system;
22namespace dummy = ba::placeholders;
23
24using namespace std::placeholders;
25using namespace std;
26
27// We can do that because we do not include other headers than HeadersBIAS
28using namespace BIAS;
29
30// ------------------------------------------------------------------------
31
32class ConnectionBias : public ConnectionUSB
33{
34 boost::asio::deadline_timer fSyncTimer;
35 boost::asio::deadline_timer fRampTimer;
36 boost::asio::deadline_timer fUpdateTimer;
37
38 vector<uint8_t> fBuffer;
39 vector<uint8_t> fBufferRamp;
40 vector<uint8_t> fBufferUpdate;
41
42 bool fIsVerbose;
43 bool fIsDummyMode;
44
45 vector<bool> fPresent;
46
47 int64_t fWrapCounter;
48 int64_t fSendCounter;
49
50 int16_t fGlobalDacCmd; // Command value to be reached
51
52 int16_t fRampStep;
53 int16_t fRampTime;
54
55 uint32_t fUpdateTime;
56 uint16_t fSyncTime;
57 uint32_t fReconnectDelay;
58
59 int fIsInitializing;
60 bool fIsRamping;
61 int fWaitingForAnswer;
62
63 vector<uint64_t> fCounter;
64
65 Time fLastConnect;
66
67 int32_t fEmergencyLimit;
68 bool fEmergencyShutdown;
69
70protected:
71
72 vector<int16_t> fCurrent; // Current in ADC units (12bit = 5mA)
73
74 virtual void UpdateV(const Time = Time())
75 {
76 }
77
78 virtual void UpdateVgapd()
79 {
80 }
81
82public:
83 virtual void UpdateVA()
84 {
85 }
86
87 // ====================================================
88
89protected:
90 vector<float> fOperationVoltage; // Operation voltage of GAPDs
91 //vector<float> fChannelOffset; // User defined channel offset
92
93 vector<float> fCalibrationOffset; // Bias crate channel offset
94 vector<float> fCalibrationSlope; // Bias crate channel slope
95
96 float fVoltageMaxAbs; // Maximum voltage
97 float fVoltageMaxRel; // Maximum voltgage above (what?)
98
99 vector<uint16_t> fDacTarget; // Target values
100 vector<uint16_t> fDacCommand; // Last sent command value
101 vector<uint16_t> fDacActual; // Actual value
102
103 // ====================================================
104
105private:
106 vector<char> GetCmd(uint16_t board, uint16_t channel, Command_t cmd, uint16_t dac=0)
107 {
108 vector<char> data(3);
109
110 /*
111 if (board>kNumBoards)
112 return;
113 if (channel>kNumChannelsPerBoard)
114 return;
115 if (dac>0xfff)
116 return;
117 */
118
119 data[0] = (cmd<<5) | (board<<1) | (((channel&16)>>4) & 1);
120 data[1] = (channel<<4) | (dac>>8);
121 data[2] = dac&0xff;
122
123 return data;
124 }
125
126 vector<char> GetCmd(Command_t cmd, uint16_t id=0, uint16_t dac=0)
127 {
128 const unsigned int board = id/kNumChannelsPerBoard;
129 const unsigned int channel = id%kNumChannelsPerBoard;
130
131 return GetCmd(board, channel, cmd, dac);
132 }
133
134 bool CheckMessageLength(int received, int expected, const string &msg)
135 {
136 if (received==expected)
137 return true;
138
139 ostringstream str;
140 str << msg << ": Expected " << expected << " bytes in answer, but got " << received << endl;
141 Error(str);
142
143 return false;
144 }
145
146 bool EvalAnswer(const uint8_t *answer, uint16_t id, int command)
147 {
148 answer += id*3;
149
150 const uint16_t status = (answer[0]>>7)&1;
151 const uint16_t wrap = (answer[0]>>4)&7;
152 const uint16_t ddd = ((uint16_t(answer[0])&0xf)<<8) | answer[1];
153 const uint16_t error = (answer[2]>>4)&0xf;
154 const uint16_t board = answer[2]&0xf;
155
156 // 0x10 00 7f
157 // status = 0
158 // wrap = 1
159 // ddd = 0
160 // error = not present
161 // board = 15
162
163 /*
164 Out() << dec << setw(2) << board << '|' << wrap << " ";
165 if (id%8==7)
166 Out() << endl;
167 */
168
169 if (fWrapCounter>=0)
170 {
171 if ((fWrapCounter+1)%8 != wrap)
172 {
173 ostringstream msg;
174 msg << "Corrupted answer (id=" << id << "): received wrap counter " << wrap << " doesn't match last one " << fWrapCounter << " ";
175 msg << " (fSendCounter=" << fSendCounter << ")";
176 Error(msg);
177 return false;
178 }
179 }
180
181 fWrapCounter = wrap;
182
183 if (command==kSynchronize)
184 {
185 ostringstream msg;
186 msg << hex << setfill('0');
187 msg << "Initial answer received: 0x";
188 msg << setw(2) << (int)answer[2];
189 msg << setw(2) << (int)answer[1];
190 msg << setw(2) << (int)answer[0];
191 Message(msg);
192
193 if (status!=0 || ddd!=0 || error!=0 || board!=0)
194 {
195 Warn("Initial answer doesn't seem to be a reset as naively expected.");
196
197 //ostringstream msg;
198 //msg << hex << setfill('0');
199 //msg << "S=" << status << " D=" << ddd << " E=" << error << " B=" << board;
200 //Message(msg);
201 }
202
203 fSendCounter = wrap;
204
205 msg.str("");
206 msg << "Setting fSendCounter to " << wrap;
207 Info(msg);
208
209 return true;
210 }
211
212 if (error==0x8) // No device
213 {
214 Message("Reset button on crate pressed!");
215 RampAllDacs(0);
216 return true;
217 }
218
219 if (command==kCmdReset)
220 {
221 if (status==0 && ddd==0 && error==0 && board==0)
222 {
223 Message("Reset successfully executed.");
224 return true;
225 }
226
227 Warn("Answer to 'reset' command contains unexpected data.");
228 return false;
229 }
230
231 if (command==kCmdGlobalSet)
232 {
233 if (status==0 && ddd==0 && error==0 && board==0)
234 {
235 for (int i=0; i<kNumChannels; i++)
236 fDacActual[i] = fGlobalDacCmd;
237
238 fGlobalDacCmd = -1;
239
240 return true;
241 }
242
243 Warn("Answer to 'global set' command contains unexpected data.");
244 return false;
245 }
246
247 if ((command&0xff)==kExpertChannelSet)
248 id = command>>8;
249
250 const int cmd = command&3;
251
252 if (cmd==kCmdRead || cmd==kCmdChannelSet)
253 {
254 if (board!=id/kNumChannelsPerBoard)
255 {
256 ostringstream out;
257 out << "Talked to board " << id/kNumChannelsPerBoard << ", but got answer from board " << board << " (fSendCounter=" << fSendCounter << ")";
258 Error(out);
259 return false;
260 }
261
262 // Not present
263 if (error==0x7 || error==0xf)
264 {
265 fPresent[board] = false;
266 fCurrent[id] = 0x8000;
267 return true;
268 }
269
270 // There is no -0 therefore we make a trick and replace it by -1.
271 // This is not harmfull, because typical zero currents are in the
272 // order of one to three bits anyway and they are never stable.
273 fCurrent[id] = status ? -(ddd==0?1:ddd) : ddd;
274 fPresent[board] = true;
275
276 if (!fEmergencyShutdown)
277 {
278 if (fCurrent[id]<0 && id!=91)
279 {
280 Warn("OverCurrent detected (first ch="+to_string(id)+").");
281 fEmergencyShutdown = true;
282 }
283
284 if (fEmergencyLimit>0 && fCurrent[id]>fEmergencyLimit && !fEmergencyShutdown)
285 {
286 Warn("Emergency limit exceeded (first ch="+to_string(id)+").");
287 fEmergencyShutdown = true;
288 }
289
290 if (fEmergencyShutdown)
291 {
292 Error("Emergency ramp down initiated.");
293 Dim::SendCommandNB("MCP/STOP");
294 RampAllDacs(0);
295 }
296 }
297 }
298
299 if (cmd==kCmdChannelSet)
300 fDacActual[id] = fDacCommand[id];
301
302 return true;
303
304 }
305
306private:
307 void DelayedReconnect()
308 {
309 const Time now;
310
311 // If we have been connected without a diconnect for at least 60s
312 // we can reset the delay.
313 if (now-fLastConnect>boost::posix_time::seconds(60))
314 fReconnectDelay = 1;
315
316 ostringstream msg;
317 msg << "Automatic reconnect in " << fReconnectDelay << "s after being connected for ";
318 msg << (now-fLastConnect).seconds() << "s";
319 Info(msg);
320
321 CloseImp(fReconnectDelay);
322 fReconnectDelay *= 2;
323 }
324
325 void HandleReceivedData(const vector<uint8_t> &buf, size_t bytes_received, int command, int send_counter)
326 {
327#ifdef DEBUG
328 ofstream fout("received.txt", ios::app);
329 fout << Time() << ": ";
330 for (unsigned int i=0; i<bytes_received; i++)
331 fout << hex << setfill('0') << setw(2) << (uint16_t)buf[i];
332 fout << endl;
333#endif
334
335 // Now print the received message if requested by the user
336 if (fIsVerbose/* && command!=kUpdate*/)
337 {
338 Out() << endl << kBold << dec << "Data received (size=" << bytes_received << "):" << endl;
339 Out() << " Command=" << command << " fWrapCounter=" << fWrapCounter << " fSendCounter=" << fSendCounter << " fIsInitializing=" << fIsInitializing << " fIsRamping=" << fIsRamping;
340 Out() << hex << setfill('0');
341
342 for (size_t i=0; i<bytes_received/3; i++)
343 {
344 if (i%8==0)
345 Out() << '\n' << setw(2) << bytes_received/24 << "| ";
346
347 Out() << setw(2) << uint16_t(buf[i*3+2]);
348 Out() << setw(2) << uint16_t(buf[i*3+1]);
349 Out() << setw(2) << uint16_t(buf[i*3+0]) << " ";
350 }
351 Out() << endl;
352 }
353
354 const int cmd = command&0xf;
355
356 // Check the number of received_byted according to the answer expected
357 if ((cmd==kSynchronize && !CheckMessageLength(bytes_received, 3, "Synchronization")) ||
358 (cmd==kCmdReset && !CheckMessageLength(bytes_received, 3, "CmdReset")) ||
359 (cmd==kCmdRead && !CheckMessageLength(bytes_received, 3*kNumChannels, "CmdRead")) ||
360 (cmd==kCmdChannelSet && !CheckMessageLength(bytes_received, 3*kNumChannels, "CmdChannelSet")) ||
361 (cmd==kExpertChannelSet && !CheckMessageLength(bytes_received, 3, "CmdExpertChannelSet")))
362 {
363 CloseImp(-1);
364 return;
365 }
366
367 // Now evaluate the whole bunch of messages
368 for (size_t i=0; i<bytes_received/3; i++)
369 {
370 if (!EvalAnswer(buf.data(), i, command))
371 {
372 DelayedReconnect();
373 return;
374 }
375 }
376
377 if (command==kSynchronize)
378 {
379 Message("Stream successfully synchronized.");
380 fIsInitializing = 2;
381
382 // Cancel sending of the next 0
383 fSyncTimer.cancel();
384 fCounter[0]++;
385
386 // Start continous reading of all channels
387 ScheduleUpdate(100);
388 return;
389 }
390
391 if (send_counter%8 != fWrapCounter)
392 {
393 ostringstream msg;
394 msg << "Corrupted answer: received wrap counter " << fWrapCounter << " is not send counter " << send_counter << "%8.";
395 Error(msg);
396
397 DelayedReconnect();
398 }
399
400
401 // Check if new values have been received
402 if (cmd==kCmdRead || cmd==kCmdChannelSet || cmd==kExpertChannelSet)
403 UpdateVA();
404
405 // ----- Take action depending on what is going on -----
406
407 if (command==kCmdReset)
408 {
409 Message("Reset command successfully answered...");
410
411 fCounter[1]++;
412
413 // Re-start cyclic reading of values after a short time
414 // to allow the currents to become stable. This ensures that
415 // we get an update soon but wait long enough to get reasonable
416 // values
417 fUpdateTimer.cancel();
418
419 if (fUpdateTime==0)
420 ReadAllChannels(true);
421 else
422 {
423 Message("...restarting automatic readout.");
424 ScheduleUpdate(100);
425 }
426 }
427
428 if (command==kResetChannels)
429 {
430 ExpertReset(false);
431 fCounter[5]++;
432 }
433
434 if (command==kUpdate)
435 {
436 ScheduleUpdate(fUpdateTime);
437 fCounter[2]++;
438 }
439
440 // If we are ramping, schedule a new ramp step
441 if (command==kCmdChannelSet && fIsRamping)
442 {
443 bool oc = false;
444 for (int ch=0; ch<kNumChannels; ch++)
445 if (fPresent[ch/kNumChannelsPerBoard] && fCurrent[ch]<0 && ch!=91)
446 oc = true;
447
448 if (oc)
449 {
450 if (!fEmergencyShutdown)
451 {
452 Warn("OverCurrent detected - emergency ramp down initiated.");
453 Dim::SendCommandNB("MCP/STOP");
454 RampAllDacs(0);
455 fEmergencyShutdown = true;
456 }
457 }
458 else
459 ScheduleRampStep();
460
461 fCounter[3]++;
462 }
463
464 if (command==kCmdRead)
465 fCounter[4]++;
466
467 if ((command&0xff)==kExpertChannelSet)
468 fCounter[6]++;
469
470 if (command==kCmdGlobalSet)
471 fCounter[7]++;
472 }
473
474 void HandleReceivedData(const bs::error_code& err, size_t bytes_received, int command, int send_counter)
475 {
476 // Do not schedule a new read if the connection failed.
477 if (bytes_received==0 || err)
478 {
479 if (err==ba::error::eof)
480 {
481 ostringstream msg;
482 msg << "Connection closed by remote host (BIAS, fSendCounter=" << fSendCounter << ")";
483 Warn(msg);
484 }
485
486 // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category))
487 // 125: Operation canceled
488 if (err && err!=ba::error::eof && // Connection closed by remote host
489 err!=ba::error::basic_errors::not_connected && // Connection closed by remote host
490 err!=ba::error::basic_errors::operation_aborted) // Connection closed by us
491 {
492 ostringstream str;
493 str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl;
494 Error(str);
495 }
496 CloseImp(-1);//err!=ba::error::basic_errors::operation_aborted);
497 return;
498 }
499
500 // Check if the number of received bytes is correctly dividable by 3
501 // This check should never fail - just for sanity
502 if (bytes_received%3)
503 {
504 Error("Number of received bytes not a multiple of 3, can't read data.");
505 CloseImp(-1);
506 return;
507 }
508
509 // We have three different parallel streams:
510 // 1) The setting of voltages due to ramping
511 // 2) The cynclic request of the currents
512 // 3) Answers to commands
513 // For each of these three streams an own buffer is needed, otherwise
514 // a buffer which is filled in the background might overwrite
515 // a buffer which is currently evaluated. In all other programs
516 // this is no problem because the boards don't answer and if
517 // they do the answer identifies itself. Consequently,
518 // there is always only one async_read in progress. Here we have
519 // three streams which need to be connected somehow to the
520 // commands.
521
522 // Maybe a better possibility would be to setup a command
523 // queue (each command will be queued in a buffer)
524 // and whenever an answer has been received, a new async_read is
525 // scheduled.
526 // Build a command queue<pair<command, vector<char>>>
527 /// This replaces the send counter and the command argument
528 // in handleReceivedData
529
530 switch (command&0xff)
531 {
532 case kSynchronize:
533 case kCmdReset:
534 case kExpertChannelSet:
535 case kCmdGlobalSet:
536 case kResetChannels:
537 case kCmdRead:
538 HandleReceivedData(fBuffer, bytes_received, command, send_counter);
539 fWaitingForAnswer = -1;
540 return;
541
542 case kCmdChannelSet:
543 HandleReceivedData(fBufferRamp, bytes_received, command, send_counter);
544 return;
545
546 case kUpdate:
547 HandleReceivedData(fBufferUpdate, bytes_received, command, send_counter);
548 return;
549 }
550 }
551
552 // --------------------------------------------------------------------
553
554 void HandleSyncTimer(int counter, const bs::error_code &error)
555 {
556 if (error==ba::error::basic_errors::operation_aborted)
557 {
558 if (fIsInitializing==1)
559 Warn("Synchronization aborted...");
560 // case 0 and 2 should not happen
561 return;
562 }
563
564 if (error)
565 {
566 ostringstream str;
567 str << "Synchronization timer: " << error.message() << " (" << error << ")";// << endl;
568 Error(str);
569
570 CloseImp(-1);
571 return;
572 }
573
574 if (!is_open())
575 {
576 Warn("Synchronization in progress, but disconnected.");
577 return;
578 }
579
580 ostringstream msg;
581 msg << "Synchronization time expired (" << counter << ")";
582 Info(msg);
583
584 if (fIsInitializing)
585 {
586 PostMessage("\0", 1);
587
588 if (counter==2)
589 {
590 Error("Synchronization attempt timed out.");
591 CloseImp(-1);
592 return;
593 }
594
595 ScheduleSync(counter+1);
596 return;
597 }
598
599 Info("Synchronisation successfull.");
600 }
601
602 void ScheduleSync(int counter=0)
603 {
604 fSyncTimer.expires_from_now(boost::posix_time::milliseconds(fSyncTime));
605 fSyncTimer.async_wait(boost::bind(&ConnectionBias::HandleSyncTimer, this, counter, dummy::error));
606 }
607
608 // This is called when a connection was established
609 void ConnectionEstablished()
610 {
611 // We connect for the first time or haven't received
612 // a valid warp counter yet... this procedure also sets
613 // our volatges to 0 if we have connected but never received
614 // any answer.
615 if (fWrapCounter<0)
616 {
617 fDacTarget.assign(kNumChannels, 0);
618 fDacCommand.assign(kNumChannels, 0);
619 fDacActual.assign(kNumChannels, 0);
620 }
621
622 // Reset everything....
623 fSendCounter = -1;
624 fWrapCounter = -1;
625 fGlobalDacCmd = -1;
626 fIsInitializing = 1;
627 fIsRamping = false;
628
629 fLastConnect = Time();
630
631 // Send a single 0 (and possible two consecutive 0's
632 // to make sure we are in sync with the device)
633 PostMessage("\0", 1);
634 AsyncRead(ba::buffer(fBuffer, 3), kSynchronize, 0);//++fSendCounter);
635 fWaitingForAnswer = kSynchronize;
636
637 // Wait for some time before sending the next 0
638 ScheduleSync();
639 }
640
641 // --------------------------------------------------------------------
642
643 void HandleUpdateTimer(const bs::error_code &error)
644 {
645 if (error==ba::error::basic_errors::operation_aborted)
646 {
647 Warn("Update timer aborted...");
648 fIsRamping = false;
649 return;
650 }
651
652 if (error)
653 {
654 ostringstream str;
655 str << "Update timer: " << error.message() << " (" << error << ")";// << endl;
656 Error(str);
657
658 CloseImp(-1);
659 return;
660 }
661
662 if (!is_open())
663 return;
664
665 if (fUpdateTime==0 && fIsInitializing!=2)
666 return;
667
668 if (fIsRamping)
669 ScheduleUpdate(fUpdateTime);
670 else
671 ReadAllChannels(true);
672
673 fIsInitializing = 0;
674 }
675
676 void ScheduleUpdate(int millisec)
677 {
678 fUpdateTimer.expires_from_now(boost::posix_time::milliseconds(millisec));
679 fUpdateTimer.async_wait(boost::bind(&ConnectionBias::HandleUpdateTimer, this, dummy::error));
680 }
681
682 // --------------------------------------------------------------------
683
684 void PrintLineCmdDac(int b, int ch, const vector<uint16_t> &dac)
685 {
686 Out() << setw(2) << b << "|";
687
688 for (int c=ch; c<ch+4; c++)
689 {
690 const int id = c+kNumChannelsPerBoard*b;
691 Out() << " " << setw(4) << int32_t(dac[id])<<"/"<<fDacActual[id] << ":" << setw(5) << ConvertDacToVolt(id, fDacTarget[id]);
692 }
693 Out() << endl;
694 }
695
696 void PrintCommandDac(const vector<uint16_t> &dac)
697 {
698 Out() << dec << setprecision(2) << fixed << setfill(' ');
699 for (int b=0; b<kNumBoards; b++)
700 {
701 if (!fPresent[b])
702 {
703 Out() << setw(2) << b << "-" << endl;
704 continue;
705 }
706
707 PrintLineCmdDac(b, 0, dac);
708 PrintLineCmdDac(b, 4, dac);
709 PrintLineCmdDac(b, 8, dac);
710 PrintLineCmdDac(b, 12, dac);
711 PrintLineCmdDac(b, 16, dac);
712 PrintLineCmdDac(b, 20, dac);
713 PrintLineCmdDac(b, 24, dac);
714 PrintLineCmdDac(b, 28, dac);
715 }
716 }
717
718 void SetAllChannels(const vector<uint16_t> &dac, bool special=false)
719 {
720 if (fIsDummyMode)
721 {
722 PrintCommandDac(dac);
723 return;
724 }
725
726 vector<char> data;
727 data.reserve(kNumChannels*3);
728
729 for (int ch=0; ch<kNumChannels; ch++)
730 {
731 // FIXME: dac[ch] += calib_offset
732 const vector<char> cmd = GetCmd(kCmdChannelSet, ch, dac[ch]);
733 data.insert(data.end(), cmd.begin(), cmd.end());
734
735 fDacCommand[ch] = dac[ch];
736 }
737
738 fSendCounter += kNumChannels;
739
740 PostMessage(data);
741 AsyncRead(ba::buffer(special ? fBuffer : fBufferRamp, kNumChannels*3),
742 special ? kResetChannels : kCmdChannelSet, fSendCounter);
743
744 if (special)
745 fWaitingForAnswer = kResetChannels;
746 }
747
748 uint16_t RampOneStep(uint16_t ch)
749 {
750 if (fDacTarget[ch]>fDacActual[ch])
751 return fDacActual[ch]+fRampStep>fDacTarget[ch] ? fDacTarget[ch] : fDacActual[ch]+fRampStep;
752
753 if (fDacTarget[ch]<fDacActual[ch])
754 return fDacActual[ch]-fRampStep<fDacTarget[ch] ? fDacTarget[ch] : fDacActual[ch]-fRampStep;
755
756 return fDacActual[ch];
757 }
758
759 bool RampOneStep()
760 {
761 if (fRampTime<0)
762 {
763 Warn("Ramping step time not yet set... ramping not started.");
764 return false;
765 }
766 if (fRampStep<0)
767 {
768 Warn("Ramping step not yet set... ramping not started.");
769 return false;
770 }
771
772 vector<uint16_t> dac(kNumChannels);
773
774 bool identical = true;
775 for (int ch=0; ch<kNumChannels; ch++)
776 {
777 dac[ch] = RampOneStep(ch);
778 if (dac[ch]!=fDacActual[ch] && fPresent[ch/kNumChannelsPerBoard])
779 identical = false;
780 }
781
782 if (identical)
783 {
784 Info("Ramping: target values reached.");
785 return false;
786 }
787
788 if (fWaitingForAnswer<0)
789 {
790 SetAllChannels(dac);
791 return true;
792 }
793
794 ostringstream msg;
795 msg << "RampOneStep while waiting for answer to last command (id=" << fWaitingForAnswer << ")... ramp step delayed.";
796 Warn(msg);
797
798 // Delay ramping
799 ScheduleRampStep();
800 return true;
801 }
802
803 void HandleRampTimer(const bs::error_code &error)
804 {
805 if (error==ba::error::basic_errors::operation_aborted)
806 {
807 Warn("Ramping aborted...");
808 fIsRamping = false;
809 return;
810 }
811
812 if (error)
813 {
814 ostringstream str;
815 str << "Ramping timer: " << error.message() << " (" << error << ")";// << endl;
816 Error(str);
817
818 fIsRamping = false;
819 CloseImp(-1);
820 return;
821 }
822
823 if (!is_open())
824 {
825 Warn("Ramping in progress, but disconnected.");
826 fIsRamping = false;
827 return;
828 }
829
830 if (!fIsRamping)
831 {
832 Error("Ramp handler called although no ramping in progress.");
833 return;
834 }
835
836 // Check whether the deadline has passed. We compare the deadline
837 // against the current time since a new asynchronous operation
838 // may have moved the deadline before this actor had a chance
839 // to run.
840 if (fRampTimer.expires_at() > ba::deadline_timer::traits_type::now())
841 return;
842
843 fIsRamping = RampOneStep();
844 }
845
846 void ScheduleRampStep()
847 {
848 fRampTimer.expires_from_now(boost::posix_time::milliseconds(fRampTime));
849 fRampTimer.async_wait(boost::bind(&ConnectionBias::HandleRampTimer, this, dummy::error));
850 }
851
852public:
853 ConnectionBias(ba::io_service& ioservice, MessageImp &imp) : ConnectionUSB(ioservice, imp()),
854 fSyncTimer(ioservice),
855 fRampTimer(ioservice),
856 fUpdateTimer(ioservice),
857 fBuffer(3*kNumChannels),
858 fBufferRamp(3*kNumChannels),
859 fBufferUpdate(3*kNumChannels),
860 fIsVerbose(false),
861 fIsDummyMode(false),
862 fPresent(kNumBoards),
863 fWrapCounter(-1),
864 fRampStep(-1),
865 fRampTime(-1),
866 fUpdateTime(3000),
867 fSyncTime(333),
868 fReconnectDelay(1),
869 fIsRamping(false),
870 fWaitingForAnswer(-1),
871 fCounter(8),
872 fEmergencyLimit(0),
873 fEmergencyShutdown(false),
874 fCurrent(kNumChannels),
875 fOperationVoltage(kNumChannels, 0),
876 //fChannelOffset(kNumChannels),
877 fCalibrationOffset(kNumChannels),
878 fCalibrationSlope(kNumChannels, 90000),
879 fVoltageMaxAbs(75),
880 fVoltageMaxRel(2),
881 fDacTarget(kNumChannels),
882 fDacCommand(kNumChannels),
883 fDacActual(kNumChannels)
884 {
885 SetLogStream(&imp);
886 }
887
888 // --------------------------------------------------------------------
889
890 bool CheckDac(uint16_t dac)
891 {
892 if (dac<4096)
893 return true;
894
895 ostringstream msg;
896 msg << "CheckDac - Dac value of " << dac << " exceeds maximum of 4095.";
897 Error(msg);
898 return false;
899 }
900
901 bool CheckChannel(uint16_t ch)
902 {
903 if (ch<kNumChannels)
904 return true;
905
906 ostringstream msg;
907 msg << "CheckChannel - Channel " << ch << " out of range [0;" << kNumChannels-1 << "].";
908 Error(msg);
909 return false;
910 }
911
912 bool CheckChannelVoltage(uint16_t ch, float volt)
913 {
914 if (volt>fVoltageMaxAbs)
915 {
916 ostringstream msg;
917 msg << "CheckChannelVoltage - Set voltage " << volt << "V of channel " << ch << " exceeds absolute limit of " << fVoltageMaxAbs << "V.";
918 Warn(msg);
919 return false;
920 }
921
922 if (fOperationVoltage[ch]<=0)
923 return true;
924
925 if (volt>fOperationVoltage[ch]+fVoltageMaxRel) // FIXME: fVoltageMaxRel!!!
926 {
927 ostringstream msg;
928 msg << "CheckChannelVoltage - Set voltage " << volt << "V of channel " << ch << " exceeds limit of " << fVoltageMaxRel << "V above operation voltage " << fOperationVoltage[ch] << "V + limit " << fVoltageMaxRel << "V.";
929 Error(msg);
930 return false;
931 }
932
933 return true;
934 }
935
936 // --------------------------------------------------------------------
937
938 bool RampSingleChannelDac(uint16_t ch, uint16_t dac)
939 {
940 if (!CheckChannel(ch))
941 return false;
942
943 if (!CheckDac(dac))
944 return false;
945
946 fDacTarget[ch] = dac;
947 UpdateV();
948
949 if (!fIsRamping)
950 fIsRamping = RampOneStep();
951
952 return true;
953 }
954
955 bool RampAllChannelsDac(const vector<uint16_t> &dac)
956 {
957 for (int ch=0; ch<kNumChannels; ch++)
958 if (!CheckDac(dac[ch]))
959 return false;
960
961 fDacTarget = dac;
962 UpdateV();
963
964 if (!fIsRamping)
965 fIsRamping = RampOneStep();
966
967 return true;
968 }
969
970 bool RampAllDacs(uint16_t dac)
971 {
972 return RampAllChannelsDac(vector<uint16_t>(kNumChannels, dac));
973 }
974
975 // --------------------------------------------------------------------
976
977 uint16_t ConvertVoltToDac(uint16_t ch, double volt)
978 {
979 if (fCalibrationSlope[ch]<=0)
980 return 0;
981
982 const double current = (volt-fCalibrationOffset[ch])/fCalibrationSlope[ch];
983 return current<0 ? 0 : nearbyint(current*4096000); // Current [A] to dac [ /= 1mA/4096]
984 }
985
986 double ConvertDacToVolt(uint16_t ch, uint16_t dac)
987 {
988 if (fCalibrationSlope[ch]<=0)
989 return 0;
990
991 const double current = dac/4096000.; // Convert dac to current [A] [ *= 1mA/4096]
992 return current*fCalibrationSlope[ch] + fCalibrationOffset[ch];
993 }
994
995 // --------------------------------------------------------------------
996
997 bool RampSingleChannelVoltage(uint16_t ch, float volt)
998 {
999 if (!CheckChannel(ch))
1000 return false;
1001
1002 if (!CheckChannelVoltage(ch, volt))
1003 return false;
1004
1005 const uint16_t dac = ConvertVoltToDac(ch, volt);
1006 return RampSingleChannelDac(ch, dac);
1007 }
1008
1009 bool RampAllChannelsVoltage(const vector<float> &volt)
1010 {
1011 vector<uint16_t> dac(kNumChannels);
1012 for (size_t ch=0; ch<kNumChannels; ch++)
1013 {
1014 if (!CheckChannelVoltage(ch, volt[ch]))
1015 return false;
1016
1017 dac[ch] = ConvertVoltToDac(ch, volt[ch]);
1018 }
1019
1020 return RampAllChannelsDac(dac);
1021 }
1022
1023 bool RampAllVoltages(float volt)
1024 {
1025 return RampAllChannelsVoltage(vector<float>(kNumChannels, volt));
1026 }
1027
1028 // --------------------------------------------------------------------
1029
1030 /*
1031 bool RampSingleChannelOffset(uint16_t ch, float offset, bool relative)
1032 {
1033 if (!CheckChannel(ch))
1034 return false;
1035
1036// if (relative)
1037// offset += fDacActual[ch]*90./4096 - fBreakdownVoltage[ch];
1038
1039 const float volt = fBreakdownVoltage[ch]>0 ? fBreakdownVoltage[ch] + offset : 0;
1040
1041 if (!RampSingleChannelVoltage(ch, volt))
1042 return false;
1043
1044 fChannelOffset[ch] = offset;
1045
1046 return true;
1047 }
1048
1049 bool RampAllChannelsOffset(vector<float> offset, bool relative)
1050 {
1051 vector<float> volt(kNumChannels);
1052
1053// if (relative)
1054// for (size_t ch=0; ch<kNumChannels; ch++)
1055// offset[ch] += fDacActual[ch]*90./4096 - fBreakdownVoltage[ch];
1056
1057 for (size_t ch=0; ch<kNumChannels; ch++)
1058 volt[ch] = fBreakdownVoltage[ch]>0 ? fBreakdownVoltage[ch] + offset[ch] : 0;
1059
1060 if (!RampAllChannelsVoltage(volt))
1061 return false;
1062
1063 fChannelOffset = offset;
1064
1065 return true;
1066 }
1067
1068 bool RampAllOffsets(float offset, bool relative)
1069 {
1070 return RampAllChannelsOffset(vector<float>(kNumChannels, offset), relative);
1071 }
1072 */
1073
1074 /*
1075 bool RampSingleChannelOvervoltage(float offset)
1076 {
1077 return RampAllChannelsOvervoltage(vector<float>(kNumChannels, offset));
1078 }
1079 bool RampAllOvervoltages(const vector<float> &overvoltage)
1080 {
1081 vector<float> volt(kNumChannels);
1082
1083 for (size_t ch=0; ch<kNumChannels; ch++)
1084 volt[ch] = fBreakdownVoltage[ch] + fOvervoltage[ch] + fChannelOffset[ch];
1085
1086#warning What about empty channels?
1087
1088 if (!RampAllChannelsVoltage(volt))
1089 return false;
1090
1091 for (size_t ch=0; ch<kNumChannels; ch++)
1092 fOvervoltage[ch] = overvoltage[ch];
1093
1094 return true;
1095 }*/
1096
1097 // --------------------------------------------------------------------
1098
1099 void OverCurrentReset()
1100 {
1101 if (fWaitingForAnswer>=0)
1102 {
1103 ostringstream msg;
1104 msg << "OverCurrentReset - Answer on last command (id=" << fWaitingForAnswer << ") not yet received.";
1105 Error(msg);
1106 return;
1107 }
1108
1109 if (fIsRamping)
1110 {
1111 Warn("OverCurrentReset - Ramping in progres.");
1112 RampStop();
1113 }
1114
1115 vector<uint16_t> dac(fDacActual);
1116
1117 for (int ch=0; ch<kNumChannels; ch++)
1118 if (fCurrent[ch]<0)
1119 dac[ch] = 0;
1120
1121 SetAllChannels(dac, true);
1122 }
1123
1124 void ReadAllChannels(bool special = false)
1125 {
1126 if (!special && fWaitingForAnswer>=0)
1127 {
1128 ostringstream msg;
1129 msg << "ReadAllChannels - Answer on last command (id=" << fWaitingForAnswer << ") not yet received.";
1130 Error(msg);
1131 return;
1132 }
1133
1134 vector<char> data;
1135 data.reserve(kNumChannels*3);
1136
1137 for (int ch=0; ch<kNumChannels; ch++)
1138 {
1139 const vector<char> cmd = GetCmd(kCmdRead, ch);
1140 data.insert(data.end(), cmd.begin(), cmd.end());
1141 }
1142
1143 fSendCounter += kNumChannels;
1144
1145 PostMessage(data);
1146 AsyncRead(ba::buffer(special ? fBufferUpdate : fBuffer, kNumChannels*3),
1147 special ? kUpdate : kCmdRead, fSendCounter);
1148
1149 if (!special)
1150 fWaitingForAnswer = kCmdRead;
1151 }
1152
1153 bool SetReferences(const vector<float> &volt, const vector<float> &offset, const vector<float> &slope)
1154 {
1155 if (volt.size()!=kNumChannels)
1156 {
1157 ostringstream out;
1158 out << "SetReferences - Given vector has " << volt.size() << " elements - expected " << kNumChannels << endl;
1159 Error(out);
1160 return false;
1161 }
1162 if (offset.size()!=kNumChannels)
1163 {
1164 ostringstream out;
1165 out << "SetReferences - Given vector has " << offset.size() << " elements - expected " << kNumChannels << endl;
1166 Error(out);
1167 return false;
1168 }
1169 if (slope.size()!=kNumChannels)
1170 {
1171 ostringstream out;
1172 out << "SetReferences - Given vector has " << slope.size() << " elements - expected " << kNumChannels << endl;
1173 Error(out);
1174 return false;
1175 }
1176
1177 fOperationVoltage = volt;
1178 fCalibrationOffset = offset;
1179 fCalibrationSlope = slope;
1180
1181 UpdateVgapd();
1182
1183 return true;
1184 }
1185
1186 // --------------------------------------------------------------------
1187
1188 void RampStop()
1189 {
1190 fRampTimer.cancel();
1191 fIsRamping = false;
1192
1193 Message("Ramping stopped.");
1194 }
1195
1196 void RampStart()
1197 {
1198 if (fIsRamping)
1199 {
1200 Warn("RampStart - Ramping already in progress... ignored.");
1201 return;
1202 }
1203
1204 fIsRamping = RampOneStep();
1205 }
1206
1207 void SetRampTime(uint16_t val)
1208 {
1209 fRampTime = val;
1210 }
1211
1212 void SetRampStep(uint16_t val)
1213 {
1214 fRampStep = val;
1215 }
1216
1217 uint16_t GetRampStepVolt() const
1218 {
1219 return fRampStep*90./4096;
1220 }
1221
1222 bool IsRamping() const { return fIsRamping; }
1223
1224 // -------------------------------------------------------------------
1225
1226 void ExpertReset(bool expert_mode=true)
1227 {
1228 if (expert_mode && fWaitingForAnswer>=0)
1229 {
1230 ostringstream msg;
1231 msg << "ExpertReset - Answer on last command (id=" << fWaitingForAnswer << ") not yet received.";
1232 Error(msg);
1233 return;
1234 }
1235
1236 if (expert_mode)
1237 Warn("EXPERT MODE: Sending reset.");
1238
1239 PostMessage(GetCmd(kCmdReset));
1240 AsyncRead(ba::buffer(fBuffer, 3), kCmdReset, ++fSendCounter);
1241 fWaitingForAnswer = kCmdReset;
1242 }
1243
1244
1245 bool ExpertChannelSetDac(uint16_t ch, uint16_t dac)
1246 {
1247 if (fWaitingForAnswer>=0)
1248 {
1249 ostringstream msg;
1250 msg << "ExpertChannelSetDac - Answer on last command (id=" << fWaitingForAnswer << ") not yet received.";
1251 Error(msg);
1252 return false;
1253 }
1254
1255 if (!CheckDac(dac))
1256 return false;
1257
1258 fDacCommand[ch] = dac;
1259
1260 ostringstream msg;
1261 msg << "EXPERT MODE: Sending 'ChannelSet' (set ch " << ch << " to DAC=" << dac << ")";
1262 Warn(msg);
1263
1264 // FIXME: dac += calib_offset
1265 PostMessage(GetCmd(kCmdChannelSet, ch, dac));
1266 AsyncRead(ba::buffer(fBuffer, 3), kExpertChannelSet|(ch<<8), ++fSendCounter);
1267 fWaitingForAnswer = kExpertChannelSet|(ch<<8);
1268
1269 return true;
1270 }
1271
1272 bool ExpertChannelSetVolt(uint16_t ch, double volt)
1273 {
1274 return ExpertChannelSetDac(ch, volt*4096/90.);
1275 }
1276
1277 bool ExpertGlobalSetDac(uint16_t dac)
1278 {
1279 if (fWaitingForAnswer>=0)
1280 {
1281 ostringstream msg;
1282 msg << "ExpertGlobalSetDac - Answer on last command (id=" << fWaitingForAnswer << ") not yet received.";
1283 Error(msg);
1284 return false;
1285 }
1286
1287 if (!CheckDac(dac))
1288 return false;
1289
1290 if (fGlobalDacCmd>=0)
1291 {
1292 Error("ExpertGlobalSetDac - Still waiting for previous answer to 'GlobalSet'");
1293 return false;
1294 }
1295
1296 fGlobalDacCmd = dac;
1297
1298 ostringstream msg;
1299 msg << "EXPERT MODE: Sending 'GlobalSet' (DAC=" << dac << ")";
1300 Warn(msg);
1301
1302 PostMessage(GetCmd(kCmdGlobalSet, 0, dac));
1303 AsyncRead(ba::buffer(fBuffer, 3), kCmdGlobalSet, ++fSendCounter);
1304 fWaitingForAnswer = kCmdGlobalSet;
1305
1306 return true;
1307 }
1308
1309 bool ExpertGlobalSetVolt(float volt)
1310 {
1311 return ExpertGlobalSetDac(volt*4096/90);
1312 }
1313
1314 // --------------------------------------------------------------------
1315
1316 void SetVerbose(bool b)
1317 {
1318 fIsVerbose = b;
1319 }
1320
1321 void SetDummyMode(bool b)
1322 {
1323 fIsDummyMode = b;
1324 }
1325
1326 void PrintInfo()
1327 {
1328 Out() << endl << kBold << dec << '\n';
1329 Out() << "fWrapCounter = " << fWrapCounter << '\n';
1330 Out() << "fSendCounter = " << fSendCounter%8 << " (" << fSendCounter << ")" << '\n';
1331 Out() << "fIsInitializing = " << fIsInitializing << '\n';
1332 Out() << "fIsRamping = " << fIsRamping << '\n';
1333 Out() << "Answer counter:" << '\n';
1334 Out() << " - Synchronization: " << fCounter[0] << '\n';
1335 Out() << " - Reset: " << fCounter[1] << '\n';
1336 Out() << " - Request update: " << fCounter[2] << '\n';
1337 Out() << " - Ramp step: " << fCounter[3] << '\n';
1338 Out() << " - Read: " << fCounter[4] << '\n';
1339 Out() << " - Reset channels: " << fCounter[5] << '\n';
1340 Out() << " - Global set: " << fCounter[7] << '\n';
1341 Out() << " - Channel set: " << fCounter[6] << '\n' << endl;
1342 }
1343
1344 void PrintLineA(int b, int ch)
1345 {
1346 Out() << setw(2) << b << "|";
1347
1348 for (int c=ch; c<ch+8; c++)
1349 {
1350 const int id = c+kNumChannelsPerBoard*b;
1351 Out() << (fCurrent[id]<0?kRed:kGreen);
1352 Out() << " " << setw(7) << abs(fCurrent[id])*5000/4096.;
1353 }
1354 Out() << endl;
1355
1356 }
1357
1358 void PrintA()
1359 {
1360 Out() << dec << setprecision(2) << fixed << setfill(' ');
1361 for (int b=0; b<kNumBoards; b++)
1362 {
1363 if (!fPresent[b])
1364 {
1365 Out() << setw(2) << b << "-" << endl;
1366 continue;
1367 }
1368
1369 PrintLineA(b, 0);
1370 PrintLineA(b, 8);
1371 PrintLineA(b, 16);
1372 PrintLineA(b, 24);
1373 }
1374 }
1375
1376 void PrintLineV(int b, int ch)
1377 {
1378 Out() << setw(2) << b << "|";
1379
1380 for (int c=ch; c<ch+4; c++)
1381 {
1382 const int id = c+kNumChannelsPerBoard*b;
1383 Out() << " ";
1384 Out() << (fDacActual[id]==fDacTarget[id]?kGreen:kRed);
1385 //Out() << setw(5) << fDacActual[id]*90/4096. << '/';
1386 //Out() << setw(5) << fDacTarget[id]*90/4096.;
1387
1388 Out() << setw(5) << ConvertDacToVolt(id, fDacActual[id]) << '/';
1389 Out() << setw(5) << ConvertDacToVolt(id, fDacTarget[id]);
1390 }
1391 Out() << endl;
1392 }
1393
1394 void PrintV()
1395 {
1396 Out() << dec << setprecision(2) << fixed << setfill(' ');
1397 for (int b=0; b<kNumBoards; b++)
1398 {
1399 if (!fPresent[b])
1400 {
1401 Out() << setw(2) << b << "-" << endl;
1402 continue;
1403 }
1404
1405 PrintLineV(b, 0);
1406 PrintLineV(b, 4);
1407 PrintLineV(b, 8);
1408 PrintLineV(b, 12);
1409 PrintLineV(b, 16);
1410 PrintLineV(b, 20);
1411 PrintLineV(b, 24);
1412 PrintLineV(b, 28);
1413 }
1414 }
1415
1416 void PrintLineGapd(int b, int ch)
1417 {
1418 Out() << setw(2) << b << "|";
1419
1420 for (int c=ch; c<ch+8; c++)
1421 {
1422 const int id = c+kNumChannelsPerBoard*b;
1423 Out() << " " << setw(5) << fOperationVoltage[id];
1424 }
1425 Out() << endl;
1426 }
1427
1428 void PrintReferenceVoltage()
1429 {
1430 Out() << dec << setprecision(2) << fixed << setfill(' ');
1431 for (int b=0; b<kNumBoards; b++)
1432 {
1433 if (!fPresent[b])
1434 {
1435 Out() << setw(2) << b << "-" << endl;
1436 continue;
1437 }
1438
1439 PrintLineGapd(b, 0);
1440 PrintLineGapd(b, 8);
1441 PrintLineGapd(b, 16);
1442 PrintLineGapd(b, 24);
1443 }
1444 }
1445
1446 // -------------------------------------------------------------------
1447
1448 void SetUpdateInterval(uint32_t val)
1449 {
1450 fUpdateTime = val;
1451
1452 if (!IsConnected() || fIsInitializing)
1453 return;
1454
1455 fUpdateTimer.cancel();
1456
1457 if (fUpdateTime>0)
1458 ScheduleUpdate(fUpdateTime);
1459 }
1460
1461 void SetSyncDelay(uint16_t val)
1462 {
1463 fSyncTime = val;
1464 }
1465
1466 void SetVoltMaxAbs(float max)
1467 {
1468 if (max>90)
1469 max = 90;
1470 if (max<0)
1471 max = 0;
1472
1473 fVoltageMaxAbs = max;
1474 }
1475
1476 void SetVoltMaxRel(float max)
1477 {
1478 if (max>90)
1479 max = 90;
1480 if (max<0)
1481 max = 0;
1482
1483 fVoltageMaxRel = max;
1484 }
1485
1486 uint16_t GetVoltMaxAbs() const
1487 {
1488 return fVoltageMaxAbs;
1489 }
1490
1491 uint16_t GetVoltMaxRel() const
1492 {
1493 return fVoltageMaxRel;
1494 }
1495
1496 bool HasOvercurrent() const
1497 {
1498 for (int ch=0; ch<kNumChannels; ch++)
1499 if (fPresent[ch/kNumChannelsPerBoard] && fCurrent[ch]<0 && ch!=91)
1500 return true;
1501
1502 return false;
1503 }
1504
1505 bool IsVoltageOff() const
1506 {
1507 for (int ch=0; ch<kNumChannels; ch++)
1508 if (fPresent[ch/kNumChannelsPerBoard] && fDacActual[ch]!=0)
1509 return false;
1510
1511 return true;
1512 }
1513
1514 State::states_t GetStatus()
1515 {
1516 if (!IsConnected())
1517 return State::kDisconnected;
1518
1519 if (IsConnecting())
1520 return State::kConnecting;
1521
1522 if (fIsInitializing)
1523 return State::kInitializing;
1524
1525 if (fIsRamping)
1526 return State::kRamping;
1527
1528 if (HasOvercurrent())
1529 return State::kOverCurrent;
1530
1531 if (IsVoltageOff())
1532 return State::kVoltageOff;
1533
1534 for (int ch=0; ch<kNumChannels; ch++)
1535 if (fPresent[ch/kNumChannelsPerBoard] && fDacActual[ch]!=fDacTarget[ch])
1536 return State::kNotReferenced;
1537
1538 return State::kVoltageOn;
1539 }
1540
1541 void SetReconnectDelay(uint32_t delay=1)
1542 {
1543 fReconnectDelay = delay;
1544 }
1545
1546 void SetEmergencyLimit(int32_t limit=0)
1547 {
1548 fEmergencyLimit = limit;
1549 }
1550
1551 void ResetEmergencyShutdown()
1552 {
1553 fEmergencyShutdown = false;
1554 }
1555
1556 bool IsEmergencyShutdown() const
1557 {
1558 return fEmergencyShutdown;
1559 }
1560};
1561
1562// ------------------------------------------------------------------------
1563
1564#include "DimDescriptionService.h"
1565
1566class ConnectionDimBias : public ConnectionBias
1567{
1568private:
1569
1570 DimDescribedService fDimCurrent;
1571 DimDescribedService fDimDac;
1572 DimDescribedService fDimVolt;
1573 DimDescribedService fDimGapd;
1574
1575public:
1576 void UpdateVA()
1577 {
1578 const Time now;
1579
1580 UpdateV(now);
1581
1582 fDimCurrent.setTime(now);
1583 fDimCurrent.Update(fCurrent);
1584 }
1585
1586private:
1587 void UpdateV(const Time now=Time())
1588 {
1589 const bool rc = !memcmp(fDacActual.data(), fDacTarget.data(), kNumChannels*2);
1590
1591 vector<uint16_t> val(2*kNumChannels);
1592 memcpy(val.data(), fDacActual.data(), kNumChannels*2);
1593 memcpy(val.data()+kNumChannels, fDacTarget.data(), kNumChannels*2);
1594 fDimDac.setTime(now);
1595 fDimDac.setQuality(rc);
1596 fDimDac.Update(val);
1597
1598 vector<float> volt(kNumChannels);
1599 for (float ch=0; ch<kNumChannels; ch++)
1600 volt[ch] = ConvertDacToVolt(ch, fDacActual[ch]);
1601 fDimVolt.setTime(now);
1602 fDimVolt.setQuality(rc);
1603 fDimVolt.Update(volt);
1604 }
1605
1606 void UpdateVgapd()
1607 {
1608 vector<float> volt;
1609 volt.reserve(3*kNumChannels);
1610 volt.insert(volt.end(), fOperationVoltage.begin(), fOperationVoltage.end());
1611 volt.insert(volt.end(), fCalibrationOffset.begin(), fCalibrationOffset.end());
1612 volt.insert(volt.end(), fCalibrationSlope.begin(), fCalibrationSlope.end());
1613 fDimGapd.Update(volt);
1614 }
1615
1616public:
1617 ConnectionDimBias(ba::io_service& ioservice, MessageImp &imp) :
1618 ConnectionBias(ioservice, imp),
1619 fDimCurrent("BIAS_CONTROL/CURRENT", "S:416",
1620 "|I[dac]:Bias current (conversion: 5000uA/4096dac)"),
1621 fDimDac("BIAS_CONTROL/DAC", "S:416;S:416",
1622 "|U[dac]:Current dac setting"
1623 "|Uref[dac]:Reference dac setting"),
1624 fDimVolt("BIAS_CONTROL/VOLTAGE", "F:416",
1625 "|Uout[V]:Output voltage"),
1626 fDimGapd("BIAS_CONTROL/NOMINAL", "F:416;F:416;F:416",
1627 "|Uop[V]:Nominal operation voltage at 25deg C"
1628 "|Uoff[V]:Bias crate channel calibration offsets"
1629 "|Rcal[Ohm]:Bias crate channel calibration slope")
1630 {
1631 }
1632
1633 // 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
1634};
1635
1636// ------------------------------------------------------------------------
1637
1638template <class T, class S>
1639class StateMachineBias : public StateMachineAsio<T>
1640{
1641 int Wrap(boost::function<void()> f)
1642 {
1643 f();
1644 return T::GetCurrentState();
1645 }
1646
1647 function<int(const EventImp &)> Wrapper(function<void()> func)
1648 {
1649 return bind(&StateMachineBias::Wrap, this, func);
1650 }
1651
1652 bool CheckEventSize(size_t has, const char *name, size_t size)
1653 {
1654 if (has==size)
1655 return true;
1656
1657 ostringstream msg;
1658 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
1659 T::Fatal(msg);
1660 return false;
1661 }
1662
1663private:
1664 S fBias;
1665
1666 bool fExpertMode;
1667
1668 Time fSunRise;
1669
1670 // --------------------------------------------------------------------
1671
1672 // SET_GLOBAL_DAC_VALUE
1673 int SetGlobalDac(const EventImp &evt)
1674 {
1675 if (!CheckEventSize(evt.GetSize(), "SetGlobalDac", 2))
1676 return false;
1677
1678 fBias.RampAllDacs(evt.GetUShort());
1679
1680 return T::GetCurrentState();
1681 }
1682
1683 // SET_ALL_CHANNELS_DAC
1684 int SetAllChannelsDac(const EventImp &evt)
1685 {
1686 if (!CheckEventSize(evt.GetSize(), "SetAllChannelsDac", 2*416))
1687 return false;
1688
1689 const uint16_t *ptr = evt.Ptr<uint16_t>();
1690
1691 fBias.RampAllChannelsDac(vector<uint16_t>(ptr, ptr+416));
1692
1693 return T::GetCurrentState();
1694 }
1695
1696 // SET_CHANNEL_DAC_VALUE
1697 int SetChannelDac(const EventImp &evt)
1698 {
1699 if (!CheckEventSize(evt.GetSize(), "SetChannelDac", 4))
1700 return false;
1701
1702 fBias.RampSingleChannelDac(evt.Get<uint16_t>(), evt.Get<uint16_t>(2));
1703
1704 return T::GetCurrentState();
1705 }
1706
1707 // --------------------------------------------------------------------
1708
1709 // SET_CHANNEL_VOLTAGE
1710 int SetChannelVolt(const EventImp &evt)
1711 {
1712 if (!CheckEventSize(evt.GetSize(), "SetChannelVolt", 6))
1713 return false;
1714
1715 fBias.RampSingleChannelVoltage(evt.GetUShort(), evt.Get<float>(2));
1716
1717 return T::GetCurrentState();
1718 }
1719
1720 // SET_GLOBAL_VOLTAGE
1721 int SetGlobalVolt(const EventImp &evt)
1722 {
1723 if (!CheckEventSize(evt.GetSize(), "SetGlobalVolt", 4))
1724 return false;
1725
1726 fBias.RampAllVoltages(evt.GetFloat());
1727
1728 return T::GetCurrentState();
1729 }
1730
1731 // SET_ALL_CHANNELS_VOLTAGES
1732 int SetAllChannelsVolt(const EventImp &evt)
1733 {
1734 if (!CheckEventSize(evt.GetSize(), "SetAllChannelsVolt", 4*kNumChannels))
1735 return false;
1736
1737 const float *ptr = evt.Ptr<float>();
1738 fBias.RampAllChannelsVoltage(vector<float>(ptr, ptr+kNumChannels));
1739
1740 return T::GetCurrentState();
1741 }
1742
1743 // --------------------------------------------------------------------
1744
1745/* // INCREASE_GLOBAL_VOLTAGE
1746 int IncGlobalVolt(const EventImp &evt)
1747 {
1748 if (!CheckEventSize(evt.GetSize(), "IncGlobalVolt", 4))
1749 return false;
1750
1751 fBias.RampAllOffsets(evt.GetFloat(), true);
1752
1753 return T::GetCurrentState();
1754 }
1755
1756 // INCREASE_CHANNEL_VOLTAGE
1757 int IncChannelVolt(const EventImp &evt)
1758 {
1759 if (!CheckEventSize(evt.GetSize(), "IncChannelVolt", 6))
1760 return false;
1761
1762 fBias.RampSingleChannelOffset(evt.Get<uint16_t>(), evt.Get<float>(2), true);
1763
1764 return T::GetCurrentState();
1765 }
1766
1767 // INCREASE_ALL_CHANNELS_VOLTAGES
1768 int IncAllChannelsVolt(const EventImp &evt)
1769 {
1770 if (!CheckEventSize(evt.GetSize(), "IncAllChannelsVolt", 4*kNumChannels))
1771 return false;
1772
1773 const float *ptr = evt.Ptr<float>();
1774 fBias.RampAllChannelsOffset(vector<float>(ptr, ptr+416), true);
1775
1776 return T::GetCurrentState();
1777 }
1778*/
1779 // --------------------------------------------------------------------
1780
1781 int ExpertSetGlobalVolt(const EventImp &evt)
1782 {
1783 if (!CheckEventSize(evt.GetSize(), "ExpertSetGlobalVolt", 4))
1784 return false;
1785
1786 fBias.ExpertGlobalSetVolt(evt.GetFloat());
1787
1788 return T::GetCurrentState();
1789 }
1790
1791 int ExpertSetGlobalDac(const EventImp &evt)
1792 {
1793 if (!CheckEventSize(evt.GetSize(), "ExpertSetGlobalDac", 2))
1794 return false;
1795
1796 fBias.ExpertGlobalSetDac(evt.GetUShort());
1797
1798 return T::GetCurrentState();
1799 }
1800
1801 int ExpertSetChannelVolt(const EventImp &evt)
1802 {
1803 if (!CheckEventSize(evt.GetSize(), "ExpertSetChannelVolt", 6))
1804 return false;
1805
1806 fBias.ExpertChannelSetVolt(evt.GetUShort(), evt.Get<float>(2));
1807
1808 return T::GetCurrentState();
1809 }
1810
1811 int ExpertSetChannelDac(const EventImp &evt)
1812 {
1813 if (!CheckEventSize(evt.GetSize(), "ExpertSetChannelDac", 4))
1814 return false;
1815
1816 fBias.ExpertChannelSetDac(evt.Get<uint16_t>(), evt.Get<uint16_t>(2));
1817
1818 return T::GetCurrentState();
1819 }
1820
1821 int ExpertLoadMapFile(const EventImp &evt)
1822 {
1823 if (evt.GetSize()==0)
1824 {
1825 T::Warn("ExpertLoadMapFile - No file name given.");
1826 return T::GetCurrentState();
1827 }
1828
1829 if (fBias.GetStatus()!=State::kVoltageOff)
1830 {
1831 T::Warn("ExpertLoadMapFile - Voltage must have been turned off.");
1832 return T::GetCurrentState();
1833 }
1834
1835 BiasMap map;
1836
1837 try
1838 {
1839 map.Read(evt.GetText());
1840 }
1841 catch (const runtime_error &e)
1842 {
1843 T::Warn("Getting reference voltages failed: "+string(e.what()));
1844 return T::GetCurrentState();
1845 }
1846
1847 if (!fBias.SetReferences(map.Vgapd(), map.Voffset(), map.Vslope()))
1848 {
1849 T::Warn("Setting reference voltages failed.");
1850 return T::GetCurrentState();
1851 }
1852
1853 fBias.UpdateVA();
1854
1855 T::Info("Successfully loaded new mapping '"+evt.GetString()+"'");
1856
1857 return T::GetCurrentState();
1858 }
1859
1860 // --------------------------------------------------------------------
1861
1862 int SetUpdateInterval(const EventImp &evt)
1863 {
1864 if (!CheckEventSize(evt.GetSize(), "SetUpdateInterval", 4))
1865 return false;
1866
1867 fBias.SetUpdateInterval(evt.Get<int32_t>()<0 ? 0 : evt.Get<uint32_t>());
1868
1869 return T::GetCurrentState();
1870 }
1871
1872 int Disconnect()
1873 {
1874 // Close all connections
1875 fBias.PostClose(-1);
1876
1877 /*
1878 // Now wait until all connection have been closed and
1879 // all pending handlers have been processed
1880 poll();
1881 */
1882
1883 return T::GetCurrentState();
1884 }
1885
1886 int Reconnect(const EventImp &evt)
1887 {
1888 // Close all connections to supress the warning in SetEndpoint
1889 fBias.PostClose(-1);
1890
1891 // Now wait until all connection have been closed and
1892 // all pending handlers have been processed
1893 ba::io_service::poll();
1894
1895 if (evt.GetBool())
1896 fBias.SetEndpoint(evt.GetString());
1897
1898 // Now we can reopen the connection
1899 fBias.SetReconnectDelay();
1900 fBias.PostClose(0);
1901
1902 return T::GetCurrentState();
1903 }
1904
1905 int SetVerbosity(const EventImp &evt)
1906 {
1907 if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
1908 return T::kSM_FatalError;
1909
1910 fBias.SetVerbose(evt.GetBool());
1911
1912 return T::GetCurrentState();
1913 }
1914
1915 int SetDummyMode(const EventImp &evt)
1916 {
1917 if (!CheckEventSize(evt.GetSize(), "SetDummyMode", 1))
1918 return T::kSM_FatalError;
1919
1920 fBias.SetDummyMode(evt.GetBool());
1921
1922 return T::GetCurrentState();
1923 }
1924
1925 int SetExpertMode(const EventImp &evt)
1926 {
1927 if (!CheckEventSize(evt.GetSize(), "SetExpertMode", 1))
1928 return T::kSM_FatalError;
1929
1930 fExpertMode = evt.GetBool();
1931
1932 if (fExpertMode)
1933 T::Warn("Expert commands enabled -- please ensure that you EXACTLY know what you do. These commands can destroy the system.");
1934
1935 return T::GetCurrentState();
1936 }
1937
1938 int Shutdown(const string &reason)
1939 {
1940 fBias.RampAllDacs(0);
1941 T::Info("Emergency shutdown initiated ["+reason+"].");
1942 return State::kLocked;
1943 }
1944
1945 int Unlock()
1946 {
1947 fBias.ResetEmergencyShutdown();
1948 return fBias.GetStatus();
1949 }
1950
1951 int Execute()
1952 {
1953 const int state = fBias.GetStatus();
1954
1955 if (fBias.IsEmergencyShutdown()/* && state>State::kInitializing && state<State::kExpertMode*/)
1956 {
1957 // This needs to be repeated for the case that in between a different command was processed
1958 if (!fBias.IsVoltageOff())
1959 fBias.RampAllDacs(0);
1960
1961 return State::kLocked;
1962 }
1963
1964 const Time now;
1965 if (now>fSunRise)
1966 {
1967 const bool shutdown =
1968 state==State::kRamping ||
1969 state==State::kVoltageOn ||
1970 state==State::kNotReferenced ||
1971 state==State::kOverCurrent;
1972
1973 if (shutdown)
1974 Shutdown("beginning of civil twilight");
1975
1976 fSunRise = now.GetNextSunRise(-6);
1977
1978 ostringstream msg;
1979 msg << "During next sun-rise nautical twilight will end at " << fSunRise;
1980 T::Info(msg);
1981
1982 if (shutdown)
1983 return State::kLocked;
1984 }
1985
1986 if (T::GetCurrentState()==State::kLocked)
1987 return T::GetCurrentState();
1988
1989 if (fExpertMode && state>=State::kConnected)
1990 return State::kExpertMode;
1991
1992 return state;
1993 }
1994
1995public:
1996 StateMachineBias(ostream &out=cout) :
1997 StateMachineAsio<T>(out, "BIAS_CONTROL"), fBias(*this, *this),
1998 fExpertMode(false), fSunRise(Time().GetNextSunRise(-6))
1999 {
2000 // State names
2001 T::AddStateName(State::kDisconnected, "Disconnected",
2002 "Bias-power supply not connected via USB.");
2003
2004 T::AddStateName(State::kConnecting, "Connecting",
2005 "Trying to establish USB connection to bias-power supply.");
2006
2007 T::AddStateName(State::kInitializing, "Initializing",
2008 "USB connection to bias-power supply established, synchronizing USB stream.");
2009
2010 T::AddStateName(State::kConnected, "Connected",
2011 "USB connection to bias-power supply established.");
2012
2013 T::AddStateName(State::kNotReferenced, "NotReferenced",
2014 "Internal reference voltage does not match last sent voltage.");
2015
2016 T::AddStateName(State::kVoltageOff, "VoltageOff",
2017 "All voltages are supposed to be switched off.");
2018
2019 T::AddStateName(State::kVoltageOn, "VoltageOn",
2020 "At least one voltage is switched on and all are at reference.");
2021
2022 T::AddStateName(State::kOverCurrent, "OverCurrent",
2023 "At least one channel is in over current state.");
2024
2025 T::AddStateName(State::kExpertMode, "ExpertMode",
2026 "Special (risky!) mode to directly send command to the bias-power supply.");
2027
2028 T::AddStateName(State::kRamping, "Ramping",
2029 "Voltage ramping in progress.");
2030
2031 T::AddStateName(State::kLocked, "Locked",
2032 "Locked due to emergency shutdown, no commands accepted except UNLOCK.");
2033
2034 // Verbosity commands
2035 T::AddEvent("SET_VERBOSE", "B:1")
2036 (bind(&StateMachineBias::SetVerbosity, this, placeholders::_1))
2037 ("set verbosity state"
2038 "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
2039
2040 T::AddEvent("ENABLE_DUMMY_MODE", "B:1")
2041 (bind(&StateMachineBias::SetDummyMode, this, placeholders::_1))
2042 ("Enable dummy mode. In this mode SetAllChannels prints informations instead of sending anything to the bias crate."
2043 "|enable[bool]:disable or enable dummy mode");
2044
2045 // Conenction commands
2046 T::AddEvent("DISCONNECT", State::kConnected, State::kVoltageOff)
2047 (bind(&StateMachineBias::Disconnect, this))
2048 ("disconnect from USB");
2049 T::AddEvent("RECONNECT", "O", State::kDisconnected, State::kConnected, State::kVoltageOff)
2050 (bind(&StateMachineBias::Reconnect, this, placeholders::_1))
2051 ("(Re)connect USB connection to Bias power supply, a new address can be given"
2052 "|tty[string]:new USB address");
2053
2054
2055 T::AddEvent("SET_UPDATE_INTERVAL", "I:1")
2056 (bind(&StateMachineBias::SetUpdateInterval, this, placeholders::_1))
2057 ("Set the updat einterval how often the currents are requested"
2058 "|interval[ms]:Update interval in milliseconds");
2059
2060
2061
2062 T::AddEvent("REQUEST_STATUS", State::kConnected, State::kVoltageOn, State::kVoltageOff, State::kNotReferenced, State::kOverCurrent)
2063 (Wrapper(bind(&ConnectionBias::ReadAllChannels, &fBias, false)))
2064 ("Asynchronously request the status (current) of all channels.");
2065
2066 T::AddEvent("RESET_OVER_CURRENT_STATUS", State::kOverCurrent)
2067 (Wrapper(bind(&ConnectionBias::OverCurrentReset, &fBias)))
2068 ("Set all channels in over current state to 0V and send a system reset to reset the over current flags.");
2069
2070
2071 T::AddEvent("SET_CHANNEL_DAC", "S:1;S:1")(State::kConnected)(State::kVoltageOff)(State::kVoltageOn)(State::kNotReferenced)(State::kOverCurrent)(State::kRamping)
2072 (bind(&StateMachineBias::SetChannelDac, this, placeholders::_1))
2073 ("Set a new target value in DAC counts for a single channel. Starts ramping if necessary."
2074 "|channel[short]:Channel for which to set the target voltage [0-415]"
2075 "|voltage[dac]:Target voltage in DAC units for the given channel");
2076 T::AddEvent("SET_GLOBAL_DAC", "S:1")(State::kConnected)(State::kVoltageOff)(State::kVoltageOn)(State::kNotReferenced)(State::kOverCurrent)(State::kRamping)
2077 (bind(&StateMachineBias::SetGlobalDac, this, placeholders::_1))
2078 ("Set a new target value for all channels in DAC counts. Starts ramping if necessary. (This command is not realized with the GLOBAL SET command.)"
2079 "|voltage[dac]:Global target voltage in DAC counts.");
2080 T::AddEvent("SET_ALL_CHANNELS_DAC", "S:416")(State::kConnected)(State::kVoltageOff)(State::kVoltageOn)(State::kNotReferenced)(State::kOverCurrent)(State::kRamping)
2081 (bind(&StateMachineBias::SetAllChannelsDac, this, placeholders::_1))
2082 ("Set a new target value for all channels in DAC counts. Starts ramping if necessary."
2083 "|voltage[dac]:Global target voltage in DAC counts for all channels");
2084
2085
2086 T::AddEvent("SET_CHANNEL_VOLTAGE", "S:1;F:1")(State::kConnected)(State::kVoltageOff)(State::kVoltageOn)(State::kNotReferenced)(State::kOverCurrent)(State::kRamping)
2087 (bind(&StateMachineBias::SetChannelVolt, this, placeholders::_1))
2088 ("Set a new target voltage for a single channel. Starts ramping if necessary."
2089 "|channel[short]:Channel for which to set the target voltage [0-415]"
2090 "|voltage[V]:Target voltage in volts for the given channel (will be converted to DAC units)");
2091 T::AddEvent("SET_GLOBAL_VOLTAGE", "F:1")(State::kConnected)(State::kVoltageOff)(State::kVoltageOn)(State::kNotReferenced)(State::kOverCurrent)(State::kRamping)
2092 (bind(&StateMachineBias::SetGlobalVolt, this, placeholders::_1))
2093 ("Set a new target voltage for all channels. Starts ramping if necessary. (This command is not realized with the GLOBAL SET command.)"
2094 "|voltage[V]:Global target voltage in volts (will be converted to DAC units)");
2095 T::AddEvent("SET_ALL_CHANNELS_VOLTAGE", "F:416")(State::kConnected)(State::kVoltageOff)(State::kVoltageOn)(State::kNotReferenced)(State::kOverCurrent)(State::kRamping)
2096 (bind(&StateMachineBias::SetAllChannelsVolt, this, placeholders::_1))
2097 ("Set all channels to the given new reference voltage. Starts ramping if necessary."
2098 "|voltage[V]:New reference voltage for all channels");
2099
2100/*
2101 T::AddEvent("INCREASE_CHANNEL_VOLTAGE", "S:1;F:1", State::kConnected, State::kVoltageOff, State::kVoltageOn, State::kNotReferenced, State::kOverCurrent)
2102 (bind(&StateMachineBias::IncChannelVolt, this, placeholders::_1))
2103 ("Increases the voltage of all channels by the given offset. Starts ramping if necessary. (This command is not realized with the GLOBAL SET command.)"
2104 "|channel[short]:Channel for which to adapt the voltage [0-415]"
2105 "|offset[V]:Offset to be added to all channels (will be converted to DAC counts)");
2106 T::AddEvent("INCREASE_GLOBAL_VOLTAGE", "F:1", State::kConnected, State::kVoltageOff, State::kVoltageOn, State::kNotReferenced, State::kOverCurrent)
2107 (bind(&StateMachineBias::IncGlobalVolt, this, placeholders::_1))
2108 ("Increases the voltage of all channels by the given offset. Starts ramping if necessary. (This command is not realized with the GLOBAL SET command.)"
2109 "|offset[V]:Offset to be added to all channels (will be converted to DAC counts)");
2110 T::AddEvent("INCREASE_ALL_CHANNELS_VOLTAGE", "F:416", State::kConnected, State::kVoltageOff, State::kVoltageOn, State::kNotReferenced, State::kOverCurrent)
2111 (bind(&StateMachineBias::IncAllChannelsVolt, this, placeholders::_1))
2112 ("Add the given voltages to the current reference voltages. Starts ramping if necessary."
2113 "offset[V]:Offsets to be added to the reference voltage of all channels in volts");
2114*/
2115
2116
2117
2118 T::AddEvent("SET_ZERO_VOLTAGE")(State::kConnected)(State::kVoltageOff)(State::kVoltageOn)(State::kNotReferenced)(State::kOverCurrent)(State::kRamping)
2119 (Wrapper(bind(&ConnectionBias::RampAllDacs, &fBias, 0)))
2120 ("Set all channels to a zero reference voltage. Starts ramping if necessary.");
2121 T::AddEvent("SHUTDOWN")(State::kConnected)(State::kVoltageOff)(State::kVoltageOn)(State::kNotReferenced)(State::kOverCurrent)(State::kRamping)
2122 (bind(&StateMachineBias::Shutdown, this, "user request"))
2123 ("Same as SET_ZERO_VOLTAGE; but goes to locked state afterwards.");
2124
2125 T::AddEvent("UNLOCK", State::kLocked)
2126 (bind(&StateMachineBias::Unlock, this))
2127 ("Unlock if in locked state.");
2128
2129
2130
2131
2132
2133 T::AddEvent("STOP", State::kConnected, State::kRamping)
2134 (Wrapper(bind(&ConnectionBias::RampStop, &fBias)))
2135 ("Stop an on-going ramping");
2136
2137 T::AddEvent("START", State::kConnected, State::kNotReferenced)
2138 (Wrapper(bind(&ConnectionBias::RampStart, &fBias)))
2139 ("Start a ramping if no ramping is in progress and if reference values differ from current voltages");
2140
2141
2142
2143 T::AddEvent("PRINT_INFO")
2144 (Wrapper(bind(&ConnectionBias::PrintInfo, &fBias)))
2145 ("Print a table with all current read back with the last request operation");
2146 T::AddEvent("PRINT_CURRENTS")
2147 (Wrapper(bind(&ConnectionBias::PrintA, &fBias)))
2148 ("Print a table with all current read back with the last request operation");
2149 T::AddEvent("PRINT_VOLTAGES")
2150 (Wrapper(bind(&ConnectionBias::PrintV, &fBias)))
2151 ("Print a table with all voltages (current and reference voltages as currently in memory)");
2152 T::AddEvent("PRINT_GAPD_REFERENCE_VOLTAGES")
2153 (Wrapper(bind(&ConnectionBias::PrintReferenceVoltage, &fBias)))
2154 ("Print the G-APD reference values (breakdown voltage + overvoltage) obtained from file");
2155
2156
2157 T::AddEvent("EXPERT_MODE", "B:1")
2158 (bind(&StateMachineBias::SetExpertMode, this, placeholders::_1))
2159 ("Enable usage of expert commands (note that for safty reasons the are exclusive with the standard commands)");
2160
2161 T::AddEvent("EXPERT_RESET", State::kExpertMode)
2162 (Wrapper(bind(&ConnectionBias::ExpertReset, &fBias, true)))
2163 ("Send the RESET command (note that this is possibly harmfull command)");
2164
2165 T::AddEvent("EXPERT_SET_GLOBAL_VOLTAGE", "F:1", State::kExpertMode)
2166 (bind(&StateMachineBias::ExpertSetGlobalVolt, this, placeholders::_1))
2167 ("Send the global set command. The given voltage is converted to DAC counts.");
2168
2169 T::AddEvent("EXPERT_SET_GLOBAL_DAC", "S:1", State::kExpertMode)
2170 (bind(&StateMachineBias::ExpertSetGlobalDac, this, placeholders::_1))
2171 ("Send the global set command.");
2172
2173 T::AddEvent("EXPERT_SET_CHANNEL_VOLTAGE", "S:1;F:1", State::kExpertMode)
2174 (bind(&StateMachineBias::ExpertSetChannelVolt, this, placeholders::_1))
2175 ("Send a single channel set command. The given voltage is converted to DAC commands.");
2176
2177 T::AddEvent("EXPERT_SET_CHANNEL_DAC", "S:1;S:1", State::kExpertMode)
2178 (bind(&StateMachineBias::ExpertSetChannelDac, this, placeholders::_1))
2179 ("Send a single channel set command.");
2180
2181 T::AddEvent("EXPERT_LOAD_MAP_FILE", "C", State::kExpertMode)
2182 (bind(&StateMachineBias::ExpertLoadMapFile, this, placeholders::_1))
2183 ("Load a new mapping file.");
2184 }
2185
2186 ~StateMachineBias() { T::Warn("TODO: Implement rampming at shutdown!"); }
2187
2188 int EvalOptions(Configuration &conf)
2189 {
2190 // FIXME: Read calib_offset
2191 // FIXME: Check calib offset being smaller than +/-0.25V
2192
2193 fBias.SetVerbose(!conf.Get<bool>("quiet"));
2194 fBias.SetDummyMode(conf.Get<bool>("dummy-mode"));
2195
2196 if (conf.Has("dev"))
2197 {
2198 fBias.SetEndpoint(conf.Get<string>("dev"));
2199 T::Message("Setting device to "+fBias.URL());
2200 }
2201
2202 const uint16_t step = conf.Get<uint16_t>("ramp-step");
2203 const uint16_t time = conf.Get<uint16_t>("ramp-delay");
2204
2205 if (step>230) // 5V
2206 {
2207 T::Error("ramp-step exceeds allowed range.");
2208 return 1;
2209 }
2210
2211 fBias.SetRampStep(step);
2212 fBias.SetRampTime(time);
2213 fBias.SetUpdateInterval(conf.Get<uint32_t>("update-interval"));
2214 fBias.SetEmergencyLimit(conf.Get<uint16_t>("emergency-limit"));
2215 fBias.SetSyncDelay(conf.Get<uint16_t>("sync-delay"));
2216
2217 ostringstream str1, str2;
2218 str1 << "Ramping in effective steps of " << fBias.GetRampStepVolt() << "V";
2219 str2 << "Ramping with a delay per step of " << time << "ms";
2220 T::Message(str1);
2221 T::Message(str2);
2222
2223 // --------------------------------------------------------------------------
2224
2225 const float maxabsv = conf.Get<float>("volt-max-abs");
2226 const float maxrelv = conf.Get<float>("volt-max-rel");
2227 if (maxabsv>90)
2228 {
2229 T::Error("volt-max exceeds 90V.");
2230 return 2;
2231 }
2232 if (maxabsv>75)
2233 T::Warn("volt-max exceeds 75V.");
2234 if (maxabsv<70)
2235 T::Warn("volt-max below 70V.");
2236 if (maxabsv<0)
2237 {
2238 T::Error("volt-max negative.");
2239 return 3;
2240 }
2241
2242 fBias.SetVoltMaxAbs(maxabsv);
2243 fBias.SetVoltMaxRel(maxrelv);
2244
2245 ostringstream str3, str4;
2246 str3 << "Effective maximum allowed absolute voltage: " << fBias.GetVoltMaxAbs() << "V";
2247 str4 << "Effective maximum difference w.r.t to G-APD reference: " << fBias.GetVoltMaxRel() << "V";
2248 T::Message(str3);
2249 T::Message(str4);
2250
2251 // --------------------------------------------------------------------------
2252
2253 BiasMap map;
2254
2255 if (!conf.Has("bias-map-file") && !conf.Has("bias-database"))
2256 {
2257 T::Error("Neither bias-map-file not bias-database specified.");
2258 return 5;
2259 }
2260
2261 try
2262 {
2263 if (conf.Has("bias-map-file"))
2264 map.Read(conf.Get<string>("bias-map-file"));
2265
2266 //if (conf.Has("bias-database"))
2267 // map.Retrieve(conf.Get<string>("bias-database"));
2268 }
2269 catch (const runtime_error &e)
2270 {
2271 T::Error("Getting reference voltages failed: "+string(e.what()));
2272 return 7;
2273 }
2274
2275 if (!fBias.SetReferences(map.Vgapd(), map.Voffset(), map.Vslope()))
2276 {
2277 T::Error("Setting reference voltages failed.");
2278 return 8;
2279 }
2280
2281 // --------------------------------------------------------------------------
2282
2283 if (conf.Has("dev"))
2284 fBias.Connect();
2285
2286 return -1;
2287 }
2288};
2289
2290// ------------------------------------------------------------------------
2291
2292#include "Main.h"
2293
2294template<class T, class S, class R>
2295int RunShell(Configuration &conf)
2296{
2297 return Main::execute<T, StateMachineBias<S, R>>(conf);
2298}
2299
2300void SetupConfiguration(Configuration &conf)
2301{
2302 po::options_description control("BIAS control options");
2303 control.add_options()
2304 ("no-dim,d", po_bool(), "Disable dim services")
2305 ("dev", var<string>(), "Device address of USB port to bias-power supply")
2306 ("quiet,q", po_bool(true), "Disable printing contents of all received messages (except dynamic data) in clear text.")
2307 ("dummy-mode", po_bool(), "Dummy mode - SetAllChannels prints info instead of sending new values.")
2308 ("ramp-delay", var<uint16_t>(15), "Delay between the answer of one ramping step and sending the next ramp command to all channels in milliseconds.")
2309 ("ramp-step", var<uint16_t>(46), "Maximum step in DAC counts during ramping (Volt = DAC*90/4096)")
2310 ("update-interval", var<uint32_t>(3000), "Interval between two current requests in milliseconds")
2311 ("sync-delay", var<uint16_t>(500), "Delay between sending the inital 0's after a newly established connection to synchronize the output stream in milliseconds")
2312 ("volt-max-abs", var<float>(75), "Absolte upper limit for the voltage (in Volts)")
2313 ("volt-max-rel", var<float>(3.5), "Relative upper limit for the voltage w.r.t. the G-APD reference voltage (in Volts)")
2314 ("bias-map-file", var<string>(), "File with nominal and offset voltages for each channel.")
2315 ("bias-database", var<string>(), "")
2316 ("emergency-limit", var<uint16_t>(2200), "A current limit in ADC counts which, if exceeded, will initiate an emergency shutdown (0=off)")
2317 ;
2318
2319 conf.AddOptions(control);
2320}
2321
2322/*
2323 Extract usage clause(s) [if any] for SYNOPSIS.
2324 Translators: "Usage" and "or" here are patterns (regular expressions) which
2325 are used to match the usage synopsis in program output. An example from cp
2326 (GNU coreutils) which contains both strings:
2327 Usage: cp [OPTION]... [-T] SOURCE DEST
2328 or: cp [OPTION]... SOURCE... DIRECTORY
2329 or: cp [OPTION]... -t DIRECTORY SOURCE...
2330 */
2331void PrintUsage()
2332{
2333 cout <<
2334 "The biasctrl program controls the bias-power supply boards.\n"
2335 "\n"
2336 "Note: At default the program is started without a command line (user) "
2337 "interface. In this case Actions/Commands are available via Dim "
2338 "exclusively.\n"
2339 "Use the -c option to start the program with a command line interface.\n"
2340 "\n"
2341 "In the running application:\n"
2342 "Use h or help to print a short help message about its usage.\n"
2343 "\n"
2344 "Usage: biasctrl [-c type] [OPTIONS]\n"
2345 " or: biasctrl [OPTIONS]\n";
2346 cout << endl;
2347}
2348
2349void PrintHelp()
2350{
2351 Main::PrintHelp<StateMachineBias<StateMachine,ConnectionBias>>();
2352
2353 /* Additional help text which is printed after the configuration
2354 options goes here */
2355
2356 /*
2357 cout << "bla bla bla" << endl << endl;
2358 cout << endl;
2359 cout << "Environment:" << endl;
2360 cout << "environment" << endl;
2361 cout << endl;
2362 cout << "Examples:" << endl;
2363 cout << "test exam" << endl;
2364 cout << endl;
2365 cout << "Files:" << endl;
2366 cout << "files" << endl;
2367 cout << endl;
2368 */
2369}
2370
2371int main(int argc, const char* argv[])
2372{
2373 Configuration conf(argv[0]);
2374 conf.SetPrintUsage(PrintUsage);
2375 Main::SetupConfiguration(conf);
2376 SetupConfiguration(conf);
2377
2378 if (!conf.DoParse(argc, argv, PrintHelp))
2379 return 127;
2380
2381 //try
2382 {
2383 // No console access at all
2384 if (!conf.Has("console"))
2385 {
2386 if (conf.Get<bool>("no-dim"))
2387 return RunShell<LocalStream, StateMachine, ConnectionBias>(conf);
2388 else
2389 return RunShell<LocalStream, StateMachineDim, ConnectionDimBias>(conf);
2390 }
2391 // Cosole access w/ and w/o Dim
2392 if (conf.Get<bool>("no-dim"))
2393 {
2394 if (conf.Get<int>("console")==0)
2395 return RunShell<LocalShell, StateMachine, ConnectionBias>(conf);
2396 else
2397 return RunShell<LocalConsole, StateMachine, ConnectionBias>(conf);
2398 }
2399 else
2400 {
2401 if (conf.Get<int>("console")==0)
2402 return RunShell<LocalShell, StateMachineDim, ConnectionDimBias>(conf);
2403 else
2404 return RunShell<LocalConsole, StateMachineDim, ConnectionDimBias>(conf);
2405 }
2406 }
2407 /*catch (std::exception& e)
2408 {
2409 cerr << "Exception: " << e.what() << endl;
2410 return -1;
2411 }*/
2412
2413 return 0;
2414}
Note: See TracBrowser for help on using the repository browser.