lasp/cpp_src/device/lasp_daq.h

231 lines
6.3 KiB
C++

#pragma once
#include "lasp_config.h"
#include "lasp_daqdata.h"
#include "lasp_deviceinfo.h"
#include "lasp_types.h"
#include <functional>
#include <memory>
#include <mutex>
#include <atomic>
/**
* \defgroup device Device interfacing
* \addtogroup device
* @{
* @brief Callback of DAQ for input data. Callback should return
* false for a stop request.
*/
using InDaqCallback = std::function<void(const DaqData &)>;
/**
* @brief
*/
using OutDaqCallback = std::function<void(DaqData &)>;
/**
* @brief Base cass for all DAQ (Data Acquisition) interfaces. A DAQ can be a
* custom device, or for example a sound card.
*/
class Daq : public DaqConfiguration, public DeviceInfo {
protected:
Daq(const DeviceInfo &devinfo, const DaqConfiguration &config);
Daq(const Daq &) = delete;
public:
/**
* @brief Information regarding a stream.
*/
class StreamStatus {
public:
enum class StreamError {
noError,
inputXRun,
outputXRun,
driverError,
systemError,
threadError,
logicError,
};
// Below the only members of this class, which are public.
bool isRunning = false;
StreamError errorType{StreamError::noError};
/**
* @brief Map between error types and messages
*/
inline static const std::map<StreamError, std::string> errorMessages{
{StreamError::noError, "No error"},
{StreamError::inputXRun, "Input buffer overrun"},
{StreamError::outputXRun, "Output buffer underrun"},
{StreamError::driverError, "Driver error"},
{StreamError::systemError, "System error"},
{StreamError::threadError, "Thread error"},
{StreamError::logicError, "Logic error (probably a bug)"},
};
/**
* @brief Check if stream has error
*
* @return true if there is an error.
*/
bool error() const { return errorType != StreamError::noError; };
std::string errorMsg() const { return errorMessages.at(errorType); }
/**
* @brief Returns true if everything is OK with a certain stream and the
* stream is running.
*
* @return as described above.
*/
bool runningOK() const { return isRunning && !error(); }
}; // End of class StreamStatus
using rte = std::runtime_error;
/**
* @brief Used for internal throwing of exceptions.
*/
class StreamException : public rte {
using StreamError = StreamStatus::StreamError;
public:
StreamStatus::StreamError e;
StreamException(const StreamStatus::StreamError e)
: rte(StreamStatus::errorMessages.at(e)), e(e) {}
StreamException(const StreamStatus::StreamError e,
const std::string &additional_info)
: rte(StreamStatus::errorMessages.at(e) + ": " + additional_info),
e(e) {}
operator StreamError() { return e; }
};
/**
* @brief Create a Daq based on given device info and configuration
*
* @param devinfo Device information of device to be used.
* @param config Configuation to apply to the deviec
*
* @return Pointer to Daq device created.
*/
static std::unique_ptr<Daq> createDaq(const DeviceInfo &devinfo,
const DaqConfiguration &config);
/**
* @brief Start the Daq.
*
* @param inCallback Function that is called with input data as argument,
* and which expects output data as a return value. If no input data is
* required, this callback should be unset.
* @param outCallback Function that is called when output data is required.
* If the stream does not require output data, this callback should be unset.
*/
virtual void start(InDaqCallback inCallback, OutDaqCallback outCallback) = 0;
/**
* @brief Stop the Daq device. Throws an exception if the device is not
* running at the time this method is called.
*/
virtual void stop() = 0;
virtual ~Daq() = 0;
/**
* @brief Returns the number of enabled input channels
*
* @param include_monitorchannels if true, all channels that are internally
* monitored are added to the list.
*
* @return number of enabled input channels
*/
us neninchannels(bool include_monitorchannels = true) const;
/**
* @brief Returns the number of enabled output channels
*
* @return Number of enabled output channels
*/
us nenoutchannels() const;
/**
* @brief Create a vector of boolean values of the enabled input channels.
*
* @param include_monitor If set to true and a monitor channel is available,
* the first index in the array will correspond to the monitor channel, and
* whether it is enabled
*
* @return Boolean vector
*/
boolvec eninchannels(const bool include_monitor = true) const;
/**
* @brief Create an array of booleans for each enabled output channel.
*
* @return Boolean vector
*/
boolvec enoutchannels() const;
/**
* @brief Returns current sample rate
*
* @return Sample rate in [Hz]
*/
double samplerate() const;
/**
* @brief Returns the input range for each channel. Maximum allowed absolute
* value of the signal that can pass unclipped.
*
* @param include_monitor If set to true and a monitor channel is available,
* the first index in the array will correspond to the monitor channel.
* *
* @return Maximum offset from 0 before clipping.
*/
dvec inputRangeForEnabledChannels(const bool include_monitor = true) const;
/**
* @brief Returns datatype (enum) corresponding to the datatype of the
* samples.
*
* @return That as stated aboce
*/
DataTypeDescriptor::DataType dataType() const;
/**
* @brief More elaborate description of the datatypes.
*
* @return A DataTypeDescriptor
*/
const DataTypeDescriptor &dtypeDescr() const;
/**
* @brief The number of frames that is send in a block of DaqData.
*
* @return The number of frames per block
*/
us framesPerBlock() const {
return availableFramesPerBlock.at(framesPerBlockIndex);
}
/**
* @brief Get stream status corresponding to current DAQ.
*
* @return StreamStatus object.
*/
virtual StreamStatus getStreamStatus() const = 0;
/**
* @brief Whether the device runs in duplex mode (both input and output), or
* false if only input / only output.
*
* @return true if duplex
*/
bool duplexMode() const {
return (neninchannels() > 0 && nenoutchannels() > 0);
}
};
/** @} */