From ff5afb8eb1bc51422ad0dfc9bc6ce8379498af75 Mon Sep 17 00:00:00 2001 From: "J.A. de Jong - Redu-Sone B.V., ASCEE V.O.F" Date: Fri, 7 May 2021 22:48:02 +0200 Subject: [PATCH] Added pickling functionality for deviceInfo C++ object. Ugly but working implementation. --- lasp/device/lasp_common_decls.pxd | 3 + lasp/device/lasp_cppdaq.h | 238 ++++++++++++++++++++++-------- lasp/device/lasp_deviceinfo.pyx | 7 + 3 files changed, 183 insertions(+), 65 deletions(-) diff --git a/lasp/device/lasp_common_decls.pxd b/lasp/device/lasp_common_decls.pxd index 144b418..8152470 100644 --- a/lasp/device/lasp_common_decls.pxd +++ b/lasp/device/lasp_common_decls.pxd @@ -87,6 +87,9 @@ cdef extern from "lasp_cppdaq.h" nogil: unsigned ninchannels unsigned noutchannels + string serialize() + + cppDeviceInfo deserialize(string) bool hasInputIEPE bool hasInputACCouplingSwitch bool hasInputTrigger diff --git a/lasp/device/lasp_cppdaq.h b/lasp/device/lasp_cppdaq.h index 29eaa44..ea4d250 100644 --- a/lasp/device/lasp_cppdaq.h +++ b/lasp/device/lasp_cppdaq.h @@ -19,6 +19,7 @@ using std::cerr; using std::cout; using std::endl; +using std::getline; using std::runtime_error; using std::string; using std::vector; @@ -78,106 +79,213 @@ const DaqApi uldaqapi("UlDaq", 0); #ifdef HAS_RTAUDIO_API const DaqApi rtaudioAlsaApi("RtAudio Linux ALSA", 1, RtAudio::Api::LINUX_ALSA); const DaqApi rtaudioPulseaudioApi("RtAudio Linux Pulseaudio", 2, - RtAudio::Api::LINUX_PULSE); + RtAudio::Api::LINUX_PULSE); const DaqApi rtaudioWasapiApi("RtAudio Windows Wasapi", 3, - RtAudio::Api::WINDOWS_WASAPI); + RtAudio::Api::WINDOWS_WASAPI); const DaqApi rtaudioDsApi("RtAudio Windows DirectSound", 4, - RtAudio::Api::WINDOWS_DS); + RtAudio::Api::WINDOWS_DS); const DaqApi rtaudioAsioApi("RtAudio Windows ASIO", 5, - RtAudio::Api::WINDOWS_ASIO); + RtAudio::Api::WINDOWS_ASIO); #endif // Structure containing device info parameters class DeviceInfo { -public: - DaqApi api; - string device_name = ""; + public: + DaqApi api; + string device_name = ""; - int api_specific_devindex = -1; + int api_specific_devindex = -1; - vector availableDataTypes; - int prefDataTypeIndex = 0; + vector availableDataTypes; + int prefDataTypeIndex = 0; - vector availableSampleRates; - int prefSampleRateIndex = -1; + vector availableSampleRates; + int prefSampleRateIndex = -1; - vector availableFramesPerBlock; - unsigned prefFramesPerBlockIndex = 0; + vector availableFramesPerBlock; + unsigned prefFramesPerBlockIndex = 0; - dvec availableInputRanges; - int prefInputRangeIndex = 0; + dvec availableInputRanges; + int prefInputRangeIndex = 0; - unsigned ninchannels = 0; - unsigned noutchannels = 0; + unsigned ninchannels = 0; + unsigned noutchannels = 0; - bool hasInputIEPE = false; - bool hasInputACCouplingSwitch = false; - bool hasInputTrigger = false; + bool hasInputIEPE = false; + bool hasInputACCouplingSwitch = false; + bool hasInputTrigger = false; - /* DeviceInfo(): */ - /* datatype(dtype_invalid) { } */ + /* DeviceInfo(): */ + /* datatype(dtype_invalid) { } */ - double prefSampleRate() const { - if (((us)prefSampleRateIndex < availableSampleRates.size()) && - (prefSampleRateIndex >= 0)) { - return availableSampleRates.at(prefSampleRateIndex); - } else { - throw std::runtime_error("No prefered sample rate available"); + double prefSampleRate() const { + if (((us)prefSampleRateIndex < availableSampleRates.size()) && + (prefSampleRateIndex >= 0)) { + return availableSampleRates.at(prefSampleRateIndex); + } else { + throw std::runtime_error("No prefered sample rate available"); + } } - } - operator string() const { - std::stringstream str; - str << api.apiname + " " << api_specific_devindex + operator string() const { + std::stringstream str; + str << api.apiname + " " << api_specific_devindex << " number of input channels: " << ninchannels << " 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 inchannel_sensitivities; - vector inchannel_names; - vector inchannel_metadata; + vector inchannel_sensitivities; + vector inchannel_names; + vector inchannel_metadata; - vector outchannel_sensitivities; - vector outchannel_names; - vector outchannel_metadata; + vector outchannel_sensitivities; + vector outchannel_names; + vector 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 - // present in the list + us dataTypeIndex = 0; // Required datatype for output, should be + // present in the list - us framesPerBlockIndex = 0; + us framesPerBlockIndex = 0; - bool monitorOutput = false; + bool monitorOutput = false; - boolvec inputIEPEEnabled; - boolvec inputACCouplingMode; + boolvec inputIEPEEnabled; + boolvec inputACCouplingMode; - usvec inputRangeIndices; + usvec inputRangeIndices; - // Create a default configuration, with all channels disabled on both - // input and output, and default channel names - DaqConfiguration(const DeviceInfo &device); - DaqConfiguration() {} + // Create a default configuration, with all channels disabled on both + // input and output, and default channel names + DaqConfiguration(const DeviceInfo &device); + DaqConfiguration() {} - bool match(const DeviceInfo &devinfo) const; + bool match(const DeviceInfo &devinfo) const; - int getHighestInChannel() const; - int getHighestOutChannel() const; + int getHighestInChannel() const; + int getHighestOutChannel() const; - int getLowestInChannel() const; - int getLowestOutChannel() const; + int getLowestInChannel() const; + int getLowestOutChannel() const; }; class Daq; @@ -185,7 +293,7 @@ class Daq : public DaqConfiguration, public DeviceInfo { mutable std::mutex mutex; -public: + public: static vector getDeviceInfo(); static Daq *createDaq(const DeviceInfo &, const DaqConfiguration &config); @@ -193,7 +301,7 @@ public: Daq(const DeviceInfo &devinfo, const DaqConfiguration &config); virtual void start(SafeQueue *inqueue, - SafeQueue *outqueue) = 0; + SafeQueue *outqueue) = 0; virtual void stop() = 0; diff --git a/lasp/device/lasp_deviceinfo.pyx b/lasp/device/lasp_deviceinfo.pyx index c5e991b..38454a0 100644 --- a/lasp/device/lasp_deviceinfo.pyx +++ b/lasp/device/lasp_deviceinfo.pyx @@ -9,6 +9,10 @@ DeviceInfo C++ object wrapper """ __all__ = ['DeviceInfo'] +def pickle(dat): + dev = DeviceInfo() + dev.devinfo.deserialize(dat) + return dev cdef class DeviceInfo: def __cinit__(self): @@ -17,6 +21,9 @@ cdef class DeviceInfo: def __init__(self): pass + def __reduce__(self): + return (pickle, (self.devinfo.serialize(),)) + @property def api(self): return self.devinfo.api.apiname.decode('utf-8')