diff --git a/src/lasp/device/lasp_deviceinfo.cpp b/src/lasp/device/lasp_deviceinfo.cpp index ea382bc..f90e34e 100644 --- a/src/lasp/device/lasp_deviceinfo.cpp +++ b/src/lasp/device/lasp_deviceinfo.cpp @@ -12,8 +12,8 @@ #endif -std::vector DeviceInfo::getDeviceInfo() { - std::vector devs; +DeviceInfoList DeviceInfo::getDeviceInfo() { + DeviceInfoList devs; #if LASP_HAS_ULDAQ ==1 fillUlDaqDeviceInfo(devs); #endif diff --git a/src/lasp/device/lasp_deviceinfo.h b/src/lasp/device/lasp_deviceinfo.h index 11f1a43..81772ed 100644 --- a/src/lasp/device/lasp_deviceinfo.h +++ b/src/lasp/device/lasp_deviceinfo.h @@ -1,17 +1,34 @@ #pragma once #include "lasp_config.h" -#include "lasp_types.h" #include "lasp_daqconfig.h" +#include "lasp_types.h" +#include /** \addtogroup device * @{ */ +using DeviceInfoList = std::vector>; + /** * @brief Structure containing device info parameters */ class DeviceInfo { public: + /** + * @brief Virtual desctructor. Can be derived class. + */ + virtual ~DeviceInfo() {} + + /** + * @brief Clone a device info. + * + * @return Cloned device info + */ + virtual std::unique_ptr clone() const { + return std::make_unique(*this); + } + /** * @brief Backend API corresponding to device */ @@ -21,12 +38,6 @@ public: */ string device_name = ""; - /** - * @brief Specific for the device (Sub-API). Important for the RtAudio - * backend, as RtAudio is able to handle different API's. - */ - int api_specific_devindex = -1; - /** * @brief The available data types the device can output */ @@ -104,6 +115,14 @@ public: */ bool hasInternalOutputMonitor = false; + /** + * @brief This flag is used to be able to indicate that the device cannot run + * input and output streams independently, without opening the device in + * duplex mode. This is for example true for the UlDaq: only one handle to + * the device can be given at the same time. + */ + bool duplexModeForced = false; + /** * @brief The physical quantity of the output signal. For 'normal' audio * devices, this is typically a 'number' between +/- full scale. For some @@ -112,7 +131,6 @@ public: */ DaqChannel::Qty physicalOutputQty = DaqChannel::Qty::Number; - /** * @brief String representation of DeviceInfo * @@ -121,11 +139,9 @@ public: operator string() const { using std::endl; std::stringstream str; - str << api.apiname + " " << api_specific_devindex << endl - << " number of input channels: " << ninchannels << endl - /** - * @brief - */ + str << api.apiname + " " << endl + << " number of input channels: " << ninchannels + << endl << " number of output channels: " << noutchannels << endl; return str.str(); } @@ -135,7 +151,7 @@ public: * * @return The device info's for each found device. */ - static std::vector getDeviceInfo(); + static DeviceInfoList getDeviceInfo(); }; /** @} */ diff --git a/src/lasp/device/lasp_rtaudiodaq.cpp b/src/lasp/device/lasp_rtaudiodaq.cpp index 30a77af..7c0742c 100644 --- a/src/lasp/device/lasp_rtaudiodaq.cpp +++ b/src/lasp/device/lasp_rtaudiodaq.cpp @@ -17,7 +17,19 @@ using rte = std::runtime_error; using std::vector; using lck = std::scoped_lock; -void fillRtAudioDeviceInfo(vector &devinfolist) { +class RtAudioDeviceInfo : public DeviceInfo { +public: + /** + * @brief Specific for the device (Sub-API). Important for the RtAudio + * backend, as RtAudio is able to handle different API's. + */ + int _api_devindex; + virtual std::unique_ptr clone() const override { + return std::make_unique(*this); + } +}; + +void fillRtAudioDeviceInfo(DeviceInfoList &devinfolist) { DEBUGTRACE_ENTER; vector apis; @@ -34,7 +46,7 @@ void fillRtAudioDeviceInfo(vector &devinfolist) { continue; } // "Our device info struct" - DeviceInfo d; + RtAudioDeviceInfo d; switch (api) { case RtAudio::LINUX_ALSA: d.api = rtaudioAlsaApi; @@ -58,7 +70,7 @@ void fillRtAudioDeviceInfo(vector &devinfolist) { } d.device_name = devinfo.name; - d.api_specific_devindex = devno; + d._api_devindex = devno; /// When 48k is available we overwrite the default sample rate with the 48 /// kHz value, which is our preffered rate, @@ -118,7 +130,7 @@ void fillRtAudioDeviceInfo(vector &devinfolist) { d.availableFramesPerBlock = {512, 1024, 2048, 4096, 8192}; d.prefFramesPerBlockIndex = 2; - devinfolist.push_back(d); + devinfolist.push_back(std::make_unique(d)); } } } @@ -143,9 +155,9 @@ class RtAudioDaq : public Daq { std::atomic _streamStatus{}; public: - RtAudioDaq(const DeviceInfo &devinfo, const DaqConfiguration &config) - : Daq(devinfo, config), - rtaudio(static_cast(devinfo.api.api_specific_subcode)), + RtAudioDaq(const DeviceInfo &devinfo_gen, const DaqConfiguration &config) + : Daq(devinfo_gen, config), rtaudio(static_cast( + devinfo_gen.api.api_specific_subcode)), nFramesPerBlock(Daq::framesPerBlock()) { DEBUGTRACE_ENTER; @@ -156,6 +168,8 @@ public: throw rte("RtAudio backend cannot run in duplex mode."); } assert(!monitorOutput); + const RtAudioDeviceInfo &devinfo = + static_cast(devinfo_gen); std::unique_ptr inParams, outParams; @@ -169,7 +183,7 @@ public: throw rte("Invalid input number of channels"); } inParams->firstChannel = 0; - inParams->deviceId = devinfo.api_specific_devindex; + inParams->deviceId = devinfo._api_devindex; } else { @@ -180,7 +194,7 @@ public: throw rte("Invalid output number of channels"); } outParams->firstChannel = 0; - outParams->deviceId = devinfo.api_specific_devindex; + outParams->deviceId = devinfo._api_devindex; } RtAudio::StreamOptions streamoptions; diff --git a/src/lasp/device/lasp_rtaudiodaq.h b/src/lasp/device/lasp_rtaudiodaq.h index 47a8cbb..74b6d3e 100644 --- a/src/lasp/device/lasp_rtaudiodaq.h +++ b/src/lasp/device/lasp_rtaudiodaq.h @@ -10,5 +10,5 @@ std::unique_ptr createRtAudioDevice(const DeviceInfo& devinfo, * * @param devinfolist List to append to */ -void fillRtAudioDeviceInfo(std::vector &devinfolist); +void fillRtAudioDeviceInfo(DeviceInfoList &devinfolist); diff --git a/src/lasp/device/lasp_streammgr.cpp b/src/lasp/device/lasp_streammgr.cpp index 1369842..4ae3fa7 100644 --- a/src/lasp/device/lasp_streammgr.cpp +++ b/src/lasp/device/lasp_streammgr.cpp @@ -261,22 +261,23 @@ void StreamMgr::startStream(const DaqConfiguration &config) { [](auto &i) { return i.enabled; }); // Find the first device that matches with the configuration + std::scoped_lock lck(_devices_mtx); - DeviceInfo devinfo; - { - std::scoped_lock lck(_devices_mtx); + DeviceInfo *devinfo = nullptr; + bool found = false; - auto devinfo2 = std::find_if( - _devices.cbegin(), _devices.cend(), - [&config](const DeviceInfo &d) { return config.match(d); }); - if (devinfo2 == std::cend(_devices)) { - throw rte("Could not find a device with name " + config.device_name + - " in list of devices."); + for (auto &devinfoi : _devices) { + if (config.match(*devinfoi)) { + devinfo = devinfoi.get(); + break; } - devinfo = *devinfo2; + } + if (!devinfo) { + throw rte("Could not find a device with name " + config.device_name + + " in list of devices."); } - isInput |= (config.monitorOutput && devinfo.hasInternalOutputMonitor); + isInput |= (config.monitorOutput && devinfo->hasInternalOutputMonitor); DEBUGTRACE_PRINT(isInput); bool isDuplex = isInput && isOutput; @@ -300,11 +301,23 @@ void StreamMgr::startStream(const DaqConfiguration &config) { } } + if (_outputStream && isInput && _outputStream->duplexModeForced && + config.match(*_outputStream)) { + throw rte("This device is already opened for output. If input is also " + "required, please enable duplex mode for this device"); + } + + if (_inputStream && isOutput && _inputStream->duplexModeForced && + config.match(*_inputStream)) { + throw rte("This device is already opened for input. If output is also " + "required, please enable duplex mode for this device"); + } + InDaqCallback inCallback; OutDaqCallback outCallback; using namespace std::placeholders; - std::unique_ptr daq = Daq::createDaq(devinfo, config); + std::unique_ptr daq = Daq::createDaq(*devinfo, config); assert(daq); @@ -321,7 +334,7 @@ void StreamMgr::startStream(const DaqConfiguration &config) { /// Create input filters _inputFilters.clear(); /// No input filter for monitor channel. - if (config.monitorOutput && devinfo.hasInternalOutputMonitor) { + if (config.monitorOutput && devinfo->hasInternalOutputMonitor) { _inputFilters.push_back(nullptr); } for (auto &ch : daq->inchannel_config) { diff --git a/src/lasp/device/lasp_streammgr.h b/src/lasp/device/lasp_streammgr.h index 05c49fa..fc69222 100644 --- a/src/lasp/device/lasp_streammgr.h +++ b/src/lasp/device/lasp_streammgr.h @@ -107,7 +107,7 @@ class StreamMgr { std::mutex _siggen_mtx; std::mutex _devices_mtx; - std::vector _devices; + DeviceInfoList _devices; StreamMgr(); @@ -145,9 +145,13 @@ class StreamMgr { * * @return A copy of the internal stored list of devices */ - std::vector getDeviceInfo() const { + DeviceInfoList getDeviceInfo() const { std::scoped_lock lck(const_cast(_devices_mtx)); - return _devices; + DeviceInfoList d2; + for(const auto& dev: _devices) { + d2.push_back(dev->clone()); + } + return d2; } /** diff --git a/src/lasp/device/lasp_uldaq.cpp b/src/lasp/device/lasp_uldaq.cpp index cad6db3..41de7dc 100644 --- a/src/lasp/device/lasp_uldaq.cpp +++ b/src/lasp/device/lasp_uldaq.cpp @@ -6,13 +6,12 @@ #include "lasp_uldaq_impl.h" #include -void fillUlDaqDeviceInfo(std::vector &devinfolist, - void *vDescriptors) { + + +void fillUlDaqDeviceInfo(DeviceInfoList &devinfolist) { DEBUGTRACE_ENTER; - DaqDeviceDescriptor *descriptors_copy = - static_cast(vDescriptors); UlError err; unsigned int numdevs = MAX_ULDAQ_DEV_COUNT_PER_API; @@ -32,16 +31,14 @@ void fillUlDaqDeviceInfo(std::vector &devinfolist, descriptor = devdescriptors[i]; - // Copy structure over, if given as not nullptr - if (descriptors_copy) { - descriptors_copy[i] = descriptor; - } + UlDaqDeviceInfo devinfo; + devinfo._uldaqDescriptor = descriptor; - DeviceInfo devinfo; devinfo.api = uldaqapi; string name, interface; - if (string(descriptor.productName) != "DT9837A") { - throw rte("Unknown UlDAQ type"); + string productname = descriptor.productName; + if (productname != "DT9837A") { + throw rte("Unknown UlDAQ type: " + productname); } switch (descriptor.devInterface) { @@ -66,7 +63,6 @@ void fillUlDaqDeviceInfo(std::vector &devinfolist, devinfo.physicalOutputQty = DaqChannel::Qty::Voltage; - devinfo.api_specific_devindex = i; devinfo.availableDataTypes.push_back( DataTypeDescriptor::DataType::dtype_fl64); devinfo.prefDataTypeIndex = 0; @@ -91,8 +87,10 @@ void fillUlDaqDeviceInfo(std::vector &devinfolist, devinfo.hasInternalOutputMonitor = true; + devinfo.duplexModeForced = true; + // Finally, this devinfo is pushed back in list - devinfolist.push_back(devinfo); + devinfolist.push_back(std::make_unique(devinfo)); } } diff --git a/src/lasp/device/lasp_uldaq.h b/src/lasp/device/lasp_uldaq.h index c5593e9..d6fb226 100644 --- a/src/lasp/device/lasp_uldaq.h +++ b/src/lasp/device/lasp_uldaq.h @@ -14,9 +14,5 @@ std::unique_ptr createUlDaqDevice(const DeviceInfo &devinfo, * @brief Fill device info list with UlDaq specific devices, if any. * * @param devinfolist Info list to append to - * @param descriptors Pointer to array - * DaqDeviceDescriptors[MAX_ULDAQ_DEV_COUNT_PER_API]. If a pointer is given, a - * copy of the device descriptors is set to the memory of this pointer. We use - * a void* pointer here to not expose the implementation of UlDaq. */ -void fillUlDaqDeviceInfo(std::vector&, void *descriptors = nullptr); +void fillUlDaqDeviceInfo(DeviceInfoList&); diff --git a/src/lasp/device/lasp_uldaq_impl.cpp b/src/lasp/device/lasp_uldaq_impl.cpp index 8e9c35f..1afe301 100644 --- a/src/lasp/device/lasp_uldaq_impl.cpp +++ b/src/lasp/device/lasp_uldaq_impl.cpp @@ -35,7 +35,10 @@ inline void showErr(string errstr) { std::cerr << errstr << std::endl; std::cerr << "***********************************************\n\n"; } -inline void showErr(UlError err) { showErr(getErrMsg(err)); } +inline void showErr(UlError err) { + if (err != ERR_NO_ERROR) + showErr(getErrMsg(err)); +} DT9837A::~DT9837A() { UlError err; @@ -71,21 +74,14 @@ DT9837A::DT9837A(const DeviceInfo &devinfo, const DaqConfiguration &config) throw rte("Invalid sample rate"); } - std::vector devs; - DaqDeviceDescriptor devdescriptors[MAX_ULDAQ_DEV_COUNT_PER_API]; - fillUlDaqDeviceInfo(devs, static_cast(devdescriptors)); - - if (devs.size() == 0) { - throw rte("Unable to find any UlDaq devices"); - } - - if (devinfo.api_specific_devindex < 0 || - devinfo.api_specific_devindex >= (int)MAX_ULDAQ_DEV_COUNT_PER_API) { - throw rte("Invalid device index"); + const UlDaqDeviceInfo *_info = + dynamic_cast(&devinfo); + if (_info == nullptr) { + throw rte("BUG: Could not cast DeviceInfo to UlDaqDeviceInfo"); } // get a handle to the DAQ device associated with the first descriptor - _handle = ulCreateDaqDevice(devdescriptors[devinfo.api_specific_devindex]); + _handle = ulCreateDaqDevice(_info->_uldaqDescriptor); if (_handle == 0) { throw rte("Unable to create a handle to the specified DAQ " diff --git a/src/lasp/device/lasp_uldaq_impl.h b/src/lasp/device/lasp_uldaq_impl.h index 3cc588a..9fce2e5 100644 --- a/src/lasp/device/lasp_uldaq_impl.h +++ b/src/lasp/device/lasp_uldaq_impl.h @@ -14,6 +14,19 @@ using std::cerr; using std::endl; using rte = std::runtime_error; +/** + * @brief UlDaq-specific device information. Adds a copy of the underlying + * DaqDeDaqDeviceDescriptor. + */ +class UlDaqDeviceInfo : public DeviceInfo { + +public: + DaqDeviceDescriptor _uldaqDescriptor; + virtual std::unique_ptr clone() const { + return std::make_unique(*this); + } +}; + class DT9837A : public Daq { DaqDeviceHandle _handle = 0; @@ -123,7 +136,6 @@ public: */ bool operator()(); ~InBufHandler(); - }; class OutBufHandler : public BufHandler { OutDaqCallback cb;