Skip to content

Commit

Permalink
Updated to version to 1.1.8. Now uses the Laser Controller as Supervi…
Browse files Browse the repository at this point in the history
…sor. Fixes the deadlock that prevents the application from exiting properly.
  • Loading branch information
Christoph Schmidt committed Sep 13, 2024
1 parent b53f74c commit 2dda736
Show file tree
Hide file tree
Showing 9 changed files with 297 additions and 54 deletions.
6 changes: 3 additions & 3 deletions examples/CaptDeviceConfig.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# - Configuration file stored 2024-07-31 15:06:57.834858 -
# - Configuration file stored 2024-09-13 12:23:31.370031 -
CaptDeviceConfig: #!!python/object:controller.CaptDeviceConfig
selected_device_index: 0 # Selected device: Selected device from the device list provided by the DreamWaves API.
sample_rate: 500 # Sample rate: Sample rate of the device
streaming_rate: 500 # Streaming rate: Streaming rate in Hz (should be below 1kHz)
ain_channel: <1>[(0, 'Channel 0'), (1, 'Channel 1')]> # -> 1 # Analog In Channel: Analog in channel. Defines which channel is used for capturing.
ain_channel: <0>[(0, 'Channel 0'), (1, 'Channel 1')]> # -> 0 # Analog In Channel: Analog in channel. Defines which channel is used for capturing.
show_simulator: True # Show Simulators: Show available simulators in the device list provided by the DreamWaves API.
streaming_history: <8>[(100, '100 ms'), (200, '200 ms'), (500, '500 ms'), (1000, '1 s'), (2000, '2 s'), (5000, '5 s'), (10000, '10 s'), (20000, '20 s'), (30000, '30 s')]> # -> 30000 # Streaming history: Defines the range of the stream in ms
streaming_history: <3>[(100, '100 ms'), (200, '200 ms'), (500, '500 ms'), (1000, '1 s'), (2000, '2 s'), (5000, '5 s'), (10000, '10 s'), (20000, '20 s'), (30000, '30 s')]> # -> 1000 # Streaming history: Defines the range of the stream in ms
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ packages = ["src/ADScopeControl"]

[project]
name = "PyADScopeControl"
version = "1.1.7"
version = "1.1.8"
authors = [
{ name="Christoph Schmidt", email="[email protected]" },
]
Expand Down
71 changes: 51 additions & 20 deletions src/ADScopeControl/controller/BaseADScopeController.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import mpPy6
import pandas as pd
from PySide6.QtCore import QThreadPool, Signal
from PySide6.QtWidgets import QMessageBox
from numpy import ndarray

from ADScopeControl.controller.mp_AD2Capture.MPCaptDevice import MPCaptDevice
Expand Down Expand Up @@ -55,7 +56,6 @@ def __init__(self, ad2capt_model: AD2ScopeModel, start_capture_flag: Value):
self.stream_data_queue = Queue()
self.capture_data_queue = Queue()


if start_capture_flag is None:
self.start_capture_flag = Value('i', 0, lock=self.lock)
else:
Expand All @@ -65,7 +65,6 @@ def __init__(self, ad2capt_model: AD2ScopeModel, start_capture_flag: Value):
# Number of sa
self.streaming_dqueue: deque = None # a dqueue, initialize later


self.register_child_process(
MPCaptDevice,
self.stream_data_queue,
Expand All @@ -83,7 +82,16 @@ def __init__(self, ad2capt_model: AD2ScopeModel, start_capture_flag: Value):

self.selected_ain_channel = self.model.analog_in.selected_ain_channel

# Some Signals and slots to connect
self.model.supervisor_information.signals.supervised_changed.connect(self._on_supervised_changed)
self.model.supervisor_information.signals.supervisor_name_changed.connect(self._on_supervisor_name_changed)

# Supervision slots
def _on_supervised_changed(self):
self.logger.info(f"Device is now supervised.")

def _on_supervisor_name_changed(self, name: str):
self.logger.info(f"Device is supervised by {name}")

def connect_signals(self):
self.dwf_version_changed.connect(self._on_dwf_version_changed)
Expand Down Expand Up @@ -115,9 +123,7 @@ def connect_signals(self):

self.device_state_changed.connect(
lambda x: type(self.model.device_information).device_state.fset(self.model.device_information, x))
self.capture_process_state_changed.connect(
lambda x: type(self.model.capturing_information).device_capturing_state.fset(
self.model.capturing_information, x))
self.capture_process_state_changed.connect(self._on_capture_process_state_changed)
self.ready_for_recording_changed.connect(
lambda x: type(self.model.capturing_information).ready_for_recording.fset(
self.model.capturing_information, x))
Expand Down Expand Up @@ -164,8 +170,10 @@ def on_open_device_finished(self, device_handle: int):
self.logger.info(f"Opening device finished with handle {device_handle}")
self.start_capturing_process()

@mpPy6.CProcessControl.register_function(close_device_finished)
def close_device(self):
pass
self.kill_capture_flag.value = int(True)
print("Closing device")
# self.close_device()

@mpPy6.CProcessControl.register_function(capture_process_state_changed)
Expand Down Expand Up @@ -204,7 +212,6 @@ def discover_connected_devices(self):
:return:
"""


def on_discovered_devices_changed(self, devices: list):
self.logger.info(f"Discovered devices: {len(devices)}")
self.logger.debug(f"Discovered devices: {devices}")
Expand All @@ -221,6 +228,19 @@ def update_device_information(self):
def _capture(self):
raise NotImplementedError

def _on_capture_process_state_changed(self, state):
self.model.capturing_information.device_capturing_state = state

def read_supervisor_state(self):
if self.model.supervisor_information.supervised and self.model.supervisor_information.supervisor_model is not None:
self.model.supervisor_information.sweep_start_wavelength = (
self.model.supervisor_information.supervisor_model.laser_config.wl_sweep_start.get())
self.model.supervisor_information.sweep_stop_wavelength = (
self.model.supervisor_information.supervisor_model.laser_config.wl_sweep_stop.get())
self.model.supervisor_information.velocity = self.model.supervisor_information.supervisor_model.laser_config.velocity.get()
self.model.supervisor_information.acceleration = self.model.supervisor_information.supervisor_model.laser_config.acceleration.get()
self.model.supervisor_information.deceleration = self.model.supervisor_information.supervisor_model.laser_config.deceleration.get()

def set_ad2_acq_status(self, record):
if record:
self.model.start_recording = True
Expand Down Expand Up @@ -257,7 +277,7 @@ def clear_data(self):
self.model.recorded_samples = []
self.model.recorded_sample_stream = []

def set_recorded_data_time_axis(self, func = None):
def set_recorded_data_time_axis(self, func=None):

# Create a new column same as the index
self.model.capturing_information.recorded_samples_df['time (s)'] = (
Expand All @@ -268,7 +288,7 @@ def set_recorded_data_time_axis(self, func = None):

self.model.capturing_information.recorded_samples_df['time (ms)'] = (
self.model.capturing_information.recorded_samples_df.index.to_series().apply(
lambda x: (x / self.model.capturing_information.sample_rate)*1000
lambda x: (x / self.model.capturing_information.sample_rate) * 1000
)
)

Expand All @@ -283,13 +303,19 @@ def set_recorded_data_time_axis(self, func = None):
# self.model.device_capturing_state = AD2Constants.CapturingState.RUNNING()

def create_dataframe(self):

# self.model.supervisor_information.sweep_start_wavelength
# self.model.supervisor_information.sweep_stop_wavelength
# self.model.supervisor_information.velocity
# self.model.supervisor_information.acceleration
# self.model.supervisor_information.deceleration

self.model.capturing_information.recorded_samples_df = (
pd.DataFrame(self.model.capturing_information.recorded_samples,
columns=['Amplitude']))

self.set_recorded_data_time_axis()


def stop_capture(self):
self.start_capture_flag.value = 0

Expand Down Expand Up @@ -326,10 +352,11 @@ def start_device_process(self):

def qt_consume_data(self):
itoogle = 0
while True:
while not self.kill_thread:
t = time.time()
try:
capture_data = self.capture_data_queue.get(block=True)
capture_data = self.capture_data_queue.get(block=True, timeout=1)

if isinstance(capture_data, ndarray):
# print(f"Stream data queue size {len(stream_data)}")
for d in capture_data:
Expand All @@ -344,29 +371,32 @@ def qt_consume_data(self):
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}")
pass
#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_stream_data(self):
itoogle = 0
while True:

while not self.kill_thread:
t = time.time()
try:
stream_data = self.stream_data_queue.get(block=True)
stream_data = self.stream_data_queue.get(block=True, timeout=1)
if isinstance(stream_data, ndarray):
# print(f"Stream data queue size {len(stream_data)}")
for d in stream_data:
if itoogle == math.ceil(self.model.capturing_information.sample_rate/1000):
if itoogle == math.ceil(self.model.capturing_information.sample_rate / 1000):
self.streaming_dqueue.append(d)
itoogle = 0
else:
itoogle = itoogle + 1
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}")
pass
# 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):
Expand All @@ -376,9 +406,10 @@ def qt_get_state(self):
# time.sleep(0.1)
self.logger.info("Status data consume thread ended")


# ==================================================================================================================
# Destructor
# ==================================================================================================================
def exit(self):
for c in self.thread_manager.children():
c.exit()
self.safe_exit()
28 changes: 14 additions & 14 deletions src/ADScopeControl/controller/mp_AD2Capture/MPCaptDevice.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
from mpPy6.CProperty import CProperty

from ADScopeControl.model.AD2Constants import AD2Constants
from ADScopeControl.constants.dwfconstants import enumfilterType, enumfilterDemo, enumfilterUSB, acqmodeRecord, DwfStateConfig, \
from ADScopeControl.constants.dwfconstants import enumfilterType, enumfilterDemo, enumfilterUSB, acqmodeRecord, \
DwfStateConfig, \
DwfStatePrefill, DwfStateArmed


class MPCaptDevice(mpPy6.CProcess, ):


#@staticmethod
def timeit(func):
def wrapper(self, *args, **kwargs):
Expand Down Expand Up @@ -73,6 +73,8 @@ def __init__(self, state_queue: Queue, cmd_queue: Queue,
self._samples_lost = 0
self._samples_corrupted = 0



# ==================================================================================================================
# Getter and Setter
# ==================================================================================================================
Expand Down Expand Up @@ -166,14 +168,14 @@ def selected_device_index(self, device_index: int):
self.ain_channels = self.get_ain_channels()
self.ain_buffer_size = self.get_ain_buffer_size(self._selected_device_index)


@CProperty
def ready_for_recording(self):
return self._ready_for_recording

@ready_for_recording.setter(emit_to='ready_for_recording_changed')
def ready_for_recording(self, value: bool):
self._ready_for_recording = value

# ==================================================================================================================
#
# ==================================================================================================================
Expand Down Expand Up @@ -201,8 +203,8 @@ def get_dwf_version(self) -> str:
# Device Enumeration without connecting to the device
# ==================================================================================================================
@mpPy6.CProcess.register_signal()
def discover_connected_devices(self, filter_type: int = enumfilterType.value | enumfilterDemo.value | enumfilterUSB.value):

def discover_connected_devices(self,
filter_type: int = enumfilterType.value | enumfilterDemo.value | enumfilterUSB.value):

self.logger.info(f"Discovering connected devices...")
# enumerate connected devices
Expand Down Expand Up @@ -233,6 +235,7 @@ def discover_connected_devices(self, filter_type: int = enumfilterType.value | e
# _mp_log_debug(f"Found {type} device: {devicename.value.decode('UTF-8')} ({serialnum.value.decode('UTF-8')})")
self.logger.debug(f"Found {len(connected_devices)} devices.")
return connected_devices

# ==================================================================================================================
# Settings from process Control
# ==================================================================================================================
Expand All @@ -250,6 +253,7 @@ def set_selected_device(self, ain_channel):
@mpPy6.CProcess.register_signal()
def set_sample_rate(self, sample_rate):
self.sample_rate = sample_rate

# ==================================================================================================================
# Functions for opening and closing the device
# ==================================================================================================================
Expand Down Expand Up @@ -286,6 +290,7 @@ def open_device(self) -> int:
self.device_state(AD2Constants.DeviceState.ACQ_NOT_STARTED())
return int(self.hdwf.value)

@mpPy6.CProcess.register_signal()
def close_device(self):
# self.dwf.FDwfAnalogOutReset(self.hdwf, c_int(channel))
self.logger.debug(f"[Task] Closing device...")
Expand All @@ -305,13 +310,13 @@ def get_ain_channels(self) -> list:
#print(f">>><<<< {cInfo}")
#self.ain_channels = cInfo.value
#if self.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
# 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()
self.ain_channels = self.analog_in_channels_count()
self.close_device()
self.logger.info(f"Device {self.device_name} (#{self.selected_device_index}, SNR: {self.device_serial_number}) "
f"AIn: {self.ain_channels}")
f"AIn: {self.ain_channels}")
return list(range(0, self.ain_channels))

def get_ain_buffer_size(self, device_id) -> int:
Expand Down Expand Up @@ -537,7 +542,6 @@ def start_capturing_process(self):
hdwf = self.hdwf
self.device_state(AD2Constants.DeviceState.DEV_CAPT_SETUP())


self.setup_sine_wave(self.selected_ain_channel)

self.setup_acquisition(self.sample_rate, self.selected_ain_channel)
Expand Down Expand Up @@ -572,7 +576,7 @@ def start_capturing_process(self):
# self.dwf.FDwfAnalogOutReset(self.hdwf, c_int(0))
self.device_state(AD2Constants.DeviceState.DEV_CAPT_STREAMING())
self.ready_for_recording = True
while self.kill_capture_flag.value == int(False):
while self.kill_capture_flag.value == int(False) and self._kill_flag.value == int(True):
self.dwf.FDwfAnalogInStatus(hdwf, c_int(1), byref(sts))
# self._c_samples = 0

Expand All @@ -596,7 +600,6 @@ def start_capturing_process(self):
arr = np.array(rgd_samples, copy=True)
iteration_time = time.time() - time_start


if self.start_capture_flag.value == int(True):
if not capture_started:
self.capture_process_state(AD2Constants.CapturingState.RUNNING())
Expand Down Expand Up @@ -648,9 +651,6 @@ def setup_sine_wave(self, channel: int = 0, amplitude: float = 1, frequency: flo
self.logger.debug(f"Sine wave on output channel {channel} configured.")





if __name__ == "__main__":
state_queue = Queue()
cmd_queue = Queue()
Expand Down

This file was deleted.

2 changes: 2 additions & 0 deletions src/ADScopeControl/model/AD2ScopeModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from ADScopeControl.model.submodels.AD2CaptDeviceAnalogInModel import AD2CaptDeviceAnalogInModel
from ADScopeControl.model.submodels.AD2CaptDeviceCapturingModel import AD2CaptDeviceCapturingModel
from ADScopeControl.model.submodels.AD2CaptDeviceInformationModel import AD2CaptDeviceInformationModel
from ADScopeControl.model.submodels.AD2CaptDeviceSupervisorModel import AD2CaptDeviceSupervisorModel


# from MeasurementData.Properties.AD2CaptDeviceProperties import AD2CaptDeviceProperties
Expand Down Expand Up @@ -97,6 +98,7 @@ def __init__(self, ad2captdev_config: Config):
self.device_information = AD2CaptDeviceInformationModel(self.ad2captdev_config)
self.analog_in = AD2CaptDeviceAnalogInModel(self.ad2captdev_config)
self.capturing_information = AD2CaptDeviceCapturingModel(self.ad2captdev_config)
self.supervisor_information = AD2CaptDeviceSupervisorModel()
# Acquisition Settings

# Analog Out Information
Expand Down
Loading

0 comments on commit 2dda736

Please sign in to comment.