Added pickling functionality for deviceInfo C++ object. Ugly but working implementation.

This commit is contained in:
Anne de Jong 2021-05-07 22:48:02 +02:00
parent 466a6f5cc1
commit ff5afb8eb1
3 changed files with 183 additions and 65 deletions

View File

@ -87,6 +87,9 @@ cdef extern from "lasp_cppdaq.h" nogil:
unsigned ninchannels unsigned ninchannels
unsigned noutchannels unsigned noutchannels
string serialize()
cppDeviceInfo deserialize(string)
bool hasInputIEPE bool hasInputIEPE
bool hasInputACCouplingSwitch bool hasInputACCouplingSwitch
bool hasInputTrigger bool hasInputTrigger

View File

@ -19,6 +19,7 @@
using std::cerr; using std::cerr;
using std::cout; using std::cout;
using std::endl; using std::endl;
using std::getline;
using std::runtime_error; using std::runtime_error;
using std::string; using std::string;
using std::vector; using std::vector;
@ -78,106 +79,213 @@ const DaqApi uldaqapi("UlDaq", 0);
#ifdef HAS_RTAUDIO_API #ifdef HAS_RTAUDIO_API
const DaqApi rtaudioAlsaApi("RtAudio Linux ALSA", 1, RtAudio::Api::LINUX_ALSA); const DaqApi rtaudioAlsaApi("RtAudio Linux ALSA", 1, RtAudio::Api::LINUX_ALSA);
const DaqApi rtaudioPulseaudioApi("RtAudio Linux Pulseaudio", 2, const DaqApi rtaudioPulseaudioApi("RtAudio Linux Pulseaudio", 2,
RtAudio::Api::LINUX_PULSE); RtAudio::Api::LINUX_PULSE);
const DaqApi rtaudioWasapiApi("RtAudio Windows Wasapi", 3, const DaqApi rtaudioWasapiApi("RtAudio Windows Wasapi", 3,
RtAudio::Api::WINDOWS_WASAPI); RtAudio::Api::WINDOWS_WASAPI);
const DaqApi rtaudioDsApi("RtAudio Windows DirectSound", 4, const DaqApi rtaudioDsApi("RtAudio Windows DirectSound", 4,
RtAudio::Api::WINDOWS_DS); RtAudio::Api::WINDOWS_DS);
const DaqApi rtaudioAsioApi("RtAudio Windows ASIO", 5, const DaqApi rtaudioAsioApi("RtAudio Windows ASIO", 5,
RtAudio::Api::WINDOWS_ASIO); RtAudio::Api::WINDOWS_ASIO);
#endif #endif
// Structure containing device info parameters // Structure containing device info parameters
class DeviceInfo { class DeviceInfo {
public: public:
DaqApi api; DaqApi api;
string device_name = ""; string device_name = "";
int api_specific_devindex = -1; int api_specific_devindex = -1;
vector<DataType> availableDataTypes; vector<DataType> availableDataTypes;
int prefDataTypeIndex = 0; int prefDataTypeIndex = 0;
vector<double> availableSampleRates; vector<double> availableSampleRates;
int prefSampleRateIndex = -1; int prefSampleRateIndex = -1;
vector<us> availableFramesPerBlock; vector<us> availableFramesPerBlock;
unsigned prefFramesPerBlockIndex = 0; unsigned prefFramesPerBlockIndex = 0;
dvec availableInputRanges; dvec availableInputRanges;
int prefInputRangeIndex = 0; int prefInputRangeIndex = 0;
unsigned ninchannels = 0; unsigned ninchannels = 0;
unsigned noutchannels = 0; unsigned noutchannels = 0;
bool hasInputIEPE = false; bool hasInputIEPE = false;
bool hasInputACCouplingSwitch = false; bool hasInputACCouplingSwitch = false;
bool hasInputTrigger = false; bool hasInputTrigger = false;
/* DeviceInfo(): */ /* DeviceInfo(): */
/* datatype(dtype_invalid) { } */ /* datatype(dtype_invalid) { } */
double prefSampleRate() const { double prefSampleRate() const {
if (((us)prefSampleRateIndex < availableSampleRates.size()) && if (((us)prefSampleRateIndex < availableSampleRates.size()) &&
(prefSampleRateIndex >= 0)) { (prefSampleRateIndex >= 0)) {
return availableSampleRates.at(prefSampleRateIndex); return availableSampleRates.at(prefSampleRateIndex);
} else { } else {
throw std::runtime_error("No prefered sample rate available"); throw std::runtime_error("No prefered sample rate available");
}
} }
}
operator string() const { operator string() const {
std::stringstream str; std::stringstream str;
str << api.apiname + " " << api_specific_devindex str << api.apiname + " " << api_specific_devindex
<< " number of input channels: " << ninchannels << " number of input channels: " << ninchannels
<< " number of output channels: " << noutchannels; << " number of output channels: " << noutchannels;
return str.str(); return str.str();
} }
string serialize() const {
// Simple serializer for this object, used because we found a bit late that
// this object needs to be send over the wire. We do not want to make this
// implementation in Python, as these objects are created here, in the C++
// code. The Python wrapper is just a readonly wrapper.
std::stringstream str;
str << api.apiname << "\t";
str << api.apicode << "\t";
str << api.api_specific_subcode << "\t";
str << device_name << "\t";
str << availableDataTypes.size() << "\t";
for(const DataType& dtype: availableDataTypes) {
// WARNING: THIS GOES COMPLETELY WRONG WHEN NAMES contain A TAB!!!
str << dtype.name << "\t";
str << dtype.sw << "\t";
str << dtype.is_floating << "\t";
}
str << prefDataTypeIndex << "\t";
str << availableSampleRates.size() << "\t";
for(const double& fs: availableSampleRates) {
// WARNING: THIS GOES COMPLETELY WRONG WHEN NAMES contain A TAB!!!
str << fs << "\t";
}
str << prefSampleRateIndex << "\t";
str << availableFramesPerBlock.size() << "\t";
for(const us& fb: availableFramesPerBlock) {
// WARNING: THIS GOES COMPLETELY WRONG WHEN NAMES contain A TAB!!!
str << fb << "\t";
}
str << prefFramesPerBlockIndex << "\t";
str << availableInputRanges.size() << "\t";
for(const double& fs: availableInputRanges) {
// WARNING: THIS GOES COMPLETELY WRONG WHEN NAMES contain A TAB!!!
str << fs << "\t";
}
str << prefInputRangeIndex << "\t";
str << ninchannels << "\t";
str << noutchannels << "\t";
str << int(hasInputIEPE) << "\t";
str << int(hasInputACCouplingSwitch) << "\t";
str << int(hasInputTrigger) << "\t";
return str.str();
}
static DeviceInfo deserialize(const string& dstr) {
DeviceInfo devinfo;
std::stringstream str(dstr);
string tmp;
us N;
auto nexts = [&]() { getline(str, tmp, '\t'); return tmp; };
auto nexti = [&]() { getline(str, tmp, '\t'); return std::atoi(tmp.c_str()); };
auto nextf = [&]() { getline(str, tmp, '\t'); return std::atof(tmp.c_str()); };
// Api
string apiname = nexts();
auto apicode = nexti();
auto api_specific_subcode = nexti();
DaqApi api(apiname, apicode, api_specific_subcode);
devinfo.api = api;
devinfo.device_name = nexts();
N = us(nexti());
for(us i=0;i<N; i++) {
DataType dtype;
dtype.name = nexts();
dtype.sw =nexti();
dtype.is_floating = bool(nexti());
devinfo.availableDataTypes.push_back(dtype);
}
devinfo.prefDataTypeIndex = nexti();
N = us(nexti());
for(us i=0;i<N; i++) {
devinfo.availableSampleRates.push_back(nextf());
}
devinfo.prefSampleRateIndex = nexti();
N = us(nexti());
for(us i=0;i<N; i++) {
devinfo.availableSampleRates.push_back(nextf());
}
devinfo.prefSampleRateIndex = nexti();
N = us(nexti());
for(us i=0;i<N; i++) {
devinfo.availableFramesPerBlock.push_back(nexti());
}
devinfo.prefFramesPerBlockIndex = nexti();
devinfo.ninchannels = nexti();
devinfo.noutchannels = nexti();
devinfo.hasInputIEPE = bool(nexti());
devinfo.hasInputACCouplingSwitch = bool(nexti());
devinfo.hasInputTrigger = bool(nexti());
return devinfo;
}
}; };
// Device configuration parameters // Device configuration parameters
class DaqConfiguration { class DaqConfiguration {
public: public:
DaqApi api; DaqApi api;
string device_name; string device_name;
boolvec eninchannels; // Enabled input channelsvice(const DeviceInfo& devinfo, boolvec eninchannels; // Enabled input channelsvice(const DeviceInfo& devinfo,
boolvec enoutchannels; // Enabled output channels boolvec enoutchannels; // Enabled output channels
vector<double> inchannel_sensitivities; vector<double> inchannel_sensitivities;
vector<string> inchannel_names; vector<string> inchannel_names;
vector<string> inchannel_metadata; vector<string> inchannel_metadata;
vector<double> outchannel_sensitivities; vector<double> outchannel_sensitivities;
vector<string> outchannel_names; vector<string> outchannel_names;
vector<string> outchannel_metadata; vector<string> outchannel_metadata;
us sampleRateIndex = 0; // Index in list of sample rates us sampleRateIndex = 0; // Index in list of sample rates
us dataTypeIndex = 0; // Required datatype for output, should be us dataTypeIndex = 0; // Required datatype for output, should be
// present in the list // present in the list
us framesPerBlockIndex = 0; us framesPerBlockIndex = 0;
bool monitorOutput = false; bool monitorOutput = false;
boolvec inputIEPEEnabled; boolvec inputIEPEEnabled;
boolvec inputACCouplingMode; boolvec inputACCouplingMode;
usvec inputRangeIndices; usvec inputRangeIndices;
// Create a default configuration, with all channels disabled on both // Create a default configuration, with all channels disabled on both
// input and output, and default channel names // input and output, and default channel names
DaqConfiguration(const DeviceInfo &device); DaqConfiguration(const DeviceInfo &device);
DaqConfiguration() {} DaqConfiguration() {}
bool match(const DeviceInfo &devinfo) const; bool match(const DeviceInfo &devinfo) const;
int getHighestInChannel() const; int getHighestInChannel() const;
int getHighestOutChannel() const; int getHighestOutChannel() const;
int getLowestInChannel() const; int getLowestInChannel() const;
int getLowestOutChannel() const; int getLowestOutChannel() const;
}; };
class Daq; class Daq;
@ -185,7 +293,7 @@ class Daq : public DaqConfiguration, public DeviceInfo {
mutable std::mutex mutex; mutable std::mutex mutex;
public: public:
static vector<DeviceInfo> getDeviceInfo(); static vector<DeviceInfo> getDeviceInfo();
static Daq *createDaq(const DeviceInfo &, const DaqConfiguration &config); static Daq *createDaq(const DeviceInfo &, const DaqConfiguration &config);
@ -193,7 +301,7 @@ public:
Daq(const DeviceInfo &devinfo, const DaqConfiguration &config); Daq(const DeviceInfo &devinfo, const DaqConfiguration &config);
virtual void start(SafeQueue<void *> *inqueue, virtual void start(SafeQueue<void *> *inqueue,
SafeQueue<void *> *outqueue) = 0; SafeQueue<void *> *outqueue) = 0;
virtual void stop() = 0; virtual void stop() = 0;

View File

@ -9,6 +9,10 @@ DeviceInfo C++ object wrapper
""" """
__all__ = ['DeviceInfo'] __all__ = ['DeviceInfo']
def pickle(dat):
dev = DeviceInfo()
dev.devinfo.deserialize(dat)
return dev
cdef class DeviceInfo: cdef class DeviceInfo:
def __cinit__(self): def __cinit__(self):
@ -17,6 +21,9 @@ cdef class DeviceInfo:
def __init__(self): def __init__(self):
pass pass
def __reduce__(self):
return (pickle, (self.devinfo.serialize(),))
@property @property
def api(self): return self.devinfo.api.apiname.decode('utf-8') def api(self): return self.devinfo.api.apiname.decode('utf-8')