#ifndef BASE_BOARD_H #define BASE_BOARD_H #include "base/OperList.h" #include #include #include #include namespace base { class BoardFactory; enum ClientRole { roleNone = 0, //!< no defined role roleObserver = 1, //!< pure observer, no changes roleControl = 2, //!< allowed to change state, no DAQ roleDAQ = 3 //!< full control including DAQ }; extern const char* roleToString(ClientRole role); extern ClientRole defineClientRole(const char* name, ClientRole dflt = roleObserver); enum EFrontendKinds { kind_nXYTER = 1, // normal nXYTER kind_oldFEET = 2, // old GET4, keep for historical reasons kind_FEET = 3, // new GET4 v1.0, kind_newNX = 4, // new readout for nXYTER with SysCore3 kind_SPADIC = 5 // future SPADIC v1 firmware }; enum EBackendKinds { kind_Optic = 1, // normal Optic readout - CBMNet v1 kind_FX20 = 2, kind_FX40 = 3, kind_FX60 = 4, kind_newOptic = 5 // new optic readout - CBMNet v2 }; enum ETransportKinds { kind_UDP = 0, // Ethernet for SysCore2 kind_ABB = 1, // Optic via AVNet kind_File = 2, // LMD file kind_USB = 3 // USB to SysCore3 }; enum BasicAddresses { addr_HardwareType = 0x00, addr_HardwareVersion = 0x04, addr_BoardId = 0x10 }; /** Base abstract class for all kinds of boards access class */ class Board { friend class BoardFactory; protected: typedef std::map mapn2a_t; typedef std::map mapa2n_t; mapn2a_t fMapName2Addr; mapa2n_t fMapAddr2Name; BoardFactory* fBrdFactory; //!< factory used to create board instance double fDefaultTimeout; //!< default timeout int fVerbosity; //!< Debug verbosity level int fOperTrace; //!< Oper trace level ClientRole fRole; //!< client role (access rights) uint32_t fBrdNumber; //!< number of connected ROC Board(); virtual ~Board(); void traceOper(OperList& lst, int rc); void setRole(ClientRole role) { fRole = role; } uint32_t initBoard(const char* name, uint32_t minversion); public: //! Return codes for get, put and oper calls /*! * For a detailed documentation of get/put/oper return code see * \ref roc_board_opererr. */ enum operErr { kOperSuccess = 0, //!< success kOperAddrErr = 2, //!< invalid address kOperValueErr = 3, //!< value not allowed in context kOperStateErr = 4, //!< operation not allowed in state kOperNetworkErr = 6, //!< network communication error kHostError = 8, //!< PC software stack errpr kOperVerifyErr = 20, //!< readback after write mismatch kOperBusErr = 21 //!< secondary bus error }; enum addrErr { kAddrError = 0xffffffff //! failure address, returned by some methods }; //! Set default get/put/oper timeout. /*! * The value set with this method is used whenever no explicit * timeout is specified in a get(), put() or openGen() call. * \param tmout network communication timeout in sec. * \sa getDefaultTimeout() */ void setDefaultTimeout(double tmout = 2.) { fDefaultTimeout = tmout>0 ? tmout : 1.; } /** Returns default get/put/oper timeout. \sa setDefaultTimeout() */ double getDefaultTimeout() const { return fDefaultTimeout; } /** Set debug output verbosity level. \sa getVerbosity(), Debug() */ void setVerbosity(int lvl = 0) { fVerbosity = lvl; } //! Returns debug output verbosity level. \sa setVerbosity(), Debug() int getVerbosity() const { return fVerbosity; } //! Returns client role ClientRole getRole() const { return fRole; } /** Show debug information, using functionality of factory */ void Debug(int lvl, const char *fmt, ...); //! Set get/put/operGen trace level. /*! * Controls whether the full context of get(), put() and operGen() * requests is printed to stdout. The \a lvl determines whether all, * none or only failed requests are traced: * \li 0 no trace at all * \li 1 trace if network error (default setting) * \li 2 trace if any error * \li 3 trace all * * The output format is like (-> indicates get, <- indicates put): \verbatim get (3,0x00000580 ROC_GPIO_CONFIG ) -> 0x00000000 0 ok put (3,0x00000580 ROC_GPIO_CONFIG ) <- 0x00000100 256 ok op[00](3,0x00000514 ROC_SYNC2_BAUD_START ) <- 0x00000002 2 op[01](3,0x00000518 ROC_SYNC2_BAUD1 ) <- 0x00000004 4 op[02](3,0x0000051c ROC_SYNC2_BAUD2 ) <- 0x00000004 4 ok put (3,0x00000500 ROC_SYNC1_M_SCALEDOWN ) <- 0x00000004 4 ok op[00](3,0x00000504 ROC_SYNC1_BAUD_START ) <- 0x00000002 2 op[01](3,0x00000508 ROC_SYNC1_BAUD1 ) <- 0x00000004 4 op[02](3,0x0000050c ROC_SYNC1_BAUD2 ) <- 0x00000004 4 ok op[00](3,0x00000054 ROC_TESTPULSE_LENGTH ) <- 0x0000c34f 49999 op[01](3,0x00000058 ROC_TESTPULSE_NUMBER ) <- 0x00000000 0 op[02](3,0x0000005c ROC_TESTPULSE_START ) <- 0x00000001 1 ok op[00](3,0x00010100 ROC_I2C1_SWITCH ) <- 0x00000000 0 op[01](3,0x0001000c ROC_I2C1_SLAVEADDR ) <- 0x00000008 8 op[02](3,0x00010010 ROC_I2C1_REGISTER ) <- 0x00000015 21 op[03](3,0x00010000 ROC_I2C1_DATA ) -> 0x0000000f 15 op[04](3,0x00010020 ROC_I2C1_ERROR ) -> 0x00000000 0 ok \endverbatim * \sa getOperTrace() */ void setOperTrace(int lvl = 1) { fOperTrace = lvl; } //! Returns debug output verbosity level. \sa setOperTrace() int getOperTrace() const { return fOperTrace; } //! General operate, arbitrary list of get/put accesses to ROC control space. /*! * This function allow to execute a list of gets and puts from and to the * ROC control address space. It is guaranteed that the execution is an * atomic action, no control space access from other sources will be * handled concurrently. * \param isput array of flags, if \c true a put is done, otherwise a get. * \param addr array of ROC control space addresses * \param value array of data values. If the corresponding entry in \a isput * is true the array element is read and written into the ROC address, * otherwise the array element is written by data retrieved from the * ROC address. * \param num number of operates to do, size of arrays \a isput, \a addr, * and \a value * \param tmout maximal network response time * \returns see \ref roc_board_opererr * * \sa operPG(), operPP(), operPPP(), operPPPP() */ virtual int operGen(OperList& lst, double tmout = 0.) = 0; /** Method need to be virtual to satisfy compiler rules */ virtual int operGen(bool* isput, uint32_t* addr, uint32_t* value, int num, double tmout = 0.); /** Method returns board id */ virtual unsigned GetBoardId() const { return 0; } int put(uint32_t addr, uint32_t value, double tmout = 0.); int get(uint32_t addr, uint32_t& value, double tmout = 0.); int operPP(uint32_t addr0, uint32_t val0p, uint32_t addr1, uint32_t val1p, double tmout = 0.); int operPG(uint32_t addr0, uint32_t val0p, uint32_t addr1, uint32_t& val1g, double tmout = 0.); int operPPP(uint32_t addr0, uint32_t val0p, uint32_t addr1, uint32_t val1p, uint32_t addr2, uint32_t val2p, double tmout = 0.); int operGGG(uint32_t addr0, uint32_t& val0p, uint32_t addr1, uint32_t& val1p, uint32_t addr2, uint32_t& val2p, double tmout = 0.); int operPPPP(uint32_t addr0, uint32_t val0p, uint32_t addr1, uint32_t val1p, uint32_t addr2, uint32_t val2p, uint32_t addr3, uint32_t val3p, double tmout = 0.); //! Retrieve error code from get/put/oper return code. /*! * Extracts the error code, in general one of the values specified in * the board::operErr enum, from a return code \a rc of a board access * method. * \sa \ref roc_board_opererr. */ static int operErrCode(int rc) { return rc & 0xff; } //! Retrieve index from get/put/oper return code /*! * Extracts the index value from a return code \a rc of a board access. * method. The index allows to reconstruct at which point of an * access sequence the error occurred. * \sa \ref roc_board_opererr. */ static int operErrIndex(int rc) { return (rc>>8) & 0xffffff; } //! Build get/put/oper return code from error code and index value. /*! * Build a return code for a board access method from an error * code \a code, in general one of the values specified in the * board::operErr enum, and an index value \a index. * \param code error code. Only the lower 8 bits are used, to * one can use a full return code, the index part will be discarded. * \param index index or stage number at the point of failure * \sa \ref roc_board_opererr. * \sa operErrBuildInc() */ static int operErrBuild(int code, int index) { return (code & 0xff) | ((index & 0xffffff)<<8); } //! Add to index value of get/put/oper return code. /*! * \param rc return code from board access method * \param inc value to be added to the index field * \returns the return code \a rc with the index incremented by \a inc * \sa \ref roc_board_opererr. * \sa operErrBuild() */ static int operErrBuildInc(int rc, int inc) { return operErrBuild(rc, operErrIndex(rc) + inc); } static std::string operErrToString(int rc); static const char* operErrCodeName(int rc); /** Methods about map of registers addresses and their symbolic names */ uint32_t findRegAddressByName(const char* name); const char* findRegNameByAddress(uint32_t addr); void printRegAddressMap(std::ostream& os, bool byname=false); void addRegAddrMapping(const char* name, uint32_t addr); void fillRegAddrNames(std::list& lst); //! getTransportKind /*! * Returns kind of transport - base::kind_UDP, base::kind_ABB or base::kind_File */ virtual int getTransportKind() const = 0; //! isFile /*! * Returns true if board is just envelope around file reading - * most operation like put/get are nit working and should not be tried */ bool isFile() const { return getTransportKind() == base::kind_File; } //! isOptic - returns true when optic use for connection bool isOptic() const { return getTransportKind() == base::kind_ABB; } //! isUSB - returns true when optic use for connection bool isUSB() const { return getTransportKind() == base::kind_USB; } //! resetBoard - method should restart board. Return true if implemented. One should immediately close connection to board virtual bool restartBoard() { return false; } //! is_SPI_I2C - return true if SPI interface should be used to access I2C bus virtual bool is_SPI_I2C() const { return false; } //! setToDefault - method set all board registers to default values. Return 0 if ok virtual int setToDefault() { return 0; } /** Returns kind of fornend (higher 16 bit) and backend (lower 16 bit) */ virtual uint32_t getHardwareType(); /** Returns hardware version */ virtual uint32_t getHardwareVersion(); //! Return frontend kinds - kind_nXYTER (1), kind_oldFEET (2) or kind_FEET (3) virtual uint32_t getFrontendKind() { return getHardwareType() >> 16; } //! Return backend kinds - kind_Optic (1), kind_FX20 (2), kind_FX40 (3) or kind_FX60 (4) virtual uint32_t getBackendKind() { return getHardwareType() & 0xffff; } //! Returns locally stored board number uint32_t boardNumber() const { return fBrdNumber; } //! Request number from board uint32_t getBoardNumber(); //! Change board number in device void setBoardNumber(uint32_t num); //! invokeDLM - method generate DLM, which sends to every endnode virtual int invokeDLM(unsigned num, double tmout = 0.) { return 0; } // DAQ methods //! Activate DAQ. /*! * This method will initialize the data transport chain which brings * message data from the ROC. The details depend in the underlying * transport. By the daq starting commands list 0 is always executed. * This commands list can be initialized by \sa uploadStartDaqCmdList() * By default configuration (after \sa setToDefault()) frontend is initialized * (in case of nXYTER, timestamp logic initialization) and message * FIFO will be cleared. * * \sa suspendDaq(), stopDaq(), getNextBuffer() */ virtual bool startDaq() = 0; //! suspendDaq /*! * Sends to ROC ROC_SUSPEND_DAQ command. ROC will produce stop message * and will deliver it sometime to host PC. User can continue to take data until * this message. Normally no more messages will be delivered after stop daq message. */ virtual bool suspendDaq() = 0; //! stopDaq /*! * Complimentary function for startDaq. * Disables any new data requests, all coming data will be discarded */ virtual bool stopDaq() = 0; //! getNextBuffer /*! * Returns pointer on the buffer which contains received messages. * \param len returns length of buffer in bytes. * Buffer is allocated by board itself, buffer content remains valid until next * call of getNextBuffer() or when daq is stopped. * Format of messages can be defined by \ref getMsgFormat() * To extract single messages from the buffer, \ref roc::Iterator class should be used * \sa \ref roc::Iterator getMsgFormat() */ virtual bool getNextBuffer(void* &buf, unsigned& len, double tmout = 1.) = 0; //! getMsgFormat /*! * Returns message format like roc::formatEth2, roc::formatOptic2 and so on * Base on message format appropriate iterator must be used to access messages from raw buffers */ virtual int getMsgFormat() const = 0; //! Sets flush timeout of data transport /*! * Local transport accumulates data in buffer and until buffer is not filled, * data is not delivered further. Flush timeout defines time interval, * when any amount of data will be flushed. * \param tmout timeout value in sec. */ virtual void setFlushTimeout(double tmout) {} static Board* Connect(const char* url, ClientRole role = roleNone); static bool Close(Board* brd); static const char* versionToString(uint32_t ver); }; class BoardFactory { protected: enum { MaxNumFactories = 10 }; static BoardFactory* gArr[MaxNumFactories]; BoardFactory(); virtual ~BoardFactory(); public: /** Method should return true if factory designed to create boards for provided url * Either URL contains explicit protocol for the factory or name has format as expected */ virtual bool IsFactoryFor(const char* url) { return false; } virtual void ShowDebug(int lvl, const char* msg); virtual Board* DoConnect(const char* name, base::ClientRole role) = 0; virtual bool DoClose(Board* brd) = 0; static Board* CreateBoard(const char* url, ClientRole role = roleNone); static bool DestroyBoard(base::Board* brd); }; } #endif