#include #include #if BOOST_VERSION < 104400 #if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 4)) #undef BOOST_HAS_RVALUE_REFS #endif #endif #include //#include #include #include #include #include "Dim.h" #include "Event.h" #include "Shell.h" #include "StateMachineDim.h" #include "Connection.h" #include "Configuration.h" #include "Console.h" #include "Converter.h" #include "LocalControl.h" #include "HeadersFAD.h" #include "tools.h" namespace ba = boost::asio; namespace bs = boost::system; using ba::ip::tcp; using namespace std; // ------------------------------------------------------------------------ class ConnectionFAD : public Connection { vector fBuffer; protected: FAD::EventHeader fEventHeader; FAD::ChannelHeader fChannelHeader[FAD::kNumChannels]; private: bool fIsVerbose; bool fIsHexOutput; bool fIsDataOutput; uint64_t fCounter; protected: virtual void UpdateFirstHeader() { } virtual void UpdateEventHeader() { // emit service with trigger counter from header if (!fIsVerbose) return; Out() << endl << kBold << "Header received (N=" << dec << fCounter << "):" << endl; Out() << fEventHeader; if (fIsHexOutput) Out() << Converter::GetHex(fEventHeader, 16) << endl; } /* virtual void UpdateChannelHeader(int i) { // emit service with trigger counter from header if (!fIsVerbose) return; Out() << fChannelHeader[i]; if (fIsHexOutput) Out() << Converter::GetHex(fChannelHeader, 16) << endl; } */ virtual void UpdateChannelHeaders() { // emit service with trigger counter from header if (!fIsVerbose) return; Out() << dec << endl; Out() << kBold << "ID: Crate=" << fEventHeader.Crate() << " Board=" << fEventHeader.Board() << endl; for (unsigned int c=0; c(fChannelHeader, 16) << endl; } virtual void UpdateData(const uint16_t *data, size_t sz) { // emit service with trigger counter from header if (fIsVerbose && fIsDataOutput) Out() << Converter::GetHex(data, sz, 16, true) << endl; } private: enum { kReadHeader = 1, kReadData = 2, }; void HandleReceivedData(const bs::error_code& err, size_t bytes_received, int type) { // Do not schedule a new read if the connection failed. if (bytes_received==0 || err) { if (err==ba::error::eof) Warn("Connection closed by remote host (FAD)."); // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category)) // 125: Operation canceled if (err && err!=ba::error::eof && // Connection closed by remote host err!=ba::error::basic_errors::not_connected && // Connection closed by remote host err!=ba::error::basic_errors::operation_aborted) // Connection closed by us { ostringstream str; str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl; Error(str); } PostClose(err!=ba::error::basic_errors::operation_aborted); return; } if (type==kReadHeader) { if (bytes_received!=sizeof(FAD::EventHeader)) { ostringstream str; str << "Bytes received (" << bytes_received << " don't match header size " << sizeof(FAD::EventHeader); Error(str); PostClose(false); return; } fEventHeader = fBuffer; if (fEventHeader.fStartDelimiter!=FAD::kDelimiterStart) { ostringstream str; str << "Invalid header received: start delimiter wrong, received "; str << hex << fEventHeader.fStartDelimiter << ", expected " << FAD::kDelimiterStart << "."; Error(str); PostClose(false); return; } if (fCounter==0) UpdateFirstHeader(); UpdateEventHeader(); fCounter++; fBuffer.resize(fEventHeader.fPackageLength-sizeof(FAD::EventHeader)/2); AsyncRead(ba::buffer(fBuffer), kReadData); AsyncWait(fInTimeout, 50, &Connection::HandleReadTimeout); return; } fInTimeout.cancel(); if (ntohs(fBuffer.back())!=FAD::kDelimiterEnd) { ostringstream str; str << "Invalid data received: end delimiter wrong, received "; str << hex << ntohs(fBuffer.back()) << ", expected " << FAD::kDelimiterEnd << "."; Error(str); PostClose(false); return; } uint8_t *ptr = reinterpret_cast(fBuffer.data()); uint8_t *end = ptr + fBuffer.size()*2; for (unsigned int i=0; i end) { Error("Channel header exceeds buffer size."); PostClose(false); return; } fChannelHeader[i] = vector((uint16_t*)ptr, (uint16_t*)ptr+sizeof(FAD::ChannelHeader)/2); ptr += sizeof(FAD::ChannelHeader); //UpdateChannelHeader(i); if (ptr+fChannelHeader[i].fRegionOfInterest*2 > end) { Error("Data block exceeds buffer size."); PostClose(false); return; } uint16_t *data = reinterpret_cast(ptr); /* for (uint16_t *d=data; d ba::deadline_timer::traits_type::now()) return; Error("Timeout reading data from "+URL()); PostClose(); } // This is called when a connection was established void ConnectionEstablished() { fEventHeader.clear(); for (unsigned int i=0; i cmd) { ostringstream msg; msg << "Sending command:" << hex; msg << " 0x" << setw(4) << setfill('0') << cmd[0]; msg << " (+ " << cmd.size()-1 << " bytes data)"; Message(msg); transform(cmd.begin(), cmd.end(), cmd.begin(), htons); PostMessage(cmd); } void PostCmd(uint16_t cmd) { ostringstream msg; msg << "Sending command:" << hex; msg << " 0x" << setw(4) << setfill('0') << cmd; Message(msg); cmd = htons(cmd); PostMessage(&cmd, sizeof(uint16_t)); } void PostCmd(uint16_t cmd, uint16_t data) { ostringstream msg; msg << "Sending command:" << hex; msg << " 0x" << setw(4) << setfill('0') << cmd; msg << " 0x" << setw(4) << setfill('0') << data; Message(msg); const uint16_t d[2] = { htons(cmd), htons(data) }; PostMessage(d, sizeof(d)); } public: ConnectionFAD(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()), fIsVerbose(false), fIsHexOutput(false), fIsDataOutput(false), fCounter(0) { // Maximum possible needed space: // The full header, all channels with all DRS bins // Two trailing shorts fBuffer.reserve(sizeof(FAD::EventHeader) + FAD::kNumChannels*(sizeof(FAD::ChannelHeader) + FAD::kMaxBins*sizeof(uint16_t)) + 2*sizeof(uint16_t)); SetLogStream(&imp); } void Cmd(FAD::Enable cmd, bool on=true) { PostCmd(cmd + (on ? 0 : 0x100)); } // ------------------------------ // IMPLEMENT: Abs/Rel void CmdPhaseShift(int16_t val) { vector cmd(abs(val)+2, FAD::kCmdPhaseApply); cmd[0] = FAD::kCmdPhaseReset; cmd[1] = val<0 ? FAD::kCmdPhaseDecrease : FAD::kCmdPhaseIncrease; PostCmd(cmd); } bool CmdSetTriggerRate(int32_t val) { if (val<0 || val>0xffff) return false; PostCmd(FAD::kCmdWriteRate, val);//uint8_t(1000./val/12.5)); //PostCmd(kCmdContTriggerRate, uint8_t(80/val)); return true; } void CmdSetRunNumber(uint32_t num) { PostCmd(FAD::kCmdWriteRunNumberLSW, num&0xffff); PostCmd(FAD::kCmdWriteRunNumberMSW, num>>16); } void CmdSetRegister(uint8_t addr, uint16_t val) { // Allowed addr: [0, MAX_ADDR] // Allowed value: [0, MAX_VAL] PostCmd(FAD::kCmdWrite + addr, val); } bool CmdSetDacValue(uint8_t addr, uint16_t val) { if (addr>FAD::kMaxDacAddr) // NDAC return false; PostCmd(FAD::kCmdWriteDac + addr, val); return true; } bool CmdSetRoi(int8_t addr, uint16_t val) { if (val>FAD::kMaxRoiValue) return false; if (addr<0) { for (unsigned int i=0; i<=FAD::kMaxRoiAddr; i++) PostCmd(FAD::kCmdWriteRoi + i, val); return true; } if (uint8_t(addr)>FAD::kMaxRoiAddr) return false; PostCmd(FAD::kCmdWriteRoi + addr, val); return true; } bool CmdSetRoi(uint16_t val) { return CmdSetRoi(-1, val); } void AmplitudeCalibration() { // ------------- case baseline ----------------- CmdSetRoi(-1, FAD::kMaxBins); CmdSetDacValue(1, 0); CmdSetDacValue(2, 0); CmdSetDacValue(3, 0); // Take N events /* // ====== Part B: Baseline calibration ===== // Loop over all channels(ch) and time-slices (t) T0 = TriggerCell[chip] Sum[ch][(t+T0) % kMaxBins] += Data[ch][t]; // FIXME: Determine median instead of average Baseline[ch][slice] = MEDIAN( sum[ch][slice] ) */ // --------------- case gain ------------------- // Set new DAC values and start accumulation CmdSetDacValue(1, 50000); CmdSetDacValue(2, 50000); CmdSetDacValue(3, 50000); // Take N events /* // ====== Part C: Gain calibration ===== T0 = TriggerCell[chip] Sum[ch][(t+T0) % kMaxBins] += Data[ch][t]; // FIXME: Determine median instead of average Gain[ch][slice] = MEDIAN( sum[ch][slice] ) - Baseline[ch][slice] */ // --------------- secondary ------------------ // FIXME: Can most probably be done together with the baseline calibration // FIXME: Why does the secondary baseline not influence the baseline? CmdSetDacValue(1, 0); CmdSetDacValue(2, 0); CmdSetDacValue(3, 0); // Take N events /* // ====== Part D: Secondary calibration ===== T0 = TriggerCell[chip] Sum[ch][t] = Data[ch][t] - Baseline[ch][(i-T0) % kMaxBins]; // Determine secondary baseline if integration finished SecondaryBaseline[ch][t] = MEDIAN( Sum[ch][t] ) */ } void SetVerbose(bool b) { fIsVerbose = b; } void SetHexOutput(bool b) { fIsHexOutput = b; } void SetDataOutput(bool b) { fIsDataOutput = b; } }; // ------------------------------------------------------------------------ /* #include "DimDescriptionService.h" class ConnectionDimFAD : public ConnectionFAD { private: DimDescribedService fDimPassport; DimDescribedService fDimTemperatures; DimDescribedService fDimSetup; DimDescribedService fDimEventHeader; template void Update(DimDescribedService &svc, const T &data) const { //cout << "Update: " << svc.getName() << " (" << sizeof(T) << ")" << endl; svc.setData(const_cast(&data), sizeof(T)); svc.updateService(); } void UpdateFirstHeader() { ConnectionFAD::UpdateFirstHeader(); const FAD::DimPassport data(fEventHeader); Update(fDimPassport, data); } void UpdateEventHeader() { ConnectionFAD::UpdateEventHeader(); const FAD::DimTemperatures data0(fEventHeader); const FAD::DimSetup data1(fEventHeader); const FAD::DimEventHeader data2(fEventHeader); Update(fDimTemperatures, data0); Update(fDimSetup, data1); Update(fDimEventHeader, data2); } public: ConnectionDimFAD(ba::io_service& ioservice, MessageImp &imp) : ConnectionFAD(ioservice, imp), fDimPassport ("FAD_CONTROL/PASSPORT", "I:1;S:2;X:1", ""), fDimTemperatures("FAD_CONTROL/TEMPERATURES", "I:1;F:4", ""), fDimSetup ("FAD_CONTROL/SETUP", "I:2;S:12", ""), fDimEventHeader ("FAD_CONTROL/EVENT_HEADER", "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 }; */ // ------------------------------------------------------------------------ #include "EventBuilderWrapper.h" // ------------------------------------------------------------------------ template class StateMachineFAD : public T, public EventBuilderWrapper, public ba::io_service, public ba::io_service::work { private: typedef pair Connection; typedef pair Board; typedef map BoardList; BoardList fBoards; bool fIsVerbose; bool fIsHexOutput; bool fIsDataOutput; 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; } int Cmd(FAD::Enable command) { for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++) i->second.second->Cmd(command); return T::GetCurrentState(); } int CmdEnable(const EventImp &evt, FAD::Enable command) { if (!CheckEventSize(evt.GetSize(), "CmdEnable", 1)) return T::kSM_FatalError; for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++) i->second.second->Cmd(command, evt.GetBool()); return T::GetCurrentState(); } bool Check(const uint32_t *dat, uint32_t maxaddr, uint32_t maxval) { if (dat[0]>FAD::kMaxRegAddr) { ostringstream msg; msg << hex << "Address " << dat[0] << " out of range, max=" << maxaddr << "."; T::Error(msg); return false; } if (dat[1]>FAD::kMaxRegValue) { ostringstream msg; msg << hex << "Value " << dat[1] << " out of range, max=" << maxval << "."; T::Error(msg); return false; } return true; } int SetRegister(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetRegister", 8)) return T::kSM_FatalError; const uint32_t *dat = evt.Ptr(); if (!Check(dat, FAD::kMaxRegAddr, FAD::kMaxRegValue)) return T::GetCurrentState(); for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++) i->second.second->CmdSetRegister(dat[0], dat[1]); return T::GetCurrentState(); } int SetRoi(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetRoi", 8)) return T::kSM_FatalError; // ---- was uint32_t const int32_t *dat = evt.Ptr(); // ---- -1 for all //if (!Check(dat, FAD::kMaxRoiAddr, FAD::kMaxRoiValue)) // return T::GetCurrentState(); for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++) i->second.second->CmdSetRoi(dat[0], dat[1]); return T::GetCurrentState(); } int SetDac(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetDac", 8)) return T::kSM_FatalError; const uint32_t *dat = evt.Ptr(); if (!Check(dat, FAD::kMaxDacAddr, FAD::kMaxDacValue)) return T::GetCurrentState(); for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++) i->second.second->CmdSetDacValue(dat[0], dat[1]); return T::GetCurrentState(); } int Trigger(int n) { for (int nn=0; nnsecond.second->Cmd(FAD::kCmdSingleTrigger); return T::GetCurrentState(); } int SendTriggers(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SendTriggers", 4)) return T::kSM_FatalError; Trigger(evt.GetUInt()); return T::GetCurrentState(); } int StartRun(const EventImp &evt, bool start) { if (!CheckEventSize(evt.GetSize(), "StartRun", 0)) return T::kSM_FatalError; for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++) i->second.second->Cmd(FAD::kCmdRun, start); return T::GetCurrentState(); } int PhaseShift(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "PhaseShift", 2)) return T::kSM_FatalError; for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++) i->second.second->CmdPhaseShift(evt.GetShort()); return T::GetCurrentState(); } int SetTriggerRate(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetTriggerRate", 4)) return T::kSM_FatalError; if (evt.GetUShort()>0xff) { ostringstream msg; msg << hex << "Value " << evt.GetUShort() << " out of range, max=" << 0xff << "(?)"; T::Error(msg); return false; } for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++) i->second.second->CmdSetTriggerRate(evt.GetUInt()); return T::GetCurrentState(); } int SetRunNumber(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetRunNumber", 8)) return T::kSM_FatalError; const uint64_t num = evt.GetUXtra(); if (num>FAD::kMaxRunNumber) { ostringstream msg; msg << hex << "Value " << num << " out of range, max=" << FAD::kMaxRunNumber; T::Error(msg); return false; } for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++) i->second.second->CmdSetRunNumber(num); return T::GetCurrentState(); } int Test(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "Test", 2)) return T::kSM_FatalError; SetMode(evt.GetShort()); return T::GetCurrentState(); } int SetVerbosity(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1)) return T::kSM_FatalError; for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++) i->second.second->SetVerbose(evt.GetBool()); return T::GetCurrentState(); } int SetHexOutput(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetHexOutput", 1)) return T::kSM_FatalError; for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++) i->second.second->SetHexOutput(evt.GetBool()); return T::GetCurrentState(); } int SetDataOutput(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetDataOutput", 1)) return T::kSM_FatalError; for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++) i->second.second->SetDataOutput(evt.GetBool()); return T::GetCurrentState(); } const BoardList::iterator GetSlot(int slot) { const BoardList::iterator i = fBoards.find(slot); if (i!=fBoards.end()) return i; ostringstream str; str << "Slot " << slot << " not found."; T::Warn(str.str()); return fBoards.end(); } int AddAddress(const EventImp &evt) { const string addr = Tools::Trim(evt.GetText()); for (BoardList::const_iterator i=fBoards.begin(); i!=fBoards.end(); i++) { if (i->second.first==addr) { T::Warn("Address "+addr+" already known.... ignored."); return T::GetCurrentState(); } } AddEndpoint(addr); return T::GetCurrentState(); } int RemoveSlot(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "RemoveSlot", 2)) return T::kSM_FatalError; const int16_t slot = evt.GetShort(); const BoardList::iterator v = GetSlot(slot); if (v!=fBoards.end()) { delete v->second.second; fBoards.erase(v); } return T::GetCurrentState(); } int ListSlots() { for (BoardList::const_iterator i=fBoards.begin(); i!=fBoards.end(); i++) { ostringstream str; str << "Slot " << setw(2) << (int)i->first << ": " << i->second.first; const ConnectionFAD *c = i->second.second; if (c->IsConnecting()) str << " (0:connecting, "; else { if (c->IsClosed()) str << " (0:disconnected, "; if (c->IsConnected()) str << " (0:connected, "; } switch (fStatus2[i->first]) { case 0: str << "1-7:not connected)"; break; case 7: str << "1-7:connected)"; break; default: str << "1-7:connecting [" << fStatus2[i->first] << "])"; break; } T::Out() << str.str() << endl; } T::Out() << "Thread :"; if (!IsThreadRunning()) T::Out() << " not"; T::Out() << " running" << endl; // FIXME: Output state return T::GetCurrentState(); } void EnableSlot(BoardList::iterator i, bool enable=true) { if (i==fBoards.end()) return; ConnectionFAD* &ptr = i->second.second; if (!enable) ptr->PostClose(false); else { ptr->SetEndpoint(i->second.first); ptr->StartConnect(); } } void EnableAll(bool enable=true) { for (BoardList::iterator i=fBoards.begin(); i!=fBoards.end(); i++) EnableSlot(i, enable); } /* int Enable(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "Enable", 3)) return T::kSM_FatalError; const int16_t slot = evt.GetShort(); const bool enable = evt.GetText()[2]>0; if (slot<0) { EnableAll(enable); return T::GetCurrentState(); } EnableSlot(GetSlot(slot), enable); return T::GetCurrentState(); }*/ int Disconnect() { Exit(); EnableAll(false); return T::GetCurrentState(); } int Connect() { vector addr; for (BoardList::const_iterator i=fBoards.begin(); i!=fBoards.end(); i++) addr.push_back(i->second.first); Start(addr); EnableAll(true); return T::GetCurrentState(); } /* int Reconnect(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "Reconnect", 2)) return T::kSM_FatalError; const int16_t slot = evt.GetShort(); if (slot<0) { // Close all connections to supress the warning in SetEndpoint for (BoardList::const_iterator i=fBoards.begin(); i!=fBoards.end(); i++) i->second.second->PostClose(false); // Now wait until all connection have been closed and // all pending handlers have been processed poll(); // Now we can reopen the connection for (BoardList::const_iterator i=fBoards.begin(); i!=fBoards.end(); i++) i->second.second->PostClose(true); return T::GetCurrentState(); } const BoardList::const_iterator v = GetSlot(slot); if (v==fBoards.end()) return T::GetCurrentState(); // Close all connections to supress the warning in SetEndpoint v->second.second->PostClose(false); // Now wait until all connection have been closed and // all pending handlers have been processed poll(); // Now we can reopen the connection v->second.second->PostClose(true); return T::GetCurrentState(); }*/ virtual void UpdateConnectionStatus() { //cout << "Connection Status changed prop to Dim." << endl; } vector fStatus1; vector fStatus2; 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(); // ===== Evaluate connection status ===== uint16_t nconnecting1 = 0; uint16_t nconnecting2 = 0; uint16_t nconnected1 = 0; uint16_t nconnected2 = 0; vector stat1(40); vector stat2(40); int cnt = 0; for (BoardList::const_iterator i=fBoards.begin(); i!=fBoards.end(); i++) { const ConnectionFAD &c = *i->second.second; // ----- Command socket ----- if (c.IsConnecting()) { stat1[i->first] = 1; nconnecting1++; } if (c.IsConnected()) { stat1[i->first] = 2; nconnected1++; } // ----- Event builder ----- stat2[i->first] = GetNumConnected(cnt); if (!IsConnected(cnt) && !IsDisconnected(cnt)) nconnecting2++; if (IsConnected(cnt)) nconnected2++; } // ===== Send connection status via dim ===== if (fStatus1!=stat1 || fStatus2!=stat2) { fStatus1 = stat1; fStatus2 = stat2; UpdateConnectionStatus(); } // ===== Return connection status ===== // fadctrl: Always connecting if not disabled // event builder: if (nconnected1==fBoards.size() && nconnected2==fBoards.size()) return FAD::kConnected; if (nconnected1==0 && nconnected2==0) return IsThreadRunning() ? FAD::kDisconnected : FAD::kOffline; // FIXME: Evaluate event builder status return FAD::kConnecting; } void AddEndpoint(const string &addr) { if (fBoards.size()==40) { T::Warn("Not more than 40 slots allowed."); return; } int i=0; while (1) { const BoardList::const_iterator v = fBoards.find(i); if (v==fBoards.end()) break; i++; } fBoards[i] = make_pair(addr, new ConnectionFAD(*this, *this)); fBoards[i].second->SetVerbose(fIsVerbose); fBoards[i].second->SetHexOutput(fIsHexOutput); fBoards[i].second->SetDataOutput(fIsDataOutput); } public: StateMachineFAD(ostream &out=cout) : T(out, "FAD_CONTROL"), EventBuilderWrapper(static_cast(*this)), ba::io_service::work(static_cast(*this)), fStatus1(40), fStatus2(40) { // 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(FAD::kOffline, "Offline", "All enabled FAD boards are disconnected and the event-builer thread is not running."); T::AddStateName(FAD::kDisconnected, "Disconnected", "All enabled FAD boards are disconnected, but the event-builder thread is running."); T::AddStateName(FAD::kConnected, "Connected", "All enabled FAD boards are connected.."); T::AddStateName(FAD::kConnecting, "Connecting", "Only some enabled FAD boards are connected."); // FAD Commands T::AddEvent("ENABLE_SRCLK", "B:1") (boost::bind(&StateMachineFAD::CmdEnable, this, _1, FAD::kCmdSrclk)) ("Set SRCLK"); T::AddEvent("ENABLE_SCLK", "B:1") (boost::bind(&StateMachineFAD::CmdEnable, this, _1, FAD::kCmdSclk)) ("Set SCLK"); T::AddEvent("ENABLE_DRS", "B:1") (boost::bind(&StateMachineFAD::CmdEnable, this, _1, FAD::kCmdDrsEnable)) ("Switch Domino wave"); T::AddEvent("ENABLE_DWRITE", "B:1") (boost::bind(&StateMachineFAD::CmdEnable, this, _1, FAD::kCmdDwrite)) ("Set Dwrite (possibly high / always low)"); T::AddEvent("SET_DEBUG_MODE", "B:1") (boost::bind(&StateMachineFAD::CmdEnable, this, _1, FAD::kCmdSocket)) ("Set debug mode (yes: dump events through command socket, no=dump events through other sockets)"); T::AddEvent("ENABLE_TRIGGER_LINE", "B:1") (boost::bind(&StateMachineFAD::CmdEnable, this, _1, FAD::kCmdTriggerLine)) ("Incoming triggers can be accepted/will not be accepted"); T::AddEvent("SET_TRIGGER_RATE", "I:1") (boost::bind(&StateMachineFAD::SetTriggerRate, this, _1)) ("Enable continous trigger"); T::AddEvent("SEND_SINGLE_TRIGGER") (boost::bind(&StateMachineFAD::Trigger, this, 1)) ("Issue software triggers"); T::AddEvent("SEND_N_TRIGGERS", "I") (boost::bind(&StateMachineFAD::SendTriggers, this, _1)) ("Issue software triggers"); T::AddEvent("START", "") (boost::bind(&StateMachineFAD::StartRun, this, _1, true)) ("Set FAD DAQ mode. when started, no configurations must be send."); T::AddEvent("STOP") (boost::bind(&StateMachineFAD::StartRun, this, _1, false)) (""); T::AddEvent("PHASE_SHIFT", "S:1") (boost::bind(&StateMachineFAD::PhaseShift, this, _1)) ("Adjust ADC phase (in 'steps')"); T::AddEvent("CONTINOUS_TRIGGER_ON") (boost::bind(&StateMachineFAD::Cmd, this, FAD::kCmdContTriggerOn)) (""); T::AddEvent("CONTINOUS_TRIGGER_OFF") (boost::bind(&StateMachineFAD::Cmd, this, FAD::kCmdContTriggerOff)) (""); T::AddEvent("RESET_TRIGGER_ID") (boost::bind(&StateMachineFAD::Cmd, this, FAD::kCmdResetTriggerId)) (""); T::AddEvent("SET_RUN_NUMBER", "X:1") (boost::bind(&StateMachineFAD::SetRunNumber, this, _1)) (""); T::AddEvent("SET_REGISTER", "I:2") (boost::bind(&StateMachineFAD::SetRegister, this, _1)) ("set register to value" "|addr[short]:Address of register" "|val[short]:Value to be set"); // FIXME: Maybe add a mask which channels should be set? T::AddEvent("SET_REGION_OF_INTEREST", "I:2") (boost::bind(&StateMachineFAD::SetRoi, this, _1)) ("Set region-of-interest to value" "|addr[short]:Address of register" "|val[short]:Value to be set"); // FIXME: Maybe add a mask which channels should be set? T::AddEvent("SET_DAC_VALUE", "I:2") (boost::bind(&StateMachineFAD::SetDac, this, _1)) ("Set DAC numbers in range to value" "|addr[short]:Address of register" "|val[short]:Value to be set"); // Verbosity commands T::AddEvent("SET_VERBOSE", "B") (boost::bind(&StateMachineFAD::SetVerbosity, this, _1)) ("set verbosity state" "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data"); T::AddEvent("SET_HEX_OUTPUT", "B") (boost::bind(&StateMachineFAD::SetHexOutput, this, _1)) ("enable or disable hex output for received data" "|hexout[bool]:disable or enable hex output for received data (yes/no)"); T::AddEvent("SET_DATA_OUTPUT", "B") (boost::bind(&StateMachineFAD::SetDataOutput, this, _1)) (""); // Conenction commands /* T::AddEvent("ENABLE", "S:1;B:1", FAD::kDisconnected) (boost::bind(&StateMachineFAD::Enable, this, _1)) ("");*/ T::AddEvent("CONNECT", FAD::kOffline) (boost::bind(&StateMachineFAD::Connect, this)) (""); T::AddEvent("DISCONNECT", FAD::kDisconnected, FAD::kConnecting, FAD::kConnected) (boost::bind(&StateMachineFAD::Disconnect, this)) (""); T::AddEvent("TEST", "S:1") (boost::bind(&StateMachineFAD::Test, this, _1)) (""); T::AddEvent("ADD_ADDRESS", "C", FAD::kOffline) (boost::bind(&StateMachineFAD::AddAddress, this, _1)) ("Add the address of a DRS4 board to the first free slot" "|IP[string]:address in the format "); T::AddEvent("REMOVE_SLOT", "S:1", FAD::kOffline) (boost::bind(&StateMachineFAD::RemoveSlot, this, _1)) ("Remove the Iaddress in slot n. For a list see LIST" "|slot[int]:Remove the address in slot n from the list"); T::AddEvent("LIST_SLOTS") (boost::bind(&StateMachineFAD::ListSlots, this)) ("Print a list of all available board addressesa and whether they are enabled"); } ~StateMachineFAD() { for (BoardList::const_iterator i=fBoards.begin(); i!=fBoards.end(); i++) delete i->second.second; fBoards.clear(); } bool SetConfiguration(const Configuration &conf) { fIsVerbose = !conf.Get("quiet"); fIsHexOutput = conf.Get("hex-out"); fIsDataOutput = conf.Get("data-out"); SetMaxMemory(conf.Get("max-mem")); // vvvvv for debugging vvvvv if (conf.Has("debug-port")) { const int port = conf.Get("debug-port"); const int num = conf.Get("debug-num"); for (int i=0; i("base-addr"); const size_t p0 = base.find_first_of(':'); const size_t p1 = base.find_last_of(':'); if (p0==string::npos || p0!=p1) { T::Out() << kRed << "SetConfiguration - Wrong format of argument --base-addr ('host:port' expected)" << endl; return false; } tcp::resolver resolver(get_io_service()); boost::system::error_code ec; const tcp::resolver::query query(base.substr(0, p0), base.substr(p0+1)); const tcp::resolver::iterator iterator = resolver.resolve(query, ec); if (ec) { T::Out() << " " << ec.message() << " (" << ec << ")"; return false; } const tcp::endpoint endpoint = *iterator; const ba::ip::address_v4::bytes_type ip = endpoint.address().to_v4().to_bytes(); if (ip[2]>250 || ip[3]>244) { T::Out() << kRed << "SetConfiguration - IP address given by --base-addr out-of-range." << endl; return false; } for (int crate=0; crate<2; crate++) for (int board=0; board<10; board++) { //if (crate==0 && board==2) // continue; ostringstream str; str << (int)ip[0] << "." << (int)ip[1] << "."; str << (int)(ip[2]+crate) << "." << (int)(ip[3]+board) << ":"; str << endpoint.port(); AddEndpoint(str.str()); } } if (conf.Has("addr")) { const vector addrs = conf.Get>("addr"); for (vector::const_iterator i=addrs.begin(); iRun(); Readline::Stop(); } template int RunDim(Configuration &conf) { /* initscr(); // Start curses mode cbreak(); // Line buffering disabled, Pass on intrflush(stdscr, FALSE); start_color(); // Initialize ncurses colors use_default_colors(); // Assign terminal default colors to -1 for (int i=1; i<8; i++) init_pair(i, i, -1); // -1: def background scrollok(stdscr, true); */ WindowLog wout; //log.SetWindow(stdscr); if (conf.Has("log")) if (!wout.OpenLogFile(conf.Get("log"))) wout << kRed << "ERROR - Couldn't open log-file " << conf.Get("log") << ": " << strerror(errno) << endl; // Start io_service.Run to use the StateMachineImp::Run() loop // Start io_service.run to only use the commandHandler command detaching StateMachineFAD io_service(wout); if (!io_service.SetConfiguration(conf)) return -1; io_service.Run(); return 0; } template int RunShell(Configuration &conf) { static T shell(conf.GetName().c_str(), conf.Get("console")!=1); WindowLog &win = shell.GetStreamIn(); WindowLog &wout = shell.GetStreamOut(); if (conf.Has("log")) if (!wout.OpenLogFile(conf.Get("log"))) win << kRed << "ERROR - Couldn't open log-file " << conf.Get("log") << ": " << strerror(errno) << endl; StateMachineFAD io_service(wout); if (!io_service.SetConfiguration(conf)) return -1; shell.SetReceiver(io_service); boost::thread t(boost::bind(RunThread, &io_service)); //boost::thread t(boost::bind(&StateMachineFAD::Run, &io_service)); shell.Run(); // Run the shell io_service.Stop(); // Signal Loop-thread to stop // Wait until the StateMachine has finished its thread // before returning and destroying the dim objects which might // still be in use. t.join(); return 0; } void SetupConfiguration(Configuration &conf) { const string n = conf.GetName()+".log"; po::options_description config("Program options"); config.add_options() ("dns", var("localhost"), "Dim nameserver host name (Overwites DIM_DNS_NODE environment variable)") ("log,l", var(n), "Write log-file") ("no-dim,d", po_switch(), "Disable dim services") ("console,c", var(), "Use console (0=shell, 1=simple buffered, X=simple unbuffered)") ; po::options_description control("FAD control options"); control.add_options() ("quiet,q", po_bool(), "Disable printing contents of all received messages in clear text.") ("hex-out", po_bool(), "Enable printing contents of all printed messages also as hex data.") ("data-out", po_bool(), "Enable printing received event data.") ; po::options_description builder("Event builder options"); builder.add_options() ("max-mem,m", var(100), "Maximum memory the event builder thread is allowed to consume for its event buffer") ; po::options_description connect("FAD connection options"); connect.add_options() ("addr", vars(), "Network address of FAD") ("base-addr", var(), "Base address of all FAD") ("debug-num,n", var(40), "Sets the number of fake boards to be connected locally") ("debug-port,p", var(), "Sets addresses to 'localhost:' in steps of 8") ; conf.AddEnv("dns", "DIM_DNS_NODE"); conf.AddOptions(config); conf.AddOptions(control); conf.AddOptions(builder); conf.AddOptions(connect); } void PrintUsage() { cout << "The fadctrl controls the FAD 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: fadctrl [-c type] [OPTIONS]\n" " or: fadctrl [OPTIONS]\n"; cout << endl; } void PrintHelp() { /* Additional help text which is printed after the configuration options goes here */ } int main(int argc, const char* argv[]) { Configuration conf(argv[0]); conf.SetPrintUsage(PrintUsage); SetupConfiguration(conf); po::variables_map vm; try { vm = conf.Parse(argc, argv); } #if BOOST_VERSION > 104000 catch (po::multiple_occurrences &e) { cerr << "Program options invalid due to: " << e.what() << " of '" << e.get_option_name() << "'." << endl; return -1; } #endif catch (exception& e) { cerr << "Program options invalid due to: " << e.what() << endl; return -1; } if (conf.HasVersion() || conf.HasPrint()) return -1; if (conf.HasHelp()) { PrintHelp(); return -1; } Dim::Setup(conf.Get("dns")); // try { // No console access at all if (!conf.Has("console")) { if (conf.Get("no-dim")) return RunDim(conf); else return RunDim(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; }