diff --git a/examples/main.py b/examples/main.py
index 902aa55c54300c74b7bd67c61a41b4e87afd0a17..963bcc6b233562022328cdd957b1f955df4f00ef 100644
--- a/examples/main.py
+++ b/examples/main.py
@@ -11,6 +11,9 @@ from rich.logging import RichHandler
import CaptDeviceControl as CaptDevice
+
+#logging.disable(logging.INFO)
+
if __name__ == "__main__":
def setup_logging():
@@ -35,7 +38,7 @@ if __name__ == "__main__":
model = CaptDevice.Model(conf)
- controller = CaptDevice.Controller(model)
+ controller = CaptDevice.Controller(model, None)
window = CaptDevice.View(model, controller)
window.show()
diff --git a/src/CaptDeviceControl/CaptDeviceConfig.py b/src/CaptDeviceControl/CaptDeviceConfig.py
index 273afe6d7326646355ec6e11e2345959f0a80e02..fdc0a46616cf6bad4c4c52ef7f3695641bade73f 100644
--- a/src/CaptDeviceControl/CaptDeviceConfig.py
+++ b/src/CaptDeviceControl/CaptDeviceConfig.py
@@ -11,7 +11,7 @@ class CaptDeviceConfig(cfg.ConfigNode):
def __init__(self) -> None:
super().__init__()
- self.sample_rate = cfg.Field(50000, friendly_name="Sample rate",
+ self.sample_rate = cfg.Field(500, friendly_name="Sample rate",
description="Sample rate of the device")
self.streaming_rate = cfg.Field(500, friendly_name="Streaming rate",
description="Streaming rate in Hz (should be below 1kHz)")
diff --git a/src/CaptDeviceControl/__init__.py b/src/CaptDeviceControl/__init__.py
index 65ab4a9424daabd37b081ffb246d53c7ce4a46a8..eb96e39a703665131ee83f15829b3b6f0cdb3b8b 100644
--- a/src/CaptDeviceControl/__init__.py
+++ b/src/CaptDeviceControl/__init__.py
@@ -6,6 +6,6 @@ Package Version:
"""
import sys
from .CaptDeviceConfig import CaptDeviceConfig as Config
-from .controller.AD2CaptDeviceController import AD2CaptDeviceController as Controller
+from .controller.BaseAD2CaptDevice import BaseAD2CaptDevice as Controller
from .model.AD2CaptDeviceModel import AD2CaptDeviceModel as Model
from .view.AD2CaptDeviceView import ControlWindow as View
\ No newline at end of file
diff --git a/src/CaptDeviceControl/controller/AD2CaptDeviceController.py b/src/CaptDeviceControl/controller/AD2CaptDeviceController.py
deleted file mode 100644
index 24bff829e27a76bb3f409dfb2c98f4404d8358e1..0000000000000000000000000000000000000000
--- a/src/CaptDeviceControl/controller/AD2CaptDeviceController.py
+++ /dev/null
@@ -1,290 +0,0 @@
-#!/.venv/Scripts/python
-
-from ctypes import c_int, byref, create_string_buffer, cdll, c_int32, c_uint, c_double
-
-from CaptDeviceControl.controller.BaseAD2CaptDevice import BaseAD2CaptDevice
-from CaptDeviceControl.model.AD2CaptDeviceModel import AD2CaptDeviceModel
-#from .controller.BaseAD2CaptDevice import BaseAD2CaptDevice
-#from .model.AD2CaptDeviceModel import AD2CaptDeviceModel
-
-from CaptDeviceControl.constants.dwfconstants import enumfilterUSB, enumfilterType, enumfilterDemo
-from CaptDeviceControl.controller.mp_AD2Capture.MPCaptDeviceControl import MPCaptDeviceControl
-
-
-class AD2CaptDeviceController(BaseAD2CaptDevice):
-
- def __init__(self, ad2capt_model: AD2CaptDeviceModel):
- self.dwf = cdll.dwf
- super().__init__(ad2capt_model)
-
-
-
- # This is required for acquiring the data
-
-
-
- def read_hardware_config(self, iDevice):
- hw_info_dict = {}
- hdwf = c_int()
- int0 = c_int()
- int1 = c_int()
- uint0 = c_uint()
- dbl0 = c_double()
- dbl1 = c_double()
- dbl2 = c_double()
- self.dwf.FDwfDeviceConfigOpen(c_int(iDevice), c_int(0), byref(hdwf))
- if hdwf.value == 0:
- szerr = create_string_buffer(512)
- self.dwf.FDwfGetLastErrorMsg(szerr)
- raise Exception(str(szerr.value))
-
- self.dwf.FDwfAnalogInChannelCount(hdwf, byref(int0))
- hw_info_dict["analog_in_channels"] = int(int0.value)
-
- self.dwf.FDwfAnalogIOChannelCount(hdwf, byref(int0))
- hw_info_dict["analog_io_channels"] = int(int0.value)
-
- self.dwf.FDwfAnalogInBufferSizeInfo(hdwf, 0, byref(int0))
- hw_info_dict["buffer_size"] = int(int0.value)
-
- self.dwf.FDwfAnalogInBitsInfo(hdwf, byref(int0))
- hw_info_dict["adc_bits"] = int(int0.value)
-
- self.dwf.FDwfAnalogInChannelRangeInfo(hdwf, byref(dbl0), byref(dbl1), byref(dbl2))
- hw_info_dict["range"] = (int(dbl0.value), int(dbl1.value), int(dbl2.value))
-
- self.dwf.FDwfAnalogInChannelOffsetInfo(hdwf, byref(dbl0), byref(dbl1), byref(dbl2))
- hw_info_dict["offset"] = (int(dbl0.value), int(dbl1.value), int(dbl2.value))
-
- return hw_info_dict
-
-
- def discover_connected_devices(self):
- pass
- #self.mpcaptdevicecontrol.discover_connected_devices()
-
-
-
- # def _open_device(self, device_index):
- # devicename = create_string_buffer(64)
- # serialnum = create_string_buffer(16)
- #
- # self.dwf.FDwfEnumDeviceName(c_int(device_index), devicename)
- # self.dwf.FDwfEnumSN(c_int(device_index), serialnum)
- #
- # self.model.device_name = devicename
- # self.model.device_serial_number = serialnum
- # # open device
- # self.logger.info(f"[{self.pref} Task] Opening device #{device_index}...")
- #
- # # Opens a device identified by the enumeration index and retrieves a handle. To automatically
- # # enumerate all connected devices and open the first discovered device, use index -1.
- # self.dwf.FDwfDeviceOpen(c_int(device_index), byref(self.model.hdwf))
- #
- # if self.model.hdwf.value == hdwfNone.value:
- # szerr = create_string_buffer(512)
- # self.dwf.FDwfGetLastErrorMsg(szerr)
- # # print(str(szerr.value))
- # self.model.connected = False
- # raise Exception(f"Failed to open device: {szerr.value}")
- # else:
- # self.model.connected = True
- # self.get_analog_in_status()
- # self.logger.info(f"[{self.pref} Task] Device connected!")
- #
- # def _setup_acquisition(self):
- # # set up acquisition
- # self.get_analog_in_status()
- # self.logger.debug(f"[{self.pref} Task] Setup for acquisition. Wait 2 seconds for the offset to stabilize.")
- # self.dwf.FDwfAnalogInChannelEnableSet(self.model.hdwf, c_int(self.model.analog_in_channel), c_int(1))
- # self.dwf.FDwfAnalogInChannelRangeSet(self.model.hdwf, c_int(self.model.analog_in_channel), c_double(5))
- # self.dwf.FDwfAnalogInAcquisitionModeSet(self.model.hdwf, acqmodeRecord)
- # self.dwf.FDwfAnalogInFrequencySet(self.model.hdwf, c_double(self.model.hz_acquisition))
- # self.dwf.FDwfAnalogInRecordLengthSet(self.model.hdwf, 0) # -1 infinite record length
- # self.get_analog_in_status()
- # # wait at least 2 seconds for the offset to stabilize
- # time.sleep(2)
- # self.logger.info(f"[{self.pref} Task] Setup for acquisition done.")
- # return True
- #
- # # # ==================================================================================================================
- # # # Acquisition
- # # # ==================================================================================================================
- # # @Slot()
- # # def start_capture(self, capture):
- # # if not self.model.connected:
- # # self.logger.warning(f"[{self.pref} Task] No device connected. Connecting to first device.")
- # # self.connect_device(0)
- # # return False
- # #
- # # if capture:
- # # self.logger.info(f"[{self.pref} Task] Setting up device for capturing.")
- # # self.gen_sine()
- # # self._setup_acquisition()
- # # # if self._setup_acquisition():
- # # # self.logger.info(f"[{self.pref} Task] Started capturing thread")
- # # self.set_ad2_acq_status(True)
- # # return self.thread_manager.start(self._capture)
- # # else:
- # # self.set_ad2_acq_status(False)
- # # # return self._capture()
- # #
- # # def _capture(self):
- # # self.model.capturing_finished = False
- # # self.model.device_capturing = False
- # # cAvailable = c_int()
- # # cLost = c_int()
- # # cCorrupted = c_int()
- # #
- # # self.logger.info(f"[{self.pref} Report] Capturing started. "
- # # f"Waiting for start command: "
- # # f"{self.model.start_recording}<->{self.model.stop_recording}")
- # #
- # # # Configures the instrument and start or stop the acquisition. To reset the Auto trigger timeout, set
- # # # fReconfigure to TRUE.
- # # # print("Starting oscilloscope")
- # # self.dwf.FDwfAnalogInConfigure(self.model.hdwf, c_int(0), c_int(1))
- # #
- # # t0 = -1
- # #
- # # cSamples = 0
- # # self.model.device_ready = True
- # # self.logger.info(f"[{self.pref} Report] Capturing device is ready.")
- # # while True:
- # #
- # # if self.model.start_recording and not self.model.stop_recording:
- # # if t0 < 0:
- # # self.logger.info(f"[{self.pref} Report] Start command received.")
- # # self.model.capturing_finished = False
- # # self.model.device_capturing = True
- # # self.model.current_recorded_samples = []
- # # timestamp = datetime.now()
- # # t0 = time.time()
- # # # print(f"Start ({cSamples})")
- # # sts = self.get_analog_in_status()
- # #
- # # if cSamples == 0 and (
- # # sts == DwfStateConfig or
- # # sts == DwfStatePrefill or
- # # sts == DwfStateArmed):
- # # print('idle')
- # # continue # Acquisition not yet started.
- # #
- # # # Retrieves information about the recording process. The data loss occurs when the device acquisition
- # # # is faster than the read process to PC. In this case, the device recording buffer is filled and data
- # # # samples are overwritten. Corrupt samples indicate that the samples have been overwritten by the
- # # # acquisition process during the previous read. In this case, try optimizing the loop process for faster
- # # # execution or reduce the acquisition frequency or record length to be less than or equal to the device
- # # # buffer size (record length <= buffer size/frequency).
- # # self.dwf.FDwfAnalogInStatusRecord(self.model.hdwf, # Interface handle
- # # byref(cAvailable),
- # # byref(cLost),
- # # byref(cCorrupted))
- # #
- # # cSamples += cLost.value
- # #
- # # if cLost.value:
- # # self.logger.warning(f"[{self.pref} Report] - Sample(s) lost ({cLost.value})")
- # # self.model.fLost += int(cLost.value)
- # # if cCorrupted.value:
- # # self.logger.warning(f"[{self.pref} Report] - Samples(s) corrupted ({cCorrupted.value})")
- # # self.model.fCorrupted += int(cCorrupted.value)
- # #
- # # # self.dwf.FDwfAnalogInStatusSamplesValid(self.hdwf, byref(self.cValid))
- # # if cAvailable.value == 0:
- # # # print(f"Nothing available {cAvailable.value}")
- # # continue
- # # else:
- # # # print(f"Available: {cAvailable.value}")
- # #
- # # # if cSamples + cAvailable.value > self.ad2capt_model.n_samples:
- # # # cAvailable = c_int(self.ad2capt_model.n_samples - cSamples)
- # # rgdSamples = (c_double * cAvailable.value)()
- # # # Retrieves the acquired data samples from the specified idxChannel on the AnalogIn instrument. It
- # # # copies the data samples to the provided buffer.
- # # self.dwf.FDwfAnalogInStatusData(self.model.hdwf,
- # # c_int(self.model.analog_in_channel),
- # # byref(rgdSamples),
- # # cAvailable) # get channel 1 data
- # # for s in rgdSamples:
- # # self.model.current_recorded_samples.append(float(s))
- # #
- # # cSamples += cAvailable.value
- # #
- # # elif not self.model.start_recording and self.model.stop_recording:
- # # t1 = time.time()
- # # self.model.measurement_time = t1 - t0
- # # self.get_analog_in_status()
- # # self.model.capturing_finished = True
- # # self.model.device_capturing = False
- # # self.logger.info(f"Finished Thread. Acquisition took {self.model.measurement_time} s. "
- # # f"Process captured {len(self.model.current_recorded_samples)} samples.")
- # # # 1. Assign the current captured samples to a dict
- # # self.model.all_recorded_samples.append({'timestamp': timestamp,
- # # 'measurement_time': self.model.measurement_time,
- # # 'num_samples': len(
- # # self.model.current_recorded_samples),
- # # 'acqRate': self.model.hz_acquisition,
- # # 'samples': self.model.current_recorded_samples})
- # # # Reset status bits
- # # try:
- # # time.sleep(1)
- # # self.close_device()
- # # except Exception as e:
- # # print(e)
- # # return
- # # # return self.model.current_recorded_samples, self.model.measurement_time
- # # else:
- # # self.model.device_capturing = False
- #
- # # # ==================================================================================================================
- # # #
- # # # ==================================================================================================================
- # # def gen_sine(self, channel=0, frequency=1):
- # # self.logger.debug(f"[{self.pref} Task] Generating sine wave on output 0...")
- # # self.dwf.FDwfAnalogOutNodeEnableSet(self.model.hdwf, c_int(channel), AnalogOutNodeCarrier, c_int(1))
- # # self.dwf.FDwfAnalogOutNodeFunctionSet(self.model.hdwf, c_int(channel), AnalogOutNodeCarrier,
- # # funcTrapezium) # sine
- # # self.dwf.FDwfAnalogOutNodeFrequencySet(self.model.hdwf, c_int(channel), AnalogOutNodeCarrier,
- # # c_double(frequency)) # 1Hz
- # # self.dwf.FDwfAnalogOutNodeAmplitudeSet(self.model.hdwf, c_int(channel), AnalogOutNodeCarrier, c_double(2))
- # # self.dwf.FDwfAnalogOutConfigure(self.model.hdwf, c_int(channel), c_int(1))
- # # return self.model.hdwf
- #
- # # ==================================================================================================================
- # #
- # # ==================================================================================================================
- # def close_device(self):
- # # Resets and configures (by default, having auto configure enabled) all AnalogOut instrument
- # # parameters to default values for the specified channel. To reset instrument parameters across all
- # # channels, set idxChannel to -1.
- # self.model.fLost = 0
- # self.model.fCorrupted = 0
- # self.model.start_recording = True
- # self.model.stop_recording = False
- # self.model.capturing_finished = False
- # self.model.device_capturing = False
- # self.model.connected = False
- # self.model.device_state = 0
- # self.model.dwf_version = "Unknown"
- # self.model.device_serial_number = "Unknown"
- # self.model.device_name = "Unknown"
- # self.model.analog_in_channel = -1
- #
- # self.dwf.FDwfAnalogOutReset(self.model.hdwf, c_int(self.model.analog_in_channel))
- # self.dwf.FDwfDeviceCloseAll()
- # self.logger.info(f"[{self.pref} Task] Device closed.")
-
- # def get_analog_in_status(self):
- # sts: c_byte = c_byte()
- # # Checks the state of the acquisition. To read the data from the device, set fReadData to TRUE. For
- # # single acquisition mode, the data will be read only when the acquisition is finished.
- # self.dwf.FDwfAnalogInStatus(self.model.hdwf, # Interface handle.
- # c_int(1), # True, if data should be read
- # byref(sts)) # Variable to receive the acquisition state
- # self.model.device_state = sts.value
- # return sts
-
- # ==================================================================================================================
- #
- # ==================================================================================================================
diff --git a/src/CaptDeviceControl/controller/BaseAD2CaptDevice.py b/src/CaptDeviceControl/controller/BaseAD2CaptDevice.py
index 20cb325d7566c34ece9b1b15081ed978842952db..dfad80f216eb4822ec681df3616467047821ca05 100644
--- a/src/CaptDeviceControl/controller/BaseAD2CaptDevice.py
+++ b/src/CaptDeviceControl/controller/BaseAD2CaptDevice.py
@@ -1,9 +1,12 @@
import logging
+import os
import time
from abc import abstractmethod
from collections import deque
from PySide6.QtCore import QObject, QThreadPool
+from numpy import ndarray
+from rich.logging import RichHandler
from CaptDeviceControl.controller.mp_AD2Capture.AD2StateMPSetter import AD2State
from CaptDeviceControl.controller.mp_AD2Capture.MPDeviceControl import mp_capture
@@ -16,12 +19,19 @@ from CaptDeviceControl.controller.mp_AD2Capture.MPCaptDeviceControl import MPCap
class BaseAD2CaptDevice(QObject):
- def __init__(self, ad2capt_model: AD2CaptDeviceModel):
+ def __init__(self, ad2capt_model: AD2CaptDeviceModel, start_capture_flag: Value):
super().__init__()
self.model = ad2capt_model
self.pref = "AD2CaptDev"
- self.logger = logging.getLogger(f"AD2 Device")
+
+ self.handler = RichHandler(rich_tracebacks=True)
+ self.logger = logging.getLogger(f"AD2Controller({os.getpid()})")
+ self.logger.handlers = [self.handler]
+ self.logger.setLevel(logging.DEBUG)
+ formatter = logging.Formatter('%(name)s %(message)s')
+ self.handler.setFormatter(formatter)
+
self.signals = AD2CaptDeviceSignals()
@@ -32,13 +42,17 @@ class BaseAD2CaptDevice(QObject):
# self.thread_manager.setThreadPriority(QThread.HighestPriority)
self.lock = Lock()
- self.proc = None
+ #self.proc = None
self.stream_data_queue = Queue()
self.capture_data_queue = Queue()
- self.state_queue = Queue()
+ #self.state_queue = Queue()
- self.start_capture_flag = Value('i', 0, lock=self.lock)
- self.end_process_flag = Value('i', False, lock=self.lock)
+ if start_capture_flag is None:
+ self.start_capture_flag = Value('i', 0, lock=self.lock)
+ else:
+ self.start_capture_flag = start_capture_flag
+ self.kill_capture_flag = Value('i', 0, lock=self.lock)
+ #self.end_process_flag = Value('i', False, lock=self.lock)
# Number of sa
self.streaming_data_dqueue: deque = None # a dqueue, initialize later
@@ -50,31 +64,33 @@ class BaseAD2CaptDevice(QObject):
self.stream_data_queue,
self.capture_data_queue,
self.start_capture_flag,
- enable_logging=True)
+ self.kill_capture_flag,
+ enable_internal_logging=False)
#self.mpcaptdevicecontrol.discover_connected_devices_finished.connect(
# lambda x: type(self.model).connected_devices.fset(self.model, x))
- self.mpcaptdevicecontrol.connected_devices()
-
- def get_analog_in_informatio(self):
- self.mpcaptdevicecontrol.ain_channels(self.model.selected_device)
+ #self.mpcaptdevicecontrol.connected_devices()
+ def device_selected_index_changed(self):
+ print(self.model.device_information.device_index)
+ self.mpcaptdevicecontrol.ain_channels(self.model.device_information.device_index)
+ #self.mpcaptdevicecontrol
def connect_device(self, device_id):
self.mpcaptdevicecontrol.open_device(device_id)
self.mpcaptdevicecontrol.start_capture(
self.model.sample_rate,
- self.model.selected_ain_channel)
+ self.model.device_information.device_index)
self.start_device_process()
return True
def close_device(self):
self.mpcaptdevicecontrol.close_device()
- @abstractmethod
+
def discover_connected_devices(self):
- raise NotImplementedError
+ self.mpcaptdevicecontrol.connected_devices()
@abstractmethod
def update_device_information(self):
@@ -169,35 +185,36 @@ class BaseAD2CaptDevice(QObject):
#self.proc.start()
# self.thread_manager.moveToThread(())
- #self.thread_manager.start(self.qt_consume_data)
+ self.thread_manager.start(self.qt_consume_data)
self.thread_manager.start(self.qt_stream_data)
#self.thread_manager.start(self.qt_get_state)
def qt_consume_data(self):
- """Consume data from the queue and plot it. This is a QThread."""
- while not self.kill_thread and not bool(self.end_process_flag.value):
- while self.capture_data_queue.qsize() > 0:
- self.model.unconsumed_capture_data = self.capture_data_queue.qsize()
- d, s = self.capture_data_queue.get()
- [self.model.recorded_samples.append(e) for e in d]
- # self.model.samples_captured = len(self.model.recorded_samples)
- self.status_dqueue.append(s)
- #time.sleep(0.01)
+ while True:
+ try:
+ capt_data = self.capture_data_queue.get()
+ if isinstance(capt_data, ndarray):
+ print(f"Capt data queue size {self.capture_data_queue.qsize()}")
+ # for d in stream_data:
+ # self.model.recorded_samples.append(d)
+ except Exception as e:
+ self.logger.info(f"Error while consuming data {e}")
self.logger.info("Capture Data consume thread ended")
def qt_stream_data(self):
- nth_cnt = 1
- nth = 2
- while not self.kill_thread and not bool(self.end_process_flag.value):
- while True:
- #self.stream_data_queue.qsize() > 0:
- #self.model.unconsumed_stream_samples = self.stream_data_queue.qsize()
- for d in self.stream_data_queue.get()[0]:
- #if nth_cnt == nth:
- self.streaming_data_dqueue.append(d)
- # nth_cnt = 0
- #nth_cnt += 1
- #time.sleep(0.01)
+ while True:
+ t = time.time()
+ try:
+ stream_data = self.stream_data_queue.get(block=True)
+ if isinstance(stream_data, ndarray):
+ #print(f"Stream data queue size {self.stream_data_queue.qsize()}")
+ for d in stream_data:
+ self.streaming_data_dqueue.append(d)
+ t_end = time.time()
+ #print(f"Time to get data {t_end-t}")
+ except Exception as e:
+ self.logger.info(f"Timeout reached. No data in queue {self.stream_data_queue.qsize()} or"
+ f"{e}")
self.logger.info("Streaming data consume thread ended")
def qt_get_state(self):
@@ -246,13 +263,13 @@ class BaseAD2CaptDevice(QObject):
# Destructor
# ==================================================================================================================
def stop_process(self):
- self.end_process_flag.value = True
-
+ #self.end_process_flag.value = True
+
time_start = time.time()
- while self.proc.is_alive():
- time.sleep(0.1)
+ #while self.proc.is_alive():
+ # time.sleep(0.1)
self.logger.warning(f"AD2 process exited after {time.time()-time_start}s")
- self.kill_thread = True
+ self.kill_thread = True
def __del__(self):
self.logger.info("Exiting AD2 controller")
diff --git a/src/CaptDeviceControl/controller/DeviceInformation/AnalogOutChannel.py b/src/CaptDeviceControl/controller/DeviceInformation/AnalogOutChannel.py
new file mode 100644
index 0000000000000000000000000000000000000000..97f6cd2bc906a85821fdd1322ce21c3bc559aeed
--- /dev/null
+++ b/src/CaptDeviceControl/controller/DeviceInformation/AnalogOutChannel.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+"""
+Author(s): Christoph Schmidt <christoph.schmidt@tugraz.at>
+Created: 2023-10-19 12:35
+Package Version:
+"""
+from ctypes import CDLL, c_int, byref
+
+from controller.DeviceInformation.HWDeviceBase import HWDeviceBase
+
+
+class AnalogOutChannel(HWDeviceBase):
+ def __init__(self, dwf: CDLL, hwdf: c_int, device_idx, channel):
+ super().__init__(dwf, hwdf)
+
+ self._channel: int = channel
+ self._node: int = 0
+ self._buffer_size: int = 0
+ self._amplitude: tuple = (0, 0)
+ self._offset: tuple = (0, 0)
+ self._frequency: tuple = (0, 0)
+
+ def reinit(self, fields: dict):
+ for k, v in fields.items():
+ setattr(self, k, v)
+
+ @property
+ def channel(self) -> int:
+ """
+ Returns the channel number of the AnalogOut channel.
+ :return: The channel number of the AnalogOut channel.
+ """
+ return self._channel
+
+ @property
+ def node(self) -> int:
+ """
+ Returns the node number of the AnalogOut channel.
+ :return: The node number of the AnalogOut channel.
+ """
+ return self._node
+
+ @property
+ def buffer_size(self) -> int:
+ """
+ Reads the number of AnalogIn channels of the device. The oscilloscope channel settings are
+ identical across all channels.
+ Calls WaveForms API Function 'FDwfAnalogInChannelCount(HDWF hdwf, int *pcChannel)'
+ :return: The number of analog in channels.
+ """
+ self._check_device_connection()
+ int0 = c_int()
+ self.dwf.FDwfAnalogInChannelCount(self.hdwf, byref(int0))
+ self._analog_in_channels = int(int0.value)
+ return self._analog_in_channels
+
+
+class AnalogOutChannelSetter():
+ pass
\ No newline at end of file
diff --git a/src/CaptDeviceControl/controller/DeviceInformation/HWConnectedDeviceInformation.py b/src/CaptDeviceControl/controller/DeviceInformation/HWConnectedDeviceInformation.py
new file mode 100644
index 0000000000000000000000000000000000000000..39300949a20b859072a73c7323bd6b56478a8270
--- /dev/null
+++ b/src/CaptDeviceControl/controller/DeviceInformation/HWConnectedDeviceInformation.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+"""
+Author(s): Christoph Schmidt <christoph.schmidt@tugraz.at>
+Created: 2023-10-19 12:35
+Package Version:
+"""
+from ctypes import CDLL
+
+from controller.DeviceInformation.dataclasses.AnalogInChannelInfo import AnalogInChannels
+from controller.DeviceInformation.AnalogOutChannel import AnalogOutChannels
+
+
+class HWConnectedDeviceInformation:
+ """ Class for storing information about the connected device."""
+
+ def __init__(self, dwf: CDLL, device_idx, type="USB"):
+ self.dwf = dwf
+ self._type: str = type
+
+ self._device_id: int = device_idx
+ self._device_name: str = ""
+ self._serial_number: str = ""
+
+ self.analog_in_channels = list[AnalogInChannels]
+ self.analog_out_channels = list[AnalogOutChannels]
+
+ @property
+ def type(self) -> str:
+ """
+ Returns the type of the device (USB or Simulator)
+ :return: Type of the device
+ """
+ return self._type
+
+ @property
+ def device_id(self) -> int:
+ """
+ The device id of the enumerated device.
+ :return: The device id of the enumerated device.
+ """
+ return self._device_id
+
+ @property
+ def device_name(self) -> str:
+ """
+ The device name of the enumerated device.
+ :return: The device name of the enumerated device.
+ """
+ return self._device_name
+
+ @property
+ def serial_number(self) -> str:
+ """
+ The serial number of the enumerated device.
+ :return: The serial number of the enumerated device.
+ """
+ return self._serial_number
+
+ def __repr__(self):
+ return f"HW({self._device_name}, {self._serial_number}, {self._type})"
\ No newline at end of file
diff --git a/src/CaptDeviceControl/controller/DeviceInformation/HWDeviceBase.py b/src/CaptDeviceControl/controller/DeviceInformation/HWDeviceBase.py
new file mode 100644
index 0000000000000000000000000000000000000000..c1cfda7b0d2960fc55fb18de67aad4292a4493b1
--- /dev/null
+++ b/src/CaptDeviceControl/controller/DeviceInformation/HWDeviceBase.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+"""
+Author(s): Christoph Schmidt <christoph.schmidt@tugraz.at>
+Created: 2023-10-19 12:35
+Package Version:
+"""
+from ctypes import CDLL, c_int, create_string_buffer, c_int32, byref
+
+from constants.dwfconstants import enumfilterType, enumfilterUSB, enumfilterDemo
+
+
+class HWDeviceBase:
+ """Base class for retrieving information from the hardware."""
+
+ def __int__(self, dwf: CDLL, hwdf: c_int):
+ self.dwf: CDLL = dwf
+ self.hdwf: hwdf = hwdf
+
+ def _check_device_connection(self):
+ if self.hdwf.value == 0:
+ szerr = create_string_buffer(512)
+ self.dwf.FDwfGetLastErrorMsg(szerr)
+ raise Exception(str(szerr.value))
+
+
diff --git a/src/CaptDeviceControl/controller/DeviceInformation/HWDeviceInformation.py b/src/CaptDeviceControl/controller/DeviceInformation/HWDeviceInformation.py
new file mode 100644
index 0000000000000000000000000000000000000000..59d679900dd55ff7590d97c9bdc95a889902e852
--- /dev/null
+++ b/src/CaptDeviceControl/controller/DeviceInformation/HWDeviceInformation.py
@@ -0,0 +1,200 @@
+# -*- coding: utf-8 -*-
+"""
+Author(s): Christoph Schmidt <christoph.schmidt@tugraz.at>
+Created: 2023-10-19 12:35
+Package Version:
+"""
+from ctypes import c_int, create_string_buffer, CDLL, byref
+
+from controller.DeviceInformation.WaveFormsAPI import WFAPIDeviceEnumeration, WFAPIDeviceControl, WFAPIChannels
+from controller.DeviceInformation.dataclasses.AnalogInChannelInfo import AnalogInChannelInfo, AnalogInChannelRetriever
+
+
+class HWDeviceInformation:
+ """ Class for storing information about the connected device."""
+
+ def __init__(self, device_idx, type="USB"):
+ self._type: str = type
+
+ self._device_id: int = device_idx
+ self._device_name: str = ""
+ self._serial_number: str = ""
+
+ self._analog_in_channels: list[AnalogInChannelInfo] = []
+ self._analog_out_channels: list[AnalogInChannelInfo] = []
+
+ self._analog_in_channel_count: int = 0
+ self._analog_out_channel_count: int = 0
+ self._analog_io_channel_count: int = 0
+
+ self._digital_in_channel_count: int = 0
+ self._digital_out_channel_count: int = 0
+ self._digital_io_channel_count: int = 0
+
+ self._analog_in_buffer_size: int = 0
+ self._analog_out_buffer_size: int = 0
+
+ self._digital_in_buffer_size: int = 0
+ self._digital_out_buffer_size: int = 0
+ # self._reload()
+
+ @property
+ def type(self) -> str:
+ """
+ Returns the type of the device (USB or Simulator)
+ :return: Type of the device
+ """
+ return self._type
+
+ @property
+ def device_id(self) -> int:
+ """
+ The device id of the enumerated device.
+ :return: The device id of the enumerated device.
+ """
+ return self._device_id
+
+ @property
+ def device_name(self) -> str:
+ """
+ The device name of the enumerated device.
+ :return: The device name of the enumerated device.
+ """
+ return self._device_name
+
+ @property
+ def serial_number(self) -> str:
+ """
+ The serial number of the enumerated device.
+ :return: The serial number of the enumerated device.
+ """
+ return self._serial_number
+
+ @property
+ def analog_in_channels(self) -> list[AnalogInChannelInfo]:
+ """
+ The analog in channels of the device without opening it.
+ :return: The analog in channels.
+ """
+ return self._analog_in_channels
+
+ @property
+ def analog_out_channels(self) -> list[AnalogInChannelInfo]:
+ """
+ The analog out channels of the device without opening it.
+ :return: The analog out channels.
+ """
+ return self._analog_out_channels
+
+ @property
+ def analog_in_channel_count(self) -> int:
+ """
+ The number of AnalogIn channels of the device without opening it.
+ :return: The number of analog in channels.
+ """
+ return self._analog_in_channel_count
+
+ @property
+ def analog_out_channel_count(self) -> int:
+ """
+ The number of AnalogOut channels of the device without opening it.
+ :return: The number of analog out channels.
+ """
+ return self._analog_out_channel_count
+
+ @property
+ def analog_io_channel_count(self) -> int:
+ """
+ The number of AnalogIO channels of the device without opening it.
+ :return: The number of analog io channels.
+ """
+ return self._analog_io_channel_count
+
+ @property
+ def digital_in_channel_count(self) -> int:
+ """
+ The number of DigitalIn channels of the device without opening it.
+ :return: The number of digital in channels.
+ """
+ return self._digital_in_channel_count
+
+ @property
+ def digital_out_channel_count(self) -> int:
+ """
+ The number of DigitalOut channels of the device without opening it.
+ :return: The number of digital out channels.
+ """
+ return self._digital_out_channel_count
+
+ @property
+ def digital_io_channel_count(self) -> int:
+ """
+ The number of DigitalIO channels of the device without opening it.
+ :return: The number of digital io channels.
+ """
+ return self._digital_io_channel_count
+
+ @property
+ def analog_in_buffer_size(self) -> int:
+ """
+ The buffer size of the AnalogIn channels of the device without opening it.
+ :return: The buffer size of the analog in channels.
+ """
+ return self._analog_in_buffer_size
+
+ @property
+ def analog_out_buffer_size(self) -> int:
+ """
+ The buffer size of the AnalogOut channels of the device without opening it.
+ :return: The buffer size of the analog out channels.
+ """
+ return self._analog_out_buffer_size
+
+ @property
+ def digital_in_buffer_size(self) -> int:
+ """
+ The buffer size of the DigitalIn channels of the device without opening it.
+ :return: The buffer size of the digital in channels.
+ """
+ return self._digital_in_buffer_size
+
+ @property
+ def digital_out_buffer_size(self) -> int:
+ """
+ The buffer size of the DigitalOut channels of the device without opening it.
+ :return: The buffer size of the digital out channels.
+ """
+ return self._digital_out_buffer_size
+
+ def __repr__(self):
+ return f"HW({self._device_name}, {self._serial_number}, {self._type})"
+
+
+class HWDeviceInformationRetriever(HWDeviceInformation):
+ def __init__(self, dwf: CDLL, device_idx, con_type="USB"):
+ super().__init__(device_idx, con_type)
+ self._device_name = WFAPIDeviceEnumeration.device_name(dwf, self.device_id)
+ self._serial_number = WFAPIDeviceEnumeration.serial_number(dwf, self.device_id)
+
+ # Top get all settings, we need to open the device
+ try:
+ self.hwdf = WFAPIDeviceControl.device_open(dwf, self.device_id)
+ except Exception as ex:
+ print(f"Error opening device: {ex}")
+ self.hwdf = None
+
+ if self.hwdf is not None:
+ # Get the number of ain channels
+ channel_count = WFAPIChannels().analog_in_channels_count(dwf, self.hwdf)
+ #print(channel_count)
+ # Create a list of channels
+ for channel in range(channel_count):
+ o = AnalogInChannelRetriever(dwf, self.hwdf, channel).simple()
+ self._analog_in_channels.append(o)
+ print(type(o))
+
+ self._analog_in_channel_count = len(self._analog_in_channels)
+
+
+ def simple(self):
+ return super()
diff --git a/src/CaptDeviceControl/controller/DeviceInformation/WaveFormsAPI.py b/src/CaptDeviceControl/controller/DeviceInformation/WaveFormsAPI.py
new file mode 100644
index 0000000000000000000000000000000000000000..02bb60588e5c1b2a038f083d5d61651b48870731
--- /dev/null
+++ b/src/CaptDeviceControl/controller/DeviceInformation/WaveFormsAPI.py
@@ -0,0 +1,509 @@
+# -*- coding: utf-8 -*-
+"""
+Author(s): Christoph Schmidt <christoph.schmidt@tugraz.at>
+Created: 2023-10-19 12:35
+Package Version:
+"""
+import logging
+import sys
+from abc import abstractmethod
+from ctypes import c_int, byref, c_double, CDLL, create_string_buffer, cdll, c_int32
+
+from constants.dwfconstants import enumfilterType, enumfilterDemo, enumfilterUSB
+
+
+class WFAPI:
+
+ @staticmethod
+ def _check_device_connection(dwf: CDLL, hwdf: c_int):
+ if hwdf.value == 0:
+ szerr = create_string_buffer(512)
+ dwf.FDwfGetLastErrorMsg(szerr)
+ raise Exception(str(szerr.value))
+
+
+class WFAPIChannels(WFAPI):
+
+ def __init__(self):
+ super().__init__()
+
+ @staticmethod
+ def analog_in_channels_count(dwf: CDLL, hdwf: c_int) -> int:
+ """
+ Reads the number of AnalogIn channels of the device. The oscilloscope channel settings are
+ identical across all channels.
+ Calls WaveForms API Function 'FDwfAnalogInChannelCount(HDWF hdwf, int *pcChannel)'
+ :return: The number of analog in channels.
+ """
+ WFAPI._check_device_connection(dwf, hdwf)
+ int0 = c_int()
+ dwf.FDwfAnalogInChannelCount(hdwf, byref(int0))
+ _analog_in_channels = int(int0.value)
+ return _analog_in_channels
+
+ @staticmethod
+ def analog_on_channel_enable_set(dwf: CDLL, hdwf: c_int, channel: int, enable: bool):
+ """
+ Not implemented.
+ Enables or disables the specified AnalogIn channel.
+ Calls WaveForms API Function 'FDwfAnalogInChannelEnableSet(HDWF hdwf, int idxChannel, int fEnable)'
+ :param channel: The channel to enable or disable.
+ :param enable: True to enable the channel, False to disable it.
+ """
+ WFAPI._check_device_connection(dwf, hdwf)
+ raise NotImplementedError()
+
+ @staticmethod
+ def analog_in_channel_enable_get(dwf: CDLL, hdwf: c_int, channel: int) -> bool:
+ """
+ Not implemented.
+ Gets the current enable/disable status of the specified AnalogIn channel.
+ Calls WaveForms API Function 'FDwfAnalogInChannelEnableGet(HDWF hdwf, int idxChannel, int *pfEnable)'
+ :param channel: Index of channel to get the enable/disable status of.
+ """
+ WFAPI._check_device_connection(dwf, hdwf)
+ raise NotImplementedError()
+
+ @staticmethod
+ def analog_in_channel_filter_info(dwf: CDLL, hdwf: c_int, filter_number: int):
+ """
+ Not implemented.
+ Returns the supported sampling modes. They are returned (by reference) as a bit field. This bit field
+ can be parsed using the IsBitSet Macro. Individual bits are defined using the FILTER constants in dwf.h.
+ When the acquisition frequency (FDwfAnalogInFrequencySet) is less than the ADC frequency (maximum
+ acquisition frequency).
+
+ Calls the WaveForms API Function 'FDwfAnalogInChannelFilterInfo(HDWF hdwf, int *pfsfilter)'
+
+ :param filter_number:
+ - filterDecimate: 0
+ Store every Nth ADC conversion, where N = ADC frequency /acquisition frequency.
+ - filterAverage: 1
+ Store the average of N ADC conversions.
+ - filterMinMax: 2
+ Store interleaved, the minimum and maximum values, of 2xN conversions.
+ - filterAverageFit: 3
+ The stored samples match the specified range instead of the device input range options.
+ This can improve the vertical resolution of the samples.
+ """
+ WFAPI._check_device_connection(dwf, hdwf)
+ raise NotImplementedError()
+
+ @staticmethod
+ def analog_in_channel_filter_set(dwf: CDLL, hdwf: c_int, channel: int, filter_number: int):
+ """
+ Not implemented.
+ Sets the acquisition filter for each AnalogIn channel. With channel index -1, each enabled AnalogIn
+ channel filter will be configured to use the same, new option.
+
+ Calls the WaveForms API Function 'FDwfAnalogInChannelFilterSet(HDWF hdwf, int idxChannel, FILTER filter)'
+
+ :param channel: Channel index
+ :param filter_number: Acquisition sample filter to set.
+ - filterDecimate: 0
+ Store every Nth ADC conversion, where N = ADC frequency /acquisition frequency.
+ - filterAverage: 1
+ Store the average of N ADC conversions.
+ - filterMinMax: 2
+ Store interleaved, the minimum and maximum values, of 2xN conversions.
+ - filterAverageFit: 3
+ The stored samples match the specified range instead of the device input range options.
+ This can improve the vertical resolution of the samples.
+ """
+ WFAPI._check_device_connection(dwf, hdwf)
+ raise NotImplementedError()
+
+ @staticmethod
+ def analog_in_channel_filter_get(dwf: CDLL, hdwf: c_int, channel: int) -> int:
+ """
+ Not implemented.
+ Returns the configured acquisition filter.
+
+ Calls the WaveForms API Function 'FDwfAnalogInChannelFilterGet(HDWF hdwf, int idxChannel, FILTER *pfilter)'
+
+ :param channel: Channel index
+ :return: filter type (int)
+ - filterDecimate: 0
+ Store every Nth ADC conversion, where N = ADC frequency /acquisition frequency.
+ - filterAverage: 1
+ Store the average of N ADC conversions.
+ - filterMinMax: 2
+ Store interleaved, the minimum and maximum values, of 2xN conversions.
+ - filterAverageFit: 3
+ The stored samples match the specified range instead of the device input range options.
+ This can improve the vertical resolution of the samples.
+ """
+
+ @staticmethod
+ def analog_in_channel_range_info(dwf: CDLL, hdwf: c_int) -> tuple:
+ """
+ Returns the minimum and maximum range, peak to peak values, and the number of adjustable steps.
+ The oscilloscope channel settings are identical across all channels.
+ Calls WaveForms API Function
+ 'FDwfAnalogInChannelRangeInfo(HDWF hdwf, double *pvoltsMin, double *pvoltsMax, double *pnSteps)'
+ :return: The minimum and maximum range, peak to peak values, and the number of adjustable steps as a tuple
+ (min, max, steps).
+ """
+ WFAPI._check_device_connection(dwf, hdwf)
+ dbl0 = c_double()
+ dbl1 = c_double()
+ dbl2 = c_double()
+ dwf.FDwfAnalogInChannelRangeInfo(hdwf, byref(dbl0), byref(dbl1), byref(dbl2))
+ _range = (int(dbl0.value), int(dbl1.value), int(dbl2.value))
+ return _range
+
+ @staticmethod
+ def analog_in_channel_range_steps(dwf: CDLL, hdwf: c_int):
+ """
+ Not implemented.
+ Reads the range of steps supported by the device. For instance: 1, 2, 5, 10, etc.
+
+ Calls WaveForms API Function
+ 'FDwfAnalogInChannelRangeSteps(HDWF hdwf, double rgVoltsStep[32], int *pnSteps)'
+ """
+ WFAPI._check_device_connection(dwf, hdwf)
+ raise NotImplementedError()
+
+ @staticmethod
+ def analog_in_channel_range_set(dwf: CDLL, hdwf: c_int, channel: int, range: float):
+ """
+ Not implemented.
+ Configures the range for each channel. With channel index -1, each enabled Analog In channel range
+ will be configured to the same, new value.
+
+ Calls WaveForms API Function
+ 'FDwfAnalogInChannelRangeSet(HDWF hdwf, int idxChannel, double voltsRange)'
+ :param channel: Channel index
+ :param range: The range to set.
+ """
+ WFAPI._check_device_connection(dwf, hdwf)
+ raise NotImplementedError()
+
+ @staticmethod
+ def analog_in_channel_range_get(dwf: CDLL, hdwf: c_int, channel: int) -> float:
+ """
+ Not implemented.
+ Returns the real range value for the given channel.
+
+ Calls WaveForms API Function
+ 'FDwfAnalogInChannelRangeGet(HDWF hdwf, int idxChannel, double *pvoltsRange)'
+
+ :param channel: Channel index
+ :return: The real range value for the given channel.
+ """
+
+ @staticmethod
+ def analog_in_channel_offset_info(dwf: CDLL, hdwf: c_int) -> tuple:
+ """
+ Returns the minimum and maximum offset levels supported, and the number of adjustable steps. The oscilloscope
+ channel settings are identical across all channels.
+ Calls WaveForms API Function
+ 'FDwfAnalogInChannelOffsetInfo(HDWF hdwf, double *pvoltsMin, double *pvoltsMax, double *pnSteps)'
+ :return: The minimum and maximum offset levels supported, and the number of adjustable steps as
+ a tuple (min, max, steps).
+ """
+ WFAPI._check_device_connection(dwf, hdwf)
+ dbl0 = c_double()
+ dbl1 = c_double()
+ dbl2 = c_double()
+ dwf.FDwfAnalogInChannelOffsetInfo(hdwf, byref(dbl0), byref(dbl1), byref(dbl2))
+ _offset = (int(dbl0.value), int(dbl1.value), int(dbl2.value))
+ return _offset
+
+ @staticmethod
+ def analog_in_buffer_size(dwf: CDLL, hdwf: c_int) -> tuple:
+ """
+ Returns the minimum and maximum allowable buffer sizes for the instrument. The oscilloscope
+ channel settings are identical across all channels.
+ Calls WaveForms API Function 'FDwfAnalogInBufferSizeInfo(HDWF hdwf, int *pnSizeMin, int *pnSizeMax)'
+ :return: The minimum and maximum allowable buffer sizes for the instrument as a tuple (min, max).
+ """
+ WFAPI._check_device_connection(dwf, hdwf)
+ int0 = c_int()
+ int1 = c_int()
+ dwf.FDwfAnalogInBufferSizeInfo(hdwf, byref(int0), byref(int1))
+ _buffer_size = (int(int0.value), int(int0.value))
+ return _buffer_size
+
+ @staticmethod
+ def analog_in_bits(dwf: CDLL, hdwf: c_int) -> int:
+ """
+ Retrieves the number bits used by the AnalogIn ADC. The oscilloscope channel settings are identical
+ across all channels.
+ Calls WaveForms API Function 'FDwfAnalogInBitsInfo(HDWF hdwf, int *pnBits)'
+ :return: The number bits used by the AnalogIn ADC.
+ """
+ int0 = c_int()
+ WFAPI._check_device_connection(dwf, hdwf)
+ dwf.FDwfAnalogInBitsInfo(hdwf, byref(int0))
+ _adc_bits = int(int0.value)
+ return _adc_bits
+
+
+class WFAPIDeviceEnumeration(WFAPI):
+
+ def __init__(self):
+ super().__init__()
+
+ @staticmethod
+ def device_name(dwf: CDLL, device_id: int) -> str:
+ """
+ Retrieves the device name of the enumerated device.
+ Calls WaveForms API Function 'FDwfEnumDeviceName(int idxDevice, char szDeviceName[32])'
+ :return: The device name of the enumerated device
+ """
+ devicename = create_string_buffer(64)
+ dwf.FDwfEnumDeviceName(c_int(device_id), devicename)
+ return str(devicename.value.decode("utf-8"))
+
+ @staticmethod
+ def serial_number(dwf: CDLL, device_id: int) -> str:
+ """
+ Retrieves the serial number of the enumerated device.
+ :return:
+ """
+ serialnum = create_string_buffer(16)
+ dwf.FDwfEnumSN(c_int(device_id), serialnum)
+ return str(serialnum.value.decode("utf-8"))
+
+ @staticmethod
+ def analog_in_channel_count(dwf: CDLL, device_id: int) -> int:
+ """
+ Returns the number of AnalogIn channels of the device without opening it.
+ Note: These are intended for preliminary information before opening a device. Further information are available
+ with various the FDwf#Info functions.
+
+ Calls WaveForms API Function 'FDwfEnumConfigInfo(int idxConfig, DwfEnumConfigInfo info, int *pValue)'
+ with parameter info = 1.
+ :return: The number of analog in channels.
+ """
+ cInfo = c_int()
+ # c_int(1): DECIAnalogInChannelCount
+ dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(1), byref(cInfo))
+ return int(cInfo.value)
+
+ @staticmethod
+ def analog_out_channel_count(dwf: CDLL, device_id: int) -> int:
+ """
+ Returns the number of AnalogOut channels of the device without opening it.
+ Note: These are intended for preliminary information before opening a device. Further information are available
+ with various the FDwf#Info functions.
+
+ Calls WaveForms API Function 'FDwfEnumConfigInfo(int idxConfig, DwfEnumConfigInfo info, int *pValue)'
+ with parameter info = 2.
+ :return: The number of analog out channels.
+ """
+ cInfo = c_int()
+ # c_int(2): DECIAnalogOutChannelCount
+ dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(2), byref(cInfo))
+ return int(cInfo.value)
+
+ @staticmethod
+ def analog_io_channel_count(dwf: CDLL, device_id: int) -> int:
+ """
+ Returns the number of AnalogIO channels of the device without opening it.
+ Note: These are intended for preliminary information before opening a device. Further information are available
+ with various the FDwf#Info functions.
+
+ Calls WaveForms API Function 'FDwfEnumConfigInfo(int idxConfig, DwfEnumConfigInfo info, int *pValue)'
+ with parameter info = 3.
+ :return: The number of analog io channels.
+ """
+ cInfo = c_int()
+ # c_int(3): DECIAnalogIOChannelCount
+ dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(3), byref(cInfo))
+ return int(cInfo.value)
+
+ @staticmethod
+ def digital_in_channel_count(dwf: CDLL, device_id: int) -> int:
+ """
+ Returns the number of DigitalIn channels of the device without opening it.
+ Note: These are intended for preliminary information before opening a device. Further information are available
+ with various the FDwf#Info functions.
+
+ Calls WaveForms API Function 'FDwfEnumConfigInfo(int idxConfig, DwfEnumConfigInfo info, int *pValue)'
+ with parameter info = 4.
+ :return: The number of digital in channels.
+ """
+ cInfo = c_int()
+ # c_int(4): DECIDigitalInChannelCount
+ dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(4), byref(cInfo))
+
+ return int(cInfo.value)
+
+ @staticmethod
+ def digital_out_channel_count(dwf: CDLL, device_id: int) -> int:
+ """
+ Returns the number of DigitalOut channels of the device without opening it.
+ Note: These are intended for preliminary information before opening a device. Further information are available
+ with various the FDwf#Info functions.
+
+ Calls WaveForms API Function 'FDwfEnumConfigInfo(int idxConfig, DwfEnumConfigInfo info, int *pValue)'
+ with parameter info = 5.
+ :return: The number of digital out channels.
+ """
+ cInfo = c_int()
+ # c_int(5): DECIDigitalOutChannelCount
+ dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(5), byref(cInfo))
+
+ return int(cInfo.value)
+
+ @staticmethod
+ def digital_io_channel_count(dwf: CDLL, device_id: int) -> int:
+ """
+ Returns the number of DigitalIO channels of the device without opening it.
+ Note: These are intended for preliminary information before opening a device. Further information are available
+ with various the FDwf#Info functions.
+
+ Calls WaveForms API Function 'FDwfEnumConfigInfo(int idxConfig, DwfEnumConfigInfo info, int *pValue)'
+ with parameter info = 6.
+ :return: The number of digital io channels.
+ """
+ cInfo = c_int()
+ # c_int(6): DECIDigitalIOChannelCount
+ dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(6), byref(cInfo))
+ return int(cInfo.value)
+
+ @staticmethod
+ def analog_in_buffer_size(dwf: CDLL, device_id: int) -> int:
+ """
+ Returns the buffer size of the AnalogIn channels of the device without opening it.
+ Note: These are intended for preliminary information before opening a device. Further information are available
+ with various the FDwf#Info functions.
+
+ Calls WaveForms API Function 'FDwfEnumConfigInfo(int idxConfig, DwfEnumConfigInfo info, int *pValue)'
+ with parameter info = 7.
+ :return: The buffer size of the analog in channels.
+ """
+ cInfo = c_int()
+ # c_int(6): DECIDigitalIOChannelCount
+ dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(7), byref(cInfo))
+ return int(cInfo.value)
+
+ @staticmethod
+ def analog_out_buffer_size(dwf: CDLL, device_id: int) -> int:
+ """
+ Returns the buffer size of the AnalogOut channels of the device without opening it.
+ Note: These are intended for preliminary information before opening a device. Further information are available
+ with various the FDwf#Info functions.
+
+ Calls WaveForms API Function 'FDwfEnumConfigInfo(int idxConfig, DwfEnumConfigInfo info, int *pValue)'
+ with parameter info = 8.
+ :return: The buffer size of the analog out channels.
+ """
+ cInfo = c_int()
+ # c_int(6): DECIDigitalIOChannelCount
+ dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(8), byref(cInfo))
+ return int(cInfo.value)
+
+ @staticmethod
+ def digital_in_buffer_size(dwf: CDLL, device_id: int) -> int:
+ """
+ Returns the buffer size of the DigitalIn channels of the device without opening it.
+ Note: These are intended for preliminary information before opening a device. Further information are available
+ with various the FDwf#Info functions.
+
+ Calls WaveForms API Function 'FDwfEnumConfigInfo(int idxConfig, DwfEnumConfigInfo info, int *pValue)'
+ with parameter info = 9.
+ :return: The buffer size of the digital in channels.
+ """
+ cInfo = c_int()
+ # c_int(6): DECIDigitalIOChannelCount
+ dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(9), byref(cInfo))
+ return int(cInfo.value)
+
+ @staticmethod
+ def digital_out_buffer_size(dwf: CDLL, device_id: int) -> int:
+ """
+ Returns the buffer size of the DigitalOut channels of the device without opening it.
+ Note: These are intended for preliminary information before opening a device. Further information are available
+ with various the FDwf#Info functions.
+
+ Calls WaveForms API Function 'FDwfEnumConfigInfo(int idxConfig, DwfEnumConfigInfo info, int *pValue)'
+ with parameter info = 10.
+ :return: The buffer size of the digital out channels.
+ """
+ cInfo = c_int()
+ # c_int(6): DECIDigitalIOChannelCount
+ dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(10), byref(cInfo))
+ return int(cInfo.value)
+
+
+class WFAPIDeviceControl(WFAPI):
+
+ def __init__(self):
+ super().__init__()
+
+ @staticmethod
+ def device_open(dwf: CDLL, device_id: int) -> c_int:
+ """
+ Opens a device identified by the enumeration index and retrieves a handle.
+ To automatically enumerate all connected devices and open the first discovered device, use index -1.
+
+ Calls WaveForms API Function 'FDwfDeviceOpen(int idxDevice, HDWF *phdwf)'
+
+ :param dwf: Shared library handle.
+ :param device_id: The device id of the device to open.
+ :return: The handle (hdwf) of the opened device.
+ """
+ hdwf = c_int()
+ dwf.FDwfDeviceOpen(c_int(device_id), byref(hdwf))
+ return hdwf
+
+
+class WaveFormsAPI:
+
+ def __init__(self, parent_logger: logging.Logger):
+ self._logger = parent_logger
+ self.channels = WFAPIChannels()
+ self.device_enumeration = WFAPIDeviceEnumeration()
+ self.device_control = WFAPIDeviceControl()
+
+ self._dwf_init = False
+ self._dwf = None
+
+ def init_dwf(self) -> CDLL:
+ if not self._dwf_init:
+ if sys.platform.startswith("win"):
+ self._dwf = cdll.dwf
+ elif sys.platform.startswith("darwin"):
+ self._dwf = cdll.LoadLibrary("/Library/Frameworks/dwf.framework/dwf")
+ else:
+ self._dwf = cdll.LoadLibrary("libdwf.so")
+ self._dwf_init = True
+ return self._dwf
+
+
+ def get_connected_devices(self, dwf, discover_simulators: bool = False):
+ self._logger.info(f"Discovering connected devices...")
+ # enumerate connected devices
+ connected_devices = []
+ # for filter_type in [(c_int32(enumfilterType.value | enumfilterUSB.value), 'USB'),
+ # (c_int32(enumfilterType.value | enumfilterNetwork.value), 'Network'),
+ # (c_int32(enumfilterType.value | enumfilterAXI.value), 'AXI'),
+ # (c_int32(enumfilterType.value | enumfilterRemote.value), 'Remote'),
+ # (c_int32(enumfilterType.value | enumfilterAudio.value), 'Audio'),
+ # (c_int32(enumfilterType.value | enumfilterDemo.value), 'Demo')]:
+ cDevice = c_int()
+ filter, type = (c_int32(enumfilterType.value | enumfilterDemo.value | enumfilterUSB.value), 'USB')
+ # filter, type = (c_int32(enumfilterType.value | enumfilterDemo.value), 'DEMO')
+ self._logger.debug(f"Filtering {type} devices...")
+ self.dwf.FDwfEnum(filter, byref(cDevice))
+ num_of_connected_devices = cDevice
+
+ devicename = create_string_buffer(64)
+ serialnum = create_string_buffer(16)
+
+ for iDevice in range(0, cDevice.value):
+ self.dwf.FDwfEnumDeviceName(c_int(iDevice), devicename)
+ self.dwf.FDwfEnumSN(c_int(iDevice), serialnum)
+ connected_devices.append({
+ 'type': type,
+ 'device_id': int(iDevice),
+ 'device_name': str(devicename.value.decode('UTF-8')),
+ 'serial_number': str(serialnum.value.decode('UTF-8'))
+ })
+ # _mp_log_debug(f"Found {type} device: {devicename.value.decode('UTF-8')} ({serialnum.value.decode('UTF-8')})")
+ # print(connected_devices)
+ # print(f"Discoverd {len(self.model.connected_devices)} devices.")
+ return connected_devices
diff --git a/src/CaptDeviceControl/controller/DeviceInformation/dataclasses/AnalogInChannelInfo.py b/src/CaptDeviceControl/controller/DeviceInformation/dataclasses/AnalogInChannelInfo.py
new file mode 100644
index 0000000000000000000000000000000000000000..96d637bd65f7ea9337aded5de2e637575bcdedd8
--- /dev/null
+++ b/src/CaptDeviceControl/controller/DeviceInformation/dataclasses/AnalogInChannelInfo.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+"""
+Author(s): Christoph Schmidt <christoph.schmidt@tugraz.at>
+Created: 2023-10-19 12:35
+Package Version:
+"""
+from ctypes import CDLL, c_int, byref, c_double
+from dataclasses import dataclass
+
+from controller.DeviceInformation.HWDeviceBase import HWDeviceBase
+from controller.DeviceInformation.WaveFormsAPI import WFAPIChannels
+
+
+@dataclass
+class AnalogInChannelInfo:
+ """ Class for storing the information about the channels"""
+
+ def __init__(self):
+ self._channel = 0
+ self._buffer_size: tuple = (0, 0)
+ self._adc_bits: int = 0
+ self._range: tuple = (0, 0, 0)
+ self._offset: tuple = (0, 0, 0)
+
+ @property
+ def buffer_size(self) -> tuple:
+ """ Returns the minimum and maximum allowable buffer sizes for the instrument"""
+ return self._buffer_size
+
+ @property
+ def adc_bits(self) -> int:
+ """ Returns the number bits used by the AnalogIn ADC"""
+ return self._adc_bits
+
+ @property
+ def range(self) -> tuple:
+ """ Returns the minimum and maximum range, peak to peak values, and the number of adjustable steps"""
+ return self._range
+
+ @property
+ def offset(self) -> tuple:
+ """ Returns the minimum and maximum offset levels supported, and the number of adjustable steps"""
+ return self._offset
+
+
+class AnalogInChannelRetriever(AnalogInChannelInfo):
+
+ def __init__(self, dwf: CDLL, hwdf: c_int, channel: int):
+ super().__init__()
+ self._channel = channel
+ self._buffer_size: tuple = WFAPIChannels().analog_in_buffer_size(dwf, hwdf)
+ self._adc_bits: int = WFAPIChannels().analog_in_bits(dwf, hwdf)
+ self._range: tuple = WFAPIChannels().analog_in_channel_range_info(dwf, hwdf)
+ self._offset: tuple = WFAPIChannels().analog_in_channel_offset_info(dwf, hwdf)
+
+ def simple(self):
+ # TODO: Does nto work
+ return super(AnalogInChannelInfo, self)
\ No newline at end of file
diff --git a/src/CaptDeviceControl/controller/mp_AD2Capture/AD2StateMPSetter.py b/src/CaptDeviceControl/controller/mp_AD2Capture/AD2StateMPSetter.py
index b92f8ddf457581f7361ffbe8d585792f8b1d4bdc..f1f0827cdfc2f5fe380b01e0fe847fe6cb281604 100644
--- a/src/CaptDeviceControl/controller/mp_AD2Capture/AD2StateMPSetter.py
+++ b/src/CaptDeviceControl/controller/mp_AD2Capture/AD2StateMPSetter.py
@@ -6,6 +6,7 @@ class AD2State:
def __init__(self):
# Multiprocessing Information
self._pid = None
+ self._ppid = None
# WaveForms Runtime (DWF) Information
self._dwf_version: str = "Unknown"
@@ -31,7 +32,11 @@ class AD2State:
# Acquired Signal Information
self._acquisition_state: int = AD2Constants.CapturingState.STOPPED()
+
+ self._time_capture_started: float = -1 # The time the capturing has started
+ self._time_capture_ended: float = -1 # The time the capturing has ended
self._recording_time: float = -1
+
self._samples_captured: int = 0
self._samples_lost: int = -1
self._samples_corrupted: int = -1
@@ -50,6 +55,10 @@ class AD2State:
def pid(self):
return self._pid
+ @property
+ def ppid(self):
+ return self._ppid
+
# =========== WaveForms Runtime (DWF) Information
@property
def dwf_version(self):
@@ -108,6 +117,16 @@ class AD2State:
def acquisition_state(self):
return self._acquisition_state
+ @property
+ def time_capture_started(self) -> float:
+ """The time the capturing has started"""
+ return self._time_capture_started
+
+ @property
+ def time_capture_ended(self) -> float:
+ """The time the capturing has ended"""
+ return self._time_capture_ended
+
@property
def recording_time(self):
return self._recording_time
@@ -146,6 +165,11 @@ class AD2StateMPSetter(AD2State):
self._pid = value
self._state_queue.put(self.to_simple_class())
+ @AD2State.ppid.setter
+ def ppid(self, value):
+ self._ppid = value
+ self._state_queue.put(self.to_simple_class())
+
# =========== WaveForms Runtime (DWF) Information
@AD2State.dwf_version.setter
def dwf_version(self, value):
@@ -218,6 +242,17 @@ class AD2StateMPSetter(AD2State):
self._acquisition_state = value
self._state_queue.put(self.to_simple_class())
+ @AD2State.time_capture_ended.setter
+ def time_capture_started(self, value: float):
+ """The time the capturing has started"""
+ self._time_capture_started = value
+ self._state_queue.put(self.to_simple_class())
+
+ @AD2State.time_capture_ended.setter
+ def time_capture_ended(self, value: float):
+ self._time_capture_ended = value
+ self._state_queue.put(self.to_simple_class())
+
@AD2State.recording_time.setter
def recording_time(self, value):
self._recording_time = value
diff --git a/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDevice.py b/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDevice.py
index d88356356d06c97f9e8e5863a37cecd6673ae886..fa38f2e22c62df592a697951ddbd98ce3cbd8961 100644
--- a/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDevice.py
+++ b/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDevice.py
@@ -1,24 +1,44 @@
+import ctypes
import os
import sys
import time
-from ctypes import c_int, c_int32, byref, create_string_buffer, cdll, c_double, c_byte
+from ctypes import c_int, c_int32, byref, create_string_buffer, cdll, c_double, c_byte, CDLL
from multiprocessing import Queue, Value
import cmp
+import numpy as np
from constants.dwfconstants import enumfilterType, enumfilterDemo, enumfilterUSB, acqmodeRecord, DwfStateConfig, \
DwfStatePrefill, DwfStateArmed
class MPCaptDevice(cmp.CProcess):
+ @staticmethod
+ def timeit(func):
+ def wrapper(self, *args, **kwargs):
+ time_start = time.time()
+ res = func(self, *args, **kwargs)
+ time_stop = time.time()
+ print(f"Function {func.__name__} took {time_stop - time_start} seconds.")
+ return res #time_stop - time_start
+
+ return wrapper
+
def __init__(self, state_queue, cmd_queue,
- streaming_data_queue: Queue,
- capture_data_queue: Queue,
+ streaming_data_queue: Queue, capture_data_queue: Queue,
start_capture_flag: Value,
- enable_logging):
- super().__init__(state_queue, cmd_queue, enable_logging=enable_logging)
+ kill_capture_flag: Value,
+ enable_internal_logging):
+ super().__init__(state_queue, cmd_queue, enable_internal_logging=enable_internal_logging)
+
+ self._c_samples = None
+ self._c_corrupted = None
+ self._c_lost = None
+ self._c_available = None
self.start_capture_flag: Value = start_capture_flag
+ self.kill_capture_flag: Value = kill_capture_flag
+
self.stream_data_queue = streaming_data_queue
self.capture_data_queue = capture_data_queue
@@ -27,28 +47,49 @@ class MPCaptDevice(cmp.CProcess):
self.dwf = None
self.hdwf = None
+ # Capture data counters
+
self._dwf_version = None
self._device_serial_number: str = ""
- self._device_name: str = "None"
- self._connected = self.connected()
+ self._device_name: str = ""
+ self._connected = False
self._samples_lost = 0
self._samples_corrupted = 0
-
-
def postrun_init(self):
self.logger, self.ha = self.create_new_logger(f"{self.__class__.__name__}/({os.getpid()})")
+
if sys.platform.startswith("win"):
self.dwf = cdll.dwf
elif sys.platform.startswith("darwin"):
self.dwf = cdll.LoadLibrary("/Library/Frameworks/dwf.framework/dwf")
else:
self.dwf = cdll.LoadLibrary("libdwf.so")
-
+ self._connected = self.connected()
self.hdwf = c_int()
self._ain_device_state: c_byte = c_byte()
+ self._c_available = c_int()
+ self._c_lost = c_int()
+ self._c_corrupted = c_int()
+ self._c_samples = 0
+
+ @cmp.CProcess.register_for_signal('_changed')
+ def device_capturing(self, capturing: bool):
+ self.logger.info(f"Device capturing: {capturing}")
+ return capturing
+
+ @cmp.CProcess.register_for_signal('_changed')
+ def dwf_version(self):
+ self.logger.debug(f"Getting DWF version information...")
+ version = create_string_buffer(16)
+ self.dwf.FDwfGetVersion(version)
+ return version.value.decode("utf-8")
+
+ # ==================================================================================================================
+ # Device Enumeration without connecting to the device
+ # ==================================================================================================================
@cmp.CProcess.register_for_signal('_changed')
def connected_devices(self):
self.logger.info(f"Discovering connected devices...")
@@ -66,30 +107,34 @@ class MPCaptDevice(cmp.CProcess):
self.logger.debug(f"Filtering {type} devices...")
self.dwf.FDwfEnum(filter, byref(cDevice))
num_of_connected_devices = cDevice
-
- devicename = create_string_buffer(64)
- serialnum = create_string_buffer(16)
+ self.logger.debug(f"Found {cDevice.value} {type} devices.")
for iDevice in range(0, cDevice.value):
- self.dwf.FDwfEnumDeviceName(c_int(iDevice), devicename)
- self.dwf.FDwfEnumSN(c_int(iDevice), serialnum)
connected_devices.append({
'type': type,
'device_id': int(iDevice),
- 'device_name': str(devicename.value.decode('UTF-8')),
- 'serial_number': str(serialnum.value.decode('UTF-8'))
+ 'device_name': self.device_name(iDevice),
+ 'serial_number': self.device_serial_number(iDevice)
})
# _mp_log_debug(f"Found {type} device: {devicename.value.decode('UTF-8')} ({serialnum.value.decode('UTF-8')})")
# print(connected_devices)
# print(f"Discoverd {len(self.model.connected_devices)} devices.")
+ self.logger.info(f"Found {len(connected_devices)} devices.")
return connected_devices
@cmp.CProcess.register_for_signal('_changed')
def ain_channels(self, device_id) -> list:
- print(f">>> Reading available analog input channels for device {device_id}.")
cInfo = c_int()
self.dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(1), byref(cInfo))
- return list(range(0, cInfo.value))
+ ain_channels = cInfo.value
+ if ain_channels == 0:
+ # Sometimes, the device reports a wrong number of ain channels
+ # so we can try to connect to the device first and retrieve the information
+ self.open_device(device_id)
+ ain_channels = self.analog_in_channels_count()
+ self.close_device()
+ self.logger.debug(f"Device {device_id} has {ain_channels} analog input channels.")
+ return list(range(0, ain_channels))
@cmp.CProcess.register_for_signal('_changed')
def ain_buffer_size(self, device_id) -> int:
@@ -97,69 +142,178 @@ class MPCaptDevice(cmp.CProcess):
self.dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(7), byref(cInfo))
return cInfo.value
- @cmp.CProcess.register_for_signal('_changed')
- def dwf_version(self):
- self.logger.debug(f"Getting DWF version information...")
- version = create_string_buffer(16)
- self.dwf.FDwfGetVersion(version)
- return version.value.decode("utf-8")
-
@cmp.CProcess.register_for_signal('_changed')
def device_name(self, device_index: int) -> str:
- devicename = create_string_buffer(64)
- self.dwf.FDwfEnumDeviceName(c_int(device_index), devicename)
- return str(devicename.value.decode("utf-8"))
+ try:
+ devicename = create_string_buffer(64)
+ self.dwf.FDwfEnumDeviceName(c_int(device_index), devicename)
+ return str(devicename.value.decode("utf-8"))
+ except Exception as e:
+ self.logger.error(f"Error while reading device name: {e}")
+ raise Exception(f"Error while reading device name: {e}")
@cmp.CProcess.register_for_signal('_changed')
def device_serial_number(self, device_index: int) -> str:
- serialnum = create_string_buffer(16)
- self.dwf.FDwfEnumSN(c_int(device_index), serialnum)
- return str(serialnum.value.decode("utf-8")).replace("SN:", "")
-
+ try:
+ serialnum = create_string_buffer(16)
+ self.dwf.FDwfEnumSN(c_int(device_index), serialnum)
+ return str(serialnum.value.decode("utf-8")).replace("SN:", "")
+ except Exception as e:
+ self.logger.error(f"Error while reading device serial number: {e}")
+ raise Exception(f"Error while reading device serial number: {e}")
+
+ # ==================================================================================================================
+ # Device connection status
+ # ==================================================================================================================
@cmp.CProcess.register_for_signal('_changed')
def connected(self) -> bool:
if self.hdwf is None or self.hdwf.value == 0:
+ szerr = create_string_buffer(512)
+ self.dwf.FDwfGetLastErrorMsg(szerr)
+ self.logger.error(str(szerr.value))
return False
else:
+ self.logger.debug(f"Device connected: {self._device_name} ({self._device_serial_number})")
return True
+ # ==================================================================================================================
+ # Analog Input Channel Information
+ # ==================================================================================================================
+ def analog_in_channels_count(self) -> int:
+ """
+ Reads the number of AnalogIn channels of the device. The oscilloscope channel settings are
+ identical across all channels.
+ Calls WaveForms API Function 'FDwfAnalogInChannelCount(HDWF hdwf, int *pcChannel)'
+ :return: The number of analog in channels.
+ """
+ if self.connected():
+ try:
+ int0 = c_int()
+ self.dwf.FDwfAnalogInChannelCount(self.hdwf, byref(int0))
+ _analog_in_channels = int(int0.value)
+ return _analog_in_channels
+ except Exception as e:
+ self.logger.error(f"Can not read the AnalogIn Channel Count. {e}")
+ raise Exception(f"Can not read the AnalogIn Channel Count. {e}")
+ else:
+ self.logger.error(f"Can not read the AnalogIn Channel Count. Device not connected.")
+ raise Exception(f"Can not read the AnalogIn Channel Count. Device not connected.")
+
+ @cmp.CProcess.register_for_signal('_changed')
+ def analog_in_bits(self) -> int:
+ """
+ Retrieves the number bits used by the AnalogIn ADC. The oscilloscope channel settings are identical
+ across all channels.
+ Calls WaveForms API Function 'FDwfAnalogInBitsInfo(HDWF hdwf, int *pnBits)'
+ :return: The number bits used by the AnalogIn ADC.
+ """
+ int0 = c_int()
+ if self.connected():
+ self.dwf.FDwfAnalogInBitsInfo(self.hdwf, byref(int0))
+ _adc_bits = int(int0.value)
+ return _adc_bits
+ else:
+ self.logger.error(f"Can not read the AnalogIn Bits. Device not connected.")
+ raise Exception(f"Can not read the AnalogIn Bits. Device not connected.")
+
+ @cmp.CProcess.register_for_signal('_changed')
+ def analog_in_buffer_size(self) -> tuple:
+ """
+ Returns the minimum and maximum allowable buffer sizes for the instrument. The oscilloscope
+ channel settings are identical across all channels.
+ Calls WaveForms API Function 'FDwfAnalogInBufferSizeInfo(HDWF hdwf, int *pnSizeMin, int *pnSizeMax)'
+ :return: The minimum and maximum allowable buffer sizes for the instrument as a tuple (min, max).
+ """
+ if self.connected():
+ int0 = c_int()
+ int1 = c_int()
+ self.dwf.FDwfAnalogInBufferSizeInfo(self.hdwf, byref(int0), byref(int1))
+ _buffer_size = (int(int0.value), int(int0.value))
+ return _buffer_size
+ else:
+ self.logger.error(f"Can not read the AnalogIn Buffer Size. Device not connected.")
+ raise Exception(f"Can not read the AnalogIn Buffer Size. Device not connected.")
+
+ @cmp.CProcess.register_for_signal('_changed')
+ def analog_in_channel_range_info(self) -> tuple:
+ """
+ Returns the minimum and maximum range, peak to peak values, and the number of adjustable steps.
+ The oscilloscope channel settings are identical across all channels.
+ Calls WaveForms API Function
+ 'FDwfAnalogInChannelRangeInfo(HDWF hdwf, double *pvoltsMin, double *pvoltsMax, double *pnSteps)'
+ :return: The minimum and maximum range, peak to peak values, and the number of adjustable steps as a tuple
+ (min, max, steps).
+ """
+ if self.connected:
+ dbl0 = c_double()
+ dbl1 = c_double()
+ dbl2 = c_double()
+ self.dwf.FDwfAnalogInChannelRangeInfo(self.hdwf, byref(dbl0), byref(dbl1), byref(dbl2))
+ _range = (int(dbl0.value), int(dbl1.value), int(dbl2.value))
+ return _range
+ else:
+ self.logger.error(f"Can not read the AnalogIn Channel Range. Device not connected.")
+ raise Exception(f"Can not read the AnalogIn Channel Range. Device not connected.")
+
+ @cmp.CProcess.register_for_signal('_changed')
+ def analog_in_offset(self) -> tuple:
+ """ Returns the minimum and maximum offset levels supported, and the number of adjustable steps"""
+ if self.connected():
+ dbl0 = c_double()
+ dbl1 = c_double()
+ dbl2 = c_double()
+ self.dwf.FDwfAnalogInChannelOffsetInfo(self.hdwf, byref(dbl0), byref(dbl1), byref(dbl2))
+ _offset = (int(dbl0.value), int(dbl1.value), int(dbl2.value))
+ return _offset
+ else:
+ self.logger.error(f"Can not read the AnalogIn Offset. Device not connected.")
+ raise Exception(f"Can not read the AnalogIn Offset. Device not connected.")
+
+ # ==================================================================================================================
+ # Functions for opening and closing the device
+ # ==================================================================================================================
@cmp.CProcess.register_for_signal()
def open_device(self, device_index):
"""
Opens the device and returns the handle.
:return: Device handle.
"""
+ self.logger.debug(f"Opening device {device_index}...")
self._dwf_version = self.dwf_version()
- self._device_name = self.device_name(device_index)
- self._device_serial_number = self.device_serial_number(device_index)
# Opens the device specified by idxDevice. The device handle is returned in hdwf. If idxDevice is -1, the
# first available device is opened.
- self.logger.info(f"[Task] Opening device #{device_index}...")
self.dwf.FDwfDeviceOpen(c_int(device_index), byref(self.hdwf))
+ self._device_name = self.device_name(device_index)
+ self._device_serial_number = self.device_serial_number(device_index)
+
if self.hdwf.value == 0:
szerr = create_string_buffer(512)
self.dwf.FDwfGetLastErrorMsg(szerr)
- self.logger.error(f"Failed to open device: {szerr.value}")
+ err = szerr.value.decode("utf-8")
+ self.logger.error(f"Failed to open device: {err}")
# ad2_state.connected = False
- raise Exception(f"Failed to open device: {szerr.value}")
+ raise Exception(f"Failed to open device: {err}")
else:
self.logger.info(f"Device opened: {self._device_name} "
f"({self._device_serial_number})")
self._connected = self.connected()
def close_device(self):
- #self.dwf.FDwfAnalogOutReset(self.hdwf, c_int(channel))
+ # self.dwf.FDwfAnalogOutReset(self.hdwf, c_int(channel))
self.logger.info(f"[Task] Closing device...")
self.dwf.FDwfDeviceClose(self.hdwf)
self.hdwf.value = 0
self._connected = self.connected()
self.logger.info(f"[Task] Device closed.")
- def setup_aquisition(self, sample_rate: float, ain_channel: int):
+ # ==================================================================================================================
+ # Function for setting up the acquisition
+ # ==================================================================================================================
+ def setup_acquisition(self, sample_rate: float, ain_channel: int):
self.dwf.FDwfAnalogInStatus(self.hdwf, c_int(1),
- byref(self._ain_device_state)) # Variable to receive the acquisition state
+ byref(self._ain_device_state)) # Variable to receive the acquisition state
self.logger.info(f"[Task] Setup for acquisition on channel {ain_channel} with rate {sample_rate} Hz.")
self.dwf.FDwfAnalogInChannelEnableSet(self.hdwf, c_int(ain_channel), c_int(1))
self.dwf.FDwfAnalogInChannelRangeSet(self.hdwf, c_int(ain_channel), c_double(5))
@@ -168,177 +322,205 @@ class MPCaptDevice(cmp.CProcess):
self.dwf.FDwfAnalogInRecordLengthSet(self.hdwf, 0) # -1 infinite record length
# Variable to receive the acquisition state
- self.dwf.FDwfAnalogInStatus(self.hdwf, c_int(1), byref(self._ain_device_state))
- self.logger.info(f"[Task] Wait 2 seconds for the offset to stabilize.")
+ #self.dwf.FDwfAnalogInStatus(self.hdwf, c_int(1), byref(self._ain_device_state))
+ #self.logger.info(f"[Task] Wait 2 seconds for the offset to stabilize.")
# wait at least 2 seconds for the offset to stabilize
- time.sleep(2)
- self.logger.info(f"[Task] Setup for acquisition done.")
+ #time.sleep(2)
+ #self.logger.info(f"[Task] Setup for acquisition done.")
+
+ # ==================================================================================================================
+ # Python wrapper for WaveForms API Functions
+ # ==================================================================================================================
+ @timeit
+ def _dwf_analog_in_status(self, hdwf, read_data, ptr_device_state):
+ try:
+ _read_data_cint = c_int(int(read_data))
+ self.dwf.FDwfAnalogInStatus(hdwf, _read_data_cint, ptr_device_state)
+ except Exception as e:
+ self.logger.error(f"Error while getting data from device: {e}")
+ raise Exception(f"Error while getting data from device: {e}")
+ return ptr_device_state
+
+ @timeit
+ def _dwf_analog_in_status_record(self, hdwf, ptr_c_available, ptr_c_lost, ptr_c_corrupted):
+ """
+ Retrieves information about the recording process. The data loss occurs when the device acquisition
+ is faster than the read process to PC. In this case, the device recording buffer is filled and data
+ samples are overwritten. Corrupt samples indicate that the samples have been overwritten by the
+ acquisition process during the previous read.
+ :param hdwf: Interface handle
+ :param c_available: Pointer to variable to receive the available number of samples.
+ :param c_lost: Pointer to variable to receive the lost samples after the last check.
+ :param c_corrupted:Pointer to variable to receive the number of samples that could be corrupt.
+ :return:
+ """
+ try:
+ self.dwf.FDwfAnalogInStatusRecord(hdwf, ptr_c_available, ptr_c_lost, ptr_c_corrupted)
+ except Exception as e:
+ self.logger.error(f"Error while getting data from device: {e}")
+ raise Exception(f"Error while getting data from device: {e}")
+ return ptr_c_available, ptr_c_lost, ptr_c_corrupted
+
+ @timeit
+ def _dwf_analog_in_status_data(self, hdwf, channel, ptr_rgd_samples, c_available):
+ """
+ Retrieves the acquired data samples from the specified idxChannel on the AnalogIn instrument. It
+ copies the data samples to the provided buffer.
+ :param hdwf: Interface handle
+ :param channel: Channel index
+ :param rgd_samples: Pointer to allocated buffer to copy the acquisition data.
+ :return:
+ """
+ try:
+ self.dwf.FDwfAnalogInStatusData(hdwf, c_int(channel), ptr_rgd_samples, c_available) # get channel data
+ except Exception as e:
+ self.logger.error(f"Error while getting data from device: {e}")
+ raise Exception(f"Error while getting data from device: {e}")
+ return ptr_rgd_samples, c_available
def start_capture(self, sample_rate: float, ain_channel: int):
"""
Captures data from the device and puts it into a queue.
- :param capture_data_queue:
- :param state_queue:
- :param start_capture:
- :param end_process:
- :param device_id:
+ :param ain_channel:
:param sample_rate:
- :param stream_data_queue: Queue to put the data into.
- :param channel: Channel to capture data from.
:return: None
"""
- # Streaming the data should only be set to 1000Hz, otherwise the UI will freeze. The capturing however should
- # stay at the given sample rate.
- # Using the modulo operation allow us to determine the variable stream_n that is required
- # to scale down the streaming rate.
- stream_rate = 1000 # Hz
- # stream_n = sample_rate / stream_rate
- # stream_sample_cnt = 0
-
- time_capture_started = 0
- capturing_notified = False
- # Print pid and ppid
- self.logger.info(f"Starting capture thread, pid={os.getpid()}")
- #ad2_state = AD2StateMPSetter(state_queue)
-
- #ad2_state.pid = os.getpid()
-
- #ad2_state.selected_ain_channel = channel
- #ad2_state.sample_rate = sample_rate
- #self.logger.debug(f"Setting up device {device_id} with "
- # f"channel {ad2_state.selected_ain_channel} and "
- # f"acquisition rate {ad2_state.sample_rate} Hz")
-
- #dwf, hdwf = _mp_open_device(device_id, ad2_state)
-
- # acquisition_state = c_byte()
-
- cAvailable = c_int()
- cLost = c_int()
- cCorrupted = c_int()
-
# FDwfAnalogInStatus(HDWF hdwf, BOOL fReadData, DwfState* psts)
- self.setup_aquisition(ain_channel=ain_channel, sample_rate=sample_rate)
- #_t_setup_sine_wave(dwf, hdwf, ad2_state)
+ self.setup_acquisition(ain_channel=ain_channel, sample_rate=sample_rate)
+ # Creates a Sin Wave on the Analog Out Channel 0
+ self.setup_sine_wave(channel=0)
self.logger.info("Configuring acquisition. Starting oscilloscope.")
- # FDwfAnalogInConfigure(HDWF hdwf, int fReconfigure, int fStart)
+
# Configures the instrument and start or stop the acquisition. To reset the Auto trigger timeout, set
- # fReconfigure to TRUE.
- # hdwf – Interface handle.
- # fReconfigure – Configure the device.
- # fStart – Start the acquisition.
self.dwf.FDwfAnalogInConfigure(self.hdwf, c_int(0), c_int(1))
-
self.logger.info("Device configured. Starting acquisition.")
- cSamples = 0
+ time_capture_started = 0
capture_samples = 0
- #print(end_process.value)
- # Checks the state of the acquisition. To read the data from the device, set fReadData to TRUE. For
- # single acquisition mode, the data will be read only when the acquisition is finished
- time_FDwfAnalogInStatus_start = time.time()
- self.dwf.FDwfAnalogInStatus(self.hdwf, c_int(1), byref(self._ain_device_state))
- time_FDwfAnalogInStatus_stop = time.time()
- time_FDwfAnalogInStatus = time_FDwfAnalogInStatus_stop - time_FDwfAnalogInStatus_start
- print(f"FDwfAnalogInStatus took {time_FDwfAnalogInStatus} seconds")
-
- while True:
- time_start = time.time()
- print("New iteration")
-
-
-
- time_cSamples_start = time.time()
- if cSamples == 0 and (
- self._ain_device_state == DwfStateConfig or
- self._ain_device_state == DwfStatePrefill or
- self._ain_device_state == DwfStateArmed):
- self.logger.info("Device in idle state. Waiting for acquisition to start.")
- continue # Acquisition not yet started.
- time_cSamples_stop = time.time()
- time_cSamples = time_cSamples_stop - time_cSamples_start
- print(f"cSamples took {time_cSamples} seconds")
-
-
- time_FDwfAnalogInStatusRecord_start = time.time()
- self.dwf.FDwfAnalogInStatusRecord(self.hdwf, byref(cAvailable), byref(cLost), byref(cCorrupted))
- cSamples += cLost.value
- time_FDwfAnalogInStatusRecord_stop = time.time()
- time_FDwfAnalogInStatusRecord = time_FDwfAnalogInStatusRecord_stop - time_FDwfAnalogInStatusRecord_start
- print(f"FDwfAnalogInStatusRecord took {time_FDwfAnalogInStatusRecord} seconds")
-
- time_samples_l_c_start = time.time()
- if cLost.value:
- self._samples_lost += cLost.value
- if cCorrupted.value:
- self._samples_corrupted += cCorrupted.value
- time_samples_l_c_stop = time.time()
- time_samples_l_c = time_samples_l_c_stop - time_samples_l_c_start
- print(f"time_samples_l_c took {time_samples_l_c} seconds")
-
- # self.dwf.FDwfAnalogInStatusSamplesValid(self.hdwf, byref(self.cValid))
- if cAvailable.value == 0:
- print(cAvailable.value)
- continue
- else:
- # print(f"Available: {cAvailable.value}")
- # if cSamples + cAvailable.value > self.ad2capt_model.n_samples:
- # cAvailable = c_int(self.ad2capt_model.n_samples - cSamples)
- time_rgdsamples_start = time.time()
- rgdSamples = (c_double * cAvailable.value)()
- time_rgdsamples_stop = time.time()
- time_rgdsamples = time_rgdsamples_stop - time_rgdsamples_start
- print(f"rgdSamples took {time_rgdsamples} seconds")
-
- time_FDwfAnalogInStatusData_start = time.time()
- self.dwf.FDwfAnalogInStatusData(self.hdwf, c_int(ain_channel), byref(rgdSamples), cAvailable) # get channel data
- time_FDwfAnalogInStatusData_stop = time.time()
- time_FDwfAnalogInStatusData = time_FDwfAnalogInStatusData_stop - time_FDwfAnalogInStatusData_start
- print(f"FDwfAnalogInStatusData took {time_FDwfAnalogInStatusData} seconds")
-
- # Print how many samples are available
- status = {"available": cAvailable.value, 'captured': len(rgdSamples), 'lost': cLost.value,
- 'corrupted': cCorrupted.value, "time": time.time()}
- self.logger.debug(status)
- print(f"took: {time.time() - time_start} seconds")
- #self.stream_data_queue.put(
- # ([(float(s)) for s in rgdSamples], status)
- #)
-
- # if self.start_capture.value == int(True):
- # if not capturing_notified:
- # time_capture_started = time.time()
- # capture_samples = 0
- # _mp_log_info("Starting command recieved. Acquisition started.")
- # ad2_state.acquisition_state = AD2Constants.CapturingState.RUNNING()
- # capturing_notified = True
- # capture_samples = capture_samples + len(rgdSamples)
- # status = {
- # "available": cAvailable.value,
- # "captured": capture_samples,
- # "lost": cLost.value,
- # "corrupted": cCorrupted.value,
- # "recording_time": time.time() - time_capture_started}
- # capture_data_queue.put(([float(s) for s in rgdSamples], status))
- # # capture_data_queue.put([float(s) for s in rgdSamples])
- # elif start_capture.value == 0:
- # if capturing_notified:
- # ad2_state.acquisition_state = AD2Constants.CapturingState.STOPPED()
- # time_capture_stopped = time.time()
- # time_captured = time_capture_stopped - time_capture_started
- # ad2_state.recording_time = time_captured
- # _mp_log_info(f"Acquisition stopped after {time_captured} seconds. Captured {capture_samples} "
- # f"samples. Resulting in a time of {capture_samples / ad2_state.sample_rate} s.")
- # status = {
- # "available": cAvailable.value,
- # "captured": capture_samples,
- # "lost": cLost.value,
- # "corrupted": cCorrupted.value,
- # "recording_time": time.time() - time_capture_started}
- # capture_data_queue.put(([float(s) for s in rgdSamples], status))
- #
- # capturing_notified = False
- cSamples += cAvailable.value
-
+ capture_started = False
+ capture_ended = False
+
+ n_samples = int((sample_rate*2))
+ rgd_samples = (c_double * n_samples)()
+ #num_sent_samples = 0
+ try:
+ self.dwf.FDwfAnalogOutReset(self.hdwf, c_int(0))
+
+
+ while self.kill_capture_flag.value == int(False):
+ self._c_samples = 0
+
+
+ time_start = time.time()
+
+ # Checks the state of the acquisition. To read the data from the device, set fReadData to TRUE. For
+ # single acquisition mode, the data will be read only when the acquisition is finished
+ self.dwf.FDwfAnalogInStatus(self.hdwf, c_int(1), byref(self._ain_device_state))
+
+ if self._c_samples == 0 and (
+ self._ain_device_state == DwfStateConfig or
+ self._ain_device_state == DwfStatePrefill or
+ self._ain_device_state == DwfStateArmed):
+ # self.logger.info("Device in idle state. Waiting for acquisition to start.")
+ continue # Acquisition not yet started.
+
+ self.dwf.FDwfAnalogInStatusRecord(self.hdwf,
+ byref(self._c_available),
+ byref(self._c_lost), byref(self._c_corrupted)
+ )
+ self._c_samples += self._c_lost.value
+ #if self._c_lost.value:
+ # self._samples_lost += self._c_lost.value
+ #if self._c_corrupted.value:
+ # self._samples_corrupted += self._c_corrupted.value
+
+ # self.dwf.FDwfAnalogInStatusSamplesValid(self.hdwf, byref(self.cValid))
+ if self._c_available.value == 0:
+ pass
+ #print("Nothing available")
+ #continue
+
+ else:
+ if self._c_samples + self._c_available.value > n_samples:
+ self._c_available = c_int(n_samples - self._c_samples)
+
+ # print(f"Available: {self._c_available.value}")
+ # if cSamples + cAvailable.value > self.ad2capt_model.n_samples:
+ # cAvailable = c_int(self.ad2capt_model.n_samples - cSamples)
+ # time_rgdsamples_start = time.time()
+
+ # arr = [None] * cAvailable.value
+ # time_rgdsamples_stop = time.time()
+ # time_rgdsamples = time_rgdsamples_stop - time_rgdsamples_start
+ # print(f"rgd_samples took {time_rgdsamples} seconds")
+ rgd_samples = (c_double * self._c_available.value)()
+ # Get the data from the device and store it in rgd_samples
+ self.dwf.FDwfAnalogInStatusData(self.hdwf,
+ c_int(ain_channel),
+ rgd_samples,
+ self._c_available)
+ #print(f"Got data from device: {self._c_available.value}")
+ self._c_samples += self._c_available.value
+ iteration_time = time.time() - time_start
+
+ # Convert the data to a numpy array and put it into the queue
+ # self.logger.info("Convert data to numpy array and put it into the queue.")
+
+ # num_sent_samples = 0
+ arr = np.array(rgd_samples)
+ #print(f"I send {len(arr)} samples to the queue.")
+ self.stream_data_queue.put(arr)
+
+ if self.start_capture_flag.value == int(True):
+ if not capture_started:
+ self.device_capturing(True)
+ time_capture_started = time.time()
+ self.logger.info(
+ "**************************** Starting command received. Acquisition started.")
+ capture_started = True
+ capture_ended = False
+ capture_samples = 0
+ #capture_samples = capture_samples + len(arr)
+ #self.capture_data_queue.put(arr)
+ elif self.start_capture_flag.value == int(False):
+
+ if not capture_ended and capture_started:
+ self.device_capturing(False)
+ time_capture_stopped = time.time()
+ time_captured = time_capture_stopped - time_capture_started
+ self.logger.info(
+ "**************************** Stopping command received. Acquisition stopped.")
+ self.logger.info(
+ f"Acquisition stopped after {time_captured} seconds. Captured {capture_samples} "
+ f"samples. Resulting in a time of {capture_samples / sample_rate} s.")
+ capture_ended = True
+ capture_started = False
+ #self.capture_data_queue.put(arr)
+ # self._c_samples += self._c_available.value
+
+
+
+ except Exception as e:
+ self.logger.error(f"Error while capturing data from device: {e}")
+ raise Exception(f"Error while capturing data from device: {e}")
+ self.logger.info("Capture thread ended.")
self.close_device()
+
+ # ==================================================================================================================
+ # Others
+ # ==================================================================================================================
+ def setup_sine_wave(self, channel: int = 0):
+ self.logger.debug("Generating AM sine wave...")
+ self.dwf.FDwfAnalogOutNodeEnableSet(self.hdwf, c_int(0), c_int(0), c_int(1)) # carrier
+ self.dwf.FDwfAnalogOutNodeFunctionSet(self.hdwf, c_int(0), c_int(0), c_int(1)) # sine
+ self.dwf.FDwfAnalogOutNodeFrequencySet(self.hdwf, c_int(0), c_int(0), c_double(0.1))
+ self.dwf.FDwfAnalogOutNodeAmplitudeSet(self.hdwf, c_int(0), c_int(0), c_double(1))
+ # dwf.FDwfAnalogOutNodeOffsetSet(hdwf, c_int(0), c_int(0), c_double(0.5))
+ # dwf.FDwfAnalogOutNodeEnableSet(hdwf, c_int(0), c_int(2), c_int(1)) # AM
+ # dwf.FDwfAnalogOutNodeFunctionSet(hdwf, c_int(0), c_int(2), c_int(3)) # triangle
+ # dwf.FDwfAnalogOutNodeFrequencySet(hdwf, c_int(0), c_int(2), c_double(0.1))
+ # dwf.FDwfAnalogOutNodeAmplitudeSet(hdwf, c_int(0), c_int(2), c_double(50))
+ self.dwf.FDwfAnalogOutConfigure(self.hdwf, c_int(channel), c_int(1))
+ time.sleep(1)
+ self.logger.debug(f"Sine wave on output channel {channel} configured.")
diff --git a/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDeviceControl.py b/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDeviceControl.py
index e5fc099b8365e8812f8d52ace0f60538322aea07..5b73a813bf1cdb3f0fb0847cac6d007ec6e8ab63 100644
--- a/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDeviceControl.py
+++ b/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDeviceControl.py
@@ -1,8 +1,11 @@
+import os
+
import cmp
from PySide6.QtCore import Signal
from CaptDeviceControl.controller.mp_AD2Capture.MPCaptDevice import MPCaptDevice
from model.AD2CaptDeviceModel import AD2CaptDeviceSignals, AD2CaptDeviceModel
+from model.AD2Constants import AD2Constants
class MPCaptDeviceControl(cmp.CProcessControl):
@@ -17,45 +20,75 @@ class MPCaptDeviceControl(cmp.CProcessControl):
open_device_finished = Signal()
close_device_finished = Signal()
+ analog_in_bits_changed = Signal(int)
+ analog_in_buffer_size_changed = Signal(int)
+ analog_in_channel_range_changed = Signal(tuple)
+ analog_in_offset_changed = Signal(tuple)
+
+ device_capturing_changed = Signal(bool)
+
def __init__(self,
model: AD2CaptDeviceModel,
streaming_data_queue,
capturing_data_queue,
start_capture_flag,
+ kill_capture_flag,
parent=None,
- enable_logging=False):
- super().__init__(parent, enable_logging=enable_logging)
+ enable_internal_logging=False):
+ super().__init__(parent, enable_internal_logging=enable_internal_logging)
+ self.model = model
self.register_child_process(
MPCaptDevice(self.state_queue, self.cmd_queue,
streaming_data_queue,
capturing_data_queue,
start_capture_flag,
- enable_logging=enable_logging))
+ kill_capture_flag,
+ enable_internal_logging=enable_internal_logging))
+
+ self.logger, self.logger_handler = self.create_new_logger(f"{self.__class__.__name__}({os.getpid()})")
+
+ self.connected_devices_changed.connect(
+ lambda x: type(model.device_information).connected_devices.fset(model.device_information, x))
+ self.dwf_version_changed.connect(
+ lambda x: type(model).dwf_version.fset(model, x))
+ self.device_name_changed.connect(
+ lambda x: type(model.device_information).device_name.fset(model.device_information, x))
+ self.device_serial_number_changed.connect(
+ lambda x: type(model.device_information).device_serial_number.fset(model.device_information, x))
+ self.connected_changed.connect(
+ lambda x: type(model.device_information).device_connected.fset(model.device_information, x))
+
+ # Analog In Information
+ self.ain_channels_changed.connect(lambda x: type(model.analog_in).ain_channels.fset(model.analog_in, x))
+ self.ain_buffer_size_changed.connect(lambda x: type(model.analog_in).ain_buffer_size.fset(model.analog_in, x))
+ self.analog_in_bits_changed.connect(lambda x: type(model.analog_in).ain_bits.fset(model.analog_in, x))
+ self.analog_in_buffer_size_changed.connect(lambda x: type(model.analog_in).ain_buffer_size.fset(model.analog_in, x))
+ self.analog_in_channel_range_changed.connect(lambda x: type(model.analog_in).ai.fset(model.analog_in, x))
+ self.analog_in_offset_changed.connect(lambda x: type(model.analog_in).ain_offset.fset(model.analog_in, x))
- self.connected_devices_changed.connect(lambda x: type(model).connected_devices.fset(model, x))
- self.ain_channels_changed.connect(lambda x: type(model).ain_channels.fset(model, x))
- self.ain_buffer_size_changed.connect(lambda x: type(model).ain_buffer_size.fset(model, x))
- self.dwf_version_changed.connect(lambda x: type(model).dwf_version.fset(model, x))
- self.device_name_changed.connect(lambda x: type(model).device_name.fset(model, x))
- self.device_serial_number_changed.connect(lambda x: type(model).device_serial_number.fset(model, x))
- self.connected_changed.connect(lambda x: type(model).connected.fset(model, x))
+ self.device_capturing_changed.connect(self.on_capturing_state_changed)
+ def on_capturing_state_changed(self, capturing: bool):
+ if capturing:
+ self.model.device_capturing_state = AD2Constants.CapturingState.RUNNING()
+ else:
+ self.model.device_capturing_state = AD2Constants.CapturingState.STOPPED()
@cmp.CProcessControl.register_function()
def connected_devices(self):
- self._internal_logger.info("Discovering connected devices.")
+ self.logger.info("Discovering connected devices.")
@cmp.CProcessControl.register_function()
def ain_channels(self, device_id):
- self._internal_logger.info(f"Reading available analog input channels for device {device_id}.")
+ self.logger.info(f"Reading available analog input channels for device {device_id}.")
@cmp.CProcessControl.register_function()
def open_device(self, device_index):
- self._internal_logger.info(f"Opening device {device_index}.")
+ self.logger.info(f"Opening device {device_index}.")
@cmp.CProcessControl.register_function()
def close_device(self):
- self._internal_logger.info(f"Closing device device.")
+ self.logger.info(f"Closing device device.")
@cmp.CProcessControl.register_function()
def start_capture(self, sample_rate, ain_channel):
diff --git a/src/CaptDeviceControl/controller/mp_AD2Capture/MPDeviceControl.py b/src/CaptDeviceControl/controller/mp_AD2Capture/MPDeviceControl.py
index 9aadab180388d634ac475dbb4d7fe239ad2ee1fd..e6692352a63dd679fd140a890b982315c9211b2d 100644
--- a/src/CaptDeviceControl/controller/mp_AD2Capture/MPDeviceControl.py
+++ b/src/CaptDeviceControl/controller/mp_AD2Capture/MPDeviceControl.py
@@ -9,7 +9,7 @@ import os
import random
import sys
import time
-from ctypes import c_int, byref, c_double, cdll, create_string_buffer, c_int32
+from ctypes import c_int, byref, c_double, cdll, create_string_buffer, c_int32, CDLL
from CaptDeviceControl.controller.mp_AD2Capture.AD2StateMPSetter import AD2StateMPSetter
from CaptDeviceControl.model.AD2Constants import AD2Constants
@@ -54,25 +54,20 @@ def mp_capture(stream_data_queue, capture_data_queue, state_queue,
:param channel: Channel to capture data from.
:return: None
"""
- # Streaming the data should only be set to 1000Hz, otherwise the UI will freeze. The capturing however should
- # stay at the given sample rate.
- # Using the modulo operation allow us to determine the variable stream_n that is required
- # to scale down the streaming rate.
- stream_rate = 1000 # Hz
- #stream_n = sample_rate / stream_rate
- #stream_sample_cnt = 0
time_capture_started = 0
capturing_notified = False
- # Print pid and ppid
- _mp_log_info(f"Starting capture thread, pid={os.getpid()}, ppid={os.getppid()}")
- ad2_state = AD2StateMPSetter(state_queue)
+ ad2_state = AD2StateMPSetter(state_queue)
ad2_state.pid = os.getpid()
+ ad2_state.ppid = os.getppid()
+ # Print pid and ppid
+ _mp_log_info(f"Starting capture thread, pid={ad2_state.pid}, ppid={ad2_state.ppid}")
ad2_state.selected_ain_channel = channel
ad2_state.sample_rate = sample_rate
- _mp_log_debug(f"Setting up device {device_id} with "
+ ad2_state.device_index = device_id
+ _mp_log_debug(f"Setting up device {ad2_state.device_index} with "
f"channel {ad2_state.selected_ain_channel} and "
f"acquisition rate {ad2_state.sample_rate} Hz")
@@ -250,25 +245,12 @@ def _t_setup_aquisition(dwf, hdwf, ad2_state: AD2StateMPSetter):
def _mp_discover_connected_devices(dwf):
_mp_log_info(f"Discovering connected devices...")
- # enumerate connected devices
- connected_devices = []
- # for filter_type in [(c_int32(enumfilterType.value | enumfilterUSB.value), 'USB'),
- # (c_int32(enumfilterType.value | enumfilterNetwork.value), 'Network'),
- # (c_int32(enumfilterType.value | enumfilterAXI.value), 'AXI'),
- # (c_int32(enumfilterType.value | enumfilterRemote.value), 'Remote'),
- # (c_int32(enumfilterType.value | enumfilterAudio.value), 'Audio'),
- # (c_int32(enumfilterType.value | enumfilterDemo.value), 'Demo')]:
- cDevice = c_int()
- filter, type = (c_int32(enumfilterType.value | enumfilterDemo.value| enumfilterUSB.value), 'USB')
- # filter, type = (c_int32(enumfilterType.value | enumfilterDemo.value), 'DEMO')
- _mp_log_debug(f"Filtering {type} devices...")
- dwf.FDwfEnum(filter, byref(cDevice))
- num_of_connected_devices = cDevice
+ num_of_connected_devices = 0
devicename = create_string_buffer(64)
serialnum = create_string_buffer(16)
- for iDevice in range(0, cDevice.value):
+ for iDevice in enumerate_devices(dwf, show_demo_devices=True):
dwf.FDwfEnumDeviceName(c_int(iDevice), devicename)
dwf.FDwfEnumSN(c_int(iDevice), serialnum)
connected_devices.append({
@@ -282,8 +264,25 @@ def _mp_discover_connected_devices(dwf):
# print(f"Discoverd {len(self.model.connected_devices)} devices.")
return connected_devices
+def enumerate_devices(dwf: CDLL, show_demo_devices=False) -> list:
+ """
+ Enumerates all connected devices. Function is used to discover all connected, compatible devices.
+ Builds an internal list of detected devices filtered by the enumfilter parameter. It must be called
+ before using other FDwfEnum functions because they obtain information about enumerated devices
+ from this list identified by the device index.
+ :param show_demo_devices: Specify if demo devices should be shown.
+ :return: A list from 0 to n devices.
+ """
+ if show_demo_devices:
+ enum_filter = c_int32(enumfilterType.value | enumfilterUSB.value | enumfilterDemo.value)
+ else:
+ enum_filter = c_int32(enumfilterType.value | enumfilterUSB.value)
+
+ cDevice = c_int()
+ dwf.FDwfEnum(enum_filter, byref(cDevice))
+ return list(range(0, cDevice.value))
-def _mp_open_device(device_index, ad2_state: AD2StateMPSetter):
+def _mp_open_device(ad2_state: AD2StateMPSetter):
"""
Opens the device and returns the handle.
:return: Device handle.
@@ -303,14 +302,14 @@ def _mp_open_device(device_index, ad2_state: AD2StateMPSetter):
# Opens the device specified by idxDevice. The device handle is returned in hdwf. If idxDevice is -1, the
# first available device is opened.
- _mp_log_info(f"[Task] Opening device #{device_index}...")
- dwf.FDwfDeviceOpen(c_int(device_index), byref(hdwf))
+ _mp_log_info(f"[Task] Opening device #{ad2_state.device_index}...")
+ dwf.FDwfDeviceOpen(c_int(ad2_state.device_index), byref(hdwf))
devicename = create_string_buffer(64)
serialnum = create_string_buffer(16)
- dwf.FDwfEnumDeviceName(c_int(device_index), devicename)
- dwf.FDwfEnumSN(c_int(device_index), serialnum)
+ dwf.FDwfEnumDeviceName(c_int(ad2_state.device_index), devicename)
+ dwf.FDwfEnumSN(c_int(ad2_state.device_index), serialnum)
ad2_state.device_name = str(devicename.value.decode("utf-8"))
ad2_state.device_serial_number = str(serialnum.value.decode("utf-8")).replace("SN:", "")
diff --git a/src/CaptDeviceControl/model/AD2CaptDeviceAnalogInModel.py b/src/CaptDeviceControl/model/AD2CaptDeviceAnalogInModel.py
new file mode 100644
index 0000000000000000000000000000000000000000..c22edf93a6eae905a01fa5d2b1222f8c499685b5
--- /dev/null
+++ b/src/CaptDeviceControl/model/AD2CaptDeviceAnalogInModel.py
@@ -0,0 +1,86 @@
+from ctypes import c_int, c_byte
+
+from PySide6.QtCore import QObject, Signal
+from CaptDeviceControl.CaptDeviceConfig import CaptDeviceConfig
+class AD2CaptDeviceAnalogInSignals(QObject):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+ # Analog In Information
+ selected_ain_channel_changed = Signal(int)
+ ain_channels_changed = Signal(list)
+ ain_bits_changed = Signal(int)
+ ain_buffer_size_changed = Signal(int)
+ ain_channel_range_changed = Signal(tuple)
+ ain_offset_changed = Signal(tuple)
+ ain_device_state_changed = Signal(int)
+
+
+class AD2CaptDeviceAnalogInModel:
+
+ def __init__(self, config: CaptDeviceConfig):
+ self.signals = AD2CaptDeviceAnalogInSignals()
+ self.config = config
+ # Analog In Information
+ self._ain_channels: list = []
+ self._ain_bits: int = -1
+ self._ain_buffer_size: tuple = (-1, -1)
+ self._ain_channel_range: tuple = (-1, -1, -1)
+ self._ain_offset: tuple = (-1, -1, -1)
+ self._ain_device_state: int = -1
+
+ # ==================================================================================================================
+ # Analog In Information
+ # ==================================================================================================================
+ @property
+ def selected_ain_channel(self) -> int:
+ return self.config.ain_channel.get()
+
+ @selected_ain_channel.setter
+ def selected_ain_channel(self, value: int | c_int):
+ if isinstance(value, c_int):
+ value = int(value.value)
+ else:
+ value = int(value)
+ self.config.ain_channel.set(value)
+ self.signals.selected_ain_channel_changed.emit(self.selected_ain_channel)
+
+
+ @property
+ def ain_channels(self) -> list:
+ return self._ain_channels
+
+ @ain_channels.setter
+ def ain_channels(self, value: list):
+ self._ain_channels = value
+ self.signals.ain_channels_changed.emit(self.ain_channels)
+
+ @property
+ def ain_buffer_size(self) -> tuple:
+ return self._ain_buffer_size
+
+ @ain_buffer_size.setter
+ def ain_buffer_size(self, value: float):
+ self._ain_buffer_size = value
+ self.signals.ain_buffer_size_changed.emit(self.ain_buffer_size)
+
+ @property
+ def ain_bits(self) -> int:
+ return self._ain_bits
+
+ @ain_bits.setter
+ def ain_bits(self, value: int):
+ self._ain_bits = value
+ self.signals.ain_bits_changed.emit(self.ain_bits)
+
+ @property
+ def ain_device_state(self) -> int:
+ return self._ain_device_state
+
+ @ain_device_state.setter
+ def ain_device_state(self, value: c_int | int | c_byte):
+ if isinstance(value, c_int) or isinstance(value, c_byte):
+ self._ain_device_state = int(value.value)
+ else:
+ self._ain_device_state = int(value)
+ self.signals.ain_device_state_changed.emit(self.ain_device_state)
\ No newline at end of file
diff --git a/src/CaptDeviceControl/model/AD2CaptDeviceInformationModel.py b/src/CaptDeviceControl/model/AD2CaptDeviceInformationModel.py
new file mode 100644
index 0000000000000000000000000000000000000000..0e0734369f20745e1b067661b5cc2b75cc969280
--- /dev/null
+++ b/src/CaptDeviceControl/model/AD2CaptDeviceInformationModel.py
@@ -0,0 +1,117 @@
+from ctypes import c_int, Array
+
+from PySide6.QtCore import QObject, Signal
+
+
+class AD2CaptDeviceInformationSignals(QObject):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+ # Connected Device Information
+ num_of_connected_devices_changed = Signal(int)
+ connected_devices_changed = Signal(list)
+ selected_device_index_changed = Signal(int)
+
+ # Device information
+ device_connected_changed = Signal(bool)
+ device_name_changed = Signal(str)
+ device_serial_number_changed = Signal(str)
+ device_index_changed = Signal(int)
+
+
+class AD2CaptDeviceInformationModel:
+ def __init__(self):
+ self.signals = AD2CaptDeviceInformationSignals()
+
+ # Connected Device Information
+ self._num_of_connected_devices: int = 0
+ self._connected_devices: list = []
+
+ # Device Information
+ self._selected_device_index: int = 0
+ self._device_connected: bool = False
+ self._device_name: str = "Unknown"
+ self._device_serial_number: str = "Unknown"
+
+ # ==================================================================================================================
+ # Connected Device Information
+ # ==================================================================================================================
+ @property
+ def num_of_connected_devices(self) -> int:
+ return self._num_of_connected_devices
+
+ @num_of_connected_devices.setter
+ def num_of_connected_devices(self, value: c_int | int):
+ if isinstance(value, c_int):
+ self._num_of_connected_devices = int(value.value)
+ else:
+ self._num_of_connected_devices = int(value)
+ self.signals.num_of_connected_devices_changed.emit(self._num_of_connected_devices)
+
+ @property
+ def connected_devices(self) -> list:
+ return self._connected_devices
+
+ @connected_devices.setter
+ def connected_devices(self, value: list):
+ self._connected_devices = value
+ self.signals.connected_devices_changed.emit(self.connected_devices)
+
+ @property
+ def selected_device_index(self) -> int:
+ return self._selected_device_index
+
+ @selected_device_index.setter
+ def selected_device_index(self, value: int):
+ self._selected_device_index = value
+ self.signals.selected_device_index_changed.emit(self.selected_device_index)
+
+ # ==================================================================================================================
+ # Device Information
+ # ==================================================================================================================
+ @property
+ def device_connected(self):
+ return self._device_connected
+
+ @device_connected.setter
+ def device_connected(self, value):
+ self._device_connected = value
+ self.signals.device_connected_changed.emit(self._device_connected)
+
+ @property
+ def device_name(self) -> str:
+ return self._device_name
+
+ @device_name.setter
+ def device_name(self, value: Array | str):
+ if not isinstance(value, str):
+ self._device_name = str(value.value.decode('UTF-8'))
+ else:
+ self._device_name = str(value)
+ self.signals.device_name_changed.emit(self.device_name)
+
+ @property
+ def device_serial_number(self) -> str:
+ return self._device_serial_number
+
+ @device_serial_number.setter
+ def device_serial_number(self, value: Array | str):
+ if not isinstance(value, str):
+ self._device_serial_number = str(value.value.decode('UTF-8'))
+ else:
+ self._device_serial_number = str(value)
+ (self.signals.
+
+ device_serial_number_changed.emit(self.device_serial_number))
+
+ @property
+ def device_index(self) -> int:
+ return self._device_index
+
+ @device_index.setter
+ def device_index(self, value: c_int | int):
+ if isinstance(value, c_int):
+ self._device_index = int(value.value)
+ else:
+ self._device_index = int(value)
+ self.signals.device_serial_number_changed.emit(self.device_index)
diff --git a/src/CaptDeviceControl/model/AD2CaptDeviceModel.py b/src/CaptDeviceControl/model/AD2CaptDeviceModel.py
index 3b9f0dc82c3100e03ba4d0e9f295e1729032441a..5a9c6bc1d3c47d8072ac0f2d993f9cc3b8f57433 100644
--- a/src/CaptDeviceControl/model/AD2CaptDeviceModel.py
+++ b/src/CaptDeviceControl/model/AD2CaptDeviceModel.py
@@ -4,11 +4,16 @@ from PySide6.QtCore import QObject, Signal
from CaptDeviceControl.model.AD2Constants import AD2Constants
from CaptDeviceControl.CaptDeviceConfig import CaptDeviceConfig as Config
-#from MeasurementData.Properties.AD2CaptDeviceProperties import AD2CaptDeviceProperties
+from CaptDeviceControl.model.AD2CaptDeviceAnalogInModel import AD2CaptDeviceAnalogInModel
+from CaptDeviceControl.model.AD2CaptDeviceInformationModel import AD2CaptDeviceInformationSignals, \
+ AD2CaptDeviceInformationModel
+
+
+# from MeasurementData.Properties.AD2CaptDeviceProperties import AD2CaptDeviceProperties
class AD2CaptDeviceSignals(QObject):
- def __init__(self, parent = None):
+ def __init__(self, parent=None):
super().__init__(parent)
ad2captdev_config_changed = Signal(Config)
@@ -16,29 +21,11 @@ class AD2CaptDeviceSignals(QObject):
# WaveForms Runtime (DWF) Information
dwf_version_changed = Signal(str)
- # Connected Device Information
- num_of_connected_devices_changed = Signal(int)
- connected_devices_changed = Signal(list)
- selected_device_changed = Signal(int)
-
- # Device information
- connected_changed = Signal(bool)
- device_name_changed = Signal(str)
- device_serial_number_changed = Signal(str)
- device_index_changed = Signal(int)
-
# Acquisition Settings
sample_rate_changed = Signal(int)
streaming_rate_changed = Signal(int)
- selected_ain_channel_changed = Signal(int)
- duration_streaming_history_changed = Signal(int)
-
- # Analog In Information
- ain_channels_changed = Signal(list)
- ain_buffer_size_changed = Signal(int)
- ain_bits_changed = Signal(int)
- ain_device_state_changed = Signal(int)
+ duration_streaming_history_changed = Signal(int)
# Analog Out Information
aout_channels_changed = Signal(list)
@@ -63,17 +50,13 @@ class AD2CaptDeviceSignals(QObject):
# Multiprocessing Information
pid_changed = Signal(int)
-
# ==================================================================================================================
# Delete later
# Signals for device information
hwdf_changed = Signal(int)
-
device_ready_changed = Signal(bool)
-
-
# Signals for reporting if samples were lost or corrupted
fLost_changed = Signal(int)
fCorrupted_changed = Signal(int)
@@ -83,13 +66,11 @@ class AD2CaptDeviceSignals(QObject):
# Recording settings for starting, stopping and pausing
-
# Signal if new samples have been aquired
all_recorded_samples_changed = Signal(list)
num_of_current_recorded_samples_changed = Signal(int)
measurement_time_changed = Signal(float)
-
ad2_settings = Signal(dict)
error = Signal(str)
@@ -99,8 +80,6 @@ class AD2CaptDeviceSignals(QObject):
ad2_captured_value = Signal(list)
-
-
class AD2CaptDeviceModel:
def __init__(self, ad2captdev_config: Config):
@@ -110,31 +89,15 @@ class AD2CaptDeviceModel:
# WaveForms Runtime (DWF) Information
self._dwf_version: str = "Unknown"
- # Connected Device Information
- self._num_of_connected_devices: int = 0
- self._connected_devices: list = []
- self._selected_device: int = 0
-
- # Device Information
- self._connected: bool = False
- self._device_name: str = "Unknown"
- self._device_serial_number: str = "Unknown"
- self._device_index: int = -1
+ self.device_information = AD2CaptDeviceInformationModel()
+ self.analog_in = AD2CaptDeviceAnalogInModel(self.ad2captdev_config)
# Acquisition Settings
self._sample_rate: int = self.ad2captdev_config.sample_rate.value
self._streaming_rate: int = self.ad2captdev_config.streaming_rate.value
- self._selected_ain_channel: int = 0
self._duration_streaming_history: float = 0
-
- # Analog In Information
- self._ain_channels: list = []
- self._ain_buffer_size: float = -1
- self._ain_device_state: int = -1
- self._ain_bits: int = -1
-
# Analog Out Information
self.aout_channels: list = []
@@ -150,7 +113,6 @@ class AD2CaptDeviceModel:
self._unconsumed_stream_samples: int = 0
self._unconsumed_capture_samples: int = 0
-
# Recording Flags (starting, stopping and pausing)
self._device_capturing_state: AD2Constants.CapturingState = AD2Constants.CapturingState.STOPPED()
self._start_recording = False
@@ -181,8 +143,6 @@ class AD2CaptDeviceModel:
self._num_of_current_recorded_samples: int = 0
self._n_samples: int = 0
-
-
@property
def ad2captdev_config(self) -> Config:
return self._ad2captdev_config
@@ -207,89 +167,6 @@ class AD2CaptDeviceModel:
self._dwf_version = value
self.signals.dwf_version_changed.emit(self.dwf_version)
- # ==================================================================================================================
- # Connected Device Information
- # ==================================================================================================================
- @property
- def num_of_connected_devices(self) -> int:
- return self._num_of_connected_devices
-
- @num_of_connected_devices.setter
- def num_of_connected_devices(self, value: c_int | int):
- if isinstance(value, c_int):
- self._num_of_connected_devices = int(value.value)
- else:
- self._num_of_connected_devices = int(value)
- self.signals.num_of_connected_devices_changed.emit(self._num_of_connected_devices)
-
- @property
- def connected_devices(self) -> list:
- return self._connected_devices
-
- @connected_devices.setter
- def connected_devices(self, value: list):
- self._connected_devices = value
- self.signals.connected_devices_changed.emit(self.connected_devices)
-
- @property
- def selected_device(self) -> int:
- return self._selected_device
-
- @selected_device.setter
- def selected_device(self, value: int):
- self._selected_device = value
- self.signals.selected_device_changed.emit(self.selected_device)
-
-
- # ==================================================================================================================
- # Device Information
- # ==================================================================================================================
- @property
- def connected(self):
- return self._connected
-
- @connected.setter
- def connected(self, value):
- self._connected = value
- self.signals.connected_changed.emit(self._connected)
-
- @property
- def device_name(self) -> str:
- return self._device_name
-
- @device_name.setter
- def device_name(self, value: Array | str):
- if not isinstance(value, str):
- self._device_name = str(value.value.decode('UTF-8'))
- else:
- self._device_name = str(value)
- self.signals.device_name_changed.emit(self.device_name)
-
- @property
- def device_serial_number(self) -> str:
- return self._device_serial_number
-
- @device_serial_number.setter
- def device_serial_number(self, value: Array | str):
- if not isinstance(value, str):
- self._device_serial_number = str(value.value.decode('UTF-8'))
- else:
- self._device_serial_number = str(value)
- self.signals.device_serial_number_changed.emit(self.device_serial_number)
-
- @property
- def device_index(self) -> int:
- return self._device_index
-
- @device_index.setter
- def device_index(self, value: c_int | int):
- if isinstance(value, c_int):
- self._device_index = int(value.value)
- else:
- self._device_index = int(value)
- self.signals.device_serial_number_changed.emit(self.device_index)
-
-
# ==================================================================================================================
# Acquisition Settings
# ==================================================================================================================
@@ -313,19 +190,6 @@ class AD2CaptDeviceModel:
self.ad2captdev_config.streaming_rate.set(self._streaming_rate)
self.signals.streaming_rate_changed.emit(self._streaming_rate)
- @property
- def selected_ain_channel(self) -> int:
- return self._selected_ain_channel
-
- @selected_ain_channel.setter
- def selected_ain_channel(self, value: int | c_int):
- if isinstance(value, c_int):
- self._selected_ain_channel = int(value.value)
- else:
- self._selected_ain_channel = int(value)
- self.ad2captdev_config.ain_channel.set(self._selected_ain_channel)
- self.signals.selected_ain_channel_changed.emit(self.selected_ain_channel)
-
@property
def duration_streaming_history(self) -> float:
return self._duration_streaming_history
@@ -335,48 +199,6 @@ class AD2CaptDeviceModel:
self._duration_streaming_history = value
self.signals.duration_streaming_history_changed.emit(self.duration_streaming_history)
- # ==================================================================================================================
- # Analog In Information
- # ==================================================================================================================
- @property
- def ain_channels(self) -> list:
- return self._ain_channels
-
- @ain_channels.setter
- def ain_channels(self, value: list):
- self._ain_channels = value
- self.signals.ain_channels_changed.emit(self.ain_channels)
-
- @property
- def ain_buffer_size(self) -> float:
- return self._ain_buffer_size
-
- @ain_buffer_size.setter
- def ain_buffer_size(self, value: float):
- self._ain_buffer_size = value
- self.signals.ain_buffer_size_changed.emit(self.ain_buffer_size)
-
- @property
- def ain_bits(self) -> int:
- return self._ain_bits
-
- @ain_bits.setter
- def ain_bits(self, value: int):
- self._ain_bits = value
- self.signals.ain_bits_changed.emit(self.ain_bits)
-
- @property
- def ain_device_state(self) -> int:
- return self._ain_device_state
-
- @ain_device_state.setter
- def ain_device_state(self, value: c_int | int | c_byte):
- if isinstance(value, c_int) or isinstance(value, c_byte):
- self._ain_device_state = int(value.value)
- else:
- self._ain_device_state = int(value)
- self.signals.ain_device_state_changed.emit(self.ain_device_state)
-
# ==================================================================================================================
# Analog Out Information
# ==================================================================================================================
@@ -389,7 +211,6 @@ class AD2CaptDeviceModel:
self._aout_channels = value
self.signals.aout_channels_changed.emit(self.aout_channels)
-
# ==================================================================================================================
# Acquired Signal Information
# ==================================================================================================================
@@ -401,7 +222,7 @@ class AD2CaptDeviceModel:
def recorded_samples(self, value: list):
self._recorded_samples = value
self.samples_captured = len(self._recorded_samples)
- #self.signals.num_of_current_recorded_samples_changed.emit(self.num_of_current_recorded_samples)
+ # self.signals.num_of_current_recorded_samples_changed.emit(self.num_of_current_recorded_samples)
self.signals.recorded_samples_changed.emit(self.recorded_samples)
@property
@@ -440,17 +261,15 @@ class AD2CaptDeviceModel:
self._samples_corrupted = value
self.signals.samples_corrupted_changed.emit(self.samples_corrupted)
-
@property
def capturing_finished(self) -> bool:
return self._capturing_finished
-
+
@capturing_finished.setter
def capturing_finished(self, value: bool):
self._capturing_finished = value
- print(f"Set _capturing_finished to { self._capturing_finished}")
+ print(f"Set _capturing_finished to {self._capturing_finished}")
self.signals.capturing_finished_changed.emit(self._capturing_finished)
-
# ==================================================================================================================
# Recording Flags (starting, stopping and pausing)
@@ -491,7 +310,6 @@ class AD2CaptDeviceModel:
self._reset_recording = value
self.signals.reset_recording_changed.emit(self._reset_recording)
-
# ==================================================================================================================
# Multiprocessing Flags
# ==================================================================================================================
@@ -522,8 +340,6 @@ class AD2CaptDeviceModel:
self._unconsumed_capture_samples = value
self.signals.unconsumed_capture_samples_changed.emit(self.unconsumed_capture_samples)
-
-
# ==================================================================================================================
# ==================================================================================================================
# DELETE LATER
@@ -538,7 +354,7 @@ class AD2CaptDeviceModel:
self._auto_connect = value
@property
- #def ad2_properties(self) -> AD2CaptDeviceProperties:
+ # def ad2_properties(self) -> AD2CaptDeviceProperties:
# return AD2CaptDeviceProperties(self._fLost, self._fCorrupted,
# self._sample_rate, self._n_samples,
# self._measurement_time)
@@ -593,7 +409,6 @@ class AD2CaptDeviceModel:
self._all_recorded_samples = value
self.signals.all_recorded_samples_changed.emit(self.all_recorded_samples)
-
@property
def n_samples(self):
return self._n_samples
@@ -620,5 +435,3 @@ class AD2CaptDeviceModel:
def fLost(self, value):
self._fLost = value
self.signals.fLost_changed.emit(self._fLost)
-
-
diff --git a/src/CaptDeviceControl/model/AD2CaptDeviceSignals.py b/src/CaptDeviceControl/model/AD2CaptDeviceSignals.py
new file mode 100644
index 0000000000000000000000000000000000000000..81f119f75525d46bf37bfaddff45d65e97a7399b
--- /dev/null
+++ b/src/CaptDeviceControl/model/AD2CaptDeviceSignals.py
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+"""
+Author(s): Christoph Schmidt <christoph.schmidt@tugraz.at>
+Created: 2023-10-19 12:35
+Package Version:
+"""
+from PySide6.QtCore import QObject, Signal
+
+from CaptDeviceConfig import CaptDeviceConfig as Config
+from controller.DeviceInformation.HWDeviceInformation import HWDeviceInformation
+from model.AD2Constants import AD2Constants
+
+
+class AD2CaptDeviceSignals(QObject):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+ ad2captdev_config_changed = Signal(Config)
+
+ # WaveForms Runtime (DWF) Information
+ dwf_version_changed = Signal(str)
+
+ # Connected Device Information
+ num_of_discovered_devices_changed = Signal(int)
+ discovered_devices_changed = Signal(list)
+ selected_devices_changed = Signal(HWDeviceInformation)
+
+ # Device information
+ connected_changed = Signal(bool)
+ device_name_changed = Signal(str)
+ serial_number_changed = Signal(str)
+ device_index_changed = Signal(int)
+
+ # Acquisition Settings
+ sample_rate_changed = Signal(int)
+ streaming_rate_changed = Signal(int)
+ selected_ain_channel_changed = Signal(int)
+ duration_streaming_history_changed = Signal(int)
+
+ # Analog In Information
+ ain_channels_changed = Signal(list)
+ ain_buffer_size_changed = Signal(int)
+ ain_bits_changed = Signal(int)
+ ain_device_state_changed = Signal(int)
+
+ # Analog Out Information
+ aout_channels_changed = Signal(list)
+
+ # Acquired Signal Information
+ recorded_samples_changed = Signal(list)
+ recording_time_changed = Signal(float)
+ samples_captured_changed = Signal(int)
+ samples_lost_changed = Signal(int)
+ samples_corrupted_changed = Signal(int)
+ # Actually for the worker, these are the samples that have not been consumed yet by the UI thread.
+ unconsumed_stream_samples_changed = Signal(int)
+ unconsumed_capture_samples_changed = Signal(int)
+
+ # Recording Flags (starting, stopping and pausing)
+ device_capturing_state_changed = Signal(AD2Constants.CapturingState)
+ start_recording_changed = Signal(bool)
+ stop_recording_changed = Signal(bool)
+ reset_recording_changed = Signal(bool)
+ capturing_finished_changed = Signal(bool)
+
+ # Multiprocessing Information
+ pid_changed = Signal(int)
+
+ # ==================================================================================================================
+ # Delete later
+ # Signals for device information
+ hwdf_changed = Signal(int)
+
+ device_ready_changed = Signal(bool)
+
+ # Signals for reporting if samples were lost or corrupted
+ fLost_changed = Signal(int)
+ fCorrupted_changed = Signal(int)
+ # Acquisition information
+
+ n_samples_changed = Signal(int)
+
+ # Recording settings for starting, stopping and pausing
+
+ # Signal if new samples have been aquired
+
+ all_recorded_samples_changed = Signal(list)
+ num_of_current_recorded_samples_changed = Signal(int)
+ measurement_time_changed = Signal(float)
+
+ ad2_settings = Signal(dict)
+ error = Signal(str)
+ ad2_is_capturing = Signal(bool)
+
+ ad2_set_acquisition = Signal(bool)
+ ad2_captured_value = Signal(list)
diff --git a/src/CaptDeviceControl/view/AD2CaptDeviceView.py b/src/CaptDeviceControl/view/AD2CaptDeviceView.py
index cdb348a5a6cca2066d56354169b63e54c1380a5a..93aa47b51004a0c12b335db918af26768b71222c 100644
--- a/src/CaptDeviceControl/view/AD2CaptDeviceView.py
+++ b/src/CaptDeviceControl/view/AD2CaptDeviceView.py
@@ -1,4 +1,5 @@
import logging
+import os
from collections import deque
import numpy as np
@@ -9,8 +10,9 @@ from PySide6.QtWidgets import QMainWindow, QStatusBar
from pyqtgraph.dockarea import DockArea, Dock
import pyqtgraph as pg
+from rich.logging import RichHandler
-from CaptDeviceControl.controller.AD2CaptDeviceController import AD2CaptDeviceController
+from CaptDeviceControl.controller.BaseAD2CaptDevice import BaseAD2CaptDevice
from CaptDeviceControl.model.AD2CaptDeviceModel import AD2CaptDeviceModel
from CaptDeviceControl.model.AD2Constants import AD2Constants
from CaptDeviceControl.view.Ui_AD2ControlWindow import Ui_AD2ControlWindow
@@ -23,9 +25,14 @@ from fswidgets import PlayPushButton
class ControlWindow(QMainWindow):
- def __init__(self, model: AD2CaptDeviceModel, controller: AD2CaptDeviceController):
+ def __init__(self, model: AD2CaptDeviceModel, controller: BaseAD2CaptDevice):
super().__init__()
- self.logger = logging.getLogger("AD2ControlWindow")
+ self.handler = RichHandler(rich_tracebacks=True)
+ self.logger = logging.getLogger(f"AD2Window({os.getpid()})")
+ self.logger.handlers = [self.handler]
+ self.logger.setLevel(logging.DEBUG)
+ formatter = logging.Formatter('%(name)s %(message)s')
+ self.handler.setFormatter(formatter)
self.controller = controller
self.model = model
@@ -51,10 +58,10 @@ class ControlWindow(QMainWindow):
# Timer for periodically updating the plot
self.capture_update_timer = QTimer()
self.capture_update_timer.setInterval(10)
- self.capture_update_timer.timeout.connect(self._on_capture_update_plot)
+ #self.capture_update_timer.timeout.connect(self._on_capture_update_plot)
self.stream_update_timer = QTimer()
- self.stream_update_timer.setInterval(10)
+ self.stream_update_timer.setInterval(40)
self.stream_update_timer.timeout.connect(self._on_stream_update_timer_timeout)
self.stream_samples_frequency = 1000
@@ -95,33 +102,33 @@ class ControlWindow(QMainWindow):
self.model.signals.dwf_version_changed.connect(self._on_dwf_version_changed)
# Connected Device Information
- self.model.signals.num_of_connected_devices_changed.connect(self._on_num_of_connected_devices_changed)
- self.model.signals.connected_devices_changed.connect(self._on_connected_devices_changed)
+ self.model.device_information.signals.num_of_connected_devices_changed.connect(self._on_num_of_connected_devices_changed)
+ self.model.device_information.signals.connected_devices_changed.connect(self._on_connected_devices_changed)
# Device information
- self.model.signals.connected_changed.connect(self._on_connected_changed)
- self.model.signals.device_name_changed.connect(self._on_device_name_changed)
- self.model.signals.device_serial_number_changed.connect(self._on_device_serial_number_changed)
- self.model.signals.device_index_changed.connect(self._on_device_index_changed)
+ self.model.device_information.signals.device_connected_changed.connect(self._on_connected_changed)
+ self.model.device_information.signals.device_name_changed.connect(self._on_device_name_changed)
+ self.model.device_information.signals.device_serial_number_changed.connect(self._on_device_serial_number_changed)
+ self.model.device_information.signals.device_index_changed.connect(self._on_device_index_changed)
# Acquisition Settings
self.model.signals.sample_rate_changed.connect(self._model_on_sample_rate_changed)
- self.model.signals.selected_ain_channel_changed.connect(self._model_on_selected_ain_changed)
# Analog In Information
- self.model.signals.ain_channels_changed.connect(self._on_ain_channels_changed)
- self.model.signals.ain_buffer_size_changed.connect(self._on_ain_buffer_size_changed)
- self.model.signals.ain_bits_changed.connect(self._on_ain_bits_changed)
- self.model.signals.ain_device_state_changed.connect(self._on_ain_device_state_changed)
+ self.model.analog_in.signals.selected_ain_channel_changed.connect(self._model_on_selected_ain_changed)
+ self.model.analog_in.signals.ain_channels_changed.connect(self._on_ain_channels_changed)
+ self.model.analog_in.signals.ain_buffer_size_changed.connect(self._on_ain_buffer_size_changed)
+ self.model.analog_in.signals.ain_bits_changed.connect(self._on_ain_bits_changed)
+ self.model.analog_in.signals.ain_device_state_changed.connect(self._on_ain_device_state_changed)
# Analog Out Information
self.model.signals.aout_channels_changed.connect(self._on_aout_channels_changed)
# Acquired Signal Information
- self.model.signals.recorded_samples_changed.connect(self._on_recorded_samples_changed)
- self.model.signals.recording_time_changed.connect(self._on_recording_time_changed)
- self.model.signals.samples_captured_changed.connect(self._on_samples_captured_changed)
+ #self.model.signals.recorded_samples_changed.connect(self._on_recorded_samples_changed)
+ #self.model.signals.recording_time_changed.connect(self._on_recording_time_changed)
+ #self.model.signals.samples_captured_changed.connect(self._on_samples_captured_changed)
self.model.signals.samples_lost_changed.connect(self._on_samples_lost_changed)
self.model.signals.samples_corrupted_changed.connect(self._on_samples_corrupted_changed)
@@ -179,8 +186,8 @@ class ControlWindow(QMainWindow):
# Slots for Model
# ==================================================================================================================
def _on_device_selected_changed(self, index):
- self.model.selected_device = index
- self.controller.get_analog_in_informatio()
+ self.model.device_information.device_index = index
+ self.controller.device_selected_index_changed()
# First populate the AIn box+
#m: dict = self.model.connected_devices[index]
#self.model.ain_channels = list(range(0, int(m['analog_in_channels'])))
@@ -201,6 +208,7 @@ class ControlWindow(QMainWindow):
dev: dict
# 'type': type, 'device_id', 'device_name', 'serial_number'
self._ui.cb_device_select.addItem(f"{it}: {dev['type']}{dev['device_id']} - {dev['device_name']}")
+ self._ui.cb_device_select.setCurrentIndex(0)
# ============== Device information
def _on_connected_changed(self, connected):
@@ -396,7 +404,7 @@ class ControlWindow(QMainWindow):
self.scope_original.clear()
# print(self.ad2device.recorded_samples)
self.scope_original.plot(
- list(self.controller.streaming_data_dqueue),#[::self.stream_n],
+ np.array(self.controller.streaming_data_dqueue)[::100],
pen=pg.mkPen(width=1))
self._ui.lcd_unconsumed_stream.display(self.model.unconsumed_stream_samples)
@@ -405,8 +413,8 @@ class ControlWindow(QMainWindow):
#
# ==================================================================================================================
def on_btn_connect_to_device_clicked(self):
- print(self.model.connected)
- if self.model.connected:
+ print(self.model.device_information.device_connected)
+ if self.model.device_information.device_connected:
self.controller.close_device()
self._ui.btn_connect.setText("Connect")
else:
diff --git a/tests/testing_mp_pyside.py b/tests/testing_mp_pyside.py
new file mode 100644
index 0000000000000000000000000000000000000000..14a8cbb4949b99c614a7942f73d612594968c1a9
--- /dev/null
+++ b/tests/testing_mp_pyside.py
@@ -0,0 +1,42 @@
+import sys
+import multiprocessing
+from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget
+from PySide6.QtCore import Signal, QObject, Qt
+from time import sleep
+
+class ChildProcess(QObject):
+ finished = Signal(str)
+
+ def run(self):
+ app = QApplication(sys.argv)
+ main_window = MainWindow()
+ main_window.show()
+ sys.exit(app.exec_())
+
+class MainWindow(QMainWindow):
+ def __init__(self):
+ super().__init__()
+
+ self.setWindowTitle("Multiprocessing with PySide6")
+ self.setGeometry(100, 100, 400, 200)
+
+ self.central_widget = QWidget(self)
+ self.setCentralWidget(self.central_widget)
+
+ self.layout = QVBoxLayout(self.central_widget)
+
+ self.start_button = QPushButton("Start Child Process", self)
+ self.start_button.clicked.connect(self.start_child_process)
+ self.layout.addWidget(self.start_button)
+
+ self.child_process = None
+
+ def start_child_process(self):
+ if not self.child_process or not self.child_process.is_alive():
+ self.child_process = multiprocessing.Process(target=self.run_child_process)
+ self.child_process.start()
+ self.start_button.setEnabled(False)
+
+ def run_child_process(self):
+ child = ChildProcess()
+ child.finished.connect(self.handle_child_signal)