#pragma once #include "lasp_daq.h" #include "lasp_siggen.h" #include #include #include #include /** \addtogroup device * @{ */ class StreamMgr; class InDataHandler { protected: StreamMgr &_mgr; #if LASP_DEBUG == 1 std::atomic stopCalled{false}; #endif public: virtual ~InDataHandler(); /** * @brief When constructed, the handler is added to the stream manager, which * will call the handlers's inCallback() until stop() is called. * * @param mgr Stream manager. */ InDataHandler(StreamMgr &mgr); /** * @brief This function is called when input data from a DAQ is available. * * @param daqdata Input data from DAQ * * @return true if no error. False to stop the stream from running. */ virtual bool inCallback(const DaqData &daqdata) = 0; /** * @brief Reset in-data handler. * * @param daq New DAQ configuration of inCallback(). If nullptr is given, * it means that the stream is stopped. */ virtual void reset(const Daq* daq = nullptr) = 0; /** * @brief This function should be called from the constructor of the * implementation of InDataHandler. It will start the stream's calling of * inCallback(). */ void start(); /** * @brief This function should be called from the destructor of derived * classes, to disable the calls to inCallback(), such that proper * destruction of the object is allowed and no other threads call methods * from the object. It removes the inCallback() from the callback list of the * StreamMgr(). **Failing to call this function results in deadlocks, errors * like "pure virtual function called", or other**. */ void stop(); }; /** * @brief Stream manager. Used to manage the input and output streams. * Implemented as a singleton: only one stream manager can be in existance for * a given program. The thread that instantiates a stream manager is the only * thread that should interact with the stream manager. If this is not true, * undefined behavior can be expected. If LASP is compiled in debug mode, this * fact is asserted. */ class StreamMgr { #if LASP_DEBUG == 1 std::thread::id _main_thread_id; #endif /** * @brief Storage for streams. */ std::unique_ptr _inputStream, _outputStream; /** * @brief All indata handlers are called when input data is available. Note * that they can be called from different threads and should take care of * thread-safety. */ std::list _inDataHandlers; std::mutex _inDataHandler_mtx; /** * @brief Signal generator in use to generate output data. Currently * implemented as to generate the same data for all output channels. */ std::shared_ptr _siggen; std::mutex _siggen_mtx; std::mutex _devices_mtx; std::vector _devices; StreamMgr(); friend class InDataHandler; friend class Siggen; // Singleton, no public destructor ~StreamMgr(); public: enum class StreamType : us { /** * @brief Input stream */ input = 1 << 0, /** * @brief Output stream */ output = 1 << 1, }; StreamMgr(const StreamMgr &) = delete; StreamMgr &operator=(const StreamMgr &) = delete; /** * @brief Get access to stream manager instance * * @return Reference to stream manager. */ static StreamMgr &getInstance(); /** * @brief Obtain a list of devices currently available. When the StreamMgr is * first created, this * * @return A copy of the internal stored list of devices */ std::vector getDeviceInfo() const { std::scoped_lock lck(const_cast(_devices_mtx)); return _devices; } /** * @brief Triggers a background scan of the DAQ devices, which updates the * internally stored list of devices. Throws a runtime error when a * background thread is already scanning for devices. * * @param background Perform searching for DAQ devices in the background. If * set to true, the function returns immediately. * @param callback Function to call when complete. */ void rescanDAQDevices(bool background = false, std::function callback = std::function()); /** * @brief Start a stream based on given configuration. * * @param config Configuration of a device */ void startStream(const DaqConfiguration &config); /** * @brief Check if a certain stream is running. If running with no errors, it * returns true. If an error occured, or the stream is not running, it gives * false. * * @param type The type of stream to check for. */ bool isStreamRunningOK(const StreamType type) const { return getStreamStatus(type).runningOK(); } bool isStreamRunning(const StreamType type) const { switch(type) { case(StreamType::input): return bool(_inputStream); break; case(StreamType::output): return bool(_outputStream); break; } return false; } /** * @brief Get the streamstatus object corresponding to a given stream. * * @param type Type of stream, input, inputType, etc. * * @return status object. */ Daq::StreamStatus getStreamStatus(const StreamType type) const; /** * @brief Get DAQ pointer for a given stream. Gives a nullptr if stream is * not running. * * @param type The stream type to get a DAQ ptr for. * * @return Pointer to DAQ */ const Daq *getDaq(StreamType type) const; /** * @brief Stop stream of given type (input / output/ duplex); * * @param stype The stream type to stop. */ void stopStream(const StreamType stype); /** * @brief Stop and delete all streams. Also called on destruction of the * StreamMgr. */ void stopAllStreams(); /** * @brief Set active signal generator for output streams. Only one `Siggen' * is active at the same time. Siggen controls its own data race protection * using a mutex. * * @param s New Siggen pointer */ void setSiggen(std::shared_ptr s); private: bool inCallback(const DaqData &data); bool outCallback(DaqData &data); void removeInDataHandler(InDataHandler &handler); /** * @brief Add an input data handler. The handler's inCallback() function is * called with data when available. This function should *NOT* be called by * the user, instead, an InDataHandler can only be created with the StreamMgr * as argument. * * @param handler The handler to add. */ void addInDataHandler(InDataHandler &handler); /** * @brief Do the actual rescanning. * * @param callback */ void rescanDAQDevices_impl(std::function callback); #if LASP_DEBUG == 1 void checkRightThread() const; #else void checkRightThread() const {} #endif }; /** @} */