DeviceInfo is now base class for derived variants for UlDaqDeviceInfo and RtAudioDeviceInfo. Dynamic casts are used in contstructors when stream is created. For UlDaq, device inventory list is not scanned anymore when starting device. This should speed up starting the device as well. Added a flag duplexModeForced to DeviceInfo. This one is true for DT9837A, as this device can only use input and output at the same time when running in duplex mode. Fixed the bug of printing an Uldaq error called noerror.
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Anne de Jong 2023-01-20 15:50:51 +01:00
parent 1bdf318f1b
commit f3e4bc70ea
10 changed files with 123 additions and 74 deletions

View File

@ -12,8 +12,8 @@
#endif
std::vector<DeviceInfo> DeviceInfo::getDeviceInfo() {
std::vector<DeviceInfo> devs;
DeviceInfoList DeviceInfo::getDeviceInfo() {
DeviceInfoList devs;
#if LASP_HAS_ULDAQ ==1
fillUlDaqDeviceInfo(devs);
#endif

View File

@ -1,17 +1,34 @@
#pragma once
#include "lasp_config.h"
#include "lasp_types.h"
#include "lasp_daqconfig.h"
#include "lasp_types.h"
#include <memory>
/** \addtogroup device
* @{
*/
using DeviceInfoList = std::vector<std::unique_ptr<DeviceInfo>>;
/**
* @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<DeviceInfo> clone() const {
return std::make_unique<DeviceInfo>(*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<DeviceInfo> getDeviceInfo();
static DeviceInfoList getDeviceInfo();
};
/** @} */

View File

@ -17,7 +17,19 @@ using rte = std::runtime_error;
using std::vector;
using lck = std::scoped_lock<std::mutex>;
void fillRtAudioDeviceInfo(vector<DeviceInfo> &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<DeviceInfo> clone() const override {
return std::make_unique<DeviceInfo>(*this);
}
};
void fillRtAudioDeviceInfo(DeviceInfoList &devinfolist) {
DEBUGTRACE_ENTER;
vector<RtAudio::Api> apis;
@ -34,7 +46,7 @@ void fillRtAudioDeviceInfo(vector<DeviceInfo> &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<DeviceInfo> &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<DeviceInfo> &devinfolist) {
d.availableFramesPerBlock = {512, 1024, 2048, 4096, 8192};
d.prefFramesPerBlockIndex = 2;
devinfolist.push_back(d);
devinfolist.push_back(std::make_unique<RtAudioDeviceInfo>(d));
}
}
}
@ -143,9 +155,9 @@ class RtAudioDaq : public Daq {
std::atomic<StreamStatus> _streamStatus{};
public:
RtAudioDaq(const DeviceInfo &devinfo, const DaqConfiguration &config)
: Daq(devinfo, config),
rtaudio(static_cast<RtAudio::Api>(devinfo.api.api_specific_subcode)),
RtAudioDaq(const DeviceInfo &devinfo_gen, const DaqConfiguration &config)
: Daq(devinfo_gen, config), rtaudio(static_cast<RtAudio::Api>(
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<const RtAudioDeviceInfo &>(devinfo_gen);
std::unique_ptr<RtAudio::StreamParameters> 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;

View File

@ -10,5 +10,5 @@ std::unique_ptr<Daq> createRtAudioDevice(const DeviceInfo& devinfo,
*
* @param devinfolist List to append to
*/
void fillRtAudioDeviceInfo(std::vector<DeviceInfo> &devinfolist);
void fillRtAudioDeviceInfo(DeviceInfoList &devinfolist);

View File

@ -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 = Daq::createDaq(devinfo, config);
std::unique_ptr<Daq> 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) {

View File

@ -107,7 +107,7 @@ class StreamMgr {
std::mutex _siggen_mtx;
std::mutex _devices_mtx;
std::vector<DeviceInfo> _devices;
DeviceInfoList _devices;
StreamMgr();
@ -145,9 +145,13 @@ class StreamMgr {
*
* @return A copy of the internal stored list of devices
*/
std::vector<DeviceInfo> getDeviceInfo() const {
DeviceInfoList getDeviceInfo() const {
std::scoped_lock lck(const_cast<std::mutex &>(_devices_mtx));
return _devices;
DeviceInfoList d2;
for(const auto& dev: _devices) {
d2.push_back(dev->clone());
}
return d2;
}
/**

View File

@ -6,13 +6,12 @@
#include "lasp_uldaq_impl.h"
#include <uldaq.h>
void fillUlDaqDeviceInfo(std::vector<DeviceInfo> &devinfolist,
void *vDescriptors) {
void fillUlDaqDeviceInfo(DeviceInfoList &devinfolist) {
DEBUGTRACE_ENTER;
DaqDeviceDescriptor *descriptors_copy =
static_cast<DaqDeviceDescriptor *>(vDescriptors);
UlError err;
unsigned int numdevs = MAX_ULDAQ_DEV_COUNT_PER_API;
@ -32,16 +31,14 @@ void fillUlDaqDeviceInfo(std::vector<DeviceInfo> &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<DeviceInfo> &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<DeviceInfo> &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<UlDaqDeviceInfo>(devinfo));
}
}

View File

@ -14,9 +14,5 @@ std::unique_ptr<Daq> 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<DeviceInfo>&, void *descriptors = nullptr);
void fillUlDaqDeviceInfo(DeviceInfoList&);

View File

@ -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<DeviceInfo> devs;
DaqDeviceDescriptor devdescriptors[MAX_ULDAQ_DEV_COUNT_PER_API];
fillUlDaqDeviceInfo(devs, static_cast<void *>(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<const UlDaqDeviceInfo *>(&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 "

View File

@ -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<DeviceInfo> clone() const {
return std::make_unique<UlDaqDeviceInfo>(*this);
}
};
class DT9837A : public Daq {
DaqDeviceHandle _handle = 0;
@ -123,7 +136,6 @@ public:
*/
bool operator()();
~InBufHandler();
};
class OutBufHandler : public BufHandler {
OutDaqCallback cb;