Skip to content

Commit 2dda736

Browse files
committed
Updated to version to 1.1.8. Now uses the Laser Controller as Supervisor. Fixes the deadlock that prevents the application from exiting properly.
1 parent b53f74c commit 2dda736

9 files changed

Lines changed: 297 additions & 54 deletions

File tree

examples/CaptDeviceConfig.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# - Configuration file stored 2024-07-31 15:06:57.834858 -
1+
# - Configuration file stored 2024-09-13 12:23:31.370031 -
22
CaptDeviceConfig: #!!python/object:controller.CaptDeviceConfig
33
selected_device_index: 0 # Selected device: Selected device from the device list provided by the DreamWaves API.
44
sample_rate: 500 # Sample rate: Sample rate of the device
55
streaming_rate: 500 # Streaming rate: Streaming rate in Hz (should be below 1kHz)
6-
ain_channel: <1>[(0, 'Channel 0'), (1, 'Channel 1')]> # -> 1 # Analog In Channel: Analog in channel. Defines which channel is used for capturing.
6+
ain_channel: <0>[(0, 'Channel 0'), (1, 'Channel 1')]> # -> 0 # Analog In Channel: Analog in channel. Defines which channel is used for capturing.
77
show_simulator: True # Show Simulators: Show available simulators in the device list provided by the DreamWaves API.
8-
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
8+
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

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ packages = ["src/ADScopeControl"]
1010

1111
[project]
1212
name = "PyADScopeControl"
13-
version = "1.1.7"
13+
version = "1.1.8"
1414
authors = [
1515
{ name="Christoph Schmidt", email="cschmidt.fs@gmail.com" },
1616
]

src/ADScopeControl/controller/BaseADScopeController.py

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import mpPy6
99
import pandas as pd
1010
from PySide6.QtCore import QThreadPool, Signal
11+
from PySide6.QtWidgets import QMessageBox
1112
from numpy import ndarray
1213

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

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

68-
6968
self.register_child_process(
7069
MPCaptDevice,
7170
self.stream_data_queue,
@@ -83,7 +82,16 @@ def __init__(self, ad2capt_model: AD2ScopeModel, start_capture_flag: Value):
8382

8483
self.selected_ain_channel = self.model.analog_in.selected_ain_channel
8584

85+
# Some Signals and slots to connect
86+
self.model.supervisor_information.signals.supervised_changed.connect(self._on_supervised_changed)
87+
self.model.supervisor_information.signals.supervisor_name_changed.connect(self._on_supervisor_name_changed)
8688

89+
# Supervision slots
90+
def _on_supervised_changed(self):
91+
self.logger.info(f"Device is now supervised.")
92+
93+
def _on_supervisor_name_changed(self, name: str):
94+
self.logger.info(f"Device is supervised by {name}")
8795

8896
def connect_signals(self):
8997
self.dwf_version_changed.connect(self._on_dwf_version_changed)
@@ -115,9 +123,7 @@ def connect_signals(self):
115123

116124
self.device_state_changed.connect(
117125
lambda x: type(self.model.device_information).device_state.fset(self.model.device_information, x))
118-
self.capture_process_state_changed.connect(
119-
lambda x: type(self.model.capturing_information).device_capturing_state.fset(
120-
self.model.capturing_information, x))
126+
self.capture_process_state_changed.connect(self._on_capture_process_state_changed)
121127
self.ready_for_recording_changed.connect(
122128
lambda x: type(self.model.capturing_information).ready_for_recording.fset(
123129
self.model.capturing_information, x))
@@ -164,8 +170,10 @@ def on_open_device_finished(self, device_handle: int):
164170
self.logger.info(f"Opening device finished with handle {device_handle}")
165171
self.start_capturing_process()
166172

173+
@mpPy6.CProcessControl.register_function(close_device_finished)
167174
def close_device(self):
168-
pass
175+
self.kill_capture_flag.value = int(True)
176+
print("Closing device")
169177
# self.close_device()
170178

171179
@mpPy6.CProcessControl.register_function(capture_process_state_changed)
@@ -204,7 +212,6 @@ def discover_connected_devices(self):
204212
:return:
205213
"""
206214

207-
208215
def on_discovered_devices_changed(self, devices: list):
209216
self.logger.info(f"Discovered devices: {len(devices)}")
210217
self.logger.debug(f"Discovered devices: {devices}")
@@ -221,6 +228,19 @@ def update_device_information(self):
221228
def _capture(self):
222229
raise NotImplementedError
223230

231+
def _on_capture_process_state_changed(self, state):
232+
self.model.capturing_information.device_capturing_state = state
233+
234+
def read_supervisor_state(self):
235+
if self.model.supervisor_information.supervised and self.model.supervisor_information.supervisor_model is not None:
236+
self.model.supervisor_information.sweep_start_wavelength = (
237+
self.model.supervisor_information.supervisor_model.laser_config.wl_sweep_start.get())
238+
self.model.supervisor_information.sweep_stop_wavelength = (
239+
self.model.supervisor_information.supervisor_model.laser_config.wl_sweep_stop.get())
240+
self.model.supervisor_information.velocity = self.model.supervisor_information.supervisor_model.laser_config.velocity.get()
241+
self.model.supervisor_information.acceleration = self.model.supervisor_information.supervisor_model.laser_config.acceleration.get()
242+
self.model.supervisor_information.deceleration = self.model.supervisor_information.supervisor_model.laser_config.deceleration.get()
243+
224244
def set_ad2_acq_status(self, record):
225245
if record:
226246
self.model.start_recording = True
@@ -257,7 +277,7 @@ def clear_data(self):
257277
self.model.recorded_samples = []
258278
self.model.recorded_sample_stream = []
259279

260-
def set_recorded_data_time_axis(self, func = None):
280+
def set_recorded_data_time_axis(self, func=None):
261281

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

269289
self.model.capturing_information.recorded_samples_df['time (ms)'] = (
270290
self.model.capturing_information.recorded_samples_df.index.to_series().apply(
271-
lambda x: (x / self.model.capturing_information.sample_rate)*1000
291+
lambda x: (x / self.model.capturing_information.sample_rate) * 1000
272292
)
273293
)
274294

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

285305
def create_dataframe(self):
306+
307+
# self.model.supervisor_information.sweep_start_wavelength
308+
# self.model.supervisor_information.sweep_stop_wavelength
309+
# self.model.supervisor_information.velocity
310+
# self.model.supervisor_information.acceleration
311+
# self.model.supervisor_information.deceleration
312+
286313
self.model.capturing_information.recorded_samples_df = (
287314
pd.DataFrame(self.model.capturing_information.recorded_samples,
288315
columns=['Amplitude']))
289316

290317
self.set_recorded_data_time_axis()
291318

292-
293319
def stop_capture(self):
294320
self.start_capture_flag.value = 0
295321

@@ -326,10 +352,11 @@ def start_device_process(self):
326352

327353
def qt_consume_data(self):
328354
itoogle = 0
329-
while True:
355+
while not self.kill_thread:
330356
t = time.time()
331357
try:
332-
capture_data = self.capture_data_queue.get(block=True)
358+
capture_data = self.capture_data_queue.get(block=True, timeout=1)
359+
333360
if isinstance(capture_data, ndarray):
334361
# print(f"Stream data queue size {len(stream_data)}")
335362
for d in capture_data:
@@ -344,29 +371,32 @@ def qt_consume_data(self):
344371
t_end = time.time()
345372
# print(f"Time to get data {t_end-t}")
346373
except Exception as e:
347-
self.logger.info(f"Timeout reached. No data in queue {self.stream_data_queue.qsize()} or"
348-
f"{e}")
374+
pass
375+
#self.logger.info(f"Timeout reached. No data in queue {self.stream_data_queue.qsize()} or"
376+
# f"{e}")
349377
self.logger.info("Streaming data consume thread ended")
350378

351379
def qt_stream_data(self):
352380
itoogle = 0
353-
while True:
381+
382+
while not self.kill_thread:
354383
t = time.time()
355384
try:
356-
stream_data = self.stream_data_queue.get(block=True)
385+
stream_data = self.stream_data_queue.get(block=True, timeout=1)
357386
if isinstance(stream_data, ndarray):
358387
# print(f"Stream data queue size {len(stream_data)}")
359388
for d in stream_data:
360-
if itoogle == math.ceil(self.model.capturing_information.sample_rate/1000):
389+
if itoogle == math.ceil(self.model.capturing_information.sample_rate / 1000):
361390
self.streaming_dqueue.append(d)
362391
itoogle = 0
363392
else:
364393
itoogle = itoogle + 1
365394
t_end = time.time()
366395
# print(f"Time to get data {t_end-t}")
367396
except Exception as e:
368-
self.logger.info(f"Timeout reached. No data in queue {self.stream_data_queue.qsize()} or"
369-
f"{e}")
397+
pass
398+
# self.logger.info(f"Timeout reached. No data in queue {self.stream_data_queue.qsize()} or"
399+
# f"{e}")
370400
self.logger.info("Streaming data consume thread ended")
371401

372402
def qt_get_state(self):
@@ -376,9 +406,10 @@ def qt_get_state(self):
376406
# time.sleep(0.1)
377407
self.logger.info("Status data consume thread ended")
378408

379-
380409
# ==================================================================================================================
381410
# Destructor
382411
# ==================================================================================================================
383412
def exit(self):
413+
for c in self.thread_manager.children():
414+
c.exit()
384415
self.safe_exit()

src/ADScopeControl/controller/mp_AD2Capture/MPCaptDevice.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99
from mpPy6.CProperty import CProperty
1010

1111
from ADScopeControl.model.AD2Constants import AD2Constants
12-
from ADScopeControl.constants.dwfconstants import enumfilterType, enumfilterDemo, enumfilterUSB, acqmodeRecord, DwfStateConfig, \
12+
from ADScopeControl.constants.dwfconstants import enumfilterType, enumfilterDemo, enumfilterUSB, acqmodeRecord, \
13+
DwfStateConfig, \
1314
DwfStatePrefill, DwfStateArmed
1415

1516

1617
class MPCaptDevice(mpPy6.CProcess, ):
1718

18-
1919
#@staticmethod
2020
def timeit(func):
2121
def wrapper(self, *args, **kwargs):
@@ -73,6 +73,8 @@ def __init__(self, state_queue: Queue, cmd_queue: Queue,
7373
self._samples_lost = 0
7474
self._samples_corrupted = 0
7575

76+
77+
7678
# ==================================================================================================================
7779
# Getter and Setter
7880
# ==================================================================================================================
@@ -166,14 +168,14 @@ def selected_device_index(self, device_index: int):
166168
self.ain_channels = self.get_ain_channels()
167169
self.ain_buffer_size = self.get_ain_buffer_size(self._selected_device_index)
168170

169-
170171
@CProperty
171172
def ready_for_recording(self):
172173
return self._ready_for_recording
173174

174175
@ready_for_recording.setter(emit_to='ready_for_recording_changed')
175176
def ready_for_recording(self, value: bool):
176177
self._ready_for_recording = value
178+
177179
# ==================================================================================================================
178180
#
179181
# ==================================================================================================================
@@ -201,8 +203,8 @@ def get_dwf_version(self) -> str:
201203
# Device Enumeration without connecting to the device
202204
# ==================================================================================================================
203205
@mpPy6.CProcess.register_signal()
204-
def discover_connected_devices(self, filter_type: int = enumfilterType.value | enumfilterDemo.value | enumfilterUSB.value):
205-
206+
def discover_connected_devices(self,
207+
filter_type: int = enumfilterType.value | enumfilterDemo.value | enumfilterUSB.value):
206208

207209
self.logger.info(f"Discovering connected devices...")
208210
# enumerate connected devices
@@ -233,6 +235,7 @@ def discover_connected_devices(self, filter_type: int = enumfilterType.value | e
233235
# _mp_log_debug(f"Found {type} device: {devicename.value.decode('UTF-8')} ({serialnum.value.decode('UTF-8')})")
234236
self.logger.debug(f"Found {len(connected_devices)} devices.")
235237
return connected_devices
238+
236239
# ==================================================================================================================
237240
# Settings from process Control
238241
# ==================================================================================================================
@@ -250,6 +253,7 @@ def set_selected_device(self, ain_channel):
250253
@mpPy6.CProcess.register_signal()
251254
def set_sample_rate(self, sample_rate):
252255
self.sample_rate = sample_rate
256+
253257
# ==================================================================================================================
254258
# Functions for opening and closing the device
255259
# ==================================================================================================================
@@ -286,6 +290,7 @@ def open_device(self) -> int:
286290
self.device_state(AD2Constants.DeviceState.ACQ_NOT_STARTED())
287291
return int(self.hdwf.value)
288292

293+
@mpPy6.CProcess.register_signal()
289294
def close_device(self):
290295
# self.dwf.FDwfAnalogOutReset(self.hdwf, c_int(channel))
291296
self.logger.debug(f"[Task] Closing device...")
@@ -305,13 +310,13 @@ def get_ain_channels(self) -> list:
305310
#print(f">>><<<< {cInfo}")
306311
#self.ain_channels = cInfo.value
307312
#if self.ain_channels == 0:
308-
# Sometimes, the device reports a wrong number of ain channels
309-
# so we can try to connect to the device first and retrieve the information
313+
# Sometimes, the device reports a wrong number of ain channels
314+
# so we can try to connect to the device first and retrieve the information
310315
self.open_device()
311316
self.ain_channels = self.analog_in_channels_count()
312317
self.close_device()
313318
self.logger.info(f"Device {self.device_name} (#{self.selected_device_index}, SNR: {self.device_serial_number}) "
314-
f"AIn: {self.ain_channels}")
319+
f"AIn: {self.ain_channels}")
315320
return list(range(0, self.ain_channels))
316321

317322
def get_ain_buffer_size(self, device_id) -> int:
@@ -537,7 +542,6 @@ def start_capturing_process(self):
537542
hdwf = self.hdwf
538543
self.device_state(AD2Constants.DeviceState.DEV_CAPT_SETUP())
539544

540-
541545
self.setup_sine_wave(self.selected_ain_channel)
542546

543547
self.setup_acquisition(self.sample_rate, self.selected_ain_channel)
@@ -572,7 +576,7 @@ def start_capturing_process(self):
572576
# self.dwf.FDwfAnalogOutReset(self.hdwf, c_int(0))
573577
self.device_state(AD2Constants.DeviceState.DEV_CAPT_STREAMING())
574578
self.ready_for_recording = True
575-
while self.kill_capture_flag.value == int(False):
579+
while self.kill_capture_flag.value == int(False) and self._kill_flag.value == int(True):
576580
self.dwf.FDwfAnalogInStatus(hdwf, c_int(1), byref(sts))
577581
# self._c_samples = 0
578582

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

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

650653

651-
652-
653-
654654
if __name__ == "__main__":
655655
state_queue = Queue()
656656
cmd_queue = Queue()

src/ADScopeControl/controller/mp_AD2Capture/MPCaptDeviceSceleton.py

Lines changed: 0 additions & 7 deletions
This file was deleted.

src/ADScopeControl/model/AD2ScopeModel.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from ADScopeControl.model.submodels.AD2CaptDeviceAnalogInModel import AD2CaptDeviceAnalogInModel
88
from ADScopeControl.model.submodels.AD2CaptDeviceCapturingModel import AD2CaptDeviceCapturingModel
99
from ADScopeControl.model.submodels.AD2CaptDeviceInformationModel import AD2CaptDeviceInformationModel
10+
from ADScopeControl.model.submodels.AD2CaptDeviceSupervisorModel import AD2CaptDeviceSupervisorModel
1011

1112

1213
# from MeasurementData.Properties.AD2CaptDeviceProperties import AD2CaptDeviceProperties
@@ -97,6 +98,7 @@ def __init__(self, ad2captdev_config: Config):
9798
self.device_information = AD2CaptDeviceInformationModel(self.ad2captdev_config)
9899
self.analog_in = AD2CaptDeviceAnalogInModel(self.ad2captdev_config)
99100
self.capturing_information = AD2CaptDeviceCapturingModel(self.ad2captdev_config)
101+
self.supervisor_information = AD2CaptDeviceSupervisorModel()
100102
# Acquisition Settings
101103

102104
# Analog Out Information

0 commit comments

Comments
 (0)