From c79ae91c6898fbe0f4912b01fcaa202f1bdcf49e Mon Sep 17 00:00:00 2001 From: Christoph Schmidt <christoph.,schmidt@tugraz.at> Date: Thu, 21 Dec 2023 14:48:30 +0100 Subject: [PATCH] Changed behavior of the multiprocess. New UI. Better implementation of the Front/Backend --- .idea/other.xml | 1 + requirements.txt | 8 +- src/CaptDeviceControl/CaptDeviceConfig.py | 24 +- .../controller/AD2CaptDeviceSimulator.py | 2 +- .../controller/BaseAD2CaptDevice.py | 290 +-- .../controller/mp_AD2Capture/MPCaptDevice.py | 431 +++-- .../mp_AD2Capture/MPCaptDeviceControl.py | 72 +- .../mp_AD2Capture/MPCaptDeviceSceleton.py | 7 + .../controller/mp_AD2Capture/__init__.py | 0 .../model/AD2CaptDeviceModel.py | 185 +- .../model/AD2CaptDeviceProperties.py | 1 + .../model/AD2CaptDeviceSignals.py | 51 +- src/CaptDeviceControl/model/AD2Constants.py | 36 +- .../AD2CaptDeviceAnalogInModel.py | 0 .../submodels/AD2CaptDeviceCapturingModel.py | 216 +++ .../AD2CaptDeviceInformationModel.py | 14 + .../model/submodels/__init__.py | 0 src/CaptDeviceControl/resources/resources.qrc | 8 + src/CaptDeviceControl/resources_rc.py | 1570 ++++++++++++++--- .../view/AD2CaptDeviceView.py | 437 +++-- .../view/Ui_AD2ControlWindow.py | 2 +- .../view/widget/WidgetCapturingInformation.py | 9 +- 22 files changed, 2374 insertions(+), 990 deletions(-) create mode 100644 src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDeviceSceleton.py create mode 100644 src/CaptDeviceControl/controller/mp_AD2Capture/__init__.py rename src/CaptDeviceControl/model/{ => submodels}/AD2CaptDeviceAnalogInModel.py (100%) create mode 100644 src/CaptDeviceControl/model/submodels/AD2CaptDeviceCapturingModel.py rename src/CaptDeviceControl/model/{ => submodels}/AD2CaptDeviceInformationModel.py (89%) create mode 100644 src/CaptDeviceControl/model/submodels/__init__.py diff --git a/.idea/other.xml b/.idea/other.xml index 68993fb..652bf24 100644 --- a/.idea/other.xml +++ b/.idea/other.xml @@ -2,5 +2,6 @@ <project version="4"> <component name="PySciProjectComponent"> <option name="PY_SCI_VIEW" value="true" /> + <option name="PY_MATPLOTLIB_IN_TOOLWINDOW" value="false" /> </component> </project> \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 1b5a617..9a1ec90 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,10 +8,10 @@ pyqtgraph # add the git repo git+https://gitlab.tugraz.at/flexsensor-public/modules/fswidgets.git@develop -git+https://gitlab.tugraz.at/flexsensor-public/modules/confighandler.git@develop -git+https://gitlab.tugraz.at/flexsensor-public/modules/cmp.git@develop +#git+https://gitlab.tugraz.at/flexsensor-public/modules/confighandler.git@develop +#git+https://gitlab.tugraz.at/flexsensor-public/modules/cmp.git@develop # Local installs -#../confighandler +../tools/confighandler #../captdevicecontrol -#../modules/cmp \ No newline at end of file +../tools/cmp \ No newline at end of file diff --git a/src/CaptDeviceControl/CaptDeviceConfig.py b/src/CaptDeviceControl/CaptDeviceConfig.py index fdc0a46..6cf17ef 100644 --- a/src/CaptDeviceControl/CaptDeviceConfig.py +++ b/src/CaptDeviceControl/CaptDeviceConfig.py @@ -4,25 +4,39 @@ Author(s): Christoph Schmidt <christoph.schmidt@tugraz.at> Created: 2023-10-19 12:35 Package Version: """ +import logging + import confighandler as cfg class CaptDeviceConfig(cfg.ConfigNode): def __init__(self) -> None: - super().__init__() + super().__init__(internal_log=True, internal_log_level=logging.DEBUG) 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)") - self.ain_channel = cfg.Field(0, friendly_name="Analog In Channel", - description="Analog in channel. Defines which channel is used for capturing.") + + self.ain_channel = cfg.Field( + cfg.SelectableList([0, 1], + description=["Channel 0", "Channel 1"], + selected_index=0, + ), + friendly_name="Analog In Channel", + description="Analog in channel. Defines which channel is used for capturing.") + self.show_simulator = cfg.Field(True, friendly_name="Show Simulators", description="Show available simulators in the device list " "provided by the DreamWaves API.") - self.streaming_history = cfg.Field(2000, friendly_name="Streaming history (ms)", - description="Defines the range of the stream in ms") + self.streaming_history = cfg.Field( + cfg.SelectableList([100, 200, 500 ,1000, 2000, 5000, 10000, 20000, 30000], + description=["100 ms", "200 ms", "500 ms", "1 s", "2 s", "5 s", "10 s", "20 s", "30 s"], + selected_index=3, + ), + friendly_name="Streaming history", + description="Defines the range of the stream in ms") diff --git a/src/CaptDeviceControl/controller/AD2CaptDeviceSimulator.py b/src/CaptDeviceControl/controller/AD2CaptDeviceSimulator.py index 98bbd5f..409122e 100644 --- a/src/CaptDeviceControl/controller/AD2CaptDeviceSimulator.py +++ b/src/CaptDeviceControl/controller/AD2CaptDeviceSimulator.py @@ -38,7 +38,7 @@ class AD2CaptDeviceSimulator(BaseAD2CaptDevice): } @Slot() - def start_capture(self, capture): + def start_capturing_process(self, capture): if capture: self.logger.info(f"[{self.pref} Task] Setting up device for capturing.") diff --git a/src/CaptDeviceControl/controller/BaseAD2CaptDevice.py b/src/CaptDeviceControl/controller/BaseAD2CaptDevice.py index 4b7c50a..e573e3b 100644 --- a/src/CaptDeviceControl/controller/BaseAD2CaptDevice.py +++ b/src/CaptDeviceControl/controller/BaseAD2CaptDevice.py @@ -5,92 +5,178 @@ import time from abc import abstractmethod from collections import deque -from PySide6.QtCore import QObject, QThreadPool +import cmp +from PySide6.QtCore import QObject, QThreadPool, Signal + from numpy import ndarray from rich.logging import RichHandler - from CaptDeviceControl.model.AD2CaptDeviceModel import AD2CaptDeviceModel, AD2CaptDeviceSignals from CaptDeviceControl.model.AD2Constants import AD2Constants from multiprocessing import Process, Queue, Value, Lock from CaptDeviceControl.controller.mp_AD2Capture.MPCaptDeviceControl import MPCaptDeviceControl +from CaptDeviceControl.controller.mp_AD2Capture.MPCaptDevice import MPCaptDevice -class BaseAD2CaptDevice(QObject): +class BaseAD2CaptDevice(cmp.CProcessControl): + dwf_version_changed = Signal(str, name="dwf_version_changed") + discovered_devices_changed = Signal(list, name="discovered_devices_changed") - def __init__(self, ad2capt_model: AD2CaptDeviceModel, start_capture_flag: Value): - super().__init__() - self.model = ad2capt_model + selected_device_index_changed = Signal(int, name="selected_device_index_changed") + device_connected_changed = Signal(bool, name="connected_changed") + device_name_changed = Signal(str, name="device_name_changed") + device_serial_number_changed = Signal(str, name="device_serial_number_changed") - self.pref = "AD2CaptDev" + ain_channels_changed = Signal(list, name="ain_channels_changed") + ain_buffer_size_changed = Signal(int, name="ain_buffer_size_changed") + analog_in_bits_changed = Signal(int, name="analog_in_bits_changed") + analog_in_buffer_size_changed = Signal(int, name="analog_in_buffer_size_changed") + analog_in_channel_range_changed = Signal(tuple, name="analog_in_channel_range_changed") + analog_in_offset_changed = Signal(tuple, name="analog_in_offset_changed") + + open_device_finished = Signal(int, name="open_device_finished") + close_device_finished = Signal(name="close_device_finished") - 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) + device_state_changed = Signal(AD2Constants.DeviceState, name="device_state_changed") + capture_process_state_changed = Signal(AD2Constants.CapturingState, name="capture_process_state_changed") - self.signals = AD2CaptDeviceSignals() + def __init__(self, ad2capt_model: AD2CaptDeviceModel, start_capture_flag: Value): + super().__init__( + internal_log=True, + internal_log_level=logging.DEBUG) + + self.model = ad2capt_model + + self.pref = "AD2CaptDev" self.thread_manager = QThreadPool() self.kill_thread = False - # self.thread_manager.setMaxThreadCount(3) - # self.thread_manager.se - # self.thread_manager.setThreadPriority(QThread.HighestPriority) self.lock = Lock() - #self.proc = None self.stream_data_queue = Queue() self.capture_data_queue = Queue() - #self.state_queue = Queue() 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 - - self.status_dqueue = deque(maxlen=int(1)) - self.unconsumed_capture_data = 0 + self.streaming_dqueue: deque = None # a dqueue, initialize later + + self.register_child_process( + MPCaptDevice, + self.stream_data_queue, + self.capture_data_queue, + self.start_capture_flag, + self.kill_capture_flag + ) + self.connect_signals() + self._connect_config_signals() + + def connect_signals(self): + self.dwf_version_changed.connect(self._on_dwf_version_changed) + self.discovered_devices_changed.connect(self.on_discovered_devices_changed) + + self.selected_device_index_changed.connect(self.on_selected_device_index_changed) + + self.device_connected_changed.connect( + lambda x: type(self.model.device_information).device_connected.fset(self.model.device_information, x)) + self.device_name_changed.connect( + lambda x: type(self.model.device_information).device_name.fset(self.model.device_information, x)) + self.device_serial_number_changed.connect( + lambda x: type(self.model.device_information).device_serial_number.fset(self.model.device_information, x)) + + self.ain_channels_changed.connect( + lambda x: type(self.model.analog_in).ain_channels.fset(self.model.analog_in, x)) + self.ain_buffer_size_changed.connect( + lambda x: type(self.model.analog_in).ain_buffer_size.fset(self.model.analog_in, x)) + self.analog_in_bits_changed.connect( + lambda x: type(self.model.analog_in).ain_bits.fset(self.model.analog_in, x)) + self.analog_in_buffer_size_changed.connect( + lambda x: type(self.model.analog_in).ain_buffer_size.fset(self.model.analog_in, x)) + self.analog_in_channel_range_changed.connect( + lambda x: type(self.model.analog_in).ai.fset(self.model.analog_in, x)) + self.analog_in_offset_changed.connect( + lambda x: type(self.model.analog_in).ain_offset.fset(self.model.analog_in, x)) + + 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.open_device_finished.connect(self.on_open_device_finished) + + def _connect_config_signals(self): + self.model.ad2captdev_config.streaming_history.connect(self._on_streaming_history_changed) + # ================================================================================================================== + # Device control + # ================================================================================================================== + @cmp.CProcessControl.register_function(open_device_finished) + def open_device(self, device_index): + """ + Opens the device with the given id. + :param device_id: + :return: + """ - self.mpcaptdevicecontrol = MPCaptDeviceControl(self.model, - self.stream_data_queue, - self.capture_data_queue, - self.start_capture_flag, - self.kill_capture_flag, - enable_internal_logging=False) + def on_open_device_finished(self, device_handle: int): + self.logger.info(f"Opening device finished with handle {device_handle}") - #self.mpcaptdevicecontrol.discover_connected_devices_finished.connect( - # lambda x: type(self.model).connected_devices.fset(self.model, x)) + def close_device(self): + pass + # self.close_device() + + @cmp.CProcessControl.register_function(capture_process_state_changed) + def start_capturing_process(self, sample_rate: float, ain_channel: int): + """ + Starts the capturing process. + :param sample_rate: + :param ain_channel: + :return: + """ + self.streaming_dqueue = deque(maxlen=self.model.capturing_information.streaming_deque_length) + self.thread_manager.start(self.qt_consume_data) + self.thread_manager.start(self.qt_stream_data) + def _on_streaming_history_changed(self, history: float): + self.streaming_dqueue = deque(maxlen=self.model.capturing_information.streaming_deque_length) - #self.mpcaptdevicecontrol.connected_devices() + # ================================================================================================================== + # DWF Version + # ================================================================================================================== + def _on_dwf_version_changed(self, version): + self.model.dwf_version = version - 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.device_information.device_index) - self.start_device_process() - return True + # ================================================================================================================== + # Discover connected devices + # ================================================================================================================== + @cmp.CProcessControl.register_function(discovered_devices_changed) + def discover_connected_devices(self): + """ + Discover connected devices and update the model. + :return: + """ - def close_device(self): - self.mpcaptdevicecontrol.close_device() + def on_discovered_devices_changed(self, devices: list): + self.model.device_information.connected_devices = devices + # ================================================================================================================== + # Selected device index + # ================================================================================================================== + @cmp.CProcessControl.register_function(discovered_devices_changed) + def selected_device_index(self, index): + """ + Sets the selected device index. + :param index: The index of the device. + """ - def discover_connected_devices(self): - self.mpcaptdevicecontrol.connected_devices() + def on_selected_device_index_changed(self, index): + self.model.device_information.selected_device_index = index @abstractmethod def update_device_information(self): @@ -118,14 +204,14 @@ class BaseAD2CaptDevice(QObject): def _init_device_parameters(self): pass - #sample_rate = int(self.model.ad2captdev_config.get_sample_rate()) - #total_samples = int(self.model.ad2captdev_config.get_total_samples()) - #channel = 0 # TODO Read channel from input - - #self.model.sample_rate = int(sample_rate) - #self.model.n_samples = int(total_samples) - #self.model.selected_ain_channel = int(channel) - #self.logger.info(f"AD2 device initialized {self.model.selected_ain_channel} with " + # sample_rate = int(self.model.ad2captdev_config.get_sample_rate()) + # total_samples = int(self.model.ad2captdev_config.get_total_samples()) + # channel = 0 # TODO Read channel from input + + # self.model.sample_rate = int(sample_rate) + # self.model.n_samples = int(total_samples) + # self.model.selected_ain_channel = int(channel) + # self.logger.info(f"AD2 device initialized {self.model.selected_ain_channel} with " # f"acquisition rate {self.model.sample_rate} Hz and " # f"samples {self.model.n_samples}") @@ -136,70 +222,67 @@ class BaseAD2CaptDevice(QObject): self.model.recorded_samples = [] self.model.recorded_sample_stream = [] - def start_capture(self, clear=True): - print(f"Start capture. Clear {clear}") - self.start_capture_flag.value = 1 - if clear: - self.model.recorded_samples = [] - self.model.recorded_sample_stream = [] - self.model.start_recording = True - self.model.stop_recording = False - self.model.device_capturing_state = AD2Constants.CapturingState.RUNNING() + # def start_capture(self, clear=True): + # print(f"Start capture. Clear {clear}") + # self.start_capture_flag.value = 1 + # if clear: + # self.model.recorded_samples = [] + # self.model.recorded_sample_stream = [] + # self.model.start_recording = True + # self.model.stop_recording = False + # self.model.device_capturing_state = AD2Constants.CapturingState.RUNNING() def stop_capture(self): - print("Stop capture") self.start_capture_flag.value = 0 - self.model.start_recording = False - if self.model.reset_recording: - self.model.device_capturing_state = AD2Constants.CapturingState.STOPPED() - self.model.stop_recording = True - else: - self.model.device_capturing_state = AD2Constants.CapturingState.PAUSED() + def start_capture(self, clear=True): + self.start_capture_flag.value = 1 - def capture(self, start, clear=True): - if start: - self.start_capture(clear) + def reset_capture(self): + self.logger.info(f"[{self.pref} Task] Resetting capture...") + if self.model.capturing_information.device_capturing_state == AD2Constants.CapturingState.RUNNING(): + self.stop_capture() + self.model.capturing_information.recorded_samples = [] + self.start_capture() else: self.stop_capture() - - def reset_capture(self): - self.logger.info("Resetting captured samples for new measurement.") - self.model.recorded_samples = [] + self.model.capturing_information.recorded_samples = [] self.model.measurement_time = 0 - self.model.capturing_finished = False + + # ================================================================================================================== def start_device_process(self): self.logger.info(f"[{self.pref} Task] Starting capturing process...") - #self.logger.debug(f"Dataqueue maxlen={int(self.model.duration_streaming_history * self.model.sample_rate)}") - self.streaming_data_dqueue = deque(maxlen=int(self.model.duration_streaming_history * self.model.sample_rate)) - #print(self.model.duration_streaming_history * self.model.sample_rate) - self.stream_data_queue.maxsize = int(self.model.duration_streaming_history * self.model.sample_rate) - #self.proc = Process(target=mp_capture, + # self.logger.debug(f"Dataqueue maxlen={int(self.model.duration_streaming_history * self.model.sample_rate)}") + + # self.proc = Process(target=mp_capture, # args=( # self.stream_data_queue, self.capture_data_queue, self.state_queue, # self.start_capture_flag, self.end_process_flag, # device_id, self.model.selected_ain_channel, self.model.sample_rate) # ) - #self.proc.start() + # self.proc.start() # self.thread_manager.moveToThread(()) - self.thread_manager.start(self.qt_consume_data) - self.thread_manager.start(self.qt_stream_data) - #self.thread_manager.start(self.qt_get_state) - + + # self.thread_manager.start(self.qt_get_state) + def qt_consume_data(self): while True: + t = time.time() 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) + capture_data = self.capture_data_queue.get(block=True) + if isinstance(capture_data, ndarray): + # print(f"Stream data queue size {len(stream_data)}") + for d in capture_data: + self.model.capturing_information.recorded_samples.append(d) + t_end = time.time() + # print(f"Time to get data {t_end-t}") except Exception as e: - self.logger.info(f"Error while consuming data {e}") - self.logger.info("Capture Data consume thread ended") + 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): while True: @@ -207,11 +290,11 @@ class BaseAD2CaptDevice(QObject): 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()}") + # print(f"Stream data queue size {len(stream_data)}") for d in stream_data: - self.streaming_data_dqueue.append(d) + self.streaming_dqueue.append(d) t_end = time.time() - #print(f"Time to get data {t_end-t}") + # 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}") @@ -221,14 +304,11 @@ class BaseAD2CaptDevice(QObject): while not self.kill_thread and not bool(self.end_process_flag.value): while self.state_queue.qsize() > 0: self._set_ad2state_from_process(self.state_queue.get()) - #time.sleep(0.1) + # time.sleep(0.1) self.logger.info("Status data consume thread ended") # ================================================================================================================== # Destructor # ================================================================================================================== def exit(self): - self.mpcaptdevicecontrol.safe_exit() - - - \ No newline at end of file + self.mpcaptdevicecontrol.safe_exit() diff --git a/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDevice.py b/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDevice.py index b2c5af7..39e1c30 100644 --- a/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDevice.py +++ b/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDevice.py @@ -7,12 +7,14 @@ from multiprocessing import Queue, Value import cmp import numpy as np +from cmp.CProperty import CProperty from constants.dwfconstants import enumfilterType, enumfilterDemo, enumfilterUSB, acqmodeRecord, DwfStateConfig, \ DwfStatePrefill, DwfStateArmed +from CaptDeviceControl.model.AD2Constants import AD2Constants -class MPCaptDevice(cmp.CProcess): +class MPCaptDevice(cmp.CProcess, ): @staticmethod def timeit(func): def wrapper(self, *args, **kwargs): @@ -20,17 +22,22 @@ class MPCaptDevice(cmp.CProcess): 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 res # time_stop - time_start return wrapper - def __init__(self, state_queue, cmd_queue, + def __init__(self, state_queue: Queue, cmd_queue: Queue, streaming_data_queue: Queue, capture_data_queue: Queue, start_capture_flag: Value, kill_capture_flag: Value, - enable_internal_logging): - super().__init__(state_queue, cmd_queue, enable_internal_logging=enable_internal_logging) - + kill_flag: Value, + internal_log, internal_log_level): + super().__init__(state_queue, cmd_queue, + kill_flag=kill_flag, + internal_log=internal_log, + internal_log_level=internal_log_level) + + self._device_capturing = False self._c_samples = None self._c_corrupted = None self._c_lost = None @@ -42,42 +49,110 @@ class MPCaptDevice(cmp.CProcess): self.stream_data_queue = streaming_data_queue self.capture_data_queue = capture_data_queue - self.logger, self.ha = None, None - self.dwf = None self.hdwf = None # Capture data counters + self._selected_device_index = 0 self._dwf_version = None self._device_serial_number: str = "" self._device_name: str = "" self._connected = False + self._connected_devices = [] + 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()})") + # ================================================================================================================== + # Getter and Setter + # ================================================================================================================== + + @CProperty + def dwf_version(self): + return self._dwf_version + + @dwf_version.setter(emit_to='dwf_version_changed') + def dwf_version(self, value): + self._dwf_version = value + + @CProperty + def connected_devices(self) -> list: + return self._connected_devices + + @connected_devices.setter(emit_to='connected_devices_changed') + def connected_devices(self, value: list): + self._connected_devices = value + + @cmp.CProcess.register_signal(signal_name='selected_device_index_changed') + def selected_device_index(self, device_index: int): + # self.logger.debug(f"*** Selected device index {device_index}.") + self._selected_device_index = device_index + # If the selected device index change, we need to update the device information + self.device_name = self.get_device_name(self._selected_device_index) + self.device_serial_number = self.get_device_serial_number(self._selected_device_index) + + self.ain_channels = self.get_ain_channels(self._selected_device_index) + self.ain_buffer_size = self.get_ain_buffer_size(self._selected_device_index) + return self._selected_device_index + + @CProperty + def device_name(self): + return self._device_name + + @device_name.setter(emit_to='device_name_changed') + def device_name(self, value): + self._device_name = value + + @CProperty + def device_serial_number(self): + return self._device_serial_number + @device_serial_number.setter(emit_to='device_serial_number_changed') + def device_serial_number(self, value): + self._device_serial_number = value + + @CProperty + def connected(self): + return self._connected + + @connected.setter(emit_to='device_connected_changed') + def connected(self, value): + self._connected = value + + @CProperty + def ain_channels(self, device_id): + return self._ain_channels + + @ain_channels.setter(emit_to='ain_channels_changed') + def ain_channels(self, value): + self.logger.info("Setting ain channels.") + self._ain_channels = value + + # ================================================================================================================== + # + # ================================================================================================================== + def postrun_init(self): 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._connected = self.connected() + self.dwf_version = self.get_dwf_version() + #@CProperty + #def device_capturing(self, capturing: bool): + # return self._device_capturing - @cmp.CProcess.register_for_signal('_changed') - def device_capturing(self, capturing: bool): - self.logger.info(f"Device capturing: {capturing}") - return capturing + #@device_capturing.setter(emit_to='device_capturing_changed') + #def device_capturing(self, capturing: bool): + # self._device_capturing = capturing - @cmp.CProcess.register_for_signal('_changed') - def dwf_version(self): + def get_dwf_version(self) -> str: self.logger.debug(f"Getting DWF version information...") version = create_string_buffer(16) self.dwf.FDwfGetVersion(version) @@ -86,8 +161,9 @@ class MPCaptDevice(cmp.CProcess): # ================================================================================================================== # Device Enumeration without connecting to the device # ================================================================================================================== - @cmp.CProcess.register_for_signal('_changed') - def connected_devices(self): + @cmp.CProcess.register_signal() + def discover_connected_devices(self): + self.logger.info(f"Discovering connected devices...") # enumerate connected devices connected_devices = [] @@ -102,24 +178,69 @@ class MPCaptDevice(cmp.CProcess): # 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 + self.logger.debug(f"Found {cDevice.value} {type} devices.") for iDevice in range(0, cDevice.value): connected_devices.append({ 'type': type, 'device_id': int(iDevice), - 'device_name': self.device_name(iDevice), - 'serial_number': self.device_serial_number(iDevice) + 'device_name': self.get_device_name(iDevice), + 'serial_number': self.get_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: + # ================================================================================================================== + # Functions for opening and closing the device + # ================================================================================================================== + @cmp.CProcess.register_signal() + def open_device(self, device_index) -> int: + """ + Opens the device and returns the handle. + :return: Device handle. + """ + self.selected_device_index(device_index) + if self.hdwf is not None or not isinstance(self.hdwf, c_int): + self.hdwf = c_int() + + self.logger.debug(f"Opening device {self._selected_device_index}...") + self._dwf_version = self.get_dwf_version() + + # Opens the device specified by idxDevice. The device handle is returned in hdwf. If idxDevice is -1, the + # first available device is opened. + self.dwf.FDwfDeviceOpen(c_int(self._selected_device_index), byref(self.hdwf)) + + self._device_name = self.get_device_name(self._selected_device_index) + self._device_serial_number = self.get_device_serial_number(self._selected_device_index) + + if self.hdwf.value == 0: + szerr = create_string_buffer(512) + self.dwf.FDwfGetLastErrorMsg(szerr) + 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: {err}") + else: + self.logger.info(f"Device opened: {self._device_name} " + f"({self._device_serial_number})") + self.connected = self.device_connected() + self.device_state(AD2Constants.DeviceState.ACQ_NOT_STARTED()) + return int(self.hdwf.value) + + def close_device(self): + # 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.") + + # ================================================================================================================== + # Device Information + # ================================================================================================================== + def get_ain_channels(self, device_id) -> list: cInfo = c_int() self.dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(1), byref(cInfo)) ain_channels = cInfo.value @@ -132,14 +253,12 @@ class MPCaptDevice(cmp.CProcess): 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: + def get_ain_buffer_size(self, device_id) -> int: cInfo = c_int() self.dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(7), byref(cInfo)) return cInfo.value - @cmp.CProcess.register_for_signal('_changed') - def device_name(self, device_index: int) -> str: + def get_device_name(self, device_index: int) -> str: try: devicename = create_string_buffer(64) self.dwf.FDwfEnumDeviceName(c_int(device_index), devicename) @@ -148,8 +267,7 @@ class MPCaptDevice(cmp.CProcess): 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: + def get_device_serial_number(self, device_index: int) -> str: try: serialnum = create_string_buffer(16) self.dwf.FDwfEnumSN(c_int(device_index), serialnum) @@ -161,8 +279,7 @@ class MPCaptDevice(cmp.CProcess): # ================================================================================================================== # Device connection status # ================================================================================================================== - @cmp.CProcess.register_for_signal('_changed') - def connected(self) -> bool: + def device_connected(self) -> bool: if self.hdwf is None or self.hdwf.value == 0: szerr = create_string_buffer(512) self.dwf.FDwfGetLastErrorMsg(szerr) @@ -195,7 +312,7 @@ class MPCaptDevice(cmp.CProcess): 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') + @cmp.CProcess.register_signal('_changed') def analog_in_bits(self) -> int: """ Retrieves the number bits used by the AnalogIn ADC. The oscilloscope channel settings are identical @@ -212,7 +329,7 @@ class MPCaptDevice(cmp.CProcess): 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') + @cmp.CProcess.register_signal('_changed') def analog_in_buffer_size(self) -> tuple: """ Returns the minimum and maximum allowable buffer sizes for the instrument. The oscilloscope @@ -230,7 +347,7 @@ class MPCaptDevice(cmp.CProcess): 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') + @cmp.CProcess.register_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. @@ -251,7 +368,7 @@ class MPCaptDevice(cmp.CProcess): 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') + @cmp.CProcess.register_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(): @@ -265,60 +382,21 @@ class MPCaptDevice(cmp.CProcess): 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() - - # Opens the device specified by idxDevice. The device handle is returned in hdwf. If idxDevice is -1, the - # first available device is opened. - 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) - 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: {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.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.") - # ================================================================================================================== # Function for setting up the acquisition # ================================================================================================================== def setup_acquisition(self, sample_rate: float, ain_channel: int): - #self.dwf.FDwfAnalogInStatus(self.hdwf, c_int(1), + # self.dwf.FDwfAnalogInStatus(self.hdwf, c_int(1), # 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)) self.dwf.FDwfAnalogInAcquisitionModeSet(self.hdwf, acqmodeRecord) self.dwf.FDwfAnalogInFrequencySet(self.hdwf, c_double(sample_rate)) - self.dwf.FDwfAnalogInRecordLengthSet(self.hdwf, c_double(1)) # -1 infinite record length + self.dwf.FDwfAnalogInRecordLengthSet(self.hdwf, c_double(0)) # -1 infinite record length self.dwf.FDwfAnalogInConfigure(self.hdwf, c_int(1), c_int(0)) # Variable to receive the acquisition state - #self.dwf.FDwfAnalogInStatus(self.hdwf, c_int(1), byref(self._ain_device_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.") # wait at least 2 seconds for the offset to stabilize time.sleep(2) @@ -374,160 +452,122 @@ class MPCaptDevice(cmp.CProcess): 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): + @cmp.CProcess.register_signal(signal_name='device_state_changed') + def device_state(self, state): + return state + + @cmp.CProcess.register_signal(signal_name='capture_process_state_changed') + def capture_process_state(self, state): + return state + + # ================================================================================================================== + # + # ================================================================================================================== + @cmp.CProcess.register_signal() + def start_capturing_process(self, sample_rate: float, ain_channel: int): """ Captures data from the device and puts it into a queue. :param ain_channel: :param sample_rate: :return: None """ - self.close_device() - - if sys.platform.startswith("win"): - dwf = cdll.dwf - elif sys.platform.startswith("darwin"): - dwf = cdll.LoadLibrary("/Library/Frameworks/dwf.framework/dwf") - else: - dwf = cdll.LoadLibrary("libdwf.so") - - cDevice = c_int() - hdwf = c_int() - filter, type = (c_int32(enumfilterType.value | enumfilterDemo.value | enumfilterUSB.value), 'USB') - dwf.FDwfEnum(filter, byref(cDevice)) - dwf.FDwfDeviceOpen(c_int(3), byref(hdwf)) + self.logger.info(f"Starting capture on channel {ain_channel} with rate {sample_rate} Hz.") + hdwf = self.hdwf + self.device_state(AD2Constants.DeviceState.DEV_CAPT_SETUP()) ain_channel = int(0) - self.logger.debug("Generating AM sine wave...") - dwf.FDwfAnalogOutNodeEnableSet(hdwf, c_int(ain_channel), c_int(0), c_int(1)) # carrier - dwf.FDwfAnalogOutNodeFunctionSet(hdwf, c_int(ain_channel), c_int(0), c_int(1)) # sine - dwf.FDwfAnalogOutNodeFrequencySet(hdwf, c_int(ain_channel), c_int(0), c_double(1)) - dwf.FDwfAnalogOutNodeAmplitudeSet(hdwf, c_int(ain_channel), c_int(0), c_double(1)) - dwf.FDwfAnalogOutConfigure(hdwf, c_int(ain_channel), c_int(1)) - time.sleep(1) - self.logger.debug(f"Sine wave on output channel {ain_channel} configured.") + self.setup_sine_wave(ain_channel) - # FDwfAnalogInStatus(HDWF hdwf, BOOL fReadData, DwfState* psts) - self.logger.info(f"[Task] Setup for acquisition on channel {ain_channel} with rate {sample_rate} Hz.") - dwf.FDwfAnalogInChannelEnableSet(hdwf, c_int(ain_channel), c_int(1)) - dwf.FDwfAnalogInChannelRangeSet(hdwf, c_int(ain_channel), c_double(5)) - dwf.FDwfAnalogInAcquisitionModeSet(hdwf, acqmodeRecord) - dwf.FDwfAnalogInFrequencySet(hdwf, c_double(sample_rate)) - dwf.FDwfAnalogInRecordLengthSet(hdwf, c_double(9999)) # -1 infinite record length + self.setup_acquisition(sample_rate, ain_channel) # 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.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.") + # self.logger.info(f"[Task] Setup for acquisition done.") # Creates a Sin Wave on the Analog Out Channel 0 - self.logger.info("Configuring acquisition. Starting oscilloscope.") + # self.logger.info("Configuring acquisition. Starting oscilloscope.") # Configures the instrument and start or stop the acquisition. To reset the Auto trigger timeout, set self.dwf.FDwfAnalogInConfigure(hdwf, c_int(0), c_int(1)) - self.logger.info("Device configured. Starting acquisition.") + # self.logger.info("Device configured. Starting acquisition.") time_capture_started = 0 capture_samples = 0 capture_started = False capture_ended = False - - cAvailable = c_int() cLost = c_int() cCorrupted = c_int() cSamples = 0 - sts = c_byte() - + sts = c_byte() try: - #self.dwf.FDwfAnalogOutReset(self.hdwf, c_int(0)) - dwf.FDwfAnalogInStatus(hdwf, c_int(1), byref(sts)) - while self.kill_capture_flag.value == int(False): - self._c_samples = 0 + # self.dwf.FDwfAnalogOutReset(self.hdwf, c_int(0)) + ##plt.ion() + ##hl, = plt.plot([], []) + self.device_state(AD2Constants.DeviceState.DEV_CAPT_STREAMING()) + while self.kill_capture_flag.value == int(False): + self.dwf.FDwfAnalogInStatus(hdwf, c_int(1), byref(sts)) + # 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 - - if self._c_samples == 0 and ( - sts == DwfStateConfig or - sts == DwfStatePrefill or - sts == DwfStateArmed): - # self.logger.info("Device in idle state. Waiting for acquisition to start.") + if sts == DwfStateConfig or sts == DwfStatePrefill or sts == DwfStateArmed: + # self.device_state(AD2Constants.DeviceState.ACQ_NOT_STARTED()) continue # Acquisition not yet started. - dwf.FDwfAnalogInStatusRecord(hdwf, byref(cAvailable), byref(cLost), byref(cCorrupted)) + self.dwf.FDwfAnalogInStatusRecord(hdwf, byref(cAvailable), byref(cLost), byref(cCorrupted)) # self.dwf.FDwfAnalogInStatusSamplesValid(self.hdwf, byref(self.cValid)) if cAvailable.value == 0: - #print("Nothing available") + # self.device_state(AD2Constants.DeviceState.NO_SAMPLES_AVAILABLE()) continue - 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() - - # 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 * cAvailable.value)() # Get the data from the device and store it in rgd_samples - dwf.FDwfAnalogInStatusData(hdwf, - c_int(ain_channel), - byref(rgd_samples), - cAvailable) + self.dwf.FDwfAnalogInStatusData(hdwf, c_int(0), byref(rgd_samples), cAvailable) - #self._c_samples += self._c_available.value + arr = np.array(rgd_samples, copy=True) iteration_time = time.time() - time_start - print(f"Got data from device: len {cAvailable.value}," - f"time {iteration_time} seconds.") - - # 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 - if len(rgd_samples) > 0: - #arr = - print(f"I send {len(np.array(rgd_samples))} samples to the queue.") - self.stream_data_queue.put(np.array(rgd_samples)) - #np.delete(arr - else: - print(f"rgd_samples is empty.") - - # 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 + self.stream_data_queue.put(arr) + + # #np.delete(arr + # else: + # print(f"rgd_samples is empty.") + + if self.start_capture_flag.value == int(True) : + if not capture_started: + self.capture_process_state(AD2Constants.CapturingState.RUNNING()) + self.logger.info( + "**************************** START command received. Acquisition started.") + time_capture_started = time.time() + capture_started = True + capture_ended = False + # capture_samples = capture_samples + len(arr) + self.capture_data_queue.put(arr) + elif self.start_capture_flag.value == int(False) and capture_started: + capture_started = False + capture_ended = True + self.logger.info( + "**************************** STOP command received. Acquisition stopped.") + self.capture_process_state(AD2Constants.CapturingState.STOPPED()) + time_capture_stopped = time.time() + time_captured = time_capture_stopped - time_capture_started + 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 @@ -554,3 +594,22 @@ class MPCaptDevice(cmp.CProcess): 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.") + + +if __name__ == "__main__": + state_queue = Queue() + cmd_queue = Queue() + + streaming_data_queue = Queue() + capture_data_queue = Queue() + start_capture_flag = Value('i', 0) + kill_capture_flag = Value('i', 0) + + mpcapt = MPCaptDevice(state_queue, cmd_queue, + streaming_data_queue, + capture_data_queue, + start_capture_flag, + kill_capture_flag, False + ) + mpcapt.logger, _ = mpcapt.create_new_logger("MPCaptDevice") + mpcapt.start_capture(1000, 0) diff --git a/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDeviceControl.py b/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDeviceControl.py index 62ff7ea..537c1c1 100644 --- a/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDeviceControl.py +++ b/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDeviceControl.py @@ -1,31 +1,34 @@ +import logging import os import cmp from PySide6.QtCore import Signal -from CaptDeviceControl.controller.mp_AD2Capture.MPCaptDevice import MPCaptDevice + from CaptDeviceControl.model.AD2CaptDeviceModel import AD2CaptDeviceSignals, AD2CaptDeviceModel from CaptDeviceControl.model.AD2Constants import AD2Constants - class MPCaptDeviceControl(cmp.CProcessControl): - connected_devices_changed = Signal(list) - ain_channels_changed = Signal(list) - ain_buffer_size_changed = Signal(int) - dwf_version_changed = Signal(str) - device_name_changed = Signal(str) - device_serial_number_changed = Signal(str) - connected_changed = Signal(bool) - 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) + connected_devices_changed = Signal(list, name="connected_devices_changed") + + + + + device_capturing_state_changed = Signal(AD2Constants.CapturingState, name="device_capturing_state_changed") + + + open_device_finished = Signal(name="open_device_finished") + close_device_finished = Signal(name="close_device_finished") + + analog_in_bits_changed = Signal(int, name="analog_in_bits_changed") + analog_in_buffer_size_changed = Signal(int, name="analog_in_buffer_size_changed") + analog_in_channel_range_changed = Signal(tuple, name="analog_in_channel_range_changed") + analog_in_offset_changed = Signal(tuple, name="analog_in_offset_changed") + + device_capturing_changed = Signal(bool, name="device_capturing_changed") def __init__(self, model: AD2CaptDeviceModel, @@ -34,16 +37,19 @@ class MPCaptDeviceControl(cmp.CProcessControl): start_capture_flag, kill_capture_flag, parent=None, - enable_internal_logging=False): - super().__init__(parent, enable_internal_logging=enable_internal_logging) + internal_log=True, + internal_log_level=logging.DEBUG): + super().__init__(parent, + internal_log=internal_log, + internal_log_level=internal_log_level) self.model = model self.register_child_process( - MPCaptDevice(self.state_queue, self.cmd_queue, + MPCaptDevice, streaming_data_queue, capturing_data_queue, start_capture_flag, - kill_capture_flag, - enable_internal_logging=enable_internal_logging)) + kill_capture_flag + ) self.logger, self.logger_handler = self.create_new_logger(f"{self.__class__.__name__}({os.getpid()})") @@ -57,14 +63,13 @@ class MPCaptDeviceControl(cmp.CProcessControl): 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)) + self.device_capturing_state_changed.connect( + lambda x: type(model).device_capturing_state.fset(model, x)) + self.device_state_changed.connect( + lambda x: type(model).device_state.fset(model, 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.device_capturing_changed.connect(self.on_capturing_state_changed) @@ -74,15 +79,20 @@ class MPCaptDeviceControl(cmp.CProcessControl): else: self.model.device_capturing_state = AD2Constants.CapturingState.STOPPED() - @cmp.CProcessControl.register_function() + @cmp.CProcessControl.register_function(connected_devices_changed) def connected_devices(self): self.logger.info("Discovering connected devices.") + # Setter for the selected device index @cmp.CProcessControl.register_function() - def ain_channels(self, device_id): - self.logger.info(f"Reading available analog input channels for device {device_id}.") + def selected_device_index(self, device_index: int): + self.logger.info(f"Selected device index {device_index}.") - @cmp.CProcessControl.register_function() + #@cmp.CProcessControl.register_function(ain_channels_changed) + #def ain_channels(self, device_id): + # self.logger.info(f"Reading available analog input channels for device {device_id}.") + + @cmp.CProcessControl.register_function(open_device_finished) def open_device(self, device_index): self.logger.info(f"Opening device {device_index}.") diff --git a/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDeviceSceleton.py b/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDeviceSceleton.py new file mode 100644 index 0000000..a456c7e --- /dev/null +++ b/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDeviceSceleton.py @@ -0,0 +1,7 @@ +class MPCaptDeviceSceleton(): + + def discover_connected_devices(self): + """ + This function is called to discover the connected devices. + :return: + """ \ No newline at end of file diff --git a/src/CaptDeviceControl/controller/mp_AD2Capture/__init__.py b/src/CaptDeviceControl/controller/mp_AD2Capture/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/CaptDeviceControl/model/AD2CaptDeviceModel.py b/src/CaptDeviceControl/model/AD2CaptDeviceModel.py index 5a9c6bc..7223d03 100644 --- a/src/CaptDeviceControl/model/AD2CaptDeviceModel.py +++ b/src/CaptDeviceControl/model/AD2CaptDeviceModel.py @@ -1,12 +1,12 @@ -from ctypes import c_int, Array, c_byte +from ctypes import c_int, Array from PySide6.QtCore import QObject, Signal from CaptDeviceControl.model.AD2Constants import AD2Constants from CaptDeviceControl.CaptDeviceConfig import CaptDeviceConfig as Config -from CaptDeviceControl.model.AD2CaptDeviceAnalogInModel import AD2CaptDeviceAnalogInModel -from CaptDeviceControl.model.AD2CaptDeviceInformationModel import AD2CaptDeviceInformationSignals, \ - AD2CaptDeviceInformationModel +from model.submodels.AD2CaptDeviceAnalogInModel import AD2CaptDeviceAnalogInModel +from model.submodels.AD2CaptDeviceCapturingModel import AD2CaptDeviceCapturingModel +from model.submodels.AD2CaptDeviceInformationModel import AD2CaptDeviceInformationModel # from MeasurementData.Properties.AD2CaptDeviceProperties import AD2CaptDeviceProperties @@ -16,6 +16,7 @@ class AD2CaptDeviceSignals(QObject): def __init__(self, parent=None): super().__init__(parent) + ad2captdev_config_changed = Signal(Config) # WaveForms Runtime (DWF) Information @@ -47,6 +48,8 @@ class AD2CaptDeviceSignals(QObject): reset_recording_changed = Signal(bool) capturing_finished_changed = Signal(bool) + device_state_changed = Signal(AD2Constants.DeviceState) + # Multiprocessing Information pid_changed = Signal(int) @@ -85,42 +88,24 @@ class AD2CaptDeviceModel: def __init__(self, ad2captdev_config: Config): self.signals = AD2CaptDeviceSignals() self.ad2captdev_config = ad2captdev_config + self.ad2captdev_config.autosave(enable=True, path="./") # WaveForms Runtime (DWF) Information self._dwf_version: str = "Unknown" + # Multiprocessing Information + self._pid: int = 0 self.device_information = AD2CaptDeviceInformationModel() self.analog_in = AD2CaptDeviceAnalogInModel(self.ad2captdev_config) - + self.capturing_information = AD2CaptDeviceCapturingModel(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._duration_streaming_history: float = 0 # Analog Out Information self.aout_channels: list = [] - # Acquired Signal Information - self._recorded_samples: list = [] - self._recording_time: float = 0 - self._samples_captured: int = 0 - self._samples_lost: int = 0 - self._samples_corrupted: int = 0 - self._capturing_finished: bool = False - # Actually for the worker, these are the samples that have not been consumed yet by the UI thread. - 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 - self._stop_recording = True - self._reset_recording = True - # Multiprocessing Information - self._pid: int = 0 # ============================================================================================================== # Delete later @@ -167,37 +152,8 @@ class AD2CaptDeviceModel: self._dwf_version = value self.signals.dwf_version_changed.emit(self.dwf_version) - # ================================================================================================================== - # Acquisition Settings - # ================================================================================================================== - @property - def sample_rate(self): - return self._sample_rate - - @sample_rate.setter - def sample_rate(self, value): - self._sample_rate = value - self.ad2captdev_config.sample_rate.set(self._sample_rate) - self.signals.sample_rate_changed.emit(self._sample_rate) - - @property - def streaming_rate(self): - return self._sample_rate - - @streaming_rate.setter - def streaming_rate(self, value): - self._streaming_rate = value - self.ad2captdev_config.streaming_rate.set(self._streaming_rate) - self.signals.streaming_rate_changed.emit(self._streaming_rate) - @property - def duration_streaming_history(self) -> float: - return self._duration_streaming_history - @duration_streaming_history.setter - def duration_streaming_history(self, value: float): - self._duration_streaming_history = value - self.signals.duration_streaming_history_changed.emit(self.duration_streaming_history) # ================================================================================================================== # Analog Out Information @@ -211,104 +167,6 @@ class AD2CaptDeviceModel: self._aout_channels = value self.signals.aout_channels_changed.emit(self.aout_channels) - # ================================================================================================================== - # Acquired Signal Information - # ================================================================================================================== - @property - def recorded_samples(self) -> list: - return self._recorded_samples - - @recorded_samples.setter - 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.recorded_samples_changed.emit(self.recorded_samples) - - @property - def recording_time(self) -> float: - return self._recording_time - - @recording_time.setter - def recording_time(self, value: float): - self._recording_time = value - self.signals.recording_time_changed.emit(self.recording_time) - - @property - def samples_captured(self) -> int: - return self._samples_captured - - @samples_captured.setter - def samples_captured(self, value: int): - self._samples_captured = value - self.signals.samples_captured_changed.emit(self.samples_captured) - - @property - def samples_lost(self) -> int: - return self._samples_lost - - @samples_lost.setter - def samples_lost(self, value: int): - self._samples_lost = value - self.signals.samples_lost_changed.emit(self.samples_lost) - - @property - def samples_corrupted(self) -> int: - return self._samples_corrupted - - @samples_corrupted.setter - def samples_corrupted(self, value: int): - 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}") - self.signals.capturing_finished_changed.emit(self._capturing_finished) - - # ================================================================================================================== - # Recording Flags (starting, stopping and pausing) - # ================================================================================================================== - @property - def device_capturing_state(self) -> AD2Constants.CapturingState: - return self._device_capturing_state - - @device_capturing_state.setter - def device_capturing_state(self, value: int): - self._device_capturing_state = value - self.signals.device_capturing_state_changed.emit(self.device_capturing_state) - - @property - def start_recording(self) -> bool: - return self._start_recording - - @start_recording.setter - def start_recording(self, value: bool): - self._start_recording = value - self.signals.start_recording_changed.emit(self._start_recording) - - @property - def stop_recording(self) -> bool: - return self._stop_recording - - @stop_recording.setter - def stop_recording(self, value: bool): - self._stop_recording = value - self.signals.stop_recording_changed.emit(self.stop_recording) - - @property - def reset_recording(self): - return self._reset_recording - - @reset_recording.setter - def reset_recording(self, value): - self._reset_recording = value - self.signals.reset_recording_changed.emit(self._reset_recording) # ================================================================================================================== # Multiprocessing Flags @@ -322,23 +180,18 @@ class AD2CaptDeviceModel: self._pid = value self.signals.pid_changed.emit(self.pid) - @property - def unconsumed_stream_samples(self) -> int: - return self._unconsumed_stream_samples - @unconsumed_stream_samples.setter - def unconsumed_stream_samples(self, value: int): - self._unconsumed_stream_samples = value - self.signals.unconsumed_stream_samples_changed.emit(self.unconsumed_stream_samples) @property - def unconsumed_capture_samples(self) -> int: - return self._unconsumed_capture_samples + def device_state(self) -> AD2Constants.DeviceState: + return self._device_state + + @device_state.setter + def device_state(self, value: AD2Constants.DeviceState): + #print(f"Set device_state to {value}") + self._device_state = value + self.signals.device_state_changed.emit(self._device_state) - @unconsumed_capture_samples.setter - def unconsumed_capture_samples(self, value: int): - self._unconsumed_capture_samples = value - self.signals.unconsumed_capture_samples_changed.emit(self.unconsumed_capture_samples) # ================================================================================================================== # ================================================================================================================== diff --git a/src/CaptDeviceControl/model/AD2CaptDeviceProperties.py b/src/CaptDeviceControl/model/AD2CaptDeviceProperties.py index 251327b..09a54b8 100644 --- a/src/CaptDeviceControl/model/AD2CaptDeviceProperties.py +++ b/src/CaptDeviceControl/model/AD2CaptDeviceProperties.py @@ -7,6 +7,7 @@ class AD2CaptDeviceProperties(GenericProperties): measurement_time: float): super().__init__() # Laser properties + self._samples_lost: float = self.to_float(samples_lost) self._samples_currputed: float = self.to_float(samples_currputed) self._sample_rate: float = self.to_float(acquisition_rate) diff --git a/src/CaptDeviceControl/model/AD2CaptDeviceSignals.py b/src/CaptDeviceControl/model/AD2CaptDeviceSignals.py index 81f119f..fad77f2 100644 --- a/src/CaptDeviceControl/model/AD2CaptDeviceSignals.py +++ b/src/CaptDeviceControl/model/AD2CaptDeviceSignals.py @@ -19,6 +19,8 @@ class AD2CaptDeviceSignals(QObject): # WaveForms Runtime (DWF) Information dwf_version_changed = Signal(str) + # Multiprocessing Information + pid_changed = Signal(int) # Connected Device Information num_of_discovered_devices_changed = Signal(int) @@ -31,11 +33,6 @@ class AD2CaptDeviceSignals(QObject): 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) @@ -46,51 +43,7 @@ class AD2CaptDeviceSignals(QObject): # 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/model/AD2Constants.py b/src/CaptDeviceControl/model/AD2Constants.py index a534f0d..575d992 100644 --- a/src/CaptDeviceControl/model/AD2Constants.py +++ b/src/CaptDeviceControl/model/AD2Constants.py @@ -8,13 +8,45 @@ class AD2Constants: return 1 @staticmethod - def PAUSED(description: bool= False): + def PAUSED(description: bool = False): if description: return "Capturing paused" return 2 @staticmethod - def STOPPED(description: bool= False): + def STOPPED(description: bool = False): if description: return "Capturing stopped" return 3 + + class DeviceState(): + + @staticmethod + def ACQ_NOT_STARTED(description: bool = False): + if description: + return "Acquisition not started" + return 4 + + @staticmethod + def DEV_CAPT_SETUP(description: bool = False): + if description: + return "Device setting up" + return 5 + + @staticmethod + def DEV_CAPT_STREAMING(description: bool = False): + if description: + return "Device streaming" + return 6 + + @staticmethod + def NO_SAMPLES_AVAILABLE(description: bool = False): + if description: + return "No samples available" + return 7 + + @staticmethod + def SAMPLES_AVAILABLE(description: bool = False): + if description: + return "Samples streaming" + return 8 diff --git a/src/CaptDeviceControl/model/AD2CaptDeviceAnalogInModel.py b/src/CaptDeviceControl/model/submodels/AD2CaptDeviceAnalogInModel.py similarity index 100% rename from src/CaptDeviceControl/model/AD2CaptDeviceAnalogInModel.py rename to src/CaptDeviceControl/model/submodels/AD2CaptDeviceAnalogInModel.py diff --git a/src/CaptDeviceControl/model/submodels/AD2CaptDeviceCapturingModel.py b/src/CaptDeviceControl/model/submodels/AD2CaptDeviceCapturingModel.py new file mode 100644 index 0000000..0d32e92 --- /dev/null +++ b/src/CaptDeviceControl/model/submodels/AD2CaptDeviceCapturingModel.py @@ -0,0 +1,216 @@ +from ctypes import c_int, Array + +from PySide6.QtCore import QObject, Signal + +from CaptDeviceConfig import CaptDeviceConfig +from model.AD2Constants import AD2Constants + + +class AD2CaptDeviceCapturingSignals(QObject): + def __init__(self, parent=None): + super().__init__(parent) + + # Acquisition Settings + sample_rate_changed = Signal(int) + streaming_rate_changed = Signal(int) + selected_ain_channel_changed = Signal(int) + streaming_history_changed = Signal(int) + # 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) + +class AD2CaptDeviceCapturingModel: + def __init__(self, config: CaptDeviceConfig): + self.signals = AD2CaptDeviceCapturingSignals() + self.config = config + + # Acquired Signal Information + # The number of recorded samples + self._recorded_samples: list = [] + # The length of the recording + self._recording_time: float = 0 + + # Number of the captured, lost and corrupted samples + self._number_samples_captured: int = 0 + self._number_samples_lost: int = 0 + self._number_samples_corrupted: int = 0 + + # Flag if the capturing is finished + self._capturing_finished: bool = False + + # Actually for the worker, these are the samples that have not been consumed yet by the UI thread. + 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 + self._stop_recording = True + self._reset_recording = True + +# ================================================================================================================== + # Acquired Signal Information + # ================================================================================================================== + @property + def recorded_samples(self) -> list: + return self._recorded_samples + + @recorded_samples.setter + 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.recorded_samples_changed.emit(self.recorded_samples) + + @property + def recording_time(self) -> float: + return self._recording_time + + @recording_time.setter + def recording_time(self, value: float): + self._recording_time = value + self.signals.recording_time_changed.emit(self.recording_time) + + @property + def samples_captured(self) -> int: + return self._samples_captured + + @samples_captured.setter + def samples_captured(self, value: int): + self._samples_captured = value + self.signals.samples_captured_changed.emit(self.samples_captured) + + @property + def samples_lost(self) -> int: + return self._samples_lost + + @samples_lost.setter + def samples_lost(self, value: int): + self._samples_lost = value + self.signals.samples_lost_changed.emit(self.samples_lost) + + @property + def samples_corrupted(self) -> int: + return self._samples_corrupted + + @samples_corrupted.setter + def samples_corrupted(self, value: int): + 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}") + self.signals.capturing_finished_changed.emit(self._capturing_finished) + + # ================================================================================================================== + # Recording Flags (starting, stopping and pausing) + # ================================================================================================================== + @property + def device_capturing_state(self) -> AD2Constants.CapturingState: + return self._device_capturing_state + + @device_capturing_state.setter + def device_capturing_state(self, value: int): + #print(f"Set device_capturing_state to {value}") + self._device_capturing_state = value + self.signals.device_capturing_state_changed.emit(self.device_capturing_state) + + @property + def start_recording(self) -> bool: + return self._start_recording + + @start_recording.setter + def start_recording(self, value: bool): + self._start_recording = value + self.signals.start_recording_changed.emit(self._start_recording) + + @property + def stop_recording(self) -> bool: + return self._stop_recording + + @stop_recording.setter + def stop_recording(self, value: bool): + self._stop_recording = value + self.signals.stop_recording_changed.emit(self.stop_recording) + + @property + def reset_recording(self): + return self._reset_recording + + @reset_recording.setter + def reset_recording(self, value): + self._reset_recording = value + self.signals.reset_recording_changed.emit(self._reset_recording) + + @property + def unconsumed_stream_samples(self) -> int: + return self._unconsumed_stream_samples + + @unconsumed_stream_samples.setter + def unconsumed_stream_samples(self, value: int): + self._unconsumed_stream_samples = value + self.signals.unconsumed_stream_samples_changed.emit(self.unconsumed_stream_samples) + + @property + def unconsumed_capture_samples(self) -> int: + return self._unconsumed_capture_samples + + @unconsumed_capture_samples.setter + def unconsumed_capture_samples(self, value: int): + self._unconsumed_capture_samples = value + self.signals.unconsumed_capture_samples_changed.emit(self.unconsumed_capture_samples) + + + @property + def streaming_history(self) -> float: + return self.config.streaming_history.get() + + @streaming_history.setter + def streaming_history(self, value: float): + self.config.streaming_history.set(value) + self.signals.streaming_history_changed.emit(self.streaming_history) + + @property + def streaming_rate(self): + return self.config.streaming_rate.get() + + @streaming_rate.setter + def streaming_rate(self, value): + self.config.streaming_rate.set(value) + self.signals.streaming_rate_changed.emit(self.streaming_rate) + + @property + def streaming_deque_length(self): + return int((self.streaming_history / 1000) * self.sample_rate) + + # ================================================================================================================== + # Acquisition Settings + # ================================================================================================================== + @property + def sample_rate(self): + return self.config.sample_rate.get() + + @sample_rate.setter + def sample_rate(self, value): + self.config.sample_rate.set(self.value) + self.signals.sample_rate_changed.emit(self.sample_rate) + diff --git a/src/CaptDeviceControl/model/AD2CaptDeviceInformationModel.py b/src/CaptDeviceControl/model/submodels/AD2CaptDeviceInformationModel.py similarity index 89% rename from src/CaptDeviceControl/model/AD2CaptDeviceInformationModel.py rename to src/CaptDeviceControl/model/submodels/AD2CaptDeviceInformationModel.py index 0e07343..d366872 100644 --- a/src/CaptDeviceControl/model/AD2CaptDeviceInformationModel.py +++ b/src/CaptDeviceControl/model/submodels/AD2CaptDeviceInformationModel.py @@ -2,11 +2,14 @@ from ctypes import c_int, Array from PySide6.QtCore import QObject, Signal +from model.AD2Constants import AD2Constants + 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) @@ -17,6 +20,7 @@ class AD2CaptDeviceInformationSignals(QObject): device_name_changed = Signal(str) device_serial_number_changed = Signal(str) device_index_changed = Signal(int) + device_state_changed = Signal(AD2Constants.DeviceState) class AD2CaptDeviceInformationModel: @@ -32,6 +36,7 @@ class AD2CaptDeviceInformationModel: self._device_connected: bool = False self._device_name: str = "Unknown" self._device_serial_number: str = "Unknown" + self._device_state: AD2Constants.DeviceState = AD2Constants.DeviceState.ACQ_NOT_STARTED() # ================================================================================================================== # Connected Device Information @@ -115,3 +120,12 @@ class AD2CaptDeviceInformationModel: else: self._device_index = int(value) self.signals.device_serial_number_changed.emit(self.device_index) + + @property + def device_state(self) -> AD2Constants.DeviceState: + return self._device_state + + @device_state.setter + def device_state(self, value: AD2Constants.DeviceState): + self._device_state = value + self.signals.device_state_changed.emit(self.device_state) diff --git a/src/CaptDeviceControl/model/submodels/__init__.py b/src/CaptDeviceControl/model/submodels/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/CaptDeviceControl/resources/resources.qrc b/src/CaptDeviceControl/resources/resources.qrc index 45317f0..8f4b0d1 100644 --- a/src/CaptDeviceControl/resources/resources.qrc +++ b/src/CaptDeviceControl/resources/resources.qrc @@ -1,5 +1,6 @@ <RCC> <qresource prefix="icons"> + <file>icons/cil-record.png</file> <file>icons/cil-airplane-mode-off.png</file> <file>icons/cil-alarm.png</file> <file>icons/cil-align-left.png</file> @@ -258,4 +259,11 @@ <file>icons/cil-input.png</file> <file>icons/cil-pen-alt.png</file> </qresource> + <qresource prefix="icons-svg"> + <file>icons-svg/cil-reload.svg</file> + <file>icons-svg/cil-media-record.svg</file> + <file>icons-svg/cil-media-pause.svg</file> + <file>icons-svg/cil-media-stop.svg</file> + <file>icons-svg/cil-media-play.svg</file> + </qresource> </RCC> diff --git a/src/CaptDeviceControl/resources_rc.py b/src/CaptDeviceControl/resources_rc.py index 53c16bf..65554a6 100644 --- a/src/CaptDeviceControl/resources_rc.py +++ b/src/CaptDeviceControl/resources_rc.py @@ -1,11 +1,886 @@ # Resource object code (Python 3) # Created by: object code -# Created by: The Resource Compiler for Qt version 6.5.3 +# Created by: The Resource Compiler for Qt version 6.6.1 # WARNING! All changes made in this file will be lost! from PySide6 import QtCore qt_resource_data = b"\ +\x00\x00\x0c\xf5\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22utf\ +-8\x22?>\x0d\x0a<!-- Gene\ +rator: Adobe Ill\ +ustrator 24.1.2,\ + SVG Export Plug\ +-In . SVG Versio\ +n: 6.00 Build 0)\ + -->\x0d\x0a<svg vers\ +ion=\x221.1\x22 id=\x22Eb\ +ene_1\x22 xmlns=\x22ht\ +tp://www.w3.org/\ +2000/svg\x22 xmlns:\ +xlink=\x22http://ww\ +w.w3.org/1999/xl\ +ink\x22 x=\x220px\x22 y=\x22\ +0px\x22\x0d\x0a\x09 viewBox=\ +\x220 0 16 16\x22 styl\ +e=\x22enable-backgr\ +ound:new 0 0 16 \ +16;\x22 xml:space=\x22\ +preserve\x22>\x0d\x0a<sty\ +le type=\x22text/cs\ +s\x22>\x0d\x0a\x09.st0{fill:\ +#B70000;stroke:#\ +B70000;stroke-wi\ +dth:0.875;stroke\ +-miterlimit:10;}\ +\x0d\x0a</style>\x0d\x0a<ima\ +ge style=\x22displa\ +y:none;overflow:\ +visible;\x22 width=\ +\x2216\x22 height=\x2216\x22\ + xlink:href=\x22dat\ +a:image/png;base\ +64,iVBORw0KGgoAA\ +AANSUhEUgAAABAAA\ +AAQCAYAAAAf8/9hA\ +AAACXBIWXMAAAsTA\ +AALEwEAmpwYAAAG\x0d\ +\x0avmlUWHRYTUw6Y29\ +tLmFkb2JlLnhtcAA\ +AAAAAPD94cGFja2V\ +0IGJlZ2luPSLvu78\ +iIGlkPSJXNU0w\x0d\x0aT\ +XBDZWhpSHpyZVN6T\ +lRjemtjOWQiPz4gP\ +Hg6eG1wbWV0YSB4b\ +Wxuczp4PSJhZG9iZ\ +TpuczptZXRh\x0d\x0aLyI\ +geDp4bXB0az0iQWR\ +vYmUgWE1QIENvcmU\ +gNi4wLWMwMDIgNzk\ +uMTY0MzYwLCAyMDI\ +wLzAyLzEz\x0d\x0aLTAxO\ +jA3OjIyICAgICAgI\ +CAiPiA8cmRmOlJER\ +iB4bWxuczpyZGY9I\ +mh0dHA6Ly93d3cud\ +zMub3Jn\x0d\x0aLzE5OTk\ +vMDIvMjItcmRmLXN\ +5bnRheC1ucyMiPiA\ +8cmRmOkRlc2NyaXB\ +0aW9uIHJkZjphYm9\ +1dD0i\x0d\x0aIiB4bWxuc\ +zp4bXA9Imh0dHA6L\ +y9ucy5hZG9iZS5jb\ +20veGFwLzEuMC8iI\ +HhtbG5zOmRjPSJod\ +HRw\x0d\x0aOi8vcHVybC5\ +vcmcvZGMvZWxlbWV\ +udHMvMS4xLyIgeG1\ +sbnM6cGhvdG9zaG9\ +wPSJodHRwOi8vbnM\ +u\x0d\x0aYWRvYmUuY29tL\ +3Bob3Rvc2hvcC8xL\ +jAvIiB4bWxuczp4b\ +XBNTT0iaHR0cDovL\ +25zLmFkb2JlLmNv\x0d\ +\x0abS94YXAvMS4wL21\ +tLyIgeG1sbnM6c3R\ +FdnQ9Imh0dHA6Ly9\ +ucy5hZG9iZS5jb20\ +veGFwLzEuMC9z\x0d\x0aV\ +HlwZS9SZXNvdXJjZ\ +UV2ZW50IyIgeG1wO\ +kNyZWF0b3JUb29sP\ +SJBZG9iZSBQaG90b\ +3Nob3AgMjEu\x0d\x0aMCA\ +oV2luZG93cykiIHh\ +tcDpDcmVhdGVEYXR\ +lPSIyMDIwLTAzLTA\ +zVDA5OjUwOjM5LTA\ +zOjAwIiB4\x0d\x0abXA6T\ +W9kaWZ5RGF0ZT0iM\ +jAyMy0xMi0xOVQxM\ +jozMjoxMyswMTowM\ +CIgeG1wOk1ldGFkY\ +XRhRGF0\x0d\x0aZT0iMjA\ +yMy0xMi0xOVQxMjo\ +zMjoxMyswMTowMCI\ +gZGM6Zm9ybWF0PSJ\ +pbWFnZS9wbmciIHB\ +ob3Rv\x0d\x0ac2hvcDpDb\ +2xvck1vZGU9IjMiI\ +HBob3Rvc2hvcDpJQ\ +0NQcm9maWxlPSJzU\ +kdCIElFQzYxOTY2L\ +TIu\x0d\x0aMSIgeG1wTU0\ +6SW5zdGFuY2VJRD0\ +ieG1wLmlpZDo2ZmQ\ +3OTdlNi02ZmY0LTl\ +hNDQtOTU1Ny02Nzk\ +5\x0d\x0aZmU4OGJlZjkiI\ +HhtcE1NOkRvY3VtZ\ +W50SUQ9ImFkb2JlO\ +mRvY2lkOnBob3Rvc\ +2hvcDo2Zjk3ZDcx\x0d\ +\x0aOS01M2RlLTZlNDU\ +tOGZmNS0wNjI5NGY\ +0Y2I3MWIiIHhtcE1\ +NOk9yaWdpbmFsRG9\ +jdW1lbnRJRD0i\x0d\x0ae\ +G1wLmRpZDpkZDVkM\ +2EzZS1hMDQ2LTUxN\ +DYtOGU3YS0xNGI2M\ +mVlYjM1MGQiPiA8e\ +G1wTU06SGlz\x0d\x0adG9\ +yeT4gPHJkZjpTZXE\ ++IDxyZGY6bGkgc3R\ +FdnQ6YWN0aW9uPSJ\ +jcmVhdGVkIiBzdEV\ +2dDppbnN0\x0d\x0aYW5jZ\ +UlEPSJ4bXAuaWlkO\ +mRkNWQzYTNlLWEwN\ +DYtNTE0Ni04ZTdhL\ +TE0YjYyZWViMzUwZ\ +CIgc3RF\x0d\x0adnQ6d2h\ +lbj0iMjAyMC0wMy0\ +wM1QwOTo1MDozOS0\ +wMzowMCIgc3RFdnQ\ +6c29mdHdhcmVBZ2V\ +udD0i\x0d\x0aQWRvYmUgU\ +GhvdG9zaG9wIDIxL\ +jAgKFdpbmRvd3MpI\ +i8+IDxyZGY6bGkgc\ +3RFdnQ6YWN0aW9uP\ +SJz\x0d\x0aYXZlZCIgc3R\ +FdnQ6aW5zdGFuY2V\ +JRD0ieG1wLmlpZDp\ +jYTk4ZDRkYS03YmY\ +zLWYyNDMtODIyOC1\ +j\x0d\x0aOWI5YjI4ZWZlM\ +GIiIHN0RXZ0OndoZ\ +W49IjIwMjAtMDUtM\ +DJUMTc6NTg6MjUtM\ +DM6MDAiIHN0RXZ0\x0d\ +\x0aOnNvZnR3YXJlQWd\ +lbnQ9IkFkb2JlIFB\ +ob3Rvc2hvcCAyMS4\ +wIChXaW5kb3dzKSI\ +gc3RFdnQ6Y2hh\x0d\x0ab\ +mdlZD0iLyIvPiA8c\ +mRmOmxpIHN0RXZ0O\ +mFjdGlvbj0ic2F2Z\ +WQiIHN0RXZ0Omluc\ +3RhbmNlSUQ9\x0d\x0aInh\ +tcC5paWQ6NmZkNzk\ +3ZTYtNmZmNC05YTQ\ +0LTk1NTctNjc5OWZ\ +lODhiZWY5IiBzdEV\ +2dDp3aGVu\x0d\x0aPSIyM\ +DIzLTEyLTE5VDEyO\ +jMyOjEzKzAxOjAwI\ +iBzdEV2dDpzb2Z0d\ +2FyZUFnZW50PSJBZ\ +G9iZSBQ\x0d\x0aaG90b3N\ +ob3AgMjEuMSAoV2l\ +uZG93cykiIHN0RXZ\ +0OmNoYW5nZWQ9Ii8\ +iLz4gPC9yZGY6U2V\ +xPiA8\x0d\x0aL3htcE1NO\ +khpc3Rvcnk+IDwvc\ +mRmOkRlc2NyaXB0a\ +W9uPiA8L3JkZjpSR\ +EY+IDwveDp4bXBtZ\ +XRh\x0d\x0aPiA8P3hwYWN\ +rZXQgZW5kPSJyIj8\ ++h4q0ZgAAAJVJREF\ +UOMutk0EKgzAQRWc\ +l9Cqas9YsDPQupbc\ +o\x0d\x0a6j10YcRi/8BEw\ +oAFM128RZL5DzKZ0\ +E5EihrcwQAWoZe9W\ +tfniwp04AP2E/jsA\ +W5awOHnj6DmJZlD\x0d\ +\x0aEC6EE10SNGArEHC\ +mYUFbEE60JN0uFQw\ +siAZBtAom6xXe1iZ\ +6FjjDMzrLIIW/jrL\ +5M+XwaHswghXM\x0d\x0a3\ +G3Zc7r+C1ipxmHV0\ +UIdAAAAAElFTkSuQ\ +mCC\x22>\x0d\x0a</image>\x0d\ +\x0a<circle class=\x22\ +st0\x22 cx=\x228\x22 cy=\x22\ +8\x22 r=\x227\x22/>\x0d\x0a</sv\ +g>\x0d\x0a\ +\x00\x00\x0c\x1f\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22utf\ +-8\x22?>\x0d\x0a<!-- Gene\ +rator: Adobe Ill\ +ustrator 24.1.2,\ + SVG Export Plug\ +-In . SVG Versio\ +n: 6.00 Build 0)\ + -->\x0d\x0a<svg vers\ +ion=\x221.1\x22 id=\x22Eb\ +ene_1\x22 xmlns=\x22ht\ +tp://www.w3.org/\ +2000/svg\x22 xmlns:\ +xlink=\x22http://ww\ +w.w3.org/1999/xl\ +ink\x22 x=\x220px\x22 y=\x22\ +0px\x22\x0d\x0a\x09 viewBox=\ +\x220 0 16 16\x22 styl\ +e=\x22enable-backgr\ +ound:new 0 0 16 \ +16;\x22 xml:space=\x22\ +preserve\x22>\x0d\x0a<sty\ +le type=\x22text/cs\ +s\x22>\x0d\x0a\x09.st0{fill:\ +none;stroke:#FF0\ +000;stroke-width\ +:2;stroke-miterl\ +imit:10;}\x0d\x0a</sty\ +le>\x0d\x0a<image styl\ +e=\x22display:none;\ +overflow:visible\ +;\x22 width=\x2216\x22 he\ +ight=\x2216\x22 xlink:\ +href=\x22data:image\ +/png;base64,iVBO\ +Rw0KGgoAAAANSUhE\ +UgAAABAAAAAQCAYA\ +AAAf8/9hAAAACXBI\ +WXMAAAsTAAALEwEA\ +mpwYAAAF\x0d\x0a8WlUWH\ +RYTUw6Y29tLmFkb2\ +JlLnhtcAAAAAAAPD\ +94cGFja2V0IGJlZ2\ +luPSLvu78iIGlkPS\ +JXNU0w\x0d\x0aTXBDZWhp\ +SHpyZVN6TlRjemtj\ +OWQiPz4gPHg6eG1w\ +bWV0YSB4bWxuczp4\ +PSJhZG9iZTpuczpt\ +ZXRh\x0d\x0aLyIgeDp4bX\ +B0az0iQWRvYmUgWE\ +1QIENvcmUgNS42LW\ +MxNDggNzkuMTY0MD\ +M2LCAyMDE5LzA4Lz\ +Ez\x0d\x0aLTAxOjA2OjU3\ +ICAgICAgICAiPiA8\ +cmRmOlJERiB4bWxu\ +czpyZGY9Imh0dHA6\ +Ly93d3cudzMub3Jn\ +\x0d\x0aLzE5OTkvMDIvMj\ +ItcmRmLXN5bnRheC\ +1ucyMiPiA8cmRmOk\ +Rlc2NyaXB0aW9uIH\ +JkZjphYm91dD0i\x0d\x0a\ +IiB4bWxuczp4bXA9\ +Imh0dHA6Ly9ucy5h\ +ZG9iZS5jb20veGFw\ +LzEuMC8iIHhtbG5z\ +OmRjPSJodHRw\x0d\x0aOi\ +8vcHVybC5vcmcvZG\ +MvZWxlbWVudHMvMS\ +4xLyIgeG1sbnM6cG\ +hvdG9zaG9wPSJodH\ +RwOi8vbnMu\x0d\x0aYWRv\ +YmUuY29tL3Bob3Rv\ +c2hvcC8xLjAvIiB4\ +bWxuczp4bXBNTT0i\ +aHR0cDovL25zLmFk\ +b2JlLmNv\x0d\x0abS94YX\ +AvMS4wL21tLyIgeG\ +1sbnM6c3RFdnQ9Im\ +h0dHA6Ly9ucy5hZG\ +9iZS5jb20veGFwLz\ +EuMC9z\x0d\x0aVHlwZS9S\ +ZXNvdXJjZUV2ZW50\ +IyIgeG1wOkNyZWF0\ +b3JUb29sPSJBZG9i\ +ZSBQaG90b3Nob3Ag\ +MjEu\x0d\x0aMCAoV2luZG\ +93cykiIHhtcDpDcm\ +VhdGVEYXRlPSIyMD\ +IwLTAzLTAzVDA5Oj\ +UwOjQxLTAzOjAwIi\ +B4\x0d\x0abXA6TW9kaWZ5\ +RGF0ZT0iMjAyMC0w\ +NS0wMlQxNzo1OTox\ +Ny0wMzowMCIgeG1w\ +Ok1ldGFkYXRhRGF0\ +\x0d\x0aZT0iMjAyMC0wNS\ +0wMlQxNzo1OToxNy\ +0wMzowMCIgZGM6Zm\ +9ybWF0PSJpbWFnZS\ +9wbmciIHBob3Rv\x0d\x0a\ +c2hvcDpDb2xvck1v\ +ZGU9IjMiIHBob3Rv\ +c2hvcDpJQ0NQcm9m\ +aWxlPSJzUkdCIElF\ +QzYxOTY2LTIu\x0d\x0aMS\ +IgeG1wTU06SW5zdG\ +FuY2VJRD0ieG1wLm\ +lpZDoyYWE1NDM0YS\ +04MDgyLTI5NGYtYW\ +I2OC1kZWNk\x0d\x0aYWY2\ +NjUzMWIiIHhtcE1N\ +OkRvY3VtZW50SUQ9\ +ImFkb2JlOmRvY2lk\ +OnBob3Rvc2hvcDo3\ +MGNiMzhj\x0d\x0aMy02NT\ +VhLTg3NDUtYTYyZi\ +00MWZjN2RmZDdjMT\ +UiIHhtcE1NOk9yaW\ +dpbmFsRG9jdW1lbn\ +RJRD0i\x0d\x0aeG1wLmRp\ +ZDo2OTUyZjdmMS1l\ +MmI4LWQxNDMtODkz\ +Ni01MjE1M2E3NDRk\ +YjUiPiA8eG1wTU06\ +SGlz\x0d\x0adG9yeT4gPH\ +JkZjpTZXE+IDxyZG\ +Y6bGkgc3RFdnQ6YW\ +N0aW9uPSJjcmVhdG\ +VkIiBzdEV2dDppbn\ +N0\x0d\x0aYW5jZUlEPSJ4\ +bXAuaWlkOjY5NTJm\ +N2YxLWUyYjgtZDE0\ +My04OTM2LTUyMTUz\ +YTc0NGRiNSIgc3RF\ +\x0d\x0adnQ6d2hlbj0iMj\ +AyMC0wMy0wM1QwOT\ +o1MDo0MS0wMzowMC\ +Igc3RFdnQ6c29mdH\ +dhcmVBZ2VudD0i\x0d\x0a\ +QWRvYmUgUGhvdG9z\ +aG9wIDIxLjAgKFdp\ +bmRvd3MpIi8+IDxy\ +ZGY6bGkgc3RFdnQ6\ +YWN0aW9uPSJz\x0d\x0aYX\ +ZlZCIgc3RFdnQ6aW\ +5zdGFuY2VJRD0ieG\ +1wLmlpZDoyYWE1ND\ +M0YS04MDgyLTI5NG\ +YtYWI2OC1k\x0d\x0aZWNk\ +YWY2NjUzMWIiIHN0\ +RXZ0OndoZW49IjIw\ +MjAtMDUtMDJUMTc6\ +NTk6MTctMDM6MDAi\ +IHN0RXZ0\x0d\x0aOnNvZn\ +R3YXJlQWdlbnQ9Ik\ +Fkb2JlIFBob3Rvc2\ +hvcCAyMS4wIChXaW\ +5kb3dzKSIgc3RFdn\ +Q6Y2hh\x0d\x0abmdlZD0i\ +LyIvPiA8L3JkZjpT\ +ZXE+IDwveG1wTU06\ +SGlzdG9yeT4gPC9y\ +ZGY6RGVzY3JpcHRp\ +b24+\x0d\x0aIDwvcmRmOl\ +JERj4gPC94OnhtcG\ +1ldGE+IDw/eHBhY2\ +tldCBlbmQ9InIiPz\ +5YgjJvAAAAvUlEQV\ +Q4\x0d\x0ay9XTQQrCMBAF\ +0DR14y08UkHciCCK\ +tXouRUStot6n7nIA\ +T+AfmJRPSDWIGxcP\ +BtL+TqdT45wzKlNc\ +\x0d\x0ah+TMUm0MBXzFUF\ +oBd6jh3OECWxj4Tj\ +ngCjeYwBoqslIzaG\ +DuX4cDjjBOaFs6KG\ +MBJ1hq3dNhsVzP\x0d\x0a\ +6ncBJU06fHL0uv8J\ +yFID/BDzyBbaT0OU\ +g2nCZ9zrbrQBPnkD\ +D9jpxoUbKA7whGGs\ +A1nPhW5c1UE2\x0d\x0adA\ +T9n/5MPGmbqL3vBV\ +WG3M95a5ACAAAAAE\ +lFTkSuQmCC\x22>\x0d\x0a</\ +image>\x0d\x0a<rect x=\ +\x221.96\x22 y=\x222.01\x22 \ +class=\x22st0\x22 widt\ +h=\x2212\x22 height=\x221\ +2\x22/>\x0d\x0a</svg>\x0d\x0a\ +\x00\x00\x0d\x8d\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22utf\ +-8\x22?>\x0d\x0a<!-- Gene\ +rator: Adobe Ill\ +ustrator 24.1.2,\ + SVG Export Plug\ +-In . SVG Versio\ +n: 6.00 Build 0)\ + -->\x0d\x0a<svg vers\ +ion=\x221.1\x22 id=\x22Eb\ +ene_1\x22 xmlns=\x22ht\ +tp://www.w3.org/\ +2000/svg\x22 xmlns:\ +xlink=\x22http://ww\ +w.w3.org/1999/xl\ +ink\x22 x=\x220px\x22 y=\x22\ +0px\x22\x0d\x0a\x09 viewBox=\ +\x220 0 16 16\x22 styl\ +e=\x22enable-backgr\ +ound:new 0 0 16 \ +16;\x22 xml:space=\x22\ +preserve\x22>\x0d\x0a<sty\ +le type=\x22text/cs\ +s\x22>\x0d\x0a\x09.st0{fill:\ +none;stroke:#FFF\ +FFF;stroke-width\ +:2;stroke-miterl\ +imit:10;}\x0d\x0a</sty\ +le>\x0d\x0a<image styl\ +e=\x22display:none;\ +overflow:visible\ +;\x22 width=\x2216\x22 he\ +ight=\x2216\x22 xlink:\ +href=\x22data:image\ +/png;base64,iVBO\ +Rw0KGgoAAAANSUhE\ +UgAAABAAAAAQCAYA\ +AAAf8/9hAAAACXBI\ +WXMAAAsTAAALEwEA\ +mpwYAAAF\x0d\x0a8WlUWH\ +RYTUw6Y29tLmFkb2\ +JlLnhtcAAAAAAAPD\ +94cGFja2V0IGJlZ2\ +luPSLvu78iIGlkPS\ +JXNU0w\x0d\x0aTXBDZWhp\ +SHpyZVN6TlRjemtj\ +OWQiPz4gPHg6eG1w\ +bWV0YSB4bWxuczp4\ +PSJhZG9iZTpuczpt\ +ZXRh\x0d\x0aLyIgeDp4bX\ +B0az0iQWRvYmUgWE\ +1QIENvcmUgNS42LW\ +MxNDggNzkuMTY0MD\ +M2LCAyMDE5LzA4Lz\ +Ez\x0d\x0aLTAxOjA2OjU3\ +ICAgICAgICAiPiA8\ +cmRmOlJERiB4bWxu\ +czpyZGY9Imh0dHA6\ +Ly93d3cudzMub3Jn\ +\x0d\x0aLzE5OTkvMDIvMj\ +ItcmRmLXN5bnRheC\ +1ucyMiPiA8cmRmOk\ +Rlc2NyaXB0aW9uIH\ +JkZjphYm91dD0i\x0d\x0a\ +IiB4bWxuczp4bXA9\ +Imh0dHA6Ly9ucy5h\ +ZG9iZS5jb20veGFw\ +LzEuMC8iIHhtbG5z\ +OmRjPSJodHRw\x0d\x0aOi\ +8vcHVybC5vcmcvZG\ +MvZWxlbWVudHMvMS\ +4xLyIgeG1sbnM6cG\ +hvdG9zaG9wPSJodH\ +RwOi8vbnMu\x0d\x0aYWRv\ +YmUuY29tL3Bob3Rv\ +c2hvcC8xLjAvIiB4\ +bWxuczp4bXBNTT0i\ +aHR0cDovL25zLmFk\ +b2JlLmNv\x0d\x0abS94YX\ +AvMS4wL21tLyIgeG\ +1sbnM6c3RFdnQ9Im\ +h0dHA6Ly9ucy5hZG\ +9iZS5jb20veGFwLz\ +EuMC9z\x0d\x0aVHlwZS9S\ +ZXNvdXJjZUV2ZW50\ +IyIgeG1wOkNyZWF0\ +b3JUb29sPSJBZG9i\ +ZSBQaG90b3Nob3Ag\ +MjEu\x0d\x0aMCAoV2luZG\ +93cykiIHhtcDpDcm\ +VhdGVEYXRlPSIyMD\ +IwLTAzLTAzVDA5Oj\ +UwOjQxLTAzOjAwIi\ +B4\x0d\x0abXA6TW9kaWZ5\ +RGF0ZT0iMjAyMC0w\ +NS0wMlQxNzo1OToz\ +MS0wMzowMCIgeG1w\ +Ok1ldGFkYXRhRGF0\ +\x0d\x0aZT0iMjAyMC0wNS\ +0wMlQxNzo1OTozMS\ +0wMzowMCIgZGM6Zm\ +9ybWF0PSJpbWFnZS\ +9wbmciIHBob3Rv\x0d\x0a\ +c2hvcDpDb2xvck1v\ +ZGU9IjMiIHBob3Rv\ +c2hvcDpJQ0NQcm9m\ +aWxlPSJzUkdCIElF\ +QzYxOTY2LTIu\x0d\x0aMS\ +IgeG1wTU06SW5zdG\ +FuY2VJRD0ieG1wLm\ +lpZDo0MjE3MGE2Zi\ +02NzM1LWNkNDYtYm\ +JiZS00MWYx\x0d\x0aODEy\ +YmNkMWEiIHhtcE1N\ +OkRvY3VtZW50SUQ9\ +ImFkb2JlOmRvY2lk\ +OnBob3Rvc2hvcDpm\ +OGI5YmJj\x0d\x0aMS0yND\ +BjLWVmNGQtODUxYS\ +03Y2Y3NzAwYzM5Yz\ +UiIHhtcE1NOk9yaW\ +dpbmFsRG9jdW1lbn\ +RJRD0i\x0d\x0aeG1wLmRp\ +ZDphNGFkNjEzYy02\ +ODg4LTExNDQtYjMy\ +OS1jOWQ4NDA3MmZm\ +MTgiPiA8eG1wTU06\ +SGlz\x0d\x0adG9yeT4gPH\ +JkZjpTZXE+IDxyZG\ +Y6bGkgc3RFdnQ6YW\ +N0aW9uPSJjcmVhdG\ +VkIiBzdEV2dDppbn\ +N0\x0d\x0aYW5jZUlEPSJ4\ +bXAuaWlkOmE0YWQ2\ +MTNjLTY4ODgtMTE0\ +NC1iMzI5LWM5ZDg0\ +MDcyZmYxOCIgc3RF\ +\x0d\x0adnQ6d2hlbj0iMj\ +AyMC0wMy0wM1QwOT\ +o1MDo0MS0wMzowMC\ +Igc3RFdnQ6c29mdH\ +dhcmVBZ2VudD0i\x0d\x0a\ +QWRvYmUgUGhvdG9z\ +aG9wIDIxLjAgKFdp\ +bmRvd3MpIi8+IDxy\ +ZGY6bGkgc3RFdnQ6\ +YWN0aW9uPSJz\x0d\x0aYX\ +ZlZCIgc3RFdnQ6aW\ +5zdGFuY2VJRD0ieG\ +1wLmlpZDo0MjE3MG\ +E2Zi02NzM1LWNkND\ +YtYmJiZS00\x0d\x0aMWYx\ +ODEyYmNkMWEiIHN0\ +RXZ0OndoZW49IjIw\ +MjAtMDUtMDJUMTc6\ +NTk6MzEtMDM6MDAi\ +IHN0RXZ0\x0d\x0aOnNvZn\ +R3YXJlQWdlbnQ9Ik\ +Fkb2JlIFBob3Rvc2\ +hvcCAyMS4wIChXaW\ +5kb3dzKSIgc3RFdn\ +Q6Y2hh\x0d\x0abmdlZD0i\ +LyIvPiA8L3JkZjpT\ +ZXE+IDwveG1wTU06\ +SGlzdG9yeT4gPC9y\ +ZGY6RGVzY3JpcHRp\ +b24+\x0d\x0aIDwvcmRmOl\ +JERj4gPC94OnhtcG\ +1ldGE+IDw/eHBhY2\ +tldCBlbmQ9InIiPz\ +7XVRw9AAABMklEQV\ +Q4\x0d\x0ay6XTvyvFURgG\ +8HsvUeKidAcLm4GB\ +/Ig/AYsySgYpg91C\ +KaWUQVI2DGKSYiWD\ +yaJQGNxB6vMX2I/l\ +\x0d\x0aXJ2+vtdieDvnvO\ +d5n573Pc8phBAKaa\ +Ahm0vzaMYGekIIhS\ +yolOx7MYh+tCf5Mt\ +4w+osgAqZxgxc8\x0d\x0a\ +4BHP2I3FRdxjKE/B\ +Jj6xjG60ohMjuMAr\ +JnCH4RBC4Uc2VlBF\ +d94MImY5YqoY+FGA\ +Cj4wFs9NcW2J\x0d\x0a0q\ +9xiVN8xehLCVZwmX\ +0FNGISC5jDPGYxhb\ +aU4AjrtaJ6LeRFje\ +AEq3kEKKEhG1mCbR\ +z8R8E43tFa\x0d\x0az42p\ +yX4RRMAFDlPrRtOU\ +UKyroHaJLjzhGOUc\ +BTO4QiWei2kLpYTk\ +PDruDFvYw230/1K2\ +lXofaQJr\x0d\x0a2McOFt\ +Hx5wxqsv7qN2+Q33\ +rmA2LZnv3JAAAAAE\ +lFTkSuQmCC\x22>\x0d\x0a</\ +image>\x0d\x0a<path cl\ +ass=\x22st0\x22 d=\x22M11\ +.95,12.6c-1.06,0\ +.91-2.44,1.46-3.\ +95,1.46c-3.35,0-\ +6.06-2.71-6.06-6\ +.06S4.65,1.94,8,\ +1.94\x0d\x0a\x09c2.64,0,4\ +.88,1.69,5.72,4.\ +04\x22/>\x0d\x0a<line cla\ +ss=\x22st0\x22 x1=\x2215.\ +04\x22 y1=\x225.88\x22 x2\ +=\x228.36\x22 y2=\x225.88\ +\x22/>\x0d\x0a<line class\ +=\x22st0\x22 x1=\x2214.07\ +\x22 y1=\x220.76\x22 x2=\x22\ +14.07\x22 y2=\x226.85\x22\ +/>\x0d\x0a</svg>\x0d\x0a\ +\x00\x00\x0c\x9a\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22utf\ +-8\x22?>\x0d\x0a<!-- Gene\ +rator: Adobe Ill\ +ustrator 24.1.2,\ + SVG Export Plug\ +-In . SVG Versio\ +n: 6.00 Build 0)\ + -->\x0d\x0a<svg vers\ +ion=\x221.1\x22 id=\x22Eb\ +ene_1\x22 xmlns=\x22ht\ +tp://www.w3.org/\ +2000/svg\x22 xmlns:\ +xlink=\x22http://ww\ +w.w3.org/1999/xl\ +ink\x22 x=\x220px\x22 y=\x22\ +0px\x22\x0d\x0a\x09 viewBox=\ +\x220 0 16 16\x22 styl\ +e=\x22enable-backgr\ +ound:new 0 0 16 \ +16;\x22 xml:space=\x22\ +preserve\x22>\x0d\x0a<sty\ +le type=\x22text/cs\ +s\x22>\x0d\x0a\x09.st0{fill:\ +none;stroke:#FFD\ +400;stroke-width\ +:1.5119;stroke-m\ +iterlimit:10;}\x0d\x0a\ +</style>\x0d\x0a<image\ + style=\x22display:\ +none;overflow:vi\ +sible;\x22 width=\x221\ +6\x22 height=\x2216\x22 x\ +link:href=\x22data:\ +image/png;base64\ +,iVBORw0KGgoAAAA\ +NSUhEUgAAABAAAAA\ +QCAYAAAAf8/9hAAA\ +ACXBIWXMAAAsTAAA\ +LEwEAmpwYAAAF\x0d\x0a8\ +WlUWHRYTUw6Y29tL\ +mFkb2JlLnhtcAAAA\ +AAAPD94cGFja2V0I\ +GJlZ2luPSLvu78iI\ +GlkPSJXNU0w\x0d\x0aTXB\ +DZWhpSHpyZVN6TlR\ +jemtjOWQiPz4gPHg\ +6eG1wbWV0YSB4bWx\ +uczp4PSJhZG9iZTp\ +uczptZXRh\x0d\x0aLyIge\ +Dp4bXB0az0iQWRvY\ +mUgWE1QIENvcmUgN\ +S42LWMxNDggNzkuM\ +TY0MDM2LCAyMDE5L\ +zA4LzEz\x0d\x0aLTAxOjA\ +2OjU3ICAgICAgICA\ +iPiA8cmRmOlJERiB\ +4bWxuczpyZGY9Imh\ +0dHA6Ly93d3cudzM\ +ub3Jn\x0d\x0aLzE5OTkvM\ +DIvMjItcmRmLXN5b\ +nRheC1ucyMiPiA8c\ +mRmOkRlc2NyaXB0a\ +W9uIHJkZjphYm91d\ +D0i\x0d\x0aIiB4bWxuczp\ +4bXA9Imh0dHA6Ly9\ +ucy5hZG9iZS5jb20\ +veGFwLzEuMC8iIHh\ +tbG5zOmRjPSJodHR\ +w\x0d\x0aOi8vcHVybC5vc\ +mcvZGMvZWxlbWVud\ +HMvMS4xLyIgeG1sb\ +nM6cGhvdG9zaG9wP\ +SJodHRwOi8vbnMu\x0d\ +\x0aYWRvYmUuY29tL3B\ +ob3Rvc2hvcC8xLjA\ +vIiB4bWxuczp4bXB\ +NTT0iaHR0cDovL25\ +zLmFkb2JlLmNv\x0d\x0ab\ +S94YXAvMS4wL21tL\ +yIgeG1sbnM6c3RFd\ +nQ9Imh0dHA6Ly9uc\ +y5hZG9iZS5jb20ve\ +GFwLzEuMC9z\x0d\x0aVHl\ +wZS9SZXNvdXJjZUV\ +2ZW50IyIgeG1wOkN\ +yZWF0b3JUb29sPSJ\ +BZG9iZSBQaG90b3N\ +ob3AgMjEu\x0d\x0aMCAoV\ +2luZG93cykiIHhtc\ +DpDcmVhdGVEYXRlP\ +SIyMDIwLTAzLTAzV\ +DA5OjUwOjQxLTAzO\ +jAwIiB4\x0d\x0abXA6TW9\ +kaWZ5RGF0ZT0iMjA\ +yMC0wNS0wMlQxNzo\ +1OToxNS0wMzowMCI\ +geG1wOk1ldGFkYXR\ +hRGF0\x0d\x0aZT0iMjAyM\ +C0wNS0wMlQxNzo1O\ +ToxNS0wMzowMCIgZ\ +GM6Zm9ybWF0PSJpb\ +WFnZS9wbmciIHBob\ +3Rv\x0d\x0ac2hvcDpDb2x\ +vck1vZGU9IjMiIHB\ +ob3Rvc2hvcDpJQ0N\ +Qcm9maWxlPSJzUkd\ +CIElFQzYxOTY2LTI\ +u\x0d\x0aMSIgeG1wTU06S\ +W5zdGFuY2VJRD0ie\ +G1wLmlpZDowNmM0Z\ +DA3Ni0xMjA5LWRkN\ +DMtOGYxNy0zMmYx\x0d\ +\x0aMjU4NWUzZmUiIHh\ +tcE1NOkRvY3VtZW5\ +0SUQ9ImFkb2JlOmR\ +vY2lkOnBob3Rvc2h\ +vcDo3MmNhODVm\x0d\x0aO\ +C1kNWY0LTkwNDItY\ +TIxYS1mMDhhOTdlN\ +jE2ZTciIHhtcE1NO\ +k9yaWdpbmFsRG9jd\ +W1lbnRJRD0i\x0d\x0aeG1\ +wLmRpZDplYTU5ZmU\ +zMC04MzcxLWM1NGM\ +tYTJhZi1jY2U0NzA\ +4Y2Q3OTgiPiA8eG1\ +wTU06SGlz\x0d\x0adG9ye\ +T4gPHJkZjpTZXE+I\ +DxyZGY6bGkgc3RFd\ +nQ6YWN0aW9uPSJjc\ +mVhdGVkIiBzdEV2d\ +DppbnN0\x0d\x0aYW5jZUl\ +EPSJ4bXAuaWlkOmV\ +hNTlmZTMwLTgzNzE\ +tYzU0Yy1hMmFmLWN\ +jZTQ3MDhjZDc5OCI\ +gc3RF\x0d\x0adnQ6d2hlb\ +j0iMjAyMC0wMy0wM\ +1QwOTo1MDo0MS0wM\ +zowMCIgc3RFdnQ6c\ +29mdHdhcmVBZ2Vud\ +D0i\x0d\x0aQWRvYmUgUGh\ +vdG9zaG9wIDIxLjA\ +gKFdpbmRvd3MpIi8\ ++IDxyZGY6bGkgc3R\ +FdnQ6YWN0aW9uPSJ\ +z\x0d\x0aYXZlZCIgc3RFd\ +nQ6aW5zdGFuY2VJR\ +D0ieG1wLmlpZDowN\ +mM0ZDA3Ni0xMjA5L\ +WRkNDMtOGYxNy0z\x0d\ +\x0aMmYxMjU4NWUzZmU\ +iIHN0RXZ0OndoZW4\ +9IjIwMjAtMDUtMDJ\ +UMTc6NTk6MTUtMDM\ +6MDAiIHN0RXZ0\x0d\x0aO\ +nNvZnR3YXJlQWdlb\ +nQ9IkFkb2JlIFBob\ +3Rvc2hvcCAyMS4wI\ +ChXaW5kb3dzKSIgc\ +3RFdnQ6Y2hh\x0d\x0abmd\ +lZD0iLyIvPiA8L3J\ +kZjpTZXE+IDwveG1\ +wTU06SGlzdG9yeT4\ +gPC9yZGY6RGVzY3J\ +pcHRpb24+\x0d\x0aIDwvc\ +mRmOlJERj4gPC94O\ +nhtcG1ldGE+IDw/e\ +HBhY2tldCBlbmQ9I\ +nIiPz6cfMcpAAAA7\ +ElEQVQ4\x0d\x0aje3SsS5\ +EURDG8RsRncI7bKW\ +TEK2gVWgICSLYu55\ +F6wVEhHBjd2UfQUj\ +QEMIDyC/e4mjmynG\ +z3SoV\x0d\x0ak/nOzJx/5\ +uR8RUqpGCWKlFKBV\ +fRwhUsM0IneFI7Rj\ +941TtDKAQNU2MA2j\ +vAWvQV8YhdtbOED7\ +RzQ\x0d\x0axWa9Flp4Dr2\ +E+3xtXKDMAT0cZgP\ +zGWART6EnIveHAco\ +MMIuXIYCx5vw/4A8\ +B3dp5cZ5rfONj6PE\ +M\x0d\x0a8MtIN9jJANN4D\ +b2Mh4aRqto3deEU7\ +zjDOe5wmz1HXKrCh\ +V9YzwEt7OMgcgcz9\ +dpYidoeSqxh8gcw\x0d\ +\x0aSnwDnYsY9wIGXmY\ +AAAAASUVORK5CYII\ +=\x22>\x0d\x0a</image>\x0d\x0a<\ +rect x=\x222.2\x22 y=\x22\ +2\x22 class=\x22st0\x22 w\ +idth=\x224\x22 height=\ +\x2212\x22/>\x0d\x0a<rect x=\ +\x229.8\x22 y=\x222\x22 clas\ +s=\x22st0\x22 width=\x224\ +\x22 height=\x2212\x22/>\x0d\ +\x0a</svg>\x0d\x0a\ +\x00\x00\x02\xb3\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22utf\ +-8\x22?>\x0d\x0a<!-- Gene\ +rator: Adobe Ill\ +ustrator 24.1.2,\ + SVG Export Plug\ +-In . SVG Versio\ +n: 6.00 Build 0)\ + -->\x0d\x0a<svg vers\ +ion=\x221.1\x22 id=\x22Eb\ +ene_1\x22 xmlns=\x22ht\ +tp://www.w3.org/\ +2000/svg\x22 xmlns:\ +xlink=\x22http://ww\ +w.w3.org/1999/xl\ +ink\x22 x=\x220px\x22 y=\x22\ +0px\x22\x0d\x0a\x09 viewBox=\ +\x220 0 16 16\x22 styl\ +e=\x22enable-backgr\ +ound:new 0 0 16 \ +16;\x22 xml:space=\x22\ +preserve\x22>\x0d\x0a<sty\ +le type=\x22text/cs\ +s\x22>\x0d\x0a\x09.st0{fill:\ +none;stroke:#24D\ +115;stroke-width\ +:1.9944;stroke-l\ +inecap:round;str\ +oke-linejoin:rou\ +nd;stroke-miterl\ +imit:10;}\x0d\x0a</sty\ +le>\x0d\x0a<polygon cl\ +ass=\x22st0\x22 points\ +=\x223.5,14 3.5,1.7\ +6 13.6,7.88 \x22/>\x0d\ +\x0a<g>\x0d\x0a</g>\x0d\x0a<g>\x0d\ +\x0a</g>\x0d\x0a<g>\x0d\x0a</g>\ +\x0d\x0a<g>\x0d\x0a</g>\x0d\x0a<g>\ +\x0d\x0a</g>\x0d\x0a<g>\x0d\x0a</g\ +>\x0d\x0a<g>\x0d\x0a</g>\x0d\x0a<g\ +>\x0d\x0a</g>\x0d\x0a<g>\x0d\x0a</\ +g>\x0d\x0a<g>\x0d\x0a</g>\x0d\x0a<\ +g>\x0d\x0a</g>\x0d\x0a</svg>\ +\x0d\x0a\ \x00\x00\x07b\ \x89\ PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ @@ -6700,6 +7575,131 @@ ft\x03\x98\xa0\xb4\x06\x10\x0b\x90j\x00\x0b\x94\x0e\x01\ Z\x1a\x88\xf7@\xd3\x02\xd1\xd1\x88\x9e\x90\xd0\xd94\ N\xca\x14\xe7FJ\x0d\x00\x00\x00\x12\xd9\xf2\x1b\xc9=\ \xce\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x07\xad\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\ +\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\ +\x01\x00\x9a\x9c\x18\x00\x00\x06\xbeiTXtXML\ +:com.adobe.xmp\x00\x00\ +\x00\x00\x00<?xpacket beg\ +in=\x22\xef\xbb\xbf\x22 id=\x22W5M\ +0MpCehiHzreSzNTc\ +zkc9d\x22?> <x:xmpm\ +eta xmlns:x=\x22ado\ +be:ns:meta/\x22 x:x\ +mptk=\x22Adobe XMP \ +Core 6.0-c002 79\ +.164360, 2020/02\ +/13-01:07:22 \ + \x22> <rdf:RDF \ +xmlns:rdf=\x22http:\ +//www.w3.org/199\ +9/02/22-rdf-synt\ +ax-ns#\x22> <rdf:De\ +scription rdf:ab\ +out=\x22\x22 xmlns:xmp\ +=\x22http://ns.adob\ +e.com/xap/1.0/\x22 \ +xmlns:dc=\x22http:/\ +/purl.org/dc/ele\ +ments/1.1/\x22 xmln\ +s:photoshop=\x22htt\ +p://ns.adobe.com\ +/photoshop/1.0/\x22\ + xmlns:xmpMM=\x22ht\ +tp://ns.adobe.co\ +m/xap/1.0/mm/\x22 x\ +mlns:stEvt=\x22http\ +://ns.adobe.com/\ +xap/1.0/sType/Re\ +sourceEvent#\x22 xm\ +p:CreatorTool=\x22A\ +dobe Photoshop 2\ +1.0 (Windows)\x22 x\ +mp:CreateDate=\x222\ +020-03-03T09:50:\ +39-03:00\x22 xmp:Mo\ +difyDate=\x222023-1\ +2-19T12:32:13+01\ +:00\x22 xmp:Metadat\ +aDate=\x222023-12-1\ +9T12:32:13+01:00\ +\x22 dc:format=\x22ima\ +ge/png\x22 photosho\ +p:ColorMode=\x223\x22 \ +photoshop:ICCPro\ +file=\x22sRGB IEC61\ +966-2.1\x22 xmpMM:I\ +nstanceID=\x22xmp.i\ +id:6fd797e6-6ff4\ +-9a44-9557-6799f\ +e88bef9\x22 xmpMM:D\ +ocumentID=\x22adobe\ +:docid:photoshop\ +:6f97d719-53de-6\ +e45-8ff5-06294f4\ +cb71b\x22 xmpMM:Ori\ +ginalDocumentID=\ +\x22xmp.did:dd5d3a3\ +e-a046-5146-8e7a\ +-14b62eeb350d\x22> \ +<xmpMM:History> \ +<rdf:Seq> <rdf:l\ +i stEvt:action=\x22\ +created\x22 stEvt:i\ +nstanceID=\x22xmp.i\ +id:dd5d3a3e-a046\ +-5146-8e7a-14b62\ +eeb350d\x22 stEvt:w\ +hen=\x222020-03-03T\ +09:50:39-03:00\x22 \ +stEvt:softwareAg\ +ent=\x22Adobe Photo\ +shop 21.0 (Windo\ +ws)\x22/> <rdf:li s\ +tEvt:action=\x22sav\ +ed\x22 stEvt:instan\ +ceID=\x22xmp.iid:ca\ +98d4da-7bf3-f243\ +-8228-c9b9b28efe\ +0b\x22 stEvt:when=\x22\ +2020-05-02T17:58\ +:25-03:00\x22 stEvt\ +:softwareAgent=\x22\ +Adobe Photoshop \ +21.0 (Windows)\x22 \ +stEvt:changed=\x22/\ +\x22/> <rdf:li stEv\ +t:action=\x22saved\x22\ + stEvt:instanceI\ +D=\x22xmp.iid:6fd79\ +7e6-6ff4-9a44-95\ +57-6799fe88bef9\x22\ + stEvt:when=\x22202\ +3-12-19T12:32:13\ ++01:00\x22 stEvt:so\ +ftwareAgent=\x22Ado\ +be Photoshop 21.\ +1 (Windows)\x22 stE\ +vt:changed=\x22/\x22/>\ + </rdf:Seq> </xm\ +pMM:History> </r\ +df:Description> \ +</rdf:RDF> </x:x\ +mpmeta> <?xpacke\ +t end=\x22r\x22?>\x87\x8a\xb4f\x00\ +\x00\x00\x95IDAT8\xcb\xad\x93A\x0a\x830\x10\ +Eg%\xf4*\x9a\xb3\xd6,\x0c\xf4.\xa5\xb7(\xea\ +=ta\xc4b\xff\xc0D\xc2\x80\x053]\xbcE\x92\ +\xf9\x0f2\x99\xd0ND\x8a\x1a\xdc\xc1\x00\x16\xa1\x97\xbd\ +Z\xd7\xe7\x8b\x0at\xe0\x03\xf6\x13\xf8\xec\x01nZ\xc0\ +\xe1\xe7\x8f\xa0\xe6%\x99C\x10.\x84\x13]\x124`\ ++\x10p\xa6aA[\x10N\xb4$\xdd.\x15\x0c,\ +\x88\x06A\xb4\x0a&\xeb\x15\xde\xd6&z\x168\xc33\ +:\xcb \x85\xbf\x8e\xb2\xf93\xe5\xf0h{0\x82\x15\ +\xcc\xdcm\xd9s\xba\xfe\x0bX\xa9\xc6a\xd5\xd1B\x1d\ +\x00\x00\x00\x00IEND\xaeB`\x82\ \x00\x00\x07{\ \x89\ PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ @@ -30587,6 +31587,34 @@ qt_resource_name = b"\ \x00o\xa6S\ \x00i\ \x00c\x00o\x00n\x00s\ +\x00\x09\ +\x06V\x95\x87\ +\x00i\ +\x00c\x00o\x00n\x00s\x00-\x00s\x00v\x00g\ +\x00\x14\ +\x07^\x97G\ +\x00c\ +\x00i\x00l\x00-\x00m\x00e\x00d\x00i\x00a\x00-\x00r\x00e\x00c\x00o\x00r\x00d\x00.\ +\x00s\x00v\x00g\ +\x00\x12\ +\x06B\x83G\ +\x00c\ +\x00i\x00l\x00-\x00m\x00e\x00d\x00i\x00a\x00-\x00s\x00t\x00o\x00p\x00.\x00s\x00v\ +\x00g\ +\x00\x0e\ +\x0a\x9eH\xc7\ +\x00c\ +\x00i\x00l\x00-\x00r\x00e\x00l\x00o\x00a\x00d\x00.\x00s\x00v\x00g\ +\x00\x13\ +\x0e\x8d\xdag\ +\x00c\ +\x00i\x00l\x00-\x00m\x00e\x00d\x00i\x00a\x00-\x00p\x00a\x00u\x00s\x00e\x00.\x00s\ +\x00v\x00g\ +\x00\x12\ +\x0f\xad\x82\xe7\ +\x00c\ +\x00i\x00l\x00-\x00m\x00e\x00d\x00i\x00a\x00-\x00p\x00l\x00a\x00y\x00.\x00s\x00v\ +\x00g\ \x00\x10\ \x00\xa4\xda'\ \x00c\ @@ -30845,6 +31873,10 @@ qt_resource_name = b"\ \x00c\ \x00i\x00l\x00-\x00a\x00r\x00r\x00o\x00w\x00-\x00l\x00e\x00f\x00t\x00.\x00p\x00n\ \x00g\ +\x00\x0e\ +\x09nZ\xe7\ +\x00c\ +\x00i\x00l\x00-\x00r\x00e\x00c\x00o\x00r\x00d\x00.\x00p\x00n\x00g\ \x00\x0d\ \x09e\xcd\xe7\ \x00c\ @@ -31770,525 +32802,541 @@ qt_resource_name = b"\ " qt_resource_struct = b"\ -\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x09\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x10\x00\x02\x00\x00\x00\x01\x00\x00\x00\x03\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ +\x00\x00\x00\x10\x00\x02\x00\x00\x00\x05\x00\x00\x00\x04\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x00\x00\x00\x02\x00\x00\x01\x01\x00\x00\x00\x03\ +\x00\x00\x00V\x00\x00\x00\x00\x00\x01\x00\x00\x0c\xf9\ +\x00\x00\x01\x8c\x82\x0a\x8e\x98\ +\x00\x00\x00(\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00\x01\x8c\x82\x1by\xc8\ +\x00\x00\x00\x80\x00\x00\x00\x00\x00\x01\x00\x00\x19\x1c\ +\x00\x00\x01\x8c\x820\xf4\x8b\ +\x00\x00\x00\xa2\x00\x00\x00\x00\x00\x01\x00\x00&\xad\ +\x00\x00\x01\x8c\x82\x09\xe1%\ +\x00\x00\x00\xce\x00\x00\x00\x00\x00\x01\x00\x003K\ +\x00\x00\x01\x8c\x82\x01\xdf*\ +\x00\x00\x00\x00\x00\x02\x00\x00\x01\x02\x00\x00\x00\x0a\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x07\xee\x00\x00\x00\x00\x00\x01\x00\x01W\xfe\ +\x00\x00\x08\xd6\x00\x00\x00\x00\x00\x01\x00\x01\x8e\x00\ \x00\x00\x01\x88v;G>\ -\x00\x00\x1f\x00\x00\x00\x00\x00\x00\x01\x00\x05Zx\ +\x00\x00 \x0a\x00\x00\x00\x00\x00\x01\x00\x05\x98+\ \x00\x00\x01\x88v;F\xfe\ -\x00\x00\x03\xbc\x00\x00\x00\x00\x00\x01\x00\x00\x9el\ +\x00\x00\x04\xa4\x00\x00\x00\x00\x00\x01\x00\x00\xd4n\ \x00\x00\x01\x88v;G$\ -\x00\x00\x08\x16\x00\x00\x00\x00\x00\x01\x00\x01_V\ +\x00\x00\x08\xfe\x00\x00\x00\x00\x00\x01\x00\x01\x95X\ \x00\x00\x01\x88v;F\x05\ -\x00\x00\x02,\x00\x00\x00\x00\x00\x01\x00\x00[L\ +\x00\x00\x03\x14\x00\x00\x00\x00\x00\x01\x00\x00\x91N\ \x00\x00\x01\x88v;F\xa7\ -\x00\x00&\xce\x00\x00\x00\x00\x00\x01\x00\x06\xbf*\ +\x00\x00'\xd8\x00\x00\x00\x00\x00\x01\x00\x06\xfc\xdd\ \x00\x00\x01\x88v;F\xa8\ -\x00\x00$\xf2\x00\x00\x00\x00\x00\x01\x00\x06k\xd3\ +\x00\x00%\xfc\x00\x00\x00\x00\x00\x01\x00\x06\xa9\x86\ \x00\x00\x01\x88v;E\xf1\ -\x00\x00\x02\xc6\x00\x00\x00\x00\x00\x01\x00\x00y1\ +\x00\x00\x03\xae\x00\x00\x00\x00\x00\x01\x00\x00\xaf3\ \x00\x00\x01\x88v;Fj\ -\x00\x00\x13\x9c\x00\x00\x00\x00\x00\x01\x00\x03\x5c\x9c\ +\x00\x00\x14\xa6\x00\x00\x00\x00\x00\x01\x00\x03\x9aO\ \x00\x00\x01\x88v;E\xf2\ -\x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00\x00\xf8\x00\x00\x00\x00\x00\x01\x00\x006\x02\ \x00\x00\x01\x88v;F\x9d\ -\x00\x00\x14\xe4\x00\x00\x00\x00\x00\x01\x00\x03\x98n\ +\x00\x00\x15\xee\x00\x00\x00\x00\x00\x01\x00\x03\xd6!\ \x00\x00\x01\x88v;F#\ -\x00\x00\x0f\xe2\x00\x00\x00\x00\x00\x01\x00\x02\xc7M\ +\x00\x00\x10\xec\x00\x00\x00\x00\x00\x01\x00\x03\x05\x00\ \x00\x00\x01\x88v;F\x84\ -\x00\x00\x04\xf2\x00\x00\x00\x00\x00\x01\x00\x00\xd18\ +\x00\x00\x05\xda\x00\x00\x00\x00\x00\x01\x00\x01\x07:\ \x00\x00\x01\x88v;F\x86\ -\x00\x00%\x0c\x00\x00\x00\x00\x00\x01\x00\x06sx\ +\x00\x00&\x16\x00\x00\x00\x00\x00\x01\x00\x06\xb1+\ \x00\x00\x01\x88v;F\xcc\ -\x00\x00\x1d\xb6\x00\x00\x00\x00\x00\x01\x00\x05\x1f\x9e\ +\x00\x00\x1e\xc0\x00\x00\x00\x00\x00\x01\x00\x05]Q\ \x00\x00\x01\x88v;F\x1d\ -\x00\x00\x17,\x00\x00\x00\x00\x00\x01\x00\x04\x00\xf5\ +\x00\x00\x186\x00\x00\x00\x00\x00\x01\x00\x04>\xa8\ \x00\x00\x01\x88v;E\xfb\ -\x00\x00\x01\xda\x00\x00\x00\x00\x00\x01\x00\x00L\x8d\ +\x00\x00\x02\xc2\x00\x00\x00\x00\x00\x01\x00\x00\x82\x8f\ \x00\x00\x01\x88v;F3\ -\x00\x00\x02T\x00\x00\x00\x00\x00\x01\x00\x00b\xd9\ +\x00\x00\x03<\x00\x00\x00\x00\x00\x01\x00\x00\x98\xdb\ \x00\x00\x01\x88v;G=\ -\x00\x00\x1c\xfa\x00\x00\x00\x00\x00\x01\x00\x05\x01/\ +\x00\x00\x1e\x04\x00\x00\x00\x00\x00\x01\x00\x05>\xe2\ \x00\x00\x01\x88v;F6\ -\x00\x00\x1f\xb0\x00\x00\x00\x00\x00\x01\x00\x05q\xb2\ +\x00\x00 \xba\x00\x00\x00\x00\x00\x01\x00\x05\xafe\ \x00\x00\x01\x88v;F\x99\ -\x00\x00\x01\x0c\x00\x00\x00\x00\x00\x01\x00\x00&\xac\ +\x00\x00\x01\xf4\x00\x00\x00\x00\x00\x01\x00\x00\x5c\xae\ \x00\x00\x01\x88v;G\x14\ -\x00\x00\x00V\x00\x00\x00\x00\x00\x01\x00\x00\x0e\xd1\ +\x00\x00\x01>\x00\x00\x00\x00\x00\x01\x00\x00D\xd3\ \x00\x00\x01\x88v;F;\ -\x00\x00!\xfa\x00\x00\x00\x00\x00\x01\x00\x05\xd3\xa5\ +\x00\x00#\x04\x00\x00\x00\x00\x00\x01\x00\x06\x11X\ \x00\x00\x01\x88v;G\x16\ -\x00\x00(\x90\x00\x00\x00\x00\x00\x01\x00\x07\x10|\ +\x00\x00)\x9a\x00\x00\x00\x00\x00\x01\x00\x07N/\ \x00\x00\x01\x88v;F\x9b\ -\x00\x00\x10D\x00\x00\x00\x00\x00\x01\x00\x02\xd6$\ +\x00\x00\x11N\x00\x00\x00\x00\x00\x01\x00\x03\x13\xd7\ \x00\x00\x01\x88v;E\xf3\ -\x00\x00\x1a*\x00\x00\x00\x00\x00\x01\x00\x04\x82\x7f\ +\x00\x00\x1b4\x00\x00\x00\x00\x00\x01\x00\x04\xc02\ \x00\x00\x01\x88v;Fv\ -\x00\x00\x0c\xae\x00\x00\x00\x00\x00\x01\x00\x028`\ +\x00\x00\x0d\xb8\x00\x00\x00\x00\x00\x01\x00\x02v\x13\ \x00\x00\x01\x88v;Fq\ -\x00\x00\x17\xe4\x00\x00\x00\x00\x00\x01\x00\x04\x1er\ +\x00\x00\x18\xee\x00\x00\x00\x00\x00\x01\x00\x04\x5c%\ \x00\x00\x01\x88v;Fp\ -\x00\x00\x12\x88\x00\x00\x00\x00\x00\x01\x00\x03/\xab\ +\x00\x00\x13\x92\x00\x00\x00\x00\x00\x01\x00\x03m^\ \x00\x00\x01\x88v;F5\ -\x00\x00\x1f\x8e\x00\x00\x00\x00\x00\x01\x00\x05p\xdc\ +\x00\x00 \x98\x00\x00\x00\x00\x00\x01\x00\x05\xae\x8f\ \x00\x00\x01\x88v;GE\ -\x00\x00$\xd0\x00\x00\x00\x00\x00\x01\x00\x06d0\ +\x00\x00%\xda\x00\x00\x00\x00\x00\x01\x00\x06\xa1\xe3\ \x00\x00\x01\x88v;F\x94\ -\x00\x00\x098\x00\x00\x00\x00\x00\x01\x00\x01\x93\x91\ +\x00\x00\x0a \x00\x00\x00\x00\x00\x01\x00\x01\xc9\x93\ \x00\x00\x01\x88v;F\x09\ -\x00\x00\x15\x82\x00\x00\x00\x00\x00\x01\x00\x03\xb5\xcc\ +\x00\x00\x16\x8c\x00\x00\x00\x00\x00\x01\x00\x03\xf3\x7f\ \x00\x00\x01\x88v;Ff\ -\x00\x00\x1bh\x00\x00\x00\x00\x00\x01\x00\x04\xbe%\ +\x00\x00\x1cr\x00\x00\x00\x00\x00\x01\x00\x04\xfb\xd8\ \x00\x00\x01\x88v;F\xa4\ -\x00\x00\x0a(\x00\x00\x00\x00\x00\x01\x00\x01\xb9\xe0\ +\x00\x00\x0b2\x00\x00\x00\x00\x00\x01\x00\x01\xf7\x93\ \x00\x00\x01\x88v;F\xb7\ -\x00\x00\x1e$\x00\x00\x00\x00\x00\x01\x00\x055s\ +\x00\x00\x1f.\x00\x00\x00\x00\x00\x01\x00\x05s&\ \x00\x00\x01\x88v;FA\ -\x00\x00\x0c\xf8\x00\x00\x00\x00\x00\x01\x00\x02G\xa4\ +\x00\x00\x0e\x02\x00\x00\x00\x00\x00\x01\x00\x02\x85W\ \x00\x00\x01\x88v;FI\ -\x00\x00\x11\xb8\x00\x00\x00\x00\x00\x01\x00\x03\x0aR\ +\x00\x00\x12\xc2\x00\x00\x00\x00\x00\x01\x00\x03H\x05\ \x00\x00\x01\x88v;G@\ -\x00\x00\x0b\xe0\x00\x00\x00\x00\x00\x01\x00\x02\x13i\ +\x00\x00\x0c\xea\x00\x00\x00\x00\x00\x01\x00\x02Q\x1c\ \x00\x00\x01\x88v;F \ -\x00\x00\x06\x1c\x00\x00\x00\x00\x00\x01\x00\x01\x06 \ +\x00\x00\x07\x04\x00\x00\x00\x00\x00\x01\x00\x01<\x22\ \x00\x00\x01\x88v;F\xc4\ -\x00\x00\x17\x86\x00\x00\x00\x00\x00\x01\x00\x04\x0f\xce\ +\x00\x00\x18\x90\x00\x00\x00\x00\x00\x01\x00\x04M\x81\ \x00\x00\x01\x88v;F\xc7\ -\x00\x00\x19^\x00\x00\x00\x00\x00\x01\x00\x04b\xa0\ +\x00\x00\x1ah\x00\x00\x00\x00\x00\x01\x00\x04\xa0S\ \x00\x00\x01\x88v;F\xa0\ -\x00\x00':\x00\x00\x00\x00\x00\x01\x00\x06\xd5D\ +\x00\x00(D\x00\x00\x00\x00\x00\x01\x00\x07\x12\xf7\ \x00\x00\x01\x88v;G\x03\ -\x00\x00\x1e\xa0\x00\x00\x00\x00\x00\x01\x00\x05K\xaa\ +\x00\x00\x1f\xaa\x00\x00\x00\x00\x00\x01\x00\x05\x89]\ \x00\x00\x01\x88v;G\x05\ -\x00\x00\x0dT\x00\x00\x00\x00\x00\x01\x00\x02V\x94\ +\x00\x00\x0e^\x00\x00\x00\x00\x00\x01\x00\x02\x94G\ \x00\x00\x01\x88v;F\x0d\ -\x00\x00\x1fd\x00\x00\x00\x00\x00\x01\x00\x05i?\ +\x00\x00 n\x00\x00\x00\x00\x00\x01\x00\x05\xa6\xf2\ \x00\x00\x01\x88v;G/\ -\x00\x00\x03\xfc\x00\x00\x00\x00\x00\x01\x00\x00\xa5w\ +\x00\x00\x04\xe4\x00\x00\x00\x00\x00\x01\x00\x00\xdby\ \x00\x00\x01\x88v;F\xcb\ -\x00\x00!\xd0\x00\x00\x00\x00\x00\x01\x00\x05\xcc\x0b\ +\x00\x00\x22\xda\x00\x00\x00\x00\x00\x01\x00\x06\x09\xbe\ \x00\x00\x01\x88v;G\x18\ -\x00\x00\x22\xd8\x00\x00\x00\x00\x00\x01\x00\x05\xff\x99\ +\x00\x00#\xe2\x00\x00\x00\x00\x00\x01\x00\x06=L\ \x00\x00\x01\x88v;F\x87\ -\x00\x00\x06\xb8\x00\x00\x00\x00\x00\x01\x00\x01#\xfa\ +\x00\x00\x07\xa0\x00\x00\x00\x00\x00\x01\x00\x01Y\xfc\ \x00\x00\x01\x88v;FN\ -\x00\x00\x03V\x00\x00\x00\x00\x00\x01\x00\x00\x8f\xc0\ +\x00\x00\x04>\x00\x00\x00\x00\x00\x01\x00\x00\xc5\xc2\ \x00\x00\x01\x88v;F{\ -\x00\x00\x04x\x00\x00\x00\x00\x00\x01\x00\x00\xbc\x1d\ +\x00\x00\x05`\x00\x00\x00\x00\x00\x01\x00\x00\xf2\x1f\ \x00\x00\x01\x88v;G<\ -\x00\x00\x17\xb0\x00\x00\x00\x00\x00\x01\x00\x04\x17U\ +\x00\x00\x18\xba\x00\x00\x00\x00\x00\x01\x00\x04U\x08\ \x00\x00\x01\x88v;G:\ -\x00\x00!6\x00\x00\x00\x00\x00\x01\x00\x05\xaf(\ +\x00\x00\x22@\x00\x00\x00\x00\x00\x01\x00\x05\xec\xdb\ \x00\x00\x01\x88v;F\x98\ -\x00\x00\x1e|\x00\x00\x00\x00\x00\x01\x00\x05C\xf0\ +\x00\x00\x1f\x86\x00\x00\x00\x00\x00\x01\x00\x05\x81\xa3\ \x00\x00\x01\x88v;F\xd8\ -\x00\x00#\xc0\x00\x00\x00\x00\x00\x01\x00\x060`\ +\x00\x00$\xca\x00\x00\x00\x00\x00\x01\x00\x06n\x13\ \x00\x00\x01\x88v;G*\ -\x00\x00\x02\xa8\x00\x00\x00\x00\x00\x01\x00\x00q\xc6\ +\x00\x00\x03\x90\x00\x00\x00\x00\x00\x01\x00\x00\xa7\xc8\ \x00\x00\x01\x88v;F`\ -\x00\x00\x1a\x96\x00\x00\x00\x00\x00\x01\x00\x04\x91\x84\ +\x00\x00\x1b\xa0\x00\x00\x00\x00\x00\x01\x00\x04\xcf7\ \x00\x00\x01\x88v;E\xfd\ -\x00\x00\x0c\xd6\x00\x00\x00\x00\x00\x01\x00\x02@\x19\ +\x00\x00\x0d\xe0\x00\x00\x00\x00\x00\x01\x00\x02}\xcc\ \x00\x00\x01\x88v;FH\ -\x00\x00\x09\xa2\x00\x00\x00\x00\x00\x01\x00\x01\xa3\x0e\ +\x00\x00\x0a\xac\x00\x00\x00\x00\x00\x01\x00\x01\xe0\xc1\ \x00\x00\x01\x88v;F\xda\ -\x00\x00\x18\xa8\x00\x00\x00\x00\x00\x01\x00\x04<\xa5\ +\x00\x00\x19\xb2\x00\x00\x00\x00\x00\x01\x00\x04zX\ \x00\x00\x01\x88v;G\x0b\ -\x00\x00#Z\x00\x00\x00\x00\x00\x01\x00\x06\x19\x98\ +\x00\x00$d\x00\x00\x00\x00\x00\x01\x00\x06WK\ \x00\x00\x01\x88v;F+\ -\x00\x00\x22\x18\x00\x00\x00\x00\x00\x01\x00\x05\xda\xe3\ +\x00\x00#\x22\x00\x00\x00\x00\x00\x01\x00\x06\x18\x96\ \x00\x00\x01\x88v;F\x9f\ -\x00\x00\x12\xcc\x00\x00\x00\x00\x00\x01\x00\x037S\ +\x00\x00\x13\xd6\x00\x00\x00\x00\x00\x01\x00\x03u\x06\ \x00\x00\x01\x88v;F\xbf\ -\x00\x00 B\x00\x00\x00\x00\x00\x01\x00\x05\x87\xd0\ +\x00\x00!L\x00\x00\x00\x00\x00\x01\x00\x05\xc5\x83\ \x00\x00\x01\x88v;F\xbb\ -\x00\x00\x05\xf6\x00\x00\x00\x00\x00\x01\x00\x00\xfe\x7f\ +\x00\x00\x06\xde\x00\x00\x00\x00\x00\x01\x00\x014\x81\ \x00\x00\x01\x88v;F\x0e\ -\x00\x00 \xf0\x00\x00\x00\x00\x00\x01\x00\x05\xa5\xba\ +\x00\x00!\xfa\x00\x00\x00\x00\x00\x01\x00\x05\xe3m\ \x00\x00\x01\x88v;F\xe5\ -\x00\x00&l\x00\x00\x00\x00\x00\x01\x00\x06\xaf\xd2\ +\x00\x00'v\x00\x00\x00\x00\x00\x01\x00\x06\xed\x85\ \x00\x00\x01\x88v;F\xe0\ -\x00\x00\x16\x04\x00\x00\x00\x00\x00\x01\x00\x03\xcb\xd4\ +\x00\x00\x17\x0e\x00\x00\x00\x00\x00\x01\x00\x04\x09\x87\ \x00\x00\x01\x88v;G#\ -\x00\x00\x1f8\x00\x00\x00\x00\x00\x01\x00\x05a\xba\ +\x00\x00 B\x00\x00\x00\x00\x00\x01\x00\x05\x9fm\ \x00\x00\x01\x88v;F\xad\ -\x00\x00\x07\x12\x00\x00\x00\x00\x00\x01\x00\x013\x09\ +\x00\x00\x07\xfa\x00\x00\x00\x00\x00\x01\x00\x01i\x0b\ \x00\x00\x01\x88v;Ft\ -\x00\x00\x08n\x00\x00\x00\x00\x00\x01\x00\x01n\xbc\ +\x00\x00\x09V\x00\x00\x00\x00\x00\x01\x00\x01\xa4\xbe\ \x00\x00\x01\x88v;F|\ -\x00\x00\x14\xb8\x00\x00\x00\x00\x00\x01\x00\x03\x91\x18\ +\x00\x00\x15\xc2\x00\x00\x00\x00\x00\x01\x00\x03\xce\xcb\ \x00\x00\x01\x88v;FB\ -\x00\x00\x0d\x96\x00\x00\x00\x00\x00\x01\x00\x02e\xfd\ +\x00\x00\x0e\xa0\x00\x00\x00\x00\x00\x01\x00\x02\xa3\xb0\ \x00\x00\x01\x88v;F\xaf\ -\x00\x00\x1e\xd8\x00\x00\x00\x00\x00\x01\x00\x05R\xfe\ +\x00\x00\x1f\xe2\x00\x00\x00\x00\x00\x01\x00\x05\x90\xb1\ \x00\x00\x01\x88v;G\x13\ -\x00\x00\x22\xac\x00\x00\x00\x00\x00\x01\x00\x05\xf8t\ +\x00\x00#\xb6\x00\x00\x00\x00\x00\x01\x00\x066'\ \x00\x00\x01\x88v;E\xff\ -\x00\x00\x1a\xc0\x00\x00\x00\x00\x00\x01\x00\x04\x98\xb6\ +\x00\x00\x1b\xca\x00\x00\x00\x00\x00\x01\x00\x04\xd6i\ \x00\x00\x01\x88v;F\xa1\ -\x00\x00\x16\xd4\x00\x00\x00\x00\x00\x01\x00\x03\xf1\xc0\ +\x00\x00\x17\xde\x00\x00\x00\x00\x00\x01\x00\x04/s\ \x00\x00\x01\x88v;Fr\ -\x00\x00\x22l\x00\x00\x00\x00\x00\x01\x00\x05\xe9\x0a\ +\x00\x00#v\x00\x00\x00\x00\x00\x01\x00\x06&\xbd\ \x00\x00\x01\x88v;F~\ -\x00\x00\x0c\x02\x00\x00\x00\x00\x00\x01\x00\x02\x1a\xfd\ +\x00\x00\x0d\x0c\x00\x00\x00\x00\x00\x01\x00\x02X\xb0\ \x00\x00\x01\x88v;G-\ -\x00\x00)\x84\x00\x00\x00\x00\x00\x01\x00\x07<\xae\ +\x00\x00*\x8e\x00\x00\x00\x00\x00\x01\x00\x07za\ \x00\x00\x01\x88v;F\xab\ -\x00\x00\x0d\xec\x00\x00\x00\x00\x00\x01\x00\x02t\xd1\ +\x00\x00\x0e\xf6\x00\x00\x00\x00\x00\x01\x00\x02\xb2\x84\ \x00\x00\x01\x88v;F\x19\ -\x00\x00\x13~\x00\x00\x00\x00\x00\x01\x00\x03U:\ +\x00\x00\x14\x88\x00\x00\x00\x00\x00\x01\x00\x03\x92\xed\ \x00\x00\x01\x88v;F(\ -\x00\x00\x16\x86\x00\x00\x00\x00\x00\x01\x00\x03\xe2c\ +\x00\x00\x17\x90\x00\x00\x00\x00\x00\x01\x00\x04 \x16\ \x00\x00\x01\x88v;G\x10\ -\x00\x00#\x82\x00\x00\x00\x00\x00\x01\x00\x06!8\ +\x00\x00$\x8c\x00\x00\x00\x00\x00\x01\x00\x06^\xeb\ \x00\x00\x01\x88v;F'\ -\x00\x00$J\x00\x00\x00\x00\x00\x01\x00\x06FC\ +\x00\x00%T\x00\x00\x00\x00\x00\x01\x00\x06\x83\xf6\ \x00\x00\x01\x88v;G\x1a\ -\x00\x00\x150\x00\x00\x00\x00\x00\x01\x00\x03\xa7\x06\ +\x00\x00\x16:\x00\x00\x00\x00\x00\x01\x00\x03\xe4\xb9\ \x00\x00\x01\x88v;F\x97\ -\x00\x00\x08\xbc\x00\x00\x00\x00\x00\x01\x00\x01}s\ +\x00\x00\x09\xa4\x00\x00\x00\x00\x00\x01\x00\x01\xb3u\ \x00\x00\x01\x88v;F]\ -\x00\x00&\x9a\x00\x00\x00\x00\x00\x01\x00\x06\xb7w\ +\x00\x00'\xa4\x00\x00\x00\x00\x00\x01\x00\x06\xf5*\ \x00\x00\x01\x88v;Fb\ -\x00\x00'\x1c\x00\x00\x00\x00\x00\x01\x00\x06\xcd\xb1\ +\x00\x00(&\x00\x00\x00\x00\x00\x01\x00\x07\x0bd\ \x00\x00\x01\x88v;F\xfa\ -\x00\x00\x15\x0e\x00\x00\x00\x00\x00\x01\x00\x03\x9f\xaf\ +\x00\x00\x16\x18\x00\x00\x00\x00\x00\x01\x00\x03\xddb\ \x00\x00\x01\x88v;F\xe8\ -\x00\x00(F\x00\x00\x00\x00\x00\x01\x00\x07\x01\xff\ +\x00\x00)P\x00\x00\x00\x00\x00\x01\x00\x07?\xb2\ \x00\x00\x01\x88v;F\xa6\ -\x00\x00\x16R\x00\x00\x00\x00\x00\x01\x00\x03\xda\xda\ +\x00\x00\x17\x5c\x00\x00\x00\x00\x00\x01\x00\x04\x18\x8d\ \x00\x00\x01\x88v;F\x81\ -\x00\x00!V\x00\x00\x00\x00\x00\x01\x00\x05\xb6\x7f\ +\x00\x00\x22`\x00\x00\x00\x00\x00\x01\x00\x05\xf42\ \x00\x00\x01\x88v;F\xc8\ -\x00\x00\x14,\x00\x00\x00\x00\x00\x01\x00\x03z\xce\ +\x00\x00\x156\x00\x00\x00\x00\x00\x01\x00\x03\xb8\x81\ \x00\x00\x01\x88v;F\xc1\ -\x00\x00\x0fb\x00\x00\x00\x00\x00\x01\x00\x02\xb0\xcd\ +\x00\x00\x10l\x00\x00\x00\x00\x00\x01\x00\x02\xee\x80\ \x00\x00\x01\x88v;F\x1e\ -\x00\x00\x018\x00\x00\x00\x00\x00\x01\x00\x00.\x18\ +\x00\x00\x02 \x00\x00\x00\x00\x00\x01\x00\x00d\x1a\ \x00\x00\x01\x88v;FU\ -\x00\x00\x0ab\x00\x00\x00\x00\x00\x01\x00\x01\xc8\xce\ +\x00\x00\x0bl\x00\x00\x00\x00\x00\x01\x00\x02\x06\x81\ \x00\x00\x01\x88v;F/\ -\x00\x00\x1b\x10\x00\x00\x00\x00\x00\x01\x00\x04\xa7x\ +\x00\x00\x1c\x1a\x00\x00\x00\x00\x00\x01\x00\x04\xe5+\ \x00\x00\x01\x88v;F\xec\ -\x00\x00\x0e@\x00\x00\x00\x00\x00\x01\x00\x02\x83\xad\ +\x00\x00\x0fJ\x00\x00\x00\x00\x00\x01\x00\x02\xc1`\ \x00\x00\x01\x88v;Fu\ -\x00\x00\x1a\x0a\x00\x00\x00\x00\x00\x01\x00\x04z\xdb\ +\x00\x00\x1b\x14\x00\x00\x00\x00\x00\x01\x00\x04\xb8\x8e\ \x00\x00\x01\x88v;FD\ -\x00\x00\x1f\xe2\x00\x00\x00\x00\x00\x01\x00\x05x\xd0\ +\x00\x00 \xec\x00\x00\x00\x00\x00\x01\x00\x05\xb6\x83\ \x00\x00\x01\x88v;Fa\ -\x00\x00\x05\x22\x00\x00\x00\x00\x00\x01\x00\x00\xd8\xbe\ +\x00\x00\x06\x0a\x00\x00\x00\x00\x00\x01\x00\x01\x0e\xc0\ \x00\x00\x01\x88v;F0\ -\x00\x00\x10\x18\x00\x00\x00\x00\x00\x01\x00\x02\xce\xc6\ +\x00\x00\x11\x22\x00\x00\x00\x00\x00\x01\x00\x03\x0cy\ \x00\x00\x01\x88v;E\xf6\ -\x00\x00\x09\x82\x00\x00\x00\x00\x00\x01\x00\x01\xa2\x12\ +\x00\x00\x0a\x8c\x00\x00\x00\x00\x00\x01\x00\x01\xdf\xc5\ \x00\x00\x01\x88v;GH\ -\x00\x00\x13\xe0\x00\x00\x00\x00\x00\x01\x00\x03k\xeb\ +\x00\x00\x14\xea\x00\x00\x00\x00\x00\x01\x00\x03\xa9\x9e\ \x00\x00\x01\x88v;G'\ -\x00\x00\x1c\x1e\x00\x00\x00\x00\x00\x01\x00\x04\xdb\xd0\ +\x00\x00\x1d(\x00\x00\x00\x00\x00\x01\x00\x05\x19\x83\ \x00\x00\x01\x88v;G%\ -\x00\x00\x15\x5c\x00\x00\x00\x00\x00\x01\x00\x03\xae\x82\ +\x00\x00\x16f\x00\x00\x00\x00\x00\x01\x00\x03\xec5\ \x00\x00\x01\x88v;G\x1c\ -\x00\x00&4\x00\x00\x00\x00\x00\x01\x00\x06\xa8\x0b\ +\x00\x00'>\x00\x00\x00\x00\x00\x01\x00\x06\xe5\xbe\ \x00\x00\x01\x88v;E\xf7\ -\x00\x00\x01V\x00\x00\x00\x00\x00\x01\x00\x005\xa8\ +\x00\x00\x02>\x00\x00\x00\x00\x00\x01\x00\x00k\xaa\ \x00\x00\x01\x88v;F\x06\ -\x00\x00\x122\x00\x00\x00\x00\x00\x01\x00\x03 \x94\ +\x00\x00\x13<\x00\x00\x00\x00\x00\x01\x00\x03^G\ \x00\x00\x01\x88v;FW\ -\x00\x00\x09\xbe\x00\x00\x00\x00\x00\x01\x00\x01\xaa\x89\ +\x00\x00\x0a\xc8\x00\x00\x00\x00\x00\x01\x00\x01\xe8<\ \x00\x00\x01\x88v;F\x1b\ -\x00\x00%\xec\x00\x00\x00\x00\x00\x01\x00\x06\x99\x1c\ +\x00\x00&\xf6\x00\x00\x00\x00\x00\x01\x00\x06\xd6\xcf\ \x00\x00\x01\x88v;G\x0e\ -\x00\x00'\xee\x00\x00\x00\x00\x00\x01\x00\x06\xf3\x0a\ +\x00\x00(\xf8\x00\x00\x00\x00\x00\x01\x00\x070\xbd\ \x00\x00\x01\x88v;G!\ -\x00\x00\x22\xf2\x00\x00\x00\x00\x00\x01\x00\x06\x07\x22\ +\x00\x00#\xfc\x00\x00\x00\x00\x00\x01\x00\x06D\xd5\ \x00\x00\x01\x88v;FY\ -\x00\x00\x0bx\x00\x00\x00\x00\x00\x01\x00\x01\xfc\x8b\ +\x00\x00\x0c\x82\x00\x00\x00\x00\x00\x01\x00\x02:>\ \x00\x00\x01\x88v;F\x80\ -\x00\x00\x1c\x96\x00\x00\x00\x00\x00\x01\x00\x04\xf2B\ +\x00\x00\x1d\xa0\x00\x00\x00\x00\x00\x01\x00\x05/\xf5\ \x00\x00\x01\x88v;FM\ -\x00\x00\x030\x00\x00\x00\x00\x00\x01\x00\x00\x88M\ +\x00\x00\x04\x18\x00\x00\x00\x00\x00\x01\x00\x00\xbeO\ \x00\x00\x01\x88v;F\x8c\ -\x00\x00\x1d\x8a\x00\x00\x00\x00\x00\x01\x00\x05\x17\xe5\ +\x00\x00\x1e\x94\x00\x00\x00\x00\x00\x01\x00\x05U\x98\ \x00\x00\x01\x88v;G\x01\ -\x00\x00!\xa6\x00\x00\x00\x00\x00\x01\x00\x05\xc4\x9c\ +\x00\x00\x22\xb0\x00\x00\x00\x00\x00\x01\x00\x06\x02O\ \x00\x00\x01\x88v;F\x8a\ -\x00\x00(\xbe\x00\x00\x00\x00\x00\x01\x00\x07\x17\x96\ +\x00\x00)\xc8\x00\x00\x00\x00\x00\x01\x00\x07UI\ \x00\x00\x01\x88v;F\x02\ -\x00\x00\x0f\x1c\x00\x00\x00\x00\x00\x01\x00\x02\xa9(\ +\x00\x00\x10&\x00\x00\x00\x00\x00\x01\x00\x02\xe6\xdb\ \x00\x00\x01\x88v;F7\ -\x00\x00\x14\x0c\x00\x00\x00\x00\x00\x01\x00\x03s\x85\ +\x00\x00\x15\x16\x00\x00\x00\x00\x00\x01\x00\x03\xb18\ \x00\x00\x01\x88v;F2\ -\x00\x00\x22\x8c\x00\x00\x00\x00\x00\x01\x00\x05\xf0\xb7\ +\x00\x00#\x96\x00\x00\x00\x00\x00\x01\x00\x06.j\ \x00\x00\x01\x88v;G\x1d\ -\x00\x00\x19\xe2\x00\x00\x00\x00\x00\x01\x00\x04yU\ +\x00\x00\x1a\xec\x00\x00\x00\x00\x00\x01\x00\x04\xb7\x08\ \x00\x00\x01\x88v;GL\ -\x00\x00\x03\x82\x00\x00\x00\x00\x00\x01\x00\x00\x97E\ +\x00\x00\x04j\x00\x00\x00\x00\x00\x01\x00\x00\xcdG\ \x00\x00\x01\x88v;F\xdd\ -\x00\x00\x0a\xd4\x00\x00\x00\x00\x00\x01\x00\x01\xdeN\ +\x00\x00\x0b\xde\x00\x00\x00\x00\x00\x01\x00\x02\x1c\x01\ \x00\x00\x01\x88v;Fe\ -\x00\x00\x06\xe4\x00\x00\x00\x00\x00\x01\x00\x01+\x82\ +\x00\x00\x07\xcc\x00\x00\x00\x00\x00\x01\x00\x01a\x84\ \x00\x00\x01\x88v;F\xac\ -\x00\x00\x0aD\x00\x00\x00\x00\x00\x01\x00\x01\xc1X\ +\x00\x00\x0bN\x00\x00\x00\x00\x00\x01\x00\x01\xff\x0b\ \x00\x00\x01\x88v;FI\ -\x00\x00 \x9a\x00\x00\x00\x00\x00\x01\x00\x05\x96\xde\ +\x00\x00!\xa4\x00\x00\x00\x00\x00\x01\x00\x05\xd4\x91\ \x00\x00\x01\x88v;F\x96\ -\x00\x00\x0c,\x00\x00\x00\x00\x00\x01\x00\x02\x22\x91\ +\x00\x00\x0d6\x00\x00\x00\x00\x00\x01\x00\x02`D\ \x00\x00\x01\x88v;F\x0a\ -\x00\x00%h\x00\x00\x00\x00\x00\x01\x00\x06\x82\xa6\ +\x00\x00&r\x00\x00\x00\x00\x00\x01\x00\x06\xc0Y\ \x00\x00\x01\x88v;F\xb1\ -\x00\x00$p\x00\x00\x00\x00\x00\x01\x00\x06M\x94\ +\x00\x00%z\x00\x00\x00\x00\x00\x01\x00\x06\x8bG\ \x00\x00\x01\x88v;F\xea\ -\x00\x00\x19B\x00\x00\x00\x00\x00\x01\x00\x04[\x0f\ +\x00\x00\x1aL\x00\x00\x00\x00\x00\x01\x00\x04\x98\xc2\ \x00\x00\x01\x88v;F\x0f\ -\x00\x00'r\x00\x00\x00\x00\x00\x01\x00\x06\xdcF\ +\x00\x00(|\x00\x00\x00\x00\x00\x01\x00\x07\x19\xf9\ \x00\x00\x01\x88v;F\x07\ -\x00\x00\x1cr\x00\x00\x00\x00\x00\x01\x00\x04\xea\x90\ +\x00\x00\x1d|\x00\x00\x00\x00\x00\x01\x00\x05(C\ \x00\x00\x01\x88v;F\x8e\ -\x00\x00$\x92\x00\x00\x00\x00\x00\x01\x00\x06UN\ +\x00\x00%\x9c\x00\x00\x00\x00\x00\x01\x00\x06\x93\x01\ \x00\x00\x01\x88v;F\x17\ -\x00\x00\x00\xea\x00\x00\x00\x00\x00\x01\x00\x00\x1f$\ +\x00\x00\x01\xd2\x00\x00\x00\x00\x00\x01\x00\x00U&\ \x00\x00\x01\x88v;F\xf6\ -\x00\x00\x22:\x00\x00\x00\x00\x00\x01\x00\x05\xe2\x01\ +\x00\x00#D\x00\x00\x00\x00\x00\x01\x00\x06\x1f\xb4\ \x00\x00\x01\x88v;F\x01\ -\x00\x00\x02\x0c\x00\x00\x00\x00\x00\x01\x00\x00S\xed\ +\x00\x00\x02\xf4\x00\x00\x00\x00\x00\x01\x00\x00\x89\xef\ \x00\x00\x01\x88v;F\xdb\ -\x00\x00%\xbc\x00\x00\x00\x00\x00\x01\x00\x06\x91\xaf\ +\x00\x00&\xc6\x00\x00\x00\x00\x00\x01\x00\x06\xcfb\ \x00\x00\x01\x88v;F\x15\ -\x00\x00!v\x00\x00\x00\x00\x00\x01\x00\x05\xbd$\ +\x00\x00\x22\x80\x00\x00\x00\x00\x00\x01\x00\x05\xfa\xd7\ \x00\x00\x01\x88v;F\xc3\ -\x00\x00\x09b\x00\x00\x00\x00\x00\x01\x00\x01\x9a\x93\ +\x00\x00\x0al\x00\x00\x00\x00\x00\x01\x00\x01\xd8F\ \x00\x00\x01\x88v;G2\ -\x00\x00\x08\xfe\x00\x00\x00\x00\x00\x01\x00\x01\x85\x00\ +\x00\x00\x09\xe6\x00\x00\x00\x00\x00\x01\x00\x01\xbb\x02\ \x00\x00\x01\x88v;F\xc5\ -\x00\x00)\x1a\x00\x00\x00\x00\x00\x01\x00\x07&+\ +\x00\x00\x0aJ\x00\x00\x00\x00\x00\x01\x00\x01\xd0\x95\ +\x00\x00\x01\x8c\x81\xd9 \xf7\ +\x00\x00*$\x00\x00\x00\x00\x00\x01\x00\x07c\xde\ \x00\x00\x01\x88v;F\xf7\ -\x00\x00\x02\x86\x00\x00\x00\x00\x00\x01\x00\x00jX\ +\x00\x00\x03n\x00\x00\x00\x00\x00\x01\x00\x00\xa0Z\ \x00\x00\x01\x88v;FP\ -\x00\x00\x0e\xa4\x00\x00\x00\x00\x00\x01\x00\x02\x92{\ +\x00\x00\x0f\xae\x00\x00\x00\x00\x00\x01\x00\x02\xd0.\ \x00\x00\x01\x88v;F\xf9\ -\x00\x00\x006\x00\x00\x00\x00\x00\x01\x00\x00\x07f\ +\x00\x00\x01\x1e\x00\x00\x00\x00\x00\x01\x00\x00=h\ \x00\x00\x01\x88v;FE\ -\x00\x00)\xc2\x00\x00\x00\x00\x00\x01\x00\x07K\xdc\ +\x00\x00*\xcc\x00\x00\x00\x00\x00\x01\x00\x07\x89\x8f\ \x00\x00\x01\x88v;F\xb8\ -\x00\x00\x13\x08\x00\x00\x00\x00\x00\x01\x00\x03>\x9f\ +\x00\x00\x14\x12\x00\x00\x00\x00\x00\x01\x00\x03|R\ \x00\x00\x01\x88v;F\xeb\ -\x00\x00)6\x00\x00\x00\x00\x00\x01\x00\x07-\xd3\ +\x00\x00*@\x00\x00\x00\x00\x00\x01\x00\x07k\x86\ \x00\x00\x01\x88v;FC\ -\x00\x00\x06\x8c\x00\x00\x00\x00\x00\x01\x00\x01\x1c\xa8\ +\x00\x00\x07t\x00\x00\x00\x00\x00\x01\x00\x01R\xaa\ \x00\x00\x01\x88v;E\xf5\ -\x00\x00&\xf4\x00\x00\x00\x00\x00\x01\x00\x06\xc6{\ +\x00\x00'\xfe\x00\x00\x00\x00\x00\x01\x00\x07\x04.\ \x00\x00\x01\x88v;F&\ -\x00\x00\x19\x80\x00\x00\x00\x00\x00\x01\x00\x04jb\ +\x00\x00\x1a\x8a\x00\x00\x00\x00\x00\x01\x00\x04\xa8\x15\ \x00\x00\x01\x88v;FS\ -\x00\x00 ~\x00\x00\x00\x00\x00\x01\x00\x05\x8fR\ +\x00\x00!\x88\x00\x00\x00\x00\x00\x01\x00\x05\xcd\x05\ \x00\x00\x01\x88v;FQ\ -\x00\x00\x19\x1a\x00\x00\x00\x00\x00\x01\x00\x04Sw\ +\x00\x00\x1a$\x00\x00\x00\x00\x00\x01\x00\x04\x91*\ \x00\x00\x01\x88v;F\x1a\ -\x00\x00\x0f\xb6\x00\x00\x00\x00\x00\x01\x00\x02\xbf\x9e\ +\x00\x00\x10\xc0\x00\x00\x00\x00\x00\x01\x00\x02\xfdQ\ \x00\x00\x01\x88v;G\x0c\ -\x00\x00#<\x00\x00\x00\x00\x00\x01\x00\x06\x0f\x88\ +\x00\x00$F\x00\x00\x00\x00\x00\x01\x00\x06M;\ \x00\x00\x01\x88v\xe8\xa8x\ -\x00\x00!\x18\x00\x00\x00\x00\x00\x01\x00\x05\xad\x17\ +\x00\x00\x22\x22\x00\x00\x00\x00\x00\x01\x00\x05\xea\xca\ \x00\x00\x01\x88v\xecih\ -\x00\x00\x0b\x9c\x00\x00\x00\x00\x00\x01\x00\x02\x04H\ +\x00\x00\x0c\xa6\x00\x00\x00\x00\x00\x01\x00\x02A\xfb\ \x00\x00\x01\x88v;F\xf4\ -\x00\x00(\xec\x00\x00\x00\x00\x00\x01\x00\x07\x1e\xb7\ +\x00\x00)\xf6\x00\x00\x00\x00\x00\x01\x00\x07\x5cj\ \x00\x00\x01\x88v;Fl\ -\x00\x00\x13(\x00\x00\x00\x00\x00\x01\x00\x03F\x04\ +\x00\x00\x142\x00\x00\x00\x00\x00\x01\x00\x03\x83\xb7\ \x00\x00\x01\x88v;F\xe7\ -\x00\x00\x01\x90\x00\x00\x00\x00\x00\x01\x00\x00=]\ +\x00\x00\x02x\x00\x00\x00\x00\x00\x01\x00\x00s_\ \x00\x00\x01\x88v;G\x0f\ -\x00\x00\x05\xa4\x00\x00\x00\x00\x00\x01\x00\x00\xef\x85\ +\x00\x00\x06\x8c\x00\x00\x00\x00\x00\x01\x00\x01%\x87\ \x00\x00\x01\x88v;F\x83\ -\x00\x00\x06f\x00\x00\x00\x00\x00\x01\x00\x01\x15\x1f\ +\x00\x00\x07N\x00\x00\x00\x00\x00\x01\x00\x01K!\ \x00\x00\x01\x88v;Fn\ -\x00\x00$\x1a\x00\x00\x00\x00\x00\x01\x00\x06>\xd0\ +\x00\x00%$\x00\x00\x00\x00\x00\x01\x00\x06|\x83\ \x00\x00\x01\x88v;G3\ -\x00\x00 \xc0\x00\x00\x00\x00\x00\x01\x00\x05\x9e5\ +\x00\x00!\xca\x00\x00\x00\x00\x00\x01\x00\x05\xdb\xe8\ \x00\x00\x01\x88v;G5\ -\x00\x00\x04Z\x00\x00\x00\x00\x00\x01\x00\x00\xb4\xa3\ +\x00\x00\x05B\x00\x00\x00\x00\x00\x01\x00\x00\xea\xa5\ \x00\x00\x01\x88v;F\x90\ -\x00\x00\x16\xa4\x00\x00\x00\x00\x00\x01\x00\x03\xea\x07\ +\x00\x00\x17\xae\x00\x00\x00\x00\x00\x01\x00\x04'\xba\ \x00\x00\x01\x88v;G7\ -\x00\x00\x18\xea\x00\x00\x00\x00\x00\x01\x00\x04K\xdb\ +\x00\x00\x19\xf4\x00\x00\x00\x00\x00\x01\x00\x04\x89\x8e\ \x00\x00\x01\x88v;G6\ -\x00\x00&\x0a\x00\x00\x00\x00\x00\x01\x00\x06\xa0\xae\ +\x00\x00'\x14\x00\x00\x00\x00\x00\x01\x00\x06\xdea\ \x00\x00\x01\x88v;F*\ -\x00\x00\x11\xde\x00\x00\x00\x00\x00\x01\x00\x03\x12\x08\ +\x00\x00\x12\xe8\x00\x00\x00\x00\x00\x01\x00\x03O\xbb\ \x00\x00\x01\x88v;F\xfc\ -\x00\x00\x02\xf2\x00\x00\x00\x00\x00\x01\x00\x00\x80\xac\ +\x00\x00\x03\xda\x00\x00\x00\x00\x00\x01\x00\x00\xb6\xae\ \x00\x00\x01\x88v;F=\ -\x00\x00'\xa8\x00\x00\x00\x00\x00\x01\x00\x06\xe3\xfd\ +\x00\x00(\xb2\x00\x00\x00\x00\x00\x01\x00\x07!\xb0\ \x00\x00\x01\x88v;F\x5c\ -\x00\x00\x0a\x0a\x00\x00\x00\x00\x00\x01\x00\x01\xb2x\ +\x00\x00\x0b\x14\x00\x00\x00\x00\x00\x01\x00\x01\xf0+\ \x00\x00\x01\x88v;F\xb3\ -\x00\x00\x05\x84\x00\x00\x00\x00\x00\x01\x00\x00\xe8\x16\ +\x00\x00\x06l\x00\x00\x00\x00\x00\x01\x00\x01\x1e\x18\ \x00\x00\x01\x88v;F,\ -\x00\x00$\xb0\x00\x00\x00\x00\x00\x01\x00\x06\x5c\xc1\ +\x00\x00%\xba\x00\x00\x00\x00\x00\x01\x00\x06\x9at\ \x00\x00\x01\x88v;F\x8b\ -\x00\x00)\xa2\x00\x00\x00\x00\x00\x01\x00\x07D\x22\ +\x00\x00*\xac\x00\x00\x00\x00\x00\x01\x00\x07\x81\xd5\ \x00\x00\x01\x88v;E\xfa\ -\x00\x00\x1bH\x00\x00\x00\x00\x00\x01\x00\x04\xb6\xa0\ +\x00\x00\x1cR\x00\x00\x00\x00\x00\x01\x00\x04\xf4S\ \x00\x00\x01\x88v;G\x02\ -\x00\x00\x08\x90\x00\x00\x00\x00\x00\x01\x00\x01u\xea\ +\x00\x00\x09x\x00\x00\x00\x00\x00\x01\x00\x01\xab\xec\ \x00\x00\x01\x88v;G\x1f\ -\x00\x00\x15\xa8\x00\x00\x00\x00\x00\x01\x00\x03\xbd\x0b\ +\x00\x00\x16\xb2\x00\x00\x00\x00\x00\x01\x00\x03\xfa\xbe\ \x00\x00\x01\x88v;F$\ -\x00\x00\x09\x1c\x00\x00\x00\x00\x00\x01\x00\x01\x8b\xf3\ +\x00\x00\x0a\x04\x00\x00\x00\x00\x00\x01\x00\x01\xc1\xf5\ \x00\x00\x01\x88v;FZ\ -\x00\x00\x04\xd4\x00\x00\x00\x00\x00\x01\x00\x00\xc9\xb6\ +\x00\x00\x05\xbc\x00\x00\x00\x00\x00\x01\x00\x00\xff\xb8\ \x00\x00\x01\x88v;F\xd2\ -\x00\x00\x1eT\x00\x00\x00\x00\x00\x01\x00\x05<\xcc\ +\x00\x00\x1f^\x00\x00\x00\x00\x00\x01\x00\x05z\x7f\ \x00\x00\x01\x88v;F\x0b\ -\x00\x00\x05\xd8\x00\x00\x00\x00\x00\x01\x00\x00\xf7\x01\ +\x00\x00\x06\xc0\x00\x00\x00\x00\x00\x01\x00\x01-\x03\ \x00\x00\x01\x88v;F\xd5\ -\x00\x00\x10\xde\x00\x00\x00\x00\x00\x01\x00\x02\xec\xa9\ +\x00\x00\x11\xe8\x00\x00\x00\x00\x00\x01\x00\x03*\x5c\ \x00\x00\x01\x88v;FR\ -\x00\x00\x0e\xca\x00\x00\x00\x00\x00\x01\x00\x02\x9a\x17\ +\x00\x00\x0f\xd4\x00\x00\x00\x00\x00\x01\x00\x02\xd7\xca\ \x00\x00\x01\x88v;F\xb4\ -\x00\x00 \x16\x00\x00\x00\x00\x00\x01\x00\x05\x80P\ +\x00\x00! \x00\x00\x00\x00\x00\x01\x00\x05\xbe\x03\ \x00\x00\x01\x88v;G \ -\x00\x00\x11R\x00\x00\x00\x00\x00\x01\x00\x02\xfb!\ +\x00\x00\x12\x5c\x00\x00\x00\x00\x00\x01\x00\x038\xd4\ \x00\x00\x01\x88v;FG\ -\x00\x00#\xec\x00\x00\x00\x00\x00\x01\x00\x067\x80\ +\x00\x00$\xf6\x00\x00\x00\x00\x00\x01\x00\x06u3\ \x00\x00\x01\x88v;F\x22\ -\x00\x00(j\x00\x00\x00\x00\x00\x01\x00\x07\x09V\ +\x00\x00)t\x00\x00\x00\x00\x00\x01\x00\x07G\x09\ \x00\x00\x01\x88v;F\xa3\ -\x00\x00'\xc6\x00\x00\x00\x00\x00\x01\x00\x06\xebA\ +\x00\x00(\xd0\x00\x00\x00\x00\x00\x01\x00\x07(\xf4\ \x00\x00\x01\x88v;G\x06\ -\x00\x00\x1d>\x00\x00\x00\x00\x00\x01\x00\x05\x08\xd6\ +\x00\x00\x1eH\x00\x00\x00\x00\x00\x01\x00\x05F\x89\ \x00\x00\x01\x88v;Fm\ -\x00\x00\x12V\x00\x00\x00\x00\x00\x01\x00\x03(\x14\ +\x00\x00\x13`\x00\x00\x00\x00\x00\x01\x00\x03e\xc7\ \x00\x00\x01\x88v;FF\ -\x00\x00\x0f\x8e\x00\x00\x00\x00\x00\x01\x00\x02\xb8t\ +\x00\x00\x10\x98\x00\x00\x00\x00\x00\x01\x00\x02\xf6'\ \x00\x00\x01\x88v;F\x11\ -\x00\x00\x070\x00\x00\x00\x00\x00\x01\x00\x01:[\ +\x00\x00\x08\x18\x00\x00\x00\x00\x00\x01\x00\x01p]\ \x00\x00\x01\x88v;F\x12\ -\x00\x00(\x1e\x00\x00\x00\x00\x00\x01\x00\x06\xfa\xa4\ +\x00\x00)(\x00\x00\x00\x00\x00\x01\x00\x078W\ \x00\x00\x01\x88v;F\x13\ -\x00\x00\x10\xae\x00\x00\x00\x00\x00\x01\x00\x02\xe4\xf4\ +\x00\x00\x11\xb8\x00\x00\x00\x00\x00\x01\x00\x03\x22\xa7\ \x00\x00\x01\x88v;Fd\ -\x00\x00\x09\xe2\x00\x00\x00\x00\x00\x01\x00\x01\xb1\xc8\ +\x00\x00\x0a\xec\x00\x00\x00\x00\x00\x01\x00\x01\xef{\ \x00\x00\x01\x88v;GI\ -\x00\x00\x1b\xbe\x00\x00\x00\x00\x00\x01\x00\x04\xcd2\ +\x00\x00\x1c\xc8\x00\x00\x00\x00\x00\x01\x00\x05\x0a\xe5\ \x00\x00\x01\x88v;FK\ -\x00\x00\x00\x92\x00\x00\x00\x00\x00\x01\x00\x00\x16j\ +\x00\x00\x01z\x00\x00\x00\x00\x00\x01\x00\x00Ll\ \x00\x00\x01\x88v;GF\ -\x00\x00\x14V\x00\x00\x00\x00\x00\x01\x00\x03\x81\xda\ +\x00\x00\x15`\x00\x00\x00\x00\x00\x01\x00\x03\xbf\x8d\ \x00\x00\x01\x88v;F:\ -\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x01\x00\x05.T\ +\x00\x00\x1f\x0a\x00\x00\x00\x00\x00\x01\x00\x05l\x07\ \x00\x00\x01\x88v;F\xde\ -\x00\x00\x0b\xbe\x00\x00\x00\x00\x00\x01\x00\x02\x0b\xc9\ +\x00\x00\x0c\xc8\x00\x00\x00\x00\x00\x01\x00\x02I|\ \x00\x00\x01\x88v;F\xb0\ -\x00\x00\x14\x92\x00\x00\x00\x00\x00\x01\x00\x03\x89\x82\ +\x00\x00\x15\x9c\x00\x00\x00\x00\x00\x01\x00\x03\xc75\ \x00\x00\x01\x88v;GD\ -\x00\x00\x00\xba\x00\x00\x00\x00\x00\x01\x00\x00\x17f\ +\x00\x00\x01\xa2\x00\x00\x00\x00\x00\x01\x00\x00Mh\ \x00\x00\x01\x88v;F\xcf\ -\x00\x00\x0bL\x00\x00\x00\x00\x00\x01\x00\x01\xf5\x05\ +\x00\x00\x0cV\x00\x00\x00\x00\x00\x01\x00\x022\xb8\ \x00\x00\x01\x88v;FV\ -\x00\x00\x0b\x1c\x00\x00\x00\x00\x00\x01\x00\x01\xedz\ +\x00\x00\x0c&\x00\x00\x00\x00\x00\x01\x00\x02+-\ \x00\x00\x01\x88v;F\xef\ -\x00\x00\x10v\x00\x00\x00\x00\x00\x01\x00\x02\xddq\ +\x00\x00\x11\x80\x00\x00\x00\x00\x00\x01\x00\x03\x1b$\ \x00\x00\x01\x88v;FF\ -\x00\x00\x12\x10\x00\x00\x00\x00\x00\x01\x00\x03\x19G\ +\x00\x00\x13\x1a\x00\x00\x00\x00\x00\x01\x00\x03V\xfa\ \x00\x00\x01\x88v;G0\ -\x00\x00\x0d\xc6\x00\x00\x00\x00\x00\x01\x00\x02ml\ +\x00\x00\x0e\xd0\x00\x00\x00\x00\x00\x01\x00\x02\xab\x1f\ \x00\x00\x01\x88v;G\x12\ -\x00\x00\x0e\xf4\x00\x00\x00\x00\x00\x01\x00\x02\xa1\xdb\ +\x00\x00\x0f\xfe\x00\x00\x00\x00\x00\x01\x00\x02\xdf\x8e\ \x00\x00\x01\x88v;FC\ -\x00\x00\x15\xd4\x00\x00\x00\x00\x00\x01\x00\x03\xc4V\ +\x00\x00\x16\xde\x00\x00\x00\x00\x00\x01\x00\x04\x02\x09\ \x00\x00\x01\x88v;F\x16\ -\x00\x00\x0d\x1a\x00\x00\x00\x00\x00\x01\x00\x02O\x11\ +\x00\x00\x0e$\x00\x00\x00\x00\x00\x01\x00\x02\x8c\xc4\ \x00\x00\x01\x88v;F\xbd\ -\x00\x00\x11\x18\x00\x00\x00\x00\x00\x01\x00\x02\xf3\xd0\ +\x00\x00\x12\x22\x00\x00\x00\x00\x00\x01\x00\x031\x83\ \x00\x00\x01\x88v;F\xc0\ -\x00\x00\x1df\x00\x00\x00\x00\x00\x01\x00\x05\x10H\ +\x00\x00\x1ep\x00\x00\x00\x00\x00\x01\x00\x05M\xfb\ \x00\x00\x01\x88v;GB\ -\x00\x00\x18\x14\x00\x00\x00\x00\x00\x01\x00\x04%\xe4\ +\x00\x00\x19\x1e\x00\x00\x00\x00\x00\x01\x00\x04c\x97\ \x00\x00\x01\x88v;FJ\ -\x00\x00\x04\x1e\x00\x00\x00\x00\x00\x01\x00\x00\xac\xed\ +\x00\x00\x05\x06\x00\x00\x00\x00\x00\x01\x00\x00\xe2\xef\ \x00\x00\x01\x88v;F\x03\ -\x00\x00\x1a\xea\x00\x00\x00\x00\x00\x01\x00\x04\x9f\xdd\ +\x00\x00\x1b\xf4\x00\x00\x00\x00\x00\x01\x00\x04\xdd\x90\ \x00\x00\x01\x88v;G\x19\ -\x00\x00%2\x00\x00\x00\x00\x00\x01\x00\x06{,\ +\x00\x00&<\x00\x00\x00\x00\x00\x01\x00\x06\xb8\xdf\ \x00\x00\x01\x88v;F\xb5\ -\x00\x00\x18\x8a\x00\x00\x00\x00\x00\x01\x00\x045\x04\ +\x00\x00\x19\x94\x00\x00\x00\x00\x00\x01\x00\x04r\xb7\ \x00\x00\x01\x88v;Fx\ -\x00\x00\x1b\x94\x00\x00\x00\x00\x00\x01\x00\x04\xc5\xc3\ +\x00\x00\x1c\x9e\x00\x00\x00\x00\x00\x01\x00\x05\x03v\ \x00\x00\x01\x88v;G(\ -\x00\x00\x0c\x88\x00\x00\x00\x00\x00\x01\x00\x020\xaa\ +\x00\x00\x0d\x92\x00\x00\x00\x00\x00\x01\x00\x02n]\ \x00\x00\x01\x88v;F\xff\ -\x00\x00\x16\x22\x00\x00\x00\x00\x00\x01\x00\x03\xd38\ +\x00\x00\x17,\x00\x00\x00\x00\x00\x01\x00\x04\x10\xeb\ \x00\x00\x01\x88v;E\xf9\ -\x00\x00)X\x00\x00\x00\x00\x00\x01\x00\x075B\ +\x00\x00*b\x00\x00\x00\x00\x00\x01\x00\x07r\xf5\ \x00\x00\x01\x88v;Fi\ -\x00\x00\x07\xae\x00\x00\x00\x00\x00\x01\x00\x01PW\ +\x00\x00\x08\x96\x00\x00\x00\x00\x00\x01\x00\x01\x86Y\ \x00\x00\x01\x88v;F8\ -\x00\x00\x0en\x00\x00\x00\x00\x00\x01\x00\x02\x8b5\ +\x00\x00\x0fx\x00\x00\x00\x00\x00\x01\x00\x02\xc8\xe8\ \x00\x00\x01\x88v;F\xc9\ -\x00\x00\x1aV\x00\x00\x00\x00\x00\x01\x00\x04\x8aP\ +\x00\x00\x1b`\x00\x00\x00\x00\x00\x01\x00\x04\xc8\x03\ \x00\x00\x01\x88v;F>\ -\x00\x00\x0a\x8a\x00\x00\x00\x00\x00\x01\x00\x01\xcf\xbf\ +\x00\x00\x0b\x94\x00\x00\x00\x00\x00\x01\x00\x02\x0dr\ \x00\x00\x01\x88v;F\xe2\ -\x00\x00\x17Z\x00\x00\x00\x00\x00\x01\x00\x04\x08(\ +\x00\x00\x18d\x00\x00\x00\x00\x00\x01\x00\x04E\xdb\ \x00\x00\x01\x88v;F.\ -\x00\x00#\x16\x00\x00\x00\x00\x00\x01\x00\x06\x0eB\ +\x00\x00$ \x00\x00\x00\x00\x00\x01\x00\x06K\xf5\ \x00\x00\x01\x88v;GK\ -\x00\x00\x1b,\x00\x00\x00\x00\x00\x01\x00\x04\xaf\x09\ +\x00\x00\x1c6\x00\x00\x00\x00\x00\x01\x00\x04\xec\xbc\ \x00\x00\x01\x88v;F\x88\ -\x00\x00\x11\x80\x00\x00\x00\x00\x00\x01\x00\x03\x02\xc0\ +\x00\x00\x12\x8a\x00\x00\x00\x00\x00\x01\x00\x03@s\ \x00\x00\x01\x88v;F?\ -\x00\x00\x07X\x00\x00\x00\x00\x00\x01\x00\x01A\xd8\ +\x00\x00\x08@\x00\x00\x00\x00\x00\x01\x00\x01w\xda\ \x00\x00\x01\x88v;F\xb9\ -\x00\x00\x05P\x00\x00\x00\x00\x00\x01\x00\x00\xe0o\ +\x00\x00\x068\x00\x00\x00\x00\x00\x01\x00\x01\x16q\ \x00\x00\x01\x88v;G9\ -\x00\x00\x1b\xf0\x00\x00\x00\x00\x00\x01\x00\x04\xd4V\ +\x00\x00\x1c\xfa\x00\x00\x00\x00\x00\x01\x00\x05\x12\x09\ \x00\x00\x01\x88v;Fy\ -\x00\x00\x13L\x00\x00\x00\x00\x00\x01\x00\x03M\x85\ +\x00\x00\x14V\x00\x00\x00\x00\x00\x01\x00\x03\x8b8\ \x00\x00\x01\x88v;F\xd0\ -\x00\x00\x04\xac\x00\x00\x00\x00\x00\x01\x00\x00\xc2\xba\ +\x00\x00\x05\x94\x00\x00\x00\x00\x00\x01\x00\x00\xf8\xbc\ \x00\x00\x01\x88v;F\xf2\ -\x00\x00\x0a\xb6\x00\x00\x00\x00\x00\x01\x00\x01\xd7K\ +\x00\x00\x0b\xc0\x00\x00\x00\x00\x00\x01\x00\x02\x14\xfe\ \x00\x00\x01\x88v;F\xee\ -\x00\x00\x18\xca\x00\x00\x00\x00\x00\x01\x00\x04D\x07\ +\x00\x00\x19\xd4\x00\x00\x00\x00\x00\x01\x00\x04\x81\xba\ \x00\x00\x01\x88v;F\xd7\ -\x00\x00\x19\xb6\x00\x00\x00\x00\x00\x01\x00\x04q\x8f\ +\x00\x00\x1a\xc0\x00\x00\x00\x00\x00\x01\x00\x04\xafB\ \x00\x00\x01\x88v;G,\ -\x00\x00\x0dn\x00\x00\x00\x00\x00\x01\x00\x02^O\ +\x00\x00\x0ex\x00\x00\x00\x00\x00\x01\x00\x02\x9c\x02\ \x00\x00\x01\x88v;F\xcd\ -\x00\x00\x01\xae\x00\x00\x00\x00\x00\x01\x00\x00D\xf7\ +\x00\x00\x02\x96\x00\x00\x00\x00\x00\x01\x00\x00z\xf9\ \x00\x00\x01\x88v;F\xa9\ -\x00\x00\x068\x00\x00\x00\x00\x00\x01\x00\x01\x0d\xcb\ +\x00\x00\x07 \x00\x00\x00\x00\x00\x01\x00\x01C\xcd\ \x00\x00\x01\x88v;F@\ -\x00\x00\x13\xb6\x00\x00\x00\x00\x00\x01\x00\x03d<\ +\x00\x00\x14\xc0\x00\x00\x00\x00\x00\x01\x00\x03\xa1\xef\ \x00\x00\x01\x88v;F\x93\ -\x00\x00\x0cX\x00\x00\x00\x00\x00\x01\x00\x02)\x97\ +\x00\x00\x0db\x00\x00\x00\x00\x00\x01\x00\x02gJ\ \x00\x00\x01\x88v;F\x9c\ -\x00\x00\x1d\xe8\x00\x00\x00\x00\x00\x01\x00\x05'\x1c\ +\x00\x00\x1e\xf2\x00\x00\x00\x00\x00\x01\x00\x05d\xcf\ \x00\x00\x01\x88v;GA\ -\x00\x00%\x98\x00\x00\x00\x00\x00\x01\x00\x06\x8a\x15\ +\x00\x00&\xa2\x00\x00\x00\x00\x00\x01\x00\x06\xc7\xc8\ \x00\x00\x01\x88v;G\x09\ -\x00\x00\x0e\x14\x00\x00\x00\x00\x00\x01\x00\x02|/\ +\x00\x00\x0f\x1e\x00\x00\x00\x00\x00\x01\x00\x02\xb9\xe2\ \x00\x00\x01\x88v;Fg\ -\x00\x00#\xa0\x00\x00\x00\x00\x00\x01\x00\x06(\xc0\ +\x00\x00$\xaa\x00\x00\x00\x00\x00\x01\x00\x06fs\ \x00\x00\x01\x88v;F\x91\ -\x00\x00\x0a\xfc\x00\x00\x00\x00\x00\x01\x00\x01\xe5\xf2\ +\x00\x00\x0c\x06\x00\x00\x00\x00\x00\x01\x00\x02#\xa5\ \x00\x00\x01\x88v;F\xd3\ -\x00\x00\x1c\xc2\x00\x00\x00\x00\x00\x01\x00\x04\xf9\x88\ +\x00\x00\x1d\xcc\x00\x00\x00\x00\x00\x01\x00\x057;\ \x00\x00\x01\x88v;G+\ -\x00\x00\x18F\x00\x00\x00\x00\x00\x01\x00\x04-u\ +\x00\x00\x19P\x00\x00\x00\x00\x00\x01\x00\x04k(\ \x00\x00\x01\x88v;F^\ -\x00\x00\x1cJ\x00\x00\x00\x00\x00\x01\x00\x04\xe3\x1b\ +\x00\x00\x1dT\x00\x00\x00\x00\x00\x01\x00\x05 \xce\ \x00\x00\x01\x88v;F\xe3\ -\x00\x00\x17\x0c\x00\x00\x00\x00\x00\x01\x00\x03\xf9?\ +\x00\x00\x18\x16\x00\x00\x00\x00\x00\x01\x00\x046\xf2\ \x00\x00\x01\x88v;F\xf1\ -\x00\x00\x07\x84\x00\x00\x00\x00\x00\x01\x00\x01I\x13\ +\x00\x00\x08l\x00\x00\x00\x00\x00\x01\x00\x01\x7f\x15\ \x00\x00\x01\x88v;F\xba\ -\x00\x00\x08N\x00\x00\x00\x00\x00\x01\x00\x01g\x09\ +\x00\x00\x096\x00\x00\x00\x00\x00\x01\x00\x01\x9d\x0b\ \x00\x00\x01\x88v;G\x08\ " diff --git a/src/CaptDeviceControl/view/AD2CaptDeviceView.py b/src/CaptDeviceControl/view/AD2CaptDeviceView.py index 4246c8d..eb5aab5 100644 --- a/src/CaptDeviceControl/view/AD2CaptDeviceView.py +++ b/src/CaptDeviceControl/view/AD2CaptDeviceView.py @@ -16,12 +16,17 @@ 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 +from CaptDeviceControl.view.Ui_AD2ControlWindowNew import Ui_AD2ControlWindowNew from CaptDeviceControl.view.widget.WidgetCapturingInformation import WidgetCapturingInformation, WidgetDeviceInformation -from CaptDeviceControl.constants.dwfconstants import DwfStateReady, DwfStateConfig, DwfStatePrefill, DwfStateArmed, DwfStateWait, \ +from CaptDeviceControl.constants.dwfconstants import DwfStateReady, DwfStateConfig, DwfStatePrefill, DwfStateArmed, \ + DwfStateWait, \ DwfStateTriggered, DwfStateRunning, DwfStateDone from fswidgets import PlayPushButton +from model.submodels.AD2CaptDeviceAnalogInModel import AD2CaptDeviceAnalogInModel +from model.submodels.AD2CaptDeviceCapturingModel import AD2CaptDeviceCapturingModel + class ControlWindow(QMainWindow): @@ -37,9 +42,9 @@ class ControlWindow(QMainWindow): self.controller = controller self.model = model - self._ui = Ui_AD2ControlWindow() + self._ui = Ui_AD2ControlWindowNew() self._ui.setupUi(self) - # self._ui.btn_start_capture = PlayPushButton(self._ui.btn_start_capture) + # self._ui.btn_start_capture = PlayPushButton(self._ui.btn_start_capture) # @@ -58,92 +63,257 @@ 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(40) self.stream_update_timer.timeout.connect(self._on_stream_update_timer_timeout) + # self.stream_update_timer.start() self.stream_samples_frequency = 1000 self.stream_n = 1 # Connect the signals and controls + self._connect_config_properties() self._connect_controls() self._connect_signals() - self._init_other_ui_elements() - self._ui.cb_duration_streaming_history.setCurrentIndex(5) + # self._init_other_ui_elements() + # self._ui.cb_duration_streaming_history.setCurrentIndex(5) self.controller.discover_connected_devices() - - self.model.sample_rate = self.model.ad2captdev_config.sample_rate.value + self._ui.sb_acquisition_rate.setValue(self.model.capturing_information.sample_rate) + # self._ui.cb_duration_streaming_history.set(self.model.capturing_information.streaming_history) # ================================================================================================================== # # ================================================================================================================== + def _add_menues(self): + pass + + def _connect_config_properties(self): + # Connect the Controls that are also Settings + self.model.ad2captdev_config.streaming_history.view.add_new_view(self._ui.cb_streaming_history) + + # Selected Analog IN Channel + self.model.ad2captdev_config.ain_channel.view.add_new_view(self._ui.cb_channel_select) + # self.model.ad2captdev_config.ain_channel.connect_property( + # self.model.analog_in, AD2CaptDeviceAnalogInModel.selected_ain_channel, + # ) + def _connect_controls(self): - self._ui.btn_connect.clicked.connect(self.on_btn_connect_to_device_clicked) - self._ui.btn_start_capture.clicked.connect(self.on_btn_start_capture_clicked) - self._ui.btn_stop.clicked.connect(self.on_btn_stop_clicked) + self._ui.cb_device_select.currentIndexChanged.connect(self._on_ui_selected_index_changed) - self._ui.cb_device_select.currentIndexChanged.connect(self._on_device_selected_changed) - self._ui.cb_channel_select.currentIndexChanged.connect(self._ui_on_selected_ain_changed) + self._ui.btn_connect.clicked.connect(self._on_ui_btn_connect_clicked) - self._ui.sb_acquisition_rate.valueChanged.connect(self._ui_on_sample_rate_changed) - #self.model.ad2captdev_config.sample_rate.view.add_new_view(self._ui.sb_acquisition_rate) + self._ui.sb_acquisition_rate.valueChanged.connect(self._on_ui_sample_rate_changed) - #self.model.ad2captdev_config.ain_channel.view.add_new_view(self._ui.cb_channel_select) - #() + # Connect the buttons + #self._ui.btn_stop.clicked.connect(self.on_btn_stop_clicked) + self._ui.btn_play.clicked.connect( + lambda: self.controller.start_capturing_process( + self.model.capturing_information.sample_rate, + self.model.analog_in.selected_ain_channel) + ) + self._ui.btn_record.clicked.connect(self._ui_on_btn_recording_clicked) + self._ui.btn_reset.clicked.connect(self._ui_on_btn_reset_clicked) - self._ui.cb_duration_streaming_history.currentIndexChanged.connect( - self._on_cb_duration_streaming_history_currentIndexChanged) + # self._ui.cb_channel_select.currentIndexChanged.connect(self._ui_on_selected_ain_changed) def _connect_signals(self): - # WaveForms Runtime (DWF) Information self.model.signals.dwf_version_changed.connect(self._on_dwf_version_changed) - # Connected Device Information - 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.device_information.signals.device_state_changed.connect(self._on_device_state_changed) self.model.device_information.signals.device_connected_changed.connect(self._on_connected_changed) + self.model.capturing_information.signals.sample_rate_changed.connect(self._on_model_sample_rate_changed) + self.model.capturing_information.signals.device_capturing_state_changed.connect( + self._on_capture_process_state_changed) + + # # WaveForms Runtime (DWF) Information + # # Connected Device Information + # self.model.device_information.signals.num_of_connected_devices_changed.connect(self._on_num_of_connected_devices_changed) + # #self.model.device_information.signals.discovered_devices_changed.connect(self._on_connected_devices_changed) + # Device information 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) + 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) + # self.model.device_information.selected_device_index_changed.connect(self._on_selected_device_index_changed) + # # Acquisition Settings + # self.model.capturing_information.signals.streaming_history.connect(self.) + # # Analog In Information + # 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.samples_lost_changed.connect(self._on_samples_lost_changed) + # self.model.signals.samples_corrupted_changed.connect(self._on_samples_corrupted_changed) + # # Recording Flags (starting, stopping and pausing) + # self.model.signals.device_capturing_state_changed.connect(self._on_device_capturing_state_changed) + # self.model.signals.start_recording_changed.connect(self._on_start_recording_changed) + # self.model.signals.stop_recording_changed.connect(self._on_stop_recording_changed) + # self.model.signals.reset_recording_changed.connect(self._on_reset_recording_changed) + # # Multiprocessing Information + # self.model.signals.pid_changed.connect(self._on_pid_changed) + # # Plotting Timer (periodically updating the plot) + # # self.capture_update_timer.timeout.connect(self._on_capture_update_plot) + + def _on_dwf_version_changed(self, dwf_version): + """ + Gets called if the DWF version changes. Updates the UI. + :param dwf_version: The DWF version + """ + self.dev_info.dwf_version = dwf_version + + def _on_connected_devices_changed(self, connected_devices: list): + """ + Gets called if the connected devices changed. Populates the combobox with the list of the connected devices. + :param connected_devices: + :return: + """ + self._ui.cb_device_select.clear() + for it, dev in enumerate(connected_devices): + 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) + + def _on_device_state_changed(self, capturing): + if capturing == AD2Constants.DeviceState.ACQ_NOT_STARTED(): + self.capt_info.led_device_state.set_color(color="yellow") + self.capt_info.lbl_device_state.setText(AD2Constants.DeviceState.ACQ_NOT_STARTED(True)) + elif capturing == AD2Constants.DeviceState.NO_SAMPLES_AVAILABLE: + self.capt_info.led_device_state.set_color(color="red") + self.capt_info.lbl_device_state.setText(AD2Constants.DeviceState.NO_SAMPLES_AVAILABLE(True)) + elif capturing == AD2Constants.DeviceState.SAMPLES_AVAILABLE(): + self.capt_info.led_device_state.set_color(color="green") + self.capt_info.lbl_device_state.setText(AD2Constants.DeviceState.SAMPLES_AVAILABLE(True)) + elif capturing == AD2Constants.DeviceState.DEV_CAPT_SETUP(): + self.capt_info.led_device_state.set_color(color="yellow") + self.capt_info.lbl_device_state.setText(AD2Constants.DeviceState.DEV_CAPT_SETUP(True)) + self._ui.btn_pause.setEnabled(True) + self._ui.btn_stop.setEnabled(True) + self._ui.btn_record.setEnabled(False) + self._ui.btn_reset.setEnabled(False) + self._ui.btn_play.setEnabled(False) + elif capturing == AD2Constants.DeviceState.DEV_CAPT_STREAMING(): + self.capt_info.led_device_state.set_color(color="green") + self.capt_info.lbl_device_state.setText(AD2Constants.DeviceState.DEV_CAPT_STREAMING(True)) + self._ui.btn_pause.setEnabled(True) + self._ui.btn_stop.setEnabled(True) + self._ui.btn_record.setEnabled(True) + self._ui.btn_reset.setEnabled(True) + self._ui.btn_play.setEnabled(True) + self._ui.btn_play.setChecked(True) + self.stream_update_timer.start() + + # ================================================================================================================== + # UI Slots + # ================================================================================================================== + def _on_ui_btn_connect_clicked(self): + if self.model.device_information.device_connected: + self.controller.close_device() + self._ui.btn_connect.setText("Connect") + else: + try: + self.controller.open_device(self.model.device_information.selected_device_index) + self.controller.start_capturing_process( + self.model.capturing_information.sample_rate + ) + except Exception as e: + self.logger.error(f"Error: {e}") + self._ui.btn_connect.setText("Disconnect") + + def _on_ui_sample_rate_changed(self, sample_rate: int): + self.model.sample_rate = sample_rate + + def _on_model_sample_rate_changed(self, sample_rate: int): + self._ui.sb_acquisition_rate.setRange(1, 1e9) + self._ui.sb_acquisition_rate.setValue(sample_rate) + + def _ui_on_btn_recording_clicked(self): + if self._ui.btn_record.isChecked(): + print("Start Recording") + self.capture_update_timer.start() + self.controller.start_capture() + else: + print("Stop Recording") + #self._ui.btn_record.setChecked(False) + self.controller.stop_capture() + + def _ui_on_btn_reset_clicked(self): + self.controller.reset_capture() + + # ================================================================================================================== + # Device information changed + # ================================================================================================================== + def _on_connected_changed(self, connected): + if connected: + self.capt_info.lbl_conn_state.setText("Connected") + self.capt_info.led_conn_state.set_color(color="green") + self._ui.btn_pause.setEnabled(False) + self._ui.btn_stop.setEnabled(False) + self._ui.btn_record.setEnabled(False) + self._ui.btn_reset.setEnabled(False) + self._ui.btn_play.setEnabled(True) + self._ui.btn_connect.setText("Disconnect") + else: + self.capt_info.lbl_conn_state.setText("Not connected") + self._ui.btn_connect.setText("Connect") + self._ui.btn_pause.setEnabled(False) + self._ui.btn_stop.setEnabled(False) + self._ui.btn_record.setEnabled(False) + self._ui.btn_reset.setEnabled(False) + self._ui.btn_play.setEnabled(False) + self.capt_info.led_conn_state.set_color(color="red") + + # ============== Recording Flags (starting, stopping and pausing) + def _on_capture_process_state_changed(self, capturing): + if capturing == AD2Constants.CapturingState.RUNNING(): + self.capt_info.led_is_capt.set_color(color="green") + self.capt_info.lbl_is_capt.setText(AD2Constants.CapturingState.RUNNING(True)) + self._ui.btn_record.setChecked(True) + elif capturing == AD2Constants.CapturingState.PAUSED(): + self.capt_info.led_is_capt.set_color(color="yellow") + self.capt_info.lbl_is_capt.setText(AD2Constants.CapturingState.PAUSED(True)) + elif capturing == AD2Constants.CapturingState.STOPPED(): + self.capt_info.led_is_capt.set_color(color="red") + self.capt_info.lbl_is_capt.setText(AD2Constants.CapturingState.STOPPED(True)) + self._ui.btn_record.setChecked(False) + + + + + + + + + + + + + + + + + + - # Acquisition Settings - self.model.signals.sample_rate_changed.connect(self._model_on_sample_rate_changed) - # Analog In Information - 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.samples_lost_changed.connect(self._on_samples_lost_changed) - self.model.signals.samples_corrupted_changed.connect(self._on_samples_corrupted_changed) - # Recording Flags (starting, stopping and pausing) - self.model.signals.device_capturing_state_changed.connect(self._on_device_capturing_state_changed) - self.model.signals.start_recording_changed.connect(self._on_start_recording_changed) - self.model.signals.stop_recording_changed.connect(self._on_stop_recording_changed) - self.model.signals.reset_recording_changed.connect(self._on_reset_recording_changed) - # Multiprocessing Information - self.model.signals.pid_changed.connect(self._on_pid_changed) - # Plotting Timer (periodically updating the plot) - # self.capture_update_timer.timeout.connect(self._on_capture_update_plot) # ================================================================================================================== # @@ -168,64 +338,35 @@ class ControlWindow(QMainWindow): return area - def _init_other_ui_elements(self): - self._ui.cb_duration_streaming_history.addItem("100ms", 0.1) - self._ui.cb_duration_streaming_history.addItem("200ms", 0.2) - self._ui.cb_duration_streaming_history.addItem("500ms", 0.5) - self._ui.cb_duration_streaming_history.addItem("1s", 1) - self._ui.cb_duration_streaming_history.addItem("2s", 2) - self._ui.cb_duration_streaming_history.addItem("5s", 5) - self._ui.cb_duration_streaming_history.addItem("10s", 10) - self._ui.cb_duration_streaming_history.addItem("20s", 20) - self._ui.cb_duration_streaming_history.addItem("30s", 30) - self._ui.cb_duration_streaming_history.addItem("1min", 60) - self._ui.cb_duration_streaming_history.addItem("2min", 120) - self._ui.cb_duration_streaming_history.addItem("5min", 300) + # def _init_other_ui_elements(self): + # self._ui.cb_duration_streaming_history.addItem("100ms", 0.1) + # self._ui.cb_duration_streaming_history.addItem("200ms", 0.2) + # self._ui.cb_duration_streaming_history.addItem("500ms", 0.5) + # self._ui.cb_duration_streaming_history.addItem("1s", 1) + # self._ui.cb_duration_streaming_history.addItem("2s", 2) + # self._ui.cb_duration_streaming_history.addItem("5s", 5) + ## self._ui.cb_duration_streaming_history.addItem("10s", 10) + # self._ui.cb_duration_streaming_history.addItem("20s", 20) + # self._ui.cb_duration_streaming_history.addItem("30s", 30) + # self._ui.cb_duration_streaming_history.addItem("1min", 60) + # self._ui.cb_duration_streaming_history.addItem("2min", 120) + # self._ui.cb_duration_streaming_history.addItem("5min", 300) # ================================================================================================================== # Slots for Model # ================================================================================================================== - def _on_device_selected_changed(self, index): - self.model.device_information.device_index = index - self.controller.device_selected_index_changed() + def _on_ui_selected_index_changed(self, index): + self.controller.selected_device_index(index) + # self.controller.device_selected_index_changed() + # self.controller.mpcaptdevicecontrol.selected_device_index(index) # First populate the AIn box+ - #m: dict = self.model.connected_devices[index] - #self.model.ain_channels = list(range(0, int(m['analog_in_channels']))) - - - def _on_dwf_version_changed(self, dwf_version): - self.dev_info.dwf_version = dwf_version - # self.ad2_settings['DWF Version'] = dwf_version - # self.update_ad2_settings_list_view() + # m: dict = self.model.connected_devices[index] + # self.model.ain_channels = list(range(0, int(m['analog_in_channels']))) # ============== Connected Device Information def _on_num_of_connected_devices_changed(self, num_of_connected_devices): pass - def _on_connected_devices_changed(self, connected_devices: list): - self._ui.cb_device_select.clear() - for it, dev in enumerate(connected_devices): - 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): - if connected: - self.capt_info.lbl_conn_state.setText("Connected") - self.capt_info.led_conn_state.set_color(color="green") - self._ui.btn_start_capture.setEnabled(True) - self._ui.btn_connect.setText("Disconnect") - self.stream_update_timer.start() - self.capture_update_timer.start() - else: - self.capt_info.lbl_conn_state.setText("Not connected") - self._ui.btn_connect.setText("Connect") - self._ui.btn_start_capture.setEnabled(False) - self._ui.btn_stop.setEnabled(False) - self.capt_info.led_conn_state.set_color(color="red") - def _on_device_name_changed(self, device_name): self.dev_info.device_name = device_name # self.ad2_settings['Device Name'] = device_name @@ -236,31 +377,21 @@ class ControlWindow(QMainWindow): # self.ad2_settings['Serial Number'] = serial_number self.update_ad2_settings_list_view() - def _on_device_index_changed(self, device_index): - print(device_index) - # ============== Acquisition Settings - - def _model_on_sample_rate_changed(self, sample_rate: int): - self._ui.sb_acquisition_rate.setRange(1, 1e9) - self._ui.sb_acquisition_rate.setValue(sample_rate) - - def _ui_on_sample_rate_changed(self, sample_rate: int): - self.model.sample_rate = sample_rate - def _model_on_selected_ain_changed(self, channel): """ Gets called if the model is changed directly (should modify the UI)""" self._on_selected_ain_channel_changed(channel) self._ui.cb_channel_select.setCurrentIndex(channel) + def _ui_on_selected_ain_changed(self, channel): """ Gets called if the ui changes the field (should modify the model) """ self._on_selected_ain_channel_changed(channel) self.model.selected_ain_channel = channel + def _on_selected_ain_channel_changed(self, channel): self.dev_info.analog_in_channel = channel - # ============== Analog In Information def _on_ain_channels_changed(self, list_of_ad_ins): self._ui.cb_channel_select.clear() @@ -323,8 +454,8 @@ class ControlWindow(QMainWindow): # ============== Acquired Signal Information def _on_recorded_samples_changed(self, recorded_samples): pass - #print(recorded_samples) - #self._ui.lcd_captured_samples.display(len(recorded_samples)) + # print(recorded_samples) + # self._ui.lcd_captured_samples.display(len(recorded_samples)) def _on_recording_time_changed(self, recording_time): self._ui.lcd_sampled_time.display(recording_time) @@ -339,24 +470,11 @@ class ControlWindow(QMainWindow): def _on_samples_corrupted_changed(self, samples_corrupted): self._ui.lcd_samples_corrupted.display(samples_corrupted) - # ============== Recording Flags (starting, stopping and pausing) - def _on_device_capturing_state_changed(self, capturing): - if capturing == AD2Constants.CapturingState.RUNNING(): - self.capt_info.led_is_capt.set_color(color="green") - self.capt_info.lbl_is_capt.setText(AD2Constants.CapturingState.RUNNING(True)) - elif capturing == AD2Constants.CapturingState.PAUSED(): - self.capt_info.led_is_capt.set_color(color="yellow") - self.capt_info.lbl_is_capt.setText(AD2Constants.CapturingState.PAUSED(True)) - elif capturing == AD2Constants.CapturingState.STOPPED(): - self.capt_info.led_is_capt.set_color(color="red") - self.capt_info.lbl_is_capt.setText(AD2Constants.CapturingState.STOPPED(True)) - self.update_ad2_settings_list_view() - def _on_start_recording_changed(self, start_recording): self.logger.debug(f"Start Recording: {start_recording}") if start_recording: self._ui.btn_stop.setEnabled(True) - #self._ui.btn_start_capture.setStyleSheet(PlayPushButton.style_pause()) + # self._ui.btn_start_capture.setStyleSheet(PlayPushButton.style_pause()) self._ui.btn_start_capture.pause() self._ui.btn_start_capture.setText("Pause Capture") @@ -364,13 +482,13 @@ class ControlWindow(QMainWindow): self.logger.debug(f"Stop Recording: {stop_recording}") if stop_recording: self._ui.btn_stop.setEnabled(False) - #self._ui.btn_start_capture.setStyleSheet(CSSPlayPushButton.style_play()) + # self._ui.btn_start_capture.setStyleSheet(CSSPlayPushButton.style_play()) self._ui.btn_start_capture.play() self._ui.btn_start_capture.setText("Start Capture") def _on_pause_recording_changed(self, pause_recording): self._ui.btn_stop.setEnabled(True) - #self._ui.btn_start_capture.setStyleSheet(CSSPlayPushButton.style_play()) + # self._ui.btn_start_capture.setStyleSheet(CSSPlayPushButton.style_play()) self._ui.btn_start_capture.play() def _on_reset_recording_changed(self, reset_recording): @@ -383,76 +501,46 @@ class ControlWindow(QMainWindow): # ============== Plotting def _on_capture_update_plot(self): - if len(self.model.recorded_samples) > 0: + if len(self.model.capturing_information.recorded_samples) > 0: self.scope_captured.clear() # print(self.ad2device.recorded_samples) - d = self.model.recorded_samples[::self.stream_n] + d = self.model.capturing_information.recorded_samples[::self.stream_n] self.scope_captured.plot( - x=np.arange(0, len(d))/self.stream_samples_frequency, + x=np.arange(0, len(d)) / self.stream_samples_frequency, y=d, pen=pg.mkPen(width=1) ) # print(f"Length: {len(self.controller.recorded_sample_stream)}") - if len(self.controller.status_dqueue) > 0: + #if len(self.controller.status_dqueue) > 0: # print(self.controller.status_dqueue) - self.model.samples_captured = self.controller.status_dqueue[-1]["captured"] + # self.model.samples_captured = self.controller.status_dqueue[-1]["captured"] # self.model.samples_lost = d[1]["lost"] # self.model.samples_corrupted = d[1]["corrupted"] - self._ui.lcd_unconsumed_capture.display(self.model.unconsumed_capture_samples) + #self._ui.lcd_unconsumed_capture.display(self.model.unconsumed_capture_samples) def _on_stream_update_timer_timeout(self): self.scope_original.clear() # print(self.ad2device.recorded_samples) + self.scope_original.plot( - np.array(self.controller.streaming_data_dqueue),#[::100], + np.array(self.controller.streaming_dqueue), # [::100], pen=pg.mkPen(width=1)) - self._ui.lcd_unconsumed_stream.display(self.model.unconsumed_stream_samples) - + # self._ui.lcd_unconsumed_stream.display(self.model.capturing_information.unconsumed_stream_samples) # ================================================================================================================== # # ================================================================================================================== - def on_btn_connect_to_device_clicked(self): - 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: - try: - self.controller.connect_device(self._ui.cb_device_select.currentIndex()) - # self.plot_update_timer.setInterval(0.1) - #self.stream_n = int(self.model.sample_rate / self.stream_samples_frequency) - except Exception as e: - # self.status_bar.setStyleSheet('border: 0; color: red;') - # self.status_bar.showMessage(f"Error: {e}") - self.logger.error(f"Error: {e}") - self._ui.btn_connect.setText("Disconnect") - - def on_btn_start_capture_clicked(self): - if self.model.device_capturing_state == AD2Constants.CapturingState.STOPPED() or \ - self.model.device_capturing_state == AD2Constants.CapturingState.PAUSED(): - self.controller.start_capture(clear=self.model.reset_recording) - elif self.model.device_capturing_state == AD2Constants.CapturingState.RUNNING(): - self.model.reset_recording = False - self.controller.stop_capture() - - def on_btn_stop_clicked(self): - - self.model.reset_recording = True - self.controller.stop_capture() - self._ui.btn_start_capture.setText("Start Capture") - self._ui.btn_stop.setEnabled(False) - self.capture_update_timer.stop() - self.scope_captured.clear() - self.scope_captured.plot(self.model.recorded_samples, pen=pg.mkPen(width=1)) def closeEvent(self, event): super(ControlWindow, self).closeEvent(event) - def _on_cb_duration_streaming_history_currentIndexChanged(self, index): - self.model.duration_streaming_history = self._ui.cb_duration_streaming_history.currentData() - self.controller.streaming_data_dqueue = deque(maxlen=int(self.model.duration_streaming_history * self.model.sample_rate)) + # def _on_cb_duration_streaming_history_currentIndexChanged(self, index):# + # self.model.duration_streaming_history = self._ui.cb_duration_streaming_history.currentData() + # self.controller.streaming_dqueue = deque( + # maxlen=int( + # self.model.duration_streaming_history * + # self.model.capturing_information.streaming_history)) # ================================================================================================================== # @@ -486,4 +574,3 @@ class ControlWindow(QMainWindow): print("Destroyed") self.controller.exit() self.destroyed.emit() - \ No newline at end of file diff --git a/src/CaptDeviceControl/view/Ui_AD2ControlWindow.py b/src/CaptDeviceControl/view/Ui_AD2ControlWindow.py index 288514f..eefbb03 100644 --- a/src/CaptDeviceControl/view/Ui_AD2ControlWindow.py +++ b/src/CaptDeviceControl/view/Ui_AD2ControlWindow.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'AD2ControlWindow.ui' ## -## Created by: Qt User Interface Compiler version 6.5.3 +## Created by: Qt User Interface Compiler version 6.6.1 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ diff --git a/src/CaptDeviceControl/view/widget/WidgetCapturingInformation.py b/src/CaptDeviceControl/view/widget/WidgetCapturingInformation.py index cd76bd5..775551e 100644 --- a/src/CaptDeviceControl/view/widget/WidgetCapturingInformation.py +++ b/src/CaptDeviceControl/view/widget/WidgetCapturingInformation.py @@ -19,16 +19,17 @@ class WidgetCapturingInformation(QWidget): layout.addWidget(self.led_conn_state, 0, 0) layout.addWidget(self.lbl_conn_state, 0, 1) + self.lbl_is_capt = QLabel("Not capturing") + self.led_is_capt = LEDIndicatorWidget(color="red") + layout.addWidget(self.led_is_capt, 1, 0) + layout.addWidget(self.lbl_is_capt, 1, 1) self.lbl_device_state = QLabel("Device State Unknown") self.led_device_state = LEDIndicatorWidget(color="gray") layout.addWidget(self.led_device_state, 2, 0) layout.addWidget(self.lbl_device_state, 2, 1) - self.lbl_is_capt = QLabel("Not capturing") - self.led_is_capt = LEDIndicatorWidget(color="red") - layout.addWidget(self.led_is_capt, 1, 0) - layout.addWidget(self.lbl_is_capt, 1, 1) + grid_group_box.setLayout(layout) self.layout.addWidget(grid_group_box) -- GitLab