#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 "PixelMap.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; //#define DEBUG // ------------------------------------------------------------------------ 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 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; uint32_t fUpdateTime; uint16_t fSyncTime; bool fIsInitializing; bool fIsRamping; int fWaitingForAnswer; vector fCounter; protected: vector fVolt; // Current voltage in DAC units (12bit = 90V) vector fVoltRef; // Current reference voltage in DAC units (12bit = 90V) vector fVoltGapd; // Nominal G-APD voltages at 25deg C vector fCurrent; // Current in ADC units (12bit = 5mA) uint16_t fVoltMaxAbs; uint16_t fVoltMaxRel; virtual void UpdateA() { } virtual void UpdateV() { } virtual void UpdateVgapd() { } private: bool CheckChDac(const string &cmd, uint16_t dac, uint16_t ch=0) { ostringstream str; str << cmd << " - "; if (ch>=kNumChannels) { str << "Channel " << ch << " out of range [0,416]."; Error(str); return false; } if (dac>kMaxDac) { str << "DAC value " << dac << " out of range [0,4095]."; Error(str); return false; } if (dac>fVoltMaxAbs) { str << "DAC value " << dac << " exceeds allowed absolute maximum of " << fVoltMaxAbs; Error(str); return false; } if (dac>fVoltGapd[ch]+fVoltMaxRel) { str << "DAC value " << dac << " exceeds allowed channel maximum of " << fVoltGapd[ch] << " + " << fVoltMaxRel; Error(str); 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); 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; // 0x10 00 7f // status = 0 // wrap = 1 // ddd = 0 // error = not present // board = 15 /* 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 one " << fWrapCounter << " "; msg << " (fSendCounter=" << fSendCounter << ")"; 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 << " (fSendCounter=" << fSendCounter << ")"; Error(out); return false; } // Not present if (error==0x7 || error==0xf) { fPresent[board] = false; fCurrent[id] = 0x8000; return true; } // There is no -0 therefore we make a trick and replace it by -1. // This is not harmfull, because typical zero currents are in the // order of one to three bits anyway and they are never stable. fCurrent[id] = status ? -(ddd==0?1: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) { #ifdef DEBUG ofstream fout("received.txt", ios::app); fout << Time() << ": "; for (unsigned int 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 = -1; 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); CloseImp(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."); CloseImp(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() { // We connect for the first time or haven't received // a valid warp counter yet... this procedure also sets // our volatges to 0 if we have connected but never received // any answer. if (fWrapCounter<0) { fVolt.assign( kNumChannels, 0); fVoltRef.assign(kNumChannels, 0); fVoltCmd.assign(kNumChannels, 0); } // Reset everything.... fSendCounter = -1; fWrapCounter = -1; fGlobalVoltCmd = -1; fIsInitializing = true; fIsRamping = false; // 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 = kSynchronize; // 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 (fUpdateTime==0) 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 = kResetChannels; } 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), //fRefCurrent(kNumChannels), fPresent(kNumBoards), fWrapCounter(-1), fRampStep(-1), fRampTime(-1), fUpdateTime(3000), fSyncTime(333), fIsRamping(false), fWaitingForAnswer(-1), fCounter(8), fVolt(kNumChannels), fVoltRef(kNumChannels), fVoltGapd(kNumChannels), fCurrent(kNumChannels) { SetLogStream(&imp); } void OverCurrentReset() { if (fWaitingForAnswer>=0) { ostringstream msg; msg << "OverCurrentReset - Answer on last command (id=" << fWaitingForAnswer << ") not yet received."; Error(msg); return; } if (fIsRamping) { Warn("OverCurrentReset - Ramping in progres."); RampStop(); } vector dac(kNumChannels); for (int ch=0; ch=0) { ostringstream msg; msg << "ReadAllChannels - Answer on last command (id=" << fWaitingForAnswer << ") not yet received."; Error(msg); return; } vector 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 = kCmdRead; } // -------------------------------------------------------------------- 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) { if (volt<0 || volt>90) { ostringstream msg; msg << "ChannelSetVolt - Given voltage " << volt << "V out of range [0V,90V]."; Error(msg); return false; } return ChannelSetDac(ch, volt*4096/90.); } /* bool GlobalSetDac(uint16_t dac) { if (!CheckChDac("GlobalSetDac", dac)) return false; for (size_t ch=0; ch90) { Error("GlobalSetVolt - Voltage out of range [0V,90V]."); return false; } return GlobalSetDac(volt*4096/90); } */ bool AddDac(const vector &dac) { if (dac.size()!=kNumChannels) { Error("AddDac - Wrong size of array."); return false; } for (size_t ch=0; chkMaxDac) { ostringstream msg; msg << "AddDac - New voltage reference " << fVoltRef[ch] << "+" << dac[ch] << " out of range [0," << kMaxDac << " for channel " << ch << "."; Error(msg); return false; } if (fVoltRef[ch]+dac[ch]<0) fVoltRef[ch] = 0; else fVoltRef[ch] += dac[ch]; } if (!fIsRamping) fIsRamping = RampOneStep(); return true; } bool AddVolt(const vector &offset) { vector dac(offset.size()); for (size_t ch=0; ch90) { ostringstream msg; msg << "AddVolt - Offset voltage " << offset[ch] << "V for channel " << ch << " out of range [-90V,90V]."; Error(msg); return false; } dac[ch] = offset[ch]*4096/90; } return AddDac(dac); } bool GlobalAddDac(int16_t offset) { return AddDac(vector(kNumChannels, offset)); } bool GlobalAddVolt(float offset) { return AddVolt(vector(kNumChannels, offset)); } bool SetDac(const vector &dac) { if (dac.size()!=kNumChannels) { Error("SetDac - Wrong size of array."); return false; } for (size_t ch=0; ch &volt) { vector dac(volt.size()); for (size_t ch=0; ch90) { ostringstream msg; msg << "SetVolt - Voltage " << volt[ch] << "V out of range [0V,90V] for channel " << ch << "."; Error(msg); return false; } dac[ch] = volt[ch]*4096/90; } return SetDac(dac); } bool GlobalSetDac(int16_t dac) { return SetDac(vector(kNumChannels, dac)); } bool GlobalSetVolt(float volt) { return SetVolt(vector(kNumChannels, volt)); } // -------------------------------------------------------------------- bool SetGapdVoltage(float offset) { if (offset<-90 || offset>90) { ostringstream msg; msg << "SetGapdVoltage - Offset voltage " << offset << "V out of range [-90V,90V]."; Error(msg); return false; } const int16_t dac = offset*4096/90; for (size_t ch=0; chkMaxDac) { ostringstream msg; msg << "SetGapdVoltage - New voltage reference " << fVoltGapd[ch] << "+" << dac << " out of range [0," << kMaxDac << " for channel " << ch << "."; Error(msg); 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) { ostringstream msg; msg << "ExpertReset - Answer on last command (id=" << fWaitingForAnswer << ") not yet received."; Error(msg); return; } if (expert_mode) Warn("EXPERT MODE: Sending reset."); PostMessage(GetCmd(kCmdReset)); AsyncRead(ba::buffer(fBuffer, 3), kCmdReset, ++fSendCounter); fWaitingForAnswer = kCmdReset; } bool ExpertChannelSetDac(uint16_t ch, uint16_t dac) { if (fWaitingForAnswer>=0) { ostringstream msg; msg << "ExpertChannelSetDac - Answer on last command (id=" << fWaitingForAnswer << ") not yet received."; Error(msg); return false; } if (!CheckChDac("ExpertChannelSetDac", dac, ch)) return false; fVoltCmd[ch] = dac; ostringstream msg; msg << "EXPERT MODE: Sending 'ChannelSet' (set ch " << ch << " to DAC=" << dac << ")"; Warn(msg); // FIXME: dac += calib_offset PostMessage(GetCmd(kCmdChannelSet, ch, dac)); AsyncRead(ba::buffer(fBuffer, 3), kExpertChannelSet|(ch<<8), ++fSendCounter); fWaitingForAnswer = kExpertChannelSet|(ch<<8); return true; } bool ExpertChannelSetVolt(uint16_t ch, double volt) { return ExpertChannelSetDac(ch, volt*4096/90.); } bool ExpertGlobalSetDac(uint16_t dac) { if (fWaitingForAnswer>=0) { ostringstream msg; msg << "ExpertGlobalSetDac - Answer on last command (id=" << fWaitingForAnswer << ") not yet received."; Error(msg); return false; } if (!CheckChDac("ExpertGlobalSetDac", dac)) return false; if (fGlobalVoltCmd>=0) { Error("ExpertGlobalSetDac - Still waiting for previous answer to 'GlobalSet'"); 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 = kCmdGlobalSet; return true; } bool ExpertGlobalSetVolt(float volt) { return ExpertGlobalSetDac(volt*4096/90); } // -------------------------------------------------------------------- void SetVerbose(bool b) { fIsVerbose = b; } void PrintInfo() { Out() << endl << kBold << dec << '\n'; Out() << "fWrapCounter = " << fWrapCounter << '\n'; Out() << "fSendCounter = " << fSendCounter%8 << " (" << fSendCounter << ")" << '\n'; Out() << "fIsInitializing = " << fIsInitializing << '\n'; Out() << "fIsRamping = " << fIsRamping << '\n'; Out() << "Answer counter:" << '\n'; Out() << " - Synchronization: " << fCounter[0] << '\n'; Out() << " - Reset: " << fCounter[1] << '\n'; Out() << " - Request update: " << fCounter[2] << '\n'; Out() << " - Ramp step: " << fCounter[3] << '\n'; Out() << " - Read: " << fCounter[4] << '\n'; Out() << " - Reset channels: " << fCounter[5] << '\n'; Out() << " - Global set: " << fCounter[7] << '\n'; Out() << " - Channel set: " << fCounter[6] << '\n' << endl; } void PrintLineA(int b, int ch) { Out() << setw(2) << b << "|"; for (int c=ch; c0) ScheduleUpdate(fUpdateTime); } void SetSyncDelay(uint16_t val) { fSyncTime = val; } void SetVoltMaxAbs(float max) { fVoltMaxAbs = max*4096/90; if (fVoltMaxAbs>4095) fVoltMaxAbs = 4095; if (max<0) fVoltMaxAbs = 0; } void SetVoltMaxRel(float max) { fVoltMaxRel = max*4096/90; if (fVoltMaxRel>4095) fVoltMaxRel = 4095; if (max<0) fVoltMaxRel = 0; } uint16_t GetVoltMaxAbs() const { return fVoltMaxAbs * 90./4096; } uint16_t GetVoltMaxRel() const { return fVoltMaxRel * 90./4096; } /* 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); } void UpdateVgapd() { fDimGapd.Update(fVoltGapd); } public: ConnectionDimBias(ba::io_service& ioservice, MessageImp &imp) : ConnectionBias(ioservice, imp), fDimCurrent("BIAS_CONTROL/CURRENT", "S:416", "|I[uA]:Bias current"), fDimVoltage("BIAS_CONTROL/VOLTAGE", "S:416;S:416", "|U[V]:Applied bias voltage|Uref[V]:Reference bias voltage"), fDimGapd( "BIAS_CONTROL/NOMINAL", "S:416", "|U[V]:Nominal G-APD voltage at 25deg C") { } // 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 SetGapdVoltage(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetGapdVoltage", 4)) return false; fBias.SetGapdVoltage(evt.GetFloat()); return T::GetCurrentState(); } // -------------------------------------------------------------------- int SetGlobalVolt(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetGlobalVolt", 4)) return false; fBias.GlobalSetVolt(evt.GetFloat()); return T::GetCurrentState(); } int SetGlobalDac(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetGlobalDac", 2)) return false; fBias.GlobalSetDac(evt.GetUShort()); return T::GetCurrentState(); } int IncGlobalVolt(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "IncGlobalVolt", 4)) return false; fBias.GlobalAddVolt(evt.GetFloat()); return T::GetCurrentState(); } int IncGlobalDac(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "IncGlobalDac", 2)) return false; fBias.GlobalAddDac(evt.GetShort()); return T::GetCurrentState(); } int DecGlobalVolt(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "DecGlobalVolt", 4)) return false; fBias.GlobalAddVolt(-evt.GetFloat()); return T::GetCurrentState(); } int DecGlobalDac(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "DecGlobalDac", 2)) return false; fBias.GlobalAddDac(-evt.GetShort()); return T::GetCurrentState(); } int SetChannelVolt(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetChannelVolt", 6)) return false; fBias.ChannelSetVolt(evt.GetUShort(), evt.Get(2)); return T::GetCurrentState(); } int SetChannelDac(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetChannelDac", 4)) return false; fBias.ChannelSetDac(evt.Get(), evt.Get(2)); return T::GetCurrentState(); } int SetGapdReferenceCh(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetGapdReferenceCh", 2)) return false; fBias.SetGapdReferenceCh(evt.GetUShort()); return T::GetCurrentState(); } // -------------------------------------------------------------------- int AddReferenceDac(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "AddReferenceDac", 2*kNumChannels)) return false; const int16_t *ptr = evt.Ptr(); fBias.AddDac(vector(ptr, ptr+416)); return T::GetCurrentState(); } int AddReferenceVolt(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "AddReferenceVolt", 4*kNumChannels)) return false; const float_t *ptr = evt.Ptr(); fBias.AddVolt(vector(ptr, ptr+416)); return T::GetCurrentState(); } int SetReferenceDac(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetReferenceDac", 2*kNumChannels)) return false; const int16_t *ptr = evt.Ptr(); fBias.SetDac(vector(ptr, ptr+416)); return T::GetCurrentState(); } int SetReferenceVolt(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetReferenceVolt", 4*kNumChannels)) return false; const float_t *ptr = evt.Ptr(); fBias.SetVolt(vector(ptr, ptr+416)); return T::GetCurrentState(); } // -------------------------------------------------------------------- int ExpertSetGlobalVolt(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "ExpertSetGlobalVolt", 4)) return false; fBias.ExpertGlobalSetVolt(evt.GetFloat()); return T::GetCurrentState(); } int ExpertSetGlobalDac(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "ExpertSetGlobalDac", 2)) return false; fBias.ExpertGlobalSetDac(evt.GetUShort()); return T::GetCurrentState(); } int ExpertSetChannelVolt(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "ExpertSetChannelVolt", 6)) return false; fBias.ExpertChannelSetVolt(evt.GetUShort(), evt.Get(2)); return T::GetCurrentState(); } int ExpertSetChannelDac(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "ExpertSetChannelDac", 4)) return false; fBias.ExpertChannelSetDac(evt.Get(), evt.Get(2)); return T::GetCurrentState(); } // -------------------------------------------------------------------- int SetUpdateInterval(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetUpdateInterval", 4)) return false; fBias.SetUpdateInterval(evt.Get()<0 ? 0 : evt.Get()); 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(kNotReferenced, "NotReferenced", "Internal reference voltage does not match last sent voltage."); T::AddStateName(kVoltageOff, "VoltageOff", "All voltages are supposed to be switched off."); T::AddStateName(kVoltageOn, "VoltageOn", "At least one voltage is switched on and all are at reference."); 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, kVoltageOff) (bind(&StateMachineBias::Disconnect, this)) ("disconnect from USB"); T::AddEvent("RECONNECT", "O", kDisconnected, kConnected, kVoltageOff) (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("SET_UPDATE_INTERVAL", "I:1") (bind(&StateMachineBias::SetUpdateInterval, this, placeholders::_1)) ("Set the updat einterval how often the currents are requested" "|interval[ms]:Update interval in milliseconds"); T::AddEvent("REQUEST_STATUS", kConnected, kVoltageOn, kVoltageOff, kNotReferenced, 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))) ("Set all channels in over current state to 0V and send a system reset to reset the over current flags."); T::AddEvent("SET_GLOBAL_VOLTAGE", "F:1", kConnected, kVoltageOff, kVoltageOn, kNotReferenced, 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.)" "|voltage[V]:Global target voltage in volts (will be converted to DAC units)"); T::AddEvent("SET_GLOBAL_DAC", "S:1", kConnected, kVoltageOff, kVoltageOn, kNotReferenced, 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.)" "|voltage[dac]:Global target voltage as DAC counts."); T::AddEvent("INCREASE_GLOBAL_VOLTAGE", "F:1", kConnected, kVoltageOff, kVoltageOn, kNotReferenced, kOverCurrent) (bind(&StateMachineBias::IncGlobalVolt, 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.)" "|offset[V]:Offset to be added to all channels (will be converted to DAC counts)"); T::AddEvent("INCREASE_GLOBAL_DAC", "S:1", kConnected, kVoltageOff, kVoltageOn, kNotReferenced, kOverCurrent) (bind(&StateMachineBias::IncGlobalDac, 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.)" "|offset[dac]:Offset to be added to all channels as DAC counts"); T::AddEvent("DECREASE_GLOBAL_VOLTAGE", "F:1", kConnected, kVoltageOff, kVoltageOn, kNotReferenced, kOverCurrent) (bind(&StateMachineBias::DecGlobalVolt, 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.)" "|offset[V]:Offset to be subtracted from all channels (will be converted to DAC counts)"); T::AddEvent("DECREASE_GLOBAL_DAC", "S:1", kConnected, kVoltageOff, kVoltageOn, kNotReferenced, kOverCurrent) (bind(&StateMachineBias::DecGlobalDac, 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.)" "|offset[dac]:Offset to be subtracted from all channels as DAC counts"); T::AddEvent("SET_CHANNEL_VOLTAGE", "S:1;F:1", kConnected, kVoltageOff, kVoltageOn, kNotReferenced, kOverCurrent) (bind(&StateMachineBias::SetChannelVolt, this, placeholders::_1)) ("Set a single channel to a new reference voltage. Starts ramping if necessary." "|channel[short]:Channel for which to set the target voltage [0-416]" "|voltage[V]:Target voltage in volts for the given channel (will be converted to DAC units)"); T::AddEvent("SET_CHANNEL_DAC", "S:1;S:1", kConnected, kVoltageOff, kVoltageOn, kNotReferenced, kOverCurrent) (bind(&StateMachineBias::SetChannelDac, this, placeholders::_1)) ("Set a single channel to a new DAC reference value. Starts ramping if necessary." "|channel[short]:Channel for which to set the target voltage [0-416]" "|voltage[dac]:Target voltage in DAC units for the given channel"); T::AddEvent("SET_GLOBAL_GAPD_REFERENCE_VOLTAGE", kConnected, kVoltageOff, kVoltageOn, kNotReferenced, kOverCurrent) (Wrapper(bind(&ConnectionBias::SetGapdVoltage, &fBias, 0.))) ("Set all channels to their G-APD reference voltage. Starts ramping if necessary."); T::AddEvent("SET_CHANNEL_GAPD_REFERENCE_VOLTAGE", "S:1", kConnected, kVoltageOff, kVoltageOn, kNotReferenced, kOverCurrent) (bind(&StateMachineBias::SetGapdReferenceCh, this, placeholders::_1)) ("Set a single channel channels to its G-APD reference voltage. Starts ramping if necessary." "|channel[short]:Channel for which to set the target voltage [0-416]"); T::AddEvent("SET_GAPD_REFERENCE_OFFSET", "F:1", kConnected, kVoltageOff, kVoltageOn, kNotReferenced, kOverCurrent) (bind(&StateMachineBias::SetGapdVoltage, this, placeholders::_1)) ("Set all channels to their G-APD reference voltage plus the given offset. Starts ramping if necessary." "|offset[V]:Offset to be added to teh G-APD reference voltage globally"); T::AddEvent("SET_ZERO_VOLTAGE", kConnected, kVoltageOff, kVoltageOn, kNotReferenced, kOverCurrent) (Wrapper(bind(&ConnectionBias::SetZero, &fBias))) ("Set all channels to a zero reference voltage. Starts ramping if necessary."); T::AddEvent("SET_REFERENCE_VOLTAGES", "F:416", kConnected, kVoltageOff, kVoltageOn, kNotReferenced, kOverCurrent) (bind(&StateMachineBias::SetReferenceVolt, this, placeholders::_1)) ("Set all channels to the given new reference voltage. Starts ramping if necessary." "voltage[V]:New reference voltage for all channels"); T::AddEvent("SET_REFERENCE_DACS", "S:416", kConnected, kVoltageOff, kVoltageOn, kNotReferenced, kOverCurrent) (bind(&StateMachineBias::SetReferenceDac, this, placeholders::_1)) ("Set all channels to the given new reference voltage. Starts ramping if necessary." "voltage[dac]:New reference voltage for all channels in DAC units"); T::AddEvent("ADD_REFERENCE_VOLTAGES", "F:416", kConnected, kVoltageOff, kVoltageOn, kNotReferenced, kOverCurrent) (bind(&StateMachineBias::AddReferenceVolt, this, placeholders::_1)) ("Add the given voltages to the current reference voltages. Starts ramping if necessary." "offset[V]:Offsets to be added to the reference voltage of all channels in volts"); T::AddEvent("ADD_REFERENCE_DACS", "S:416", kConnected, kVoltageOff, kVoltageOn, kNotReferenced, kOverCurrent) (bind(&StateMachineBias::AddReferenceDac, this, placeholders::_1)) ("Add the given voltages to the current reference voltages. Starts ramping if necessary." "offset[dac]:Offsets to be added to the reference voltage of all channels in DAC units"); T::AddEvent("STOP", kConnected, kRamping) (Wrapper(bind(&ConnectionBias::RampStop, &fBias))) ("Stop an on-going ramping"); T::AddEvent("START", kConnected, kNotReferenced) (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_INFO") (Wrapper(bind(&ConnectionBias::PrintInfo, &fBias))) ("Print a table with all current read back with the last request operation"); 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, true))) ("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) { // FIXME: Read calib_offset // FIXME: Check calib offset being smaller than +/-0.25V 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-delay"); 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")); ostringstream str1, str2; str1 << "Ramping in effective steps of " << fBias.GetRampStepVolt() << "V"; str2 << "Ramping with a delay per step of " << time << "ms"; T::Message(str1); T::Message(str2); // -------------------------------------------------------------------------- const float maxabsv = conf.Get("volt-max-abs"); const float maxrelv = conf.Get("volt-max-rel"); if (maxabsv>90) { T::Error("volt-max exceeds 90V."); return 2; } if (maxabsv>75) T::Warn("volt-max exceeds 75V."); if (maxabsv<70) T::Warn("volt-max below 70V."); if (maxabsv<0) { T::Error("volt-max negative."); return 3; } fBias.SetVoltMaxAbs(maxabsv); fBias.SetVoltMaxRel(maxrelv); ostringstream str3, str4; str3 << "Effective maximum allowed absolute voltage: " << fBias.GetVoltMaxAbs() << "V"; str4 << "Effective maximum difference w.r.t to G-APD reference: " << fBias.GetVoltMaxRel() << "V"; T::Message(str3); T::Message(str4); // -------------------------------------------------------------------------- PixelMap map; if (!map.Read(conf.Get("pixel-map-file"))) { T::Error("Reading reference voltages from "+conf.Get("pixel-map-file")+" failed."); return 5; } if (!fBias.SetNewGapdVoltage(map.Vgapd())) { 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-delay", var(15), "Delay between the answer of one ramping step 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(500), "Delay between sending the inital 0's after a newly established connection to synchronize the output stream in milliseconds") ("volt-max-abs", var(75), "Absolte upper limit for the voltage (in Volts)") ("volt-max-rel", var(2.5), "Relative upper limit for the voltage w.r.t. the G-APD reference voltage (in Volts)") ("pixel-map-file", var("FACTmapV5a.txt"), "Pixel mapping file. Used here to get the default reference voltage.") ; 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 program controls the bias-power supply boards.\n" "\n" "Note: At default the program is started without a command line (user) " "interface. In this case Actions/Commands are available via Dim " "exclusively.\n" "Use the -c option to start the program with a command line interface.\n" "\n" "In the running application:\n" "Use h or help to print a short help message about its usage.\n" "\n" "Usage: biasctrl [-c type] [OPTIONS]\n" " or: biasctrl [OPTIONS]\n"; cout << endl; } void PrintHelp() { Main::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; }