#include #include #include "Dim.h" #include "Event.h" #include "Shell.h" #include "StateMachineDim.h" #include "ConnectionUSB.h" #include "Configuration.h" #include "Console.h" #include "Converter.h" #include "tools.h" #include "LocalControl.h" #include "HeadersBIAS.h" namespace ba = boost::asio; namespace bs = boost::system; namespace dummy = ba::placeholders; using namespace std::placeholders; using namespace std; using namespace BIAS; // ------------------------------------------------------------------------ class ConnectionBias : public ConnectionUSB { boost::asio::deadline_timer fSyncTimer; boost::asio::deadline_timer fRampTimer; boost::asio::deadline_timer fUpdateTimer; vector fBuffer; vector fBufferRamp; vector fBufferUpdate; bool fIsVerbose; vector fVoltCmd; // Current command voltage in DAC units (12bit = 90V) vector fVoltGapd; vector fPresent; int64_t fWrapCounter; int64_t fSendCounter; int16_t fGlobalVoltCmd; // Command value to be reached // uint16_t fExpertVoltRef; // Command value to be reached int16_t fRampStep; int16_t fRampTime; uint16_t fUpdateTime; uint16_t fSyncTime; bool fIsInitializing; bool fIsRamping; bool fWaitingForAnswer; protected: vector fVolt; // Current voltage in DAC units (12bit = 90V) vector fVoltRef; // Current reference voltage in DAC units (12bit = 90V) vector fCurrent; // Current in ADC units (12bit = 5mA) uint16_t fVoltMax; virtual void UpdateA() { } virtual void UpdateV() { } private: bool CheckChDac(const string &cmd, uint16_t dac, uint16_t ch=0) { if (dac>kMaxDac) { Error(cmd+" - DAC value out of range."); return false; } if (dac>fVoltMax) { Error(cmd+" - DAC value exceeds fVoltMax."); return false; } if (ch>=kNumChannels) { Error(cmd+" - Channel out of range."); return false; } return true; } vector GetCmd(uint16_t board, uint16_t channel, Command_t cmd, uint16_t dac=0) { vector data(3); /* if (board>kNumBoards) return; if (channel>kNumChannelsPerBoard) return; if (dac>0xfff) return; */ data[0] = (cmd<<5) | (board<<1) | (((channel&16)>>4) & 1); data[1] = (channel<<4) | (dac>>8); data[2] = dac&0xff; return data; } vector GetCmd(Command_t cmd, uint16_t id=0, uint16_t dac=0) { const unsigned int board = id/kNumChannelsPerBoard; const unsigned int channel = id%kNumChannelsPerBoard; return GetCmd(board, channel, cmd, dac); } bool CheckMessageLength(int received, int expected, const string &msg) { if (received==expected) return true; ostringstream str; str << msg << ": Expected " << expected << " bytes in answer, but got " << received << endl; Error(str); PostClose(false); return false; } bool EvalAnswer(const uint8_t *answer, uint16_t id, int command) { answer += id*3; const uint16_t status = (answer[0]>>7)&1; const uint16_t wrap = (answer[0]>>4)&7; const uint16_t ddd = ((uint16_t(answer[0])&0xf)<<8) | answer[1]; const uint16_t error = (answer[2]>>4)&0xf; const uint16_t board = answer[2]&0xf; /* Out() << dec << setw(2) << board << '|' << wrap << " "; if (id%8==7) Out() << endl; */ if (fWrapCounter>=0) { if ((fWrapCounter+1)%8 != wrap) { ostringstream msg; msg << "Corrupted answer (id=" << id << "): received wrap counter " << wrap << " doesn't match last received counter " << fWrapCounter << "."; Error(msg); return false; } } fWrapCounter = wrap; if (command==kSynchronize) { ostringstream msg; msg << hex << setfill('0'); msg << "Initial answer received: 0x"; msg << setw(2) << (int)answer[2]; msg << setw(2) << (int)answer[1]; msg << setw(2) << (int)answer[0]; Message(msg); if (status!=0 || ddd!=0 || error!=0 || board!=0) { Warn("Initial answer doesn't seem to be a reset as naively expected."); //ostringstream msg; //msg << hex << setfill('0'); //msg << "S=" << status << " D=" << ddd << " E=" << error << " B=" << board; //Message(msg); } fSendCounter = wrap; msg.str(""); msg << "Setting fSendCounter to " << wrap; Info(msg); return true; } if (error==0x8) // No device { Message("Reset button on crate pressed!"); SetZero(); return true; } if (command==kCmdReset) { if (status==0 && ddd==0 && error==0 && board==0) { Message("Reset successfully executed."); return true; } Warn("Answer to 'reset' command contains unexpected data."); return false; } if (command==kCmdGlobalSet) { if (status==0 && ddd==0 && error==0 && board==0) { for (int i=0; i>8; const int cmd = command&3; if (cmd==kCmdRead || cmd==kCmdChannelSet) { if (board!=id/kNumChannelsPerBoard) { ostringstream out; out << "Talked to board " << id/kNumChannelsPerBoard << ", but got answer from board " << board << "."; Error(out); return false; } // Not present if (error==0x7 || error==0xf) { fPresent[board] = false; fCurrent[id] = 0x8000; return true; } fCurrent[id] = status ? -ddd : ddd; fPresent[board] = true; } if (cmd==kCmdChannelSet) fVolt[id] = fVoltCmd[id]; return true; } private: void HandleReceivedData(const vector &buf, size_t bytes_received, int command, int send_counter) { // Now print the received message if requested by the user if (fIsVerbose/* && command!=kUpdate*/) { Out() << endl << kBold << dec << "Data received (size=" << bytes_received << "):" << endl; Out() << " Command=" << command << " fWrapCounter=" << fWrapCounter << " fSendCounter=" << fSendCounter << " fIsInitializing=" << fIsInitializing << " fIsRamping=" << fIsRamping; Out() << hex << setfill('0'); for (size_t i=0; i>> /// This replaces the send counter and the command argument // in handleReceivedData switch (command&0xff) { case kSynchronize: case kCmdReset: case kExpertChannelSet: case kCmdGlobalSet: case kResetChannels: case kCmdRead: HandleReceivedData(fBuffer, bytes_received, command, send_counter); fWaitingForAnswer = false; return; case kCmdChannelSet: HandleReceivedData(fBufferRamp, bytes_received, command, send_counter); return; case kUpdate: HandleReceivedData(fBufferUpdate, bytes_received, command, send_counter); return; } } // -------------------------------------------------------------------- void HandleSyncTimer(int counter, const bs::error_code &error) { if (error==ba::error::basic_errors::operation_aborted) { if (fIsInitializing) Warn("Synchronization aborted..."); else Info("Synchronization successfull."); return; } if (error) { ostringstream str; str << "Synchronization timer: " << error.message() << " (" << error << ")";// << endl; Error(str); PostClose(false); return; } if (!is_open()) { Warn("Synchronization in progress, but disconnected."); return; } ostringstream msg; msg << "Synchronization time expired (" << counter << ")"; Info(msg); if (fIsInitializing) { PostMessage("\0", 1); if (counter==2) { Error("Synchronization attempt timed out."); PostClose(false); return; } ScheduleSync(counter+1); return; } Info("Synchronisation successfull."); } void ScheduleSync(int counter=0) { fSyncTimer.expires_from_now(boost::posix_time::milliseconds(fSyncTime)); fSyncTimer.async_wait(boost::bind(&ConnectionBias::HandleSyncTimer, this, counter, dummy::error)); } // This is called when a connection was established void ConnectionEstablished() { // Reset everything.... fSendCounter = -1; fWrapCounter = -1; fGlobalVoltCmd = -1; fIsInitializing = true; fVolt.assign( kNumChannels, 0); fVoltRef.assign(kNumChannels, 0); fVoltCmd.assign(kNumChannels, 0); // Send a single 0 (and possible two consecutive 0's // to make sure we are in sync with the device) PostMessage("\0", 1); AsyncRead(ba::buffer(fBuffer, 3), kSynchronize, 0);//++fSendCounter); fWaitingForAnswer = true; // Wait for some time before sending the next 0 ScheduleSync(); } // -------------------------------------------------------------------- void HandleUpdateTimer(const bs::error_code &error) { if (error==ba::error::basic_errors::operation_aborted) { Warn("Update timer aborted..."); fIsRamping = false; return; } if (error) { ostringstream str; str << "Update timer: " << error.message() << " (" << error << ")";// << endl; Error(str); PostClose(false); return; } if (!is_open()) return; if (fIsRamping) ScheduleUpdate(fUpdateTime); else ReadAllChannels(true); } void ScheduleUpdate(int millisec) { fUpdateTimer.expires_from_now(boost::posix_time::milliseconds(millisec)); fUpdateTimer.async_wait(boost::bind(&ConnectionBias::HandleUpdateTimer, this, dummy::error)); } // -------------------------------------------------------------------- void SetAllChannels(const vector &dac, bool special=false) { vector data; data.reserve(kNumChannels*3); for (int ch=0; ch cmd = GetCmd(kCmdChannelSet, ch, dac[ch]); data.insert(data.end(), cmd.begin(), cmd.end()); fVoltCmd[ch] = dac[ch]; } fSendCounter += kNumChannels; PostMessage(data); AsyncRead(ba::buffer(special ? fBuffer : fBufferRamp, kNumChannels*3), special ? kResetChannels : kCmdChannelSet, fSendCounter); if (special) fWaitingForAnswer = true; } uint16_t RampOneStep(uint16_t ch) { if (fVoltRef[ch]>fVolt[ch]) return fVolt[ch]+fRampStep>fVoltRef[ch] ? fVoltRef[ch] : fVolt[ch]+fRampStep; if (fVoltRef[ch] dac(kNumChannels); bool identical = true; for (int ch=0; ch ba::deadline_timer::traits_type::now()) return; fIsRamping = RampOneStep(); } void ScheduleRampStep() { fRampTimer.expires_from_now(boost::posix_time::milliseconds(fRampTime)); fRampTimer.async_wait(boost::bind(&ConnectionBias::HandleRampTimer, this, dummy::error)); } public: ConnectionBias(ba::io_service& ioservice, MessageImp &imp) : ConnectionUSB(ioservice, imp()), fSyncTimer(ioservice), fRampTimer(ioservice), fUpdateTimer(ioservice), fBuffer(3*kNumChannels), fBufferRamp(3*kNumChannels), fBufferUpdate(3*kNumChannels), fIsVerbose(false), fVoltCmd(kNumChannels), fVoltGapd(kNumChannels), //fRefCurrent(kNumChannels), fPresent(kNumBoards), fRampStep(-1), fRampTime(-1), fUpdateTime(3000), fSyncTime(333), fIsRamping(false), fWaitingForAnswer(false), fVolt(kNumChannels), fVoltRef(kNumChannels), fCurrent(kNumChannels) { SetLogStream(&imp); } void OverCurrentReset() { if (fWaitingForAnswer) { Error("Answer on last command not yet received."); return; } if (fIsRamping) { Warn("OverCurrentReset - Ramping in progres."); RampStop(); } vector dac(kNumChannels); for (int ch=0; ch data; data.reserve(kNumChannels*3); for (int ch=0; ch cmd = GetCmd(kCmdRead, ch); data.insert(data.end(), cmd.begin(), cmd.end()); } fSendCounter += kNumChannels; PostMessage(data); AsyncRead(ba::buffer(special ? fBufferUpdate : fBuffer, kNumChannels*3), special ? kUpdate : kCmdRead, fSendCounter); if (!special) fWaitingForAnswer = true; } // -------------------------------------------------------------------- bool ChannelSetDac(uint16_t ch, uint16_t dac) { if (!CheckChDac("ChannelSetDac", dac, ch)) return false; fVoltRef[ch] = dac; if (!fIsRamping) fIsRamping = RampOneStep(); return true; } bool ChannelSetVolt(uint16_t ch, double volt) { return ChannelSetDac(ch, volt*4096/90.); } bool GlobalSetDac(uint16_t dac) { if (!CheckChDac("GlobalSetDac", dac)) return false; for (size_t ch=0; chkMaxDac) { Error("SetGapdVoltage - Voltage reference for G-APD channel out of range."); return false; } for (size_t ch=0; ch &volt) { if (volt.size()!=kNumChannels) { ostringstream out; out << "SetNewGapdVoltage - Given vector has " << volt.size() << " elements - expected " << kNumChannels << endl; Error(out); return false; } for (size_t i=0; i=0) { Error("ExpertGlobalSetDac - Still waiting for previous global-set's answer."); return false; } fGlobalVoltCmd = dac; ostringstream msg; msg << "EXPERT MODE: Sending 'GlobalSet' (DAC=" << dac << ")"; Warn(msg); PostMessage(GetCmd(kCmdGlobalSet, 0, dac)); AsyncRead(ba::buffer(fBuffer, 3), kCmdGlobalSet, ++fSendCounter); fWaitingForAnswer = true; return true; } bool ExpertGlobalSetVolt(float volt) { return ExpertGlobalSetDac(volt*4096/90); } // -------------------------------------------------------------------- void SetVerbose(bool b) { fIsVerbose = b; } void PrintLineA(int b, int ch) { Out() << setw(2) << b << "|"; for (int c=ch; c4095) fVoltMax = 4095; if (max<0) fVoltMax = 0; } /* void AdaptVoltages() { // Correct voltages according to current for (int i=0; i vec; vec.insert(vec.end(), fVolt.begin(), fVolt.end()); vec.insert(vec.end(), fVoltRef.begin(), fVoltRef.end()); fDimVoltage.Update(vec); } public: ConnectionDimBias(ba::io_service& ioservice, MessageImp &imp) : ConnectionBias(ioservice, imp), fDimCurrent("BIAS_CONTROL/CURRENT", "S:416", ""), fDimVoltage("BIAS_CONTROL/VOLTAGE", "S:416;S:416", "") { } // 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 }; // ------------------------------------------------------------------------ template class StateMachineBias : public T, public ba::io_service, public ba::io_service::work { int Wrap(boost::function f) { f(); return T::GetCurrentState(); } function Wrapper(function func) { return bind(&StateMachineBias::Wrap, this, func); } bool CheckEventSize(size_t has, const char *name, size_t size) { if (has==size) return true; ostringstream msg; msg << name << " - Received event has " << has << " bytes, but expected " << size << "."; T::Fatal(msg); return false; } private: S fBias; bool fExpertMode; // -------------------------------------------------------------------- int SetGlobalVolt(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetGlobalVolt", 4)) return false; if (!fBias.GlobalSetVolt(evt.GetFloat())) T::Error("Supplied voltage out of range (0-90)"); return T::GetCurrentState(); } int SetGlobalDac(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetGlobalDac", 2)) return false; if (!fBias.GlobalSetDac(evt.GetUShort())) T::Error("Supplied voltage out of range (0-90)"); return T::GetCurrentState(); } int SetChannelVolt(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetChannelVolt", 6)) return false; if (!fBias.ChannelSetVolt(evt.GetUShort(), evt.Get(2))) T::Error("Value out of range"); return T::GetCurrentState(); } int SetChannelDac(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetChannelDac", 4)) return false; if (!fBias.ChannelSetDac(evt.Get(), evt.Get(2))) T::Error("Value out of range"); return T::GetCurrentState(); } // -------------------------------------------------------------------- int ExpertSetGlobalVolt(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "ExpertSetGlobalVolt", 4)) return false; if (!fBias.ExpertGlobalSetVolt(evt.GetFloat())) T::Error("Supplied voltage out of range (0-90)"); return T::GetCurrentState(); } int ExpertSetGlobalDac(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "ExpertSetGlobalDac", 2)) return false; if (!fBias.ExpertGlobalSetDac(evt.GetUShort())) T::Error("Supplied voltage out of range (0-90)"); return T::GetCurrentState(); } int ExpertSetChannelVolt(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "ExpertSetChannelVolt", 6)) return false; if (!fBias.ExpertChannelSetVolt(evt.GetUShort(), evt.Get(2))) T::Error("Value out of range"); return T::GetCurrentState(); } int ExpertSetChannelDac(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "ExpertSetChannelDac", 4)) return false; if (!fBias.ExpertChannelSetDac(evt.Get(), evt.Get(2))) T::Error("Value out of range"); return T::GetCurrentState(); } // -------------------------------------------------------------------- int Disconnect() { // Close all connections fBias.PostClose(false); /* // Now wait until all connection have been closed and // all pending handlers have been processed poll(); */ return T::GetCurrentState(); } int Reconnect(const EventImp &evt) { // Close all connections to supress the warning in SetEndpoint fBias.PostClose(false); // Now wait until all connection have been closed and // all pending handlers have been processed poll(); if (evt.GetBool()) fBias.SetEndpoint(evt.GetString()); // Now we can reopen the connection fBias.PostClose(true); return T::GetCurrentState(); } int SetVerbosity(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1)) return T::kSM_FatalError; fBias.SetVerbose(evt.GetBool()); return T::GetCurrentState(); } int SetExpertMode(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetExpertMode", 1)) return T::kSM_FatalError; fExpertMode = evt.GetBool(); if (fExpertMode) T::Warn("Expert commands enabled -- please ensure that you EXACTLY know what you do. These commands can destroy the system."); return T::GetCurrentState(); } int Execute() { // Dispatch (execute) at most one handler from the queue. In contrary // to run_one(), it doesn't wait until a handler is available // which can be dispatched, so poll_one() might return with 0 // handlers dispatched. The handlers are always dispatched/executed // synchronously, i.e. within the call to poll_one() poll_one(); return fExpertMode && fBias.GetStatus()>=kConnected ? kExpertMode : fBias.GetStatus(); } public: StateMachineBias(ostream &out=cout) : T(out, "BIAS_CONTROL"), ba::io_service::work(static_cast(*this)), fBias(*this, *this), fExpertMode(false) { // ba::io_service::work is a kind of keep_alive for the loop. // It prevents the io_service to go to stopped state, which // would prevent any consecutive calls to run() // or poll() to do nothing. reset() could also revoke to the // previous state but this might introduce some overhead of // deletion and creation of threads and more. // State names T::AddStateName(kDisconnected, "Disconnected", "Bias-power supply not connected via USB."); T::AddStateName(kConnecting, "Connecting", "Trying to establish USB connection to bias-power supply."); T::AddStateName(kInitializing, "Initializing", "USB connection to bias-power supply established, synchronizing USB stream."); T::AddStateName(kConnected, "Connected", "USB connection to bias-power supply established."); T::AddStateName(kAtReference, "Referenced", "Internal reference voltage matches last sent voltage."); T::AddStateName(kOverCurrent, "OverCurrent", "At least one channel is in over current state."); T::AddStateName(kExpertMode, "ExpertMode", "Special (risky!) mode to directly send command to the bias-power supply."); T::AddStateName(kRamping, "Ramping", "Voltage ramping in progress."); // Verbosity commands T::AddEvent("SET_VERBOSE", "B") (bind(&StateMachineBias::SetVerbosity, this, placeholders::_1)) ("set verbosity state" "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data"); // Conenction commands T::AddEvent("DISCONNECT", kConnected, kAtReference) (bind(&StateMachineBias::Disconnect, this)) ("disconnect from USB"); T::AddEvent("RECONNECT", "O", kDisconnected, kConnected, kAtReference) (bind(&StateMachineBias::Reconnect, this, placeholders::_1)) ("(Re)connect USB connection to Bias power supply, a new address can be given" "|tty[string]:new USB address"); T::AddEvent("REQUEST_STATUS", kConnected, kAtReference, kOverCurrent) (Wrapper(bind(&ConnectionBias::ReadAllChannels, &fBias, false))) ("Asynchronously request the status (current) of all channels."); T::AddEvent("RESET_OVER_CURRENT_STATUS", kOverCurrent) (Wrapper(bind(&ConnectionBias::OverCurrentReset, &fBias))) ("NOT YET TESTED"); T::AddEvent("SET_GLOBAL_VOLTAGE", "F:1", kConnected, kAtReference, kOverCurrent) (bind(&StateMachineBias::SetGlobalVolt, this, placeholders::_1)) ("Set all channels to a new reference voltage. Starts ramping if necessary. (This command is not realized with the GLOBAL SET command.)"); T::AddEvent("SET_GLOBAL_DAC", "S:1", kConnected, kAtReference, kOverCurrent) (bind(&StateMachineBias::SetGlobalDac, this, placeholders::_1)) ("Set all channels to a new DAC reference. Starts ramping if necessary. (This command is not realized with the GLOBAL SET command.)"); T::AddEvent("SET_CHANNEL_VOLTAGE", "S:1;F:1", kConnected, kAtReference, kOverCurrent) (bind(&StateMachineBias::SetChannelVolt, this, placeholders::_1)) ("Set a single channel a new reference voltage. Starts ramping if necessary."); T::AddEvent("SET_CHANNEL_DAC", "S:1;S:1", kConnected, kAtReference, kOverCurrent) (bind(&StateMachineBias::SetChannelDac, this, placeholders::_1)) ("Set a single channel a new DAC reference value. Starts ramping if necessary."); T::AddEvent("SET_GAPD_REFERENCE_VOLTAGE", kConnected, kAtReference, kOverCurrent) (Wrapper(bind(&ConnectionBias::SetGapdVoltage, &fBias))) ("Set all channels to their G-APD reference voltage. Starts ramping if necessary."); T::AddEvent("SET_ZERO_VOLTAGE", kConnected, kAtReference, kOverCurrent) (Wrapper(bind(&ConnectionBias::SetZero, &fBias))) ("Set all channels to a zero reference voltage. Starts ramping if necessary."); T::AddEvent("STOP", kConnected, kRamping, kAtReference, kOverCurrent) (Wrapper(bind(&ConnectionBias::RampStop, &fBias))) ("Stop an on-going ramping"); T::AddEvent("START", kConnected, kOverCurrent) (Wrapper(bind(&ConnectionBias::RampStart, &fBias))) ("Start a ramping if no ramping is in progress and if reference values differ from current voltages"); T::AddEvent("PRINT_CURRENTS") (Wrapper(bind(&ConnectionBias::PrintA, &fBias))) ("Print a table with all current read back with the last request operation"); T::AddEvent("PRINT_VOLTAGES") (Wrapper(bind(&ConnectionBias::PrintV, &fBias))) ("Print a table with all voltages (current and reference voltages as currently in memory)"); T::AddEvent("PRINT_GAPD_REFERENCE_VOLTAGES") (Wrapper(bind(&ConnectionBias::PrintGapd, &fBias))) ("Print the G-APD reference values obtained from file"); T::AddEvent("EXPERT_MODE", "B:1") (bind(&StateMachineBias::SetExpertMode, this, placeholders::_1)) ("Enable usage of expert commands (note that for safty reasons the are exclusive with the standard commands)"); T::AddEvent("EXPERT_RESET", kExpertMode) (Wrapper(bind(&ConnectionBias::ExpertReset, &fBias))) ("Send the RESET command (note that this is possibly harmfull command)"); T::AddEvent("EXPERT_SET_GLOBAL_VOLTAGE", "F:1", kExpertMode) (bind(&StateMachineBias::ExpertSetGlobalVolt, this, placeholders::_1)) ("Send the global set command. The given voltage is converted to DAC counts."); T::AddEvent("EXPERT_SET_GLOBAL_DAC", "S:1", kExpertMode) (bind(&StateMachineBias::ExpertSetGlobalDac, this, placeholders::_1)) ("Send the global set command."); T::AddEvent("EXPERT_SET_CHANNEL_VOLTAGE", "S:1;F:1", kExpertMode) (bind(&StateMachineBias::ExpertSetChannelVolt, this, placeholders::_1)) ("Send a single channel set command. The given voltage is converted to DAC commands."); T::AddEvent("EXPERT_SET_CHANNEL_DAC", "S:1;S:1", kExpertMode) (bind(&StateMachineBias::ExpertSetChannelDac, this, placeholders::_1)) ("Send a single channel set command."); } ~StateMachineBias() { T::Warn("TODO: Implement rampming at shutdown!"); } int EvalOptions(Configuration &conf) { fBias.SetVerbose(!conf.Get("quiet")); fBias.SetEndpoint(conf.Get("dev")); T::Message("Setting device to "+fBias.URL()); const uint16_t step = conf.Get("ramp-step"); const uint16_t time = conf.Get("ramp-time"); if (step>230) // 5V { T::Error("ramp-step exceeds allowed range."); return 1; } fBias.SetRampStep(step); fBias.SetRampTime(time); fBias.SetUpdateInterval(conf.Get("update-interval")); fBias.SetSyncDelay(conf.Get("sync-delay")); const float maxv = conf.Get("volt-max"); if (maxv>90) { T::Error("volt-max exceeds 90V."); return 2; } if (maxv>75) T::Warn("volt-max exceeds 75V."); if (maxv<70) T::Warn("volt-max below 70V."); if (maxv<0) { T::Error("volt-max negative."); return 3; } fBias.SetVoltMax(maxv); // -------------------------------------------------------------------------- ifstream fin("FACTmapV5.txt"); int l = 0; vector vec(kNumChannels); string buf; while (getline(fin, buf, '\n')) { if (l>1439) break; buf = Tools::Trim(buf); if (buf[0]=='#') continue; stringstream str(buf); int idummy, board, channel; float fdummy, volt; str >> idummy >> idummy >> idummy >> idummy >> idummy; str >> volt; str >> board; str >> channel; str >> fdummy >> fdummy >> fdummy; if (channel+32*board>=kNumChannels) { T::Error("Invalid board/channel read from FACTmapV5.txt."); return 4; } vec[channel+32*board] = volt; l++; } if (l!=1440) { T::Error("Reading reference voltages from FACTmapV5.txt failed."); return 5; } if (!fBias.SetNewGapdVoltage(vec)) { T::Error("Setting reference voltages failed."); return 6; } // -------------------------------------------------------------------------- fBias.Connect(); return -1; } }; // ------------------------------------------------------------------------ #include "Main.h" template int RunShell(Configuration &conf) { return Main::execute>(conf); } void SetupConfiguration(Configuration &conf) { po::options_description control("BIAS control options"); control.add_options() ("no-dim,d", po_bool(), "Disable dim services") ("dev", var("FTE00FOH"), "Device address of USB port to bias-power supply") ("quiet,q", po_bool(), "Disable printing contents of all received messages (except dynamic data) in clear text.") ("ramp-time", var(15), "Delay between the answer of one ramping steps and sending the next ramp command to all channels in milliseconds.") ("ramp-step", var(46), "Maximum step in DAC counts during ramping (Volt = DAC*90/4096)") ("update-interval", var(3000), "Interval between two current requests in milliseconds") ("sync-delay", var(333), "Delay between sending the inital 0's after a newly established connection to synchronize the output stream in milliseconds") ("volt-max", var(75), "Upper limit for the voltage which can be applied in Volts") ; // FIXME: Make sure ramping / request and commands are // not sent at the same time. conf.AddOptions(control); } /* Extract usage clause(s) [if any] for SYNOPSIS. Translators: "Usage" and "or" here are patterns (regular expressions) which are used to match the usage synopsis in program output. An example from cp (GNU coreutils) which contains both strings: Usage: cp [OPTION]... [-T] SOURCE DEST or: cp [OPTION]... SOURCE... DIRECTORY or: cp [OPTION]... -t DIRECTORY SOURCE... */ void PrintUsage() { cout << "The biasctrl controls the bias-power supply boards.\n" "\n" "The default is that the program is started without user intercation. " "All actions are supposed to arrive as DimCommands. Using the -c " "option, a local shell can be initialized. With h or help a short " "help message about the usuage can be brought to the screen.\n" "\n" "Usage: biasctrl [-c type] [OPTIONS]\n" " or: biasctrl [OPTIONS]\n"; cout << endl; } void PrintHelp() { /* Additional help text which is printed after the configuration options goes here */ /* cout << "bla bla bla" << endl << endl; cout << endl; cout << "Environment:" << endl; cout << "environment" << endl; cout << endl; cout << "Examples:" << endl; cout << "test exam" << endl; cout << endl; cout << "Files:" << endl; cout << "files" << endl; cout << endl; */ } int main(int argc, const char* argv[]) { Configuration conf(argv[0]); conf.SetPrintUsage(PrintUsage); Main::SetupConfiguration(conf); SetupConfiguration(conf); if (!conf.DoParse(argc, argv, PrintHelp)) return -1; //try { // No console access at all if (!conf.Has("console")) { if (conf.Get("no-dim")) return RunShell(conf); else return RunShell(conf); } // Cosole access w/ and w/o Dim if (conf.Get("no-dim")) { if (conf.Get("console")==0) return RunShell(conf); else return RunShell(conf); } else { if (conf.Get("console")==0) return RunShell(conf); else return RunShell(conf); } } /*catch (std::exception& e) { cerr << "Exception: " << e.what() << endl; return -1; }*/ return 0; }