pymodaq 5.1.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pymodaq/__init__.py +98 -0
- pymodaq/control_modules/__init__.py +1 -0
- pymodaq/control_modules/daq_move.py +1238 -0
- pymodaq/control_modules/daq_move_ui/__init__.py +0 -0
- pymodaq/control_modules/daq_move_ui/factory.py +48 -0
- pymodaq/control_modules/daq_move_ui/ui_base.py +359 -0
- pymodaq/control_modules/daq_move_ui/uis/__init__.py +0 -0
- pymodaq/control_modules/daq_move_ui/uis/binary.py +139 -0
- pymodaq/control_modules/daq_move_ui/uis/original.py +120 -0
- pymodaq/control_modules/daq_move_ui/uis/relative.py +124 -0
- pymodaq/control_modules/daq_move_ui/uis/simple.py +126 -0
- pymodaq/control_modules/daq_viewer.py +1517 -0
- pymodaq/control_modules/daq_viewer_ui.py +407 -0
- pymodaq/control_modules/mocks.py +57 -0
- pymodaq/control_modules/move_utility_classes.py +1141 -0
- pymodaq/control_modules/thread_commands.py +137 -0
- pymodaq/control_modules/ui_utils.py +72 -0
- pymodaq/control_modules/utils.py +591 -0
- pymodaq/control_modules/viewer_utility_classes.py +670 -0
- pymodaq/daq_utils/__init__.py +0 -0
- pymodaq/daq_utils/daq_utils.py +6 -0
- pymodaq/dashboard.py +2396 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.aliases +3 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.lvlps +3 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.lvproj +32 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Server_1Dgaussian.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Server_2Dgaussian.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_cmd.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_float.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_int.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_data.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_int.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_scalar.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_string.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/client_state.ctl +0 -0
- pymodaq/examples/Labview_TCP_Client/cmd_types.ctl +0 -0
- pymodaq/examples/__init__.py +0 -0
- pymodaq/examples/function_plotter.py +160 -0
- pymodaq/examples/nonlinearscanner.py +126 -0
- pymodaq/examples/qt_less_standalone_module.py +165 -0
- pymodaq/examples/tcp_client.py +97 -0
- pymodaq/extensions/__init__.py +25 -0
- pymodaq/extensions/adaptive/__init__.py +2 -0
- pymodaq/extensions/adaptive/adaptive_optimization.py +179 -0
- pymodaq/extensions/adaptive/loss_function/_1d_loss_functions.py +73 -0
- pymodaq/extensions/adaptive/loss_function/_2d_loss_functions.py +73 -0
- pymodaq/extensions/adaptive/loss_function/__init__.py +3 -0
- pymodaq/extensions/adaptive/loss_function/loss_factory.py +110 -0
- pymodaq/extensions/adaptive/utils.py +123 -0
- pymodaq/extensions/bayesian/__init__.py +2 -0
- pymodaq/extensions/bayesian/acquisition/__init__.py +2 -0
- pymodaq/extensions/bayesian/acquisition/acquisition_function_factory.py +80 -0
- pymodaq/extensions/bayesian/acquisition/base_acquisition_function.py +105 -0
- pymodaq/extensions/bayesian/bayesian_optimization.py +143 -0
- pymodaq/extensions/bayesian/utils.py +180 -0
- pymodaq/extensions/console.py +73 -0
- pymodaq/extensions/daq_logger/__init__.py +1 -0
- pymodaq/extensions/daq_logger/abstract.py +52 -0
- pymodaq/extensions/daq_logger/daq_logger.py +519 -0
- pymodaq/extensions/daq_logger/db/__init__.py +0 -0
- pymodaq/extensions/daq_logger/db/db_logger.py +300 -0
- pymodaq/extensions/daq_logger/db/db_logger_models.py +100 -0
- pymodaq/extensions/daq_logger/h5logging.py +84 -0
- pymodaq/extensions/daq_scan.py +1218 -0
- pymodaq/extensions/daq_scan_ui.py +241 -0
- pymodaq/extensions/data_mixer/__init__.py +0 -0
- pymodaq/extensions/data_mixer/daq_0Dviewer_DataMixer.py +97 -0
- pymodaq/extensions/data_mixer/data_mixer.py +262 -0
- pymodaq/extensions/data_mixer/model.py +108 -0
- pymodaq/extensions/data_mixer/models/__init__.py +0 -0
- pymodaq/extensions/data_mixer/models/equation_model.py +91 -0
- pymodaq/extensions/data_mixer/models/gaussian_fit_model.py +65 -0
- pymodaq/extensions/data_mixer/parser.py +53 -0
- pymodaq/extensions/data_mixer/utils.py +23 -0
- pymodaq/extensions/h5browser.py +9 -0
- pymodaq/extensions/optimizers_base/__init__.py +0 -0
- pymodaq/extensions/optimizers_base/optimizer.py +1016 -0
- pymodaq/extensions/optimizers_base/thread_commands.py +22 -0
- pymodaq/extensions/optimizers_base/utils.py +427 -0
- pymodaq/extensions/pid/__init__.py +16 -0
- pymodaq/extensions/pid/actuator_controller.py +14 -0
- pymodaq/extensions/pid/daq_move_PID.py +154 -0
- pymodaq/extensions/pid/pid_controller.py +1016 -0
- pymodaq/extensions/pid/utils.py +189 -0
- pymodaq/extensions/utils.py +111 -0
- pymodaq/icon.ico +0 -0
- pymodaq/post_treatment/__init__.py +6 -0
- pymodaq/post_treatment/load_and_plot.py +352 -0
- pymodaq/resources/__init__.py +0 -0
- pymodaq/resources/config_template.toml +57 -0
- pymodaq/resources/preset_default.xml +1 -0
- pymodaq/resources/setup_plugin.py +73 -0
- pymodaq/splash.png +0 -0
- pymodaq/utils/__init__.py +0 -0
- pymodaq/utils/array_manipulation.py +6 -0
- pymodaq/utils/calibration_camera.py +180 -0
- pymodaq/utils/chrono_timer.py +203 -0
- pymodaq/utils/config.py +53 -0
- pymodaq/utils/conftests.py +5 -0
- pymodaq/utils/daq_utils.py +158 -0
- pymodaq/utils/data.py +128 -0
- pymodaq/utils/enums.py +6 -0
- pymodaq/utils/exceptions.py +38 -0
- pymodaq/utils/gui_utils/__init__.py +10 -0
- pymodaq/utils/gui_utils/loader_utils.py +75 -0
- pymodaq/utils/gui_utils/utils.py +18 -0
- pymodaq/utils/gui_utils/widgets/lcd.py +8 -0
- pymodaq/utils/h5modules/__init__.py +2 -0
- pymodaq/utils/h5modules/module_saving.py +526 -0
- pymodaq/utils/leco/__init__.py +25 -0
- pymodaq/utils/leco/daq_move_LECODirector.py +217 -0
- pymodaq/utils/leco/daq_xDviewer_LECODirector.py +163 -0
- pymodaq/utils/leco/director_utils.py +74 -0
- pymodaq/utils/leco/leco_director.py +166 -0
- pymodaq/utils/leco/pymodaq_listener.py +364 -0
- pymodaq/utils/leco/rpc_method_definitions.py +43 -0
- pymodaq/utils/leco/utils.py +74 -0
- pymodaq/utils/logger.py +6 -0
- pymodaq/utils/managers/__init__.py +0 -0
- pymodaq/utils/managers/batchscan_manager.py +346 -0
- pymodaq/utils/managers/modules_manager.py +589 -0
- pymodaq/utils/managers/overshoot_manager.py +242 -0
- pymodaq/utils/managers/preset_manager.py +229 -0
- pymodaq/utils/managers/preset_manager_utils.py +262 -0
- pymodaq/utils/managers/remote_manager.py +484 -0
- pymodaq/utils/math_utils.py +6 -0
- pymodaq/utils/messenger.py +6 -0
- pymodaq/utils/parameter/__init__.py +10 -0
- pymodaq/utils/parameter/utils.py +6 -0
- pymodaq/utils/scanner/__init__.py +5 -0
- pymodaq/utils/scanner/scan_config.py +16 -0
- pymodaq/utils/scanner/scan_factory.py +259 -0
- pymodaq/utils/scanner/scan_selector.py +477 -0
- pymodaq/utils/scanner/scanner.py +324 -0
- pymodaq/utils/scanner/scanners/_1d_scanners.py +174 -0
- pymodaq/utils/scanner/scanners/_2d_scanners.py +299 -0
- pymodaq/utils/scanner/scanners/__init__.py +1 -0
- pymodaq/utils/scanner/scanners/sequential.py +224 -0
- pymodaq/utils/scanner/scanners/tabular.py +319 -0
- pymodaq/utils/scanner/utils.py +110 -0
- pymodaq/utils/svg/__init__.py +6 -0
- pymodaq/utils/svg/svg_renderer.py +20 -0
- pymodaq/utils/svg/svg_view.py +35 -0
- pymodaq/utils/svg/svg_viewer2D.py +50 -0
- pymodaq/utils/tcp_ip/__init__.py +6 -0
- pymodaq/utils/tcp_ip/mysocket.py +12 -0
- pymodaq/utils/tcp_ip/serializer.py +13 -0
- pymodaq/utils/tcp_ip/tcp_server_client.py +772 -0
- pymodaq-5.1.6.dist-info/METADATA +238 -0
- pymodaq-5.1.6.dist-info/RECORD +154 -0
- pymodaq-5.1.6.dist-info/WHEEL +4 -0
- pymodaq-5.1.6.dist-info/entry_points.txt +7 -0
- pymodaq-5.1.6.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,1218 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
"""Automated scanning module functionalities for PyMoDAQ
|
|
5
|
+
|
|
6
|
+
Contains all objects related to the DAQScan module, to do automated scans, saving data...
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
from collections import OrderedDict
|
|
10
|
+
import logging
|
|
11
|
+
import os
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
import sys
|
|
14
|
+
import tempfile
|
|
15
|
+
from typing import List, Tuple, TYPE_CHECKING
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
from qtpy import QtWidgets, QtCore, QtGui
|
|
19
|
+
from qtpy.QtWidgets import QDialogButtonBox
|
|
20
|
+
from qtpy.QtCore import QObject, Slot, QThread, Signal, QDateTime, QDate, QTime
|
|
21
|
+
|
|
22
|
+
from pymodaq_utils.logger import set_logger, get_module_name
|
|
23
|
+
from pymodaq_utils.config import Config
|
|
24
|
+
from pymodaq_utils import utils
|
|
25
|
+
|
|
26
|
+
from pymodaq_data import data as data_mod
|
|
27
|
+
from pymodaq_data.h5modules import data_saving
|
|
28
|
+
|
|
29
|
+
from pymodaq_gui.parameter import ioxml
|
|
30
|
+
from pymodaq_gui.plotting.data_viewers import ViewersEnum
|
|
31
|
+
from pymodaq_gui.managers.parameter_manager import ParameterManager, Parameter, ParameterTree
|
|
32
|
+
from pymodaq_gui.plotting.navigator import Navigator
|
|
33
|
+
from pymodaq_gui.messenger import messagebox
|
|
34
|
+
from pymodaq_gui import utils as gutils
|
|
35
|
+
from pymodaq_gui.h5modules.saving import H5Saver
|
|
36
|
+
|
|
37
|
+
from pymodaq.utils.scanner.scanner import Scanner
|
|
38
|
+
from pymodaq.utils.managers.batchscan_manager import BatchScanner
|
|
39
|
+
from pymodaq.utils.managers.modules_manager import ModulesManager
|
|
40
|
+
from pymodaq.post_treatment.load_and_plot import LoaderPlotter
|
|
41
|
+
from pymodaq.extensions.daq_scan_ui import DAQScanUI
|
|
42
|
+
from pymodaq.utils.h5modules import module_saving
|
|
43
|
+
from pymodaq.utils.scanner.scan_selector import ScanSelector, SelectorItem
|
|
44
|
+
from pymodaq.utils.data import DataActuator
|
|
45
|
+
from pymodaq.utils.config import Config as ControlModulesConfig
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
if TYPE_CHECKING:
|
|
49
|
+
from pymodaq.dashboard import DashBoard
|
|
50
|
+
|
|
51
|
+
config_utils = Config()
|
|
52
|
+
config = ControlModulesConfig()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
logger = set_logger(get_module_name(__file__))
|
|
56
|
+
|
|
57
|
+
SHOW_POPUPS = config('scan', 'show_popups')
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class DAQ_ScanException(Exception):
|
|
61
|
+
"""Raised when an error occur within the DAQScan"""
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class ScanDataTemp:
|
|
66
|
+
"""Convenience class to hold temporary data to be plotted in the live plots"""
|
|
67
|
+
def __init__(self, scan_index: int, indexes: Tuple[int], data: data_mod.DataToExport):
|
|
68
|
+
self.scan_index = scan_index
|
|
69
|
+
self.indexes = indexes
|
|
70
|
+
self.data = data
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class DAQScan(QObject, ParameterManager):
|
|
74
|
+
"""
|
|
75
|
+
Main class initializing a DAQScan module with its dashboard and scanning control panel
|
|
76
|
+
"""
|
|
77
|
+
settings_name = 'daq_scan_settings'
|
|
78
|
+
command_daq_signal = Signal(utils.ThreadCommand)
|
|
79
|
+
status_signal = Signal(str)
|
|
80
|
+
live_data_1D_signal = Signal(list)
|
|
81
|
+
|
|
82
|
+
params = [
|
|
83
|
+
{'title': 'Time Flow:', 'name': 'time_flow', 'type': 'group', 'expanded': False,
|
|
84
|
+
'children': [
|
|
85
|
+
{'title': 'Wait time step (ms)', 'name': 'wait_time', 'type': 'int', 'value': 0,
|
|
86
|
+
'tip': 'Wait time in ms after each step of acquisition (move and grab)'},
|
|
87
|
+
{'title': 'Wait time between (ms)', 'name': 'wait_time_between', 'type': 'int',
|
|
88
|
+
'value': 0,
|
|
89
|
+
'tip': 'Wait time in ms between move and grab processes'},
|
|
90
|
+
{'title': 'Timeout (ms)', 'name': 'timeout', 'type': 'int', 'value': 10000},
|
|
91
|
+
]},
|
|
92
|
+
{'title': 'Scan options', 'name': 'scan_options', 'type': 'group', 'children': [
|
|
93
|
+
{'title': 'Naverage:', 'name': 'scan_average', 'type': 'int',
|
|
94
|
+
'value': config('scan', 'Naverage'), 'min': 1},
|
|
95
|
+
{'title': 'Plot on top:', 'name': 'average_on_top', 'type': 'bool',
|
|
96
|
+
'value': config('scan', 'average_on_top'),
|
|
97
|
+
'tip': 'At the second iteration will plot the averaged scan on top (True) of the current one'
|
|
98
|
+
'or in a second panel (False)'},
|
|
99
|
+
]},
|
|
100
|
+
|
|
101
|
+
{'title': 'Plotting options', 'name': 'plot_options', 'type': 'group', 'children': [
|
|
102
|
+
{'title': 'Get data', 'name': 'plot_probe', 'type': 'bool_push'},
|
|
103
|
+
{'title': 'Group 0D data:', 'name': 'group0D', 'type': 'bool', 'value': True},
|
|
104
|
+
{'title': 'Plot 0Ds:', 'name': 'plot_0d', 'type': 'itemselect', 'checkbox': True},
|
|
105
|
+
{'title': 'Plot 1Ds:', 'name': 'plot_1d', 'type': 'itemselect', 'checkbox': True},
|
|
106
|
+
{'title': 'Prepare Viewers', 'name': 'prepare_viewers', 'type': 'bool_push'},
|
|
107
|
+
{'title': 'Plot at each step?', 'name': 'plot_at_each_step', 'type': 'bool',
|
|
108
|
+
'value': True},
|
|
109
|
+
{'title': 'Refresh Plots (ms)', 'name': 'refresh_live', 'type': 'int',
|
|
110
|
+
'value': 1000, 'visible': False},
|
|
111
|
+
]},
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
def __init__(self, dockarea: gutils.DockArea = None, dashboard: DashBoard = None):
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
Parameters
|
|
118
|
+
----------
|
|
119
|
+
dockarea: DockArea
|
|
120
|
+
instance of the modified pyqtgraph Dockarea
|
|
121
|
+
dashboard: DashBoard
|
|
122
|
+
instance of the pymodaq dashboard
|
|
123
|
+
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
logger.info('Initializing DAQScan')
|
|
127
|
+
QObject.__init__(self)
|
|
128
|
+
ParameterManager.__init__(self)
|
|
129
|
+
|
|
130
|
+
self.title = __class__.__name__
|
|
131
|
+
|
|
132
|
+
self.dockarea: gutils.DockArea = dockarea
|
|
133
|
+
self.dashboard: DashBoard = dashboard
|
|
134
|
+
if dashboard is None:
|
|
135
|
+
raise Exception('No valid dashboard initialized')
|
|
136
|
+
|
|
137
|
+
self.mainwindow = self.dockarea.parent()
|
|
138
|
+
self.ui: DAQScanUI = DAQScanUI(self.dockarea)
|
|
139
|
+
|
|
140
|
+
self.wait_time = 1000
|
|
141
|
+
|
|
142
|
+
self.navigator: Navigator = None
|
|
143
|
+
self.scan_selector: ScanSelector = None
|
|
144
|
+
|
|
145
|
+
self.ind_scan = 0
|
|
146
|
+
self.ind_average = 0
|
|
147
|
+
|
|
148
|
+
self._metada_dataset_set = False
|
|
149
|
+
|
|
150
|
+
self.curvilinear_values = []
|
|
151
|
+
self.plot_colors = utils.plot_colors
|
|
152
|
+
|
|
153
|
+
self.runner_thread: QThread = None
|
|
154
|
+
|
|
155
|
+
self.modules_manager = ModulesManager(self.dashboard.detector_modules, self.dashboard.actuators_modules)
|
|
156
|
+
self.modules_manager.settings.child('data_dimensions').setOpts(expanded=False)
|
|
157
|
+
self.modules_manager.settings.child('actuators_positions').setOpts(expanded=False)
|
|
158
|
+
self.modules_manager.detectors_changed.connect(self.clear_plot_from)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
self._h5saver = H5Saver(backend=config_utils('general', 'hdf5_backend'))
|
|
162
|
+
self._h5saver.settings.child('do_save').hide()
|
|
163
|
+
self._h5saver.settings.child('custom_name').hide()
|
|
164
|
+
self._h5saver.new_file_sig.connect(self.create_new_file)
|
|
165
|
+
|
|
166
|
+
self._module_and_data_saver: module_saving.ScanSaver = module_saving.ScanSaver(self)
|
|
167
|
+
|
|
168
|
+
self.extended_saver: data_saving.DataToExportExtendedSaver = None
|
|
169
|
+
self.h5temp: H5Saver = None
|
|
170
|
+
self.temp_path: tempfile.TemporaryDirectory = None
|
|
171
|
+
|
|
172
|
+
self.scanner = Scanner(actuators=self.modules_manager.actuators) # , adaptive_losses=adaptive_losses)
|
|
173
|
+
self.scan_parameters = None
|
|
174
|
+
|
|
175
|
+
self.batcher: BatchScanner = None
|
|
176
|
+
self.batch_started = False
|
|
177
|
+
self.ind_batch = 0
|
|
178
|
+
|
|
179
|
+
self.modules_manager.actuators_changed[list].connect(self.update_actuators)
|
|
180
|
+
|
|
181
|
+
self.setup_ui()
|
|
182
|
+
self.ui.command_sig.connect(self.process_ui_cmds)
|
|
183
|
+
|
|
184
|
+
self.create_dataset_settings()
|
|
185
|
+
|
|
186
|
+
self.set_config()
|
|
187
|
+
|
|
188
|
+
self.live_plotter = LoaderPlotter(self.dockarea)
|
|
189
|
+
self.live_timer = QtCore.QTimer()
|
|
190
|
+
self.live_timer.timeout.connect(self.update_live_plots)
|
|
191
|
+
|
|
192
|
+
self.ui.enable_start_stop(True)
|
|
193
|
+
logger.info('DAQScan Initialized')
|
|
194
|
+
|
|
195
|
+
def plot_from(self):
|
|
196
|
+
self.modules_manager.get_det_data_list()
|
|
197
|
+
data0D = self.modules_manager.settings['data_dimensions', 'det_data_list0D']
|
|
198
|
+
data1D = self.modules_manager.settings['data_dimensions', 'det_data_list1D']
|
|
199
|
+
data0D['selected'] = data0D['all_items']
|
|
200
|
+
data1D['selected'] = data1D['all_items']
|
|
201
|
+
self.settings.child('plot_options', 'plot_0d').setValue(data0D)
|
|
202
|
+
self.settings.child('plot_options', 'plot_1d').setValue(data1D)
|
|
203
|
+
|
|
204
|
+
def setup_ui(self):
|
|
205
|
+
self.ui.populate_toolbox_widget([self.settings_tree, self._h5saver.settings_tree],
|
|
206
|
+
['General Settings', 'Save Settings'])
|
|
207
|
+
|
|
208
|
+
self.ui.set_scanner_settings(self.scanner.parent_widget)
|
|
209
|
+
self.ui.set_modules_settings(self.modules_manager.settings_tree)
|
|
210
|
+
|
|
211
|
+
self.plotting_settings_tree = ParameterTree()
|
|
212
|
+
self.plotting_settings_tree.setParameters(self.settings.child('plot_options'))
|
|
213
|
+
self.ui.set_plotting_settings(self.plotting_settings_tree)
|
|
214
|
+
|
|
215
|
+
################
|
|
216
|
+
# CONFIG/SETUP UI / EXIT
|
|
217
|
+
|
|
218
|
+
def set_config(self):
|
|
219
|
+
self.settings.child('time_flow', 'wait_time').setValue(config['scan']['timeflow']['wait_time'])
|
|
220
|
+
self.settings.child('time_flow', 'wait_time_between').setValue(config['scan']['timeflow']['wait_time'])
|
|
221
|
+
self.settings.child('time_flow', 'timeout').setValue(config['scan']['timeflow']['timeout'])
|
|
222
|
+
|
|
223
|
+
self.settings.child('scan_options', 'scan_average').setValue(config['scan']['Naverage'])
|
|
224
|
+
|
|
225
|
+
def process_ui_cmds(self, cmd: utils.ThreadCommand):
|
|
226
|
+
"""Process commands sent by actions done in the ui
|
|
227
|
+
|
|
228
|
+
Parameters
|
|
229
|
+
----------
|
|
230
|
+
cmd: ThreadCommand
|
|
231
|
+
Possible values are:
|
|
232
|
+
* quit
|
|
233
|
+
* ini_positions
|
|
234
|
+
* start
|
|
235
|
+
* start_batch
|
|
236
|
+
* stop
|
|
237
|
+
* move_at
|
|
238
|
+
* show_log
|
|
239
|
+
* load
|
|
240
|
+
* save
|
|
241
|
+
* show_file
|
|
242
|
+
* navigator
|
|
243
|
+
* batch
|
|
244
|
+
* viewers_changed
|
|
245
|
+
"""
|
|
246
|
+
if cmd.command == 'quit':
|
|
247
|
+
self.quit_fun()
|
|
248
|
+
elif cmd.command == 'ini_positions':
|
|
249
|
+
self.set_ini_positions()
|
|
250
|
+
elif cmd.command == 'start':
|
|
251
|
+
self.start_scan()
|
|
252
|
+
elif cmd.command == 'start_batch':
|
|
253
|
+
self.start_scan_batch()
|
|
254
|
+
elif cmd.command == 'stop':
|
|
255
|
+
self.stop_scan()
|
|
256
|
+
elif cmd.command == 'move_at':
|
|
257
|
+
self.move_to_crosshair()
|
|
258
|
+
elif cmd.command == 'show_log':
|
|
259
|
+
self.show_log()
|
|
260
|
+
elif cmd.command == 'load':
|
|
261
|
+
self.load_file()
|
|
262
|
+
elif cmd.command == 'save':
|
|
263
|
+
self.save_file()
|
|
264
|
+
elif cmd.command == 'show_file':
|
|
265
|
+
self.show_file_content()
|
|
266
|
+
elif cmd.command == 'navigator':
|
|
267
|
+
self.show_navigator()
|
|
268
|
+
elif cmd.command == 'batch':
|
|
269
|
+
self.show_batcher(self.ui.menubar)
|
|
270
|
+
elif cmd.command == 'viewers_changed':
|
|
271
|
+
...
|
|
272
|
+
|
|
273
|
+
def show_log(self):
|
|
274
|
+
"""Open the log file in the default text editor"""
|
|
275
|
+
import webbrowser
|
|
276
|
+
webbrowser.open(logger.parent.handlers[0].baseFilename)
|
|
277
|
+
|
|
278
|
+
def quit_fun(self):
|
|
279
|
+
"""
|
|
280
|
+
Quit the current instance of DAQ_scan
|
|
281
|
+
|
|
282
|
+
See Also
|
|
283
|
+
--------
|
|
284
|
+
quit_fun
|
|
285
|
+
"""
|
|
286
|
+
try:
|
|
287
|
+
if self.temp_path is not None:
|
|
288
|
+
try:
|
|
289
|
+
self.h5temp.close()
|
|
290
|
+
self.temp_path.cleanup()
|
|
291
|
+
except Exception as e:
|
|
292
|
+
logger.exception(str(e))
|
|
293
|
+
|
|
294
|
+
self.close_file()
|
|
295
|
+
self.mainwindow.close()
|
|
296
|
+
|
|
297
|
+
except Exception as e:
|
|
298
|
+
logger.exception(str(e))
|
|
299
|
+
|
|
300
|
+
def create_dataset_settings(self):
|
|
301
|
+
# params about dataset attributes and scan attibutes
|
|
302
|
+
date = QDateTime(QDate.currentDate(), QTime.currentTime())
|
|
303
|
+
params_dataset = [{'title': 'Dataset information', 'name': 'dataset_info', 'type': 'group', 'children': [
|
|
304
|
+
{'title': 'Author:', 'name': 'author', 'type': 'str', 'value': config_utils['user']['name']},
|
|
305
|
+
{'title': 'Date/time:', 'name': 'date_time', 'type': 'date_time', 'value': date},
|
|
306
|
+
{'title': 'Sample:', 'name': 'sample', 'type': 'str', 'value': ''},
|
|
307
|
+
{'title': 'Experiment type:', 'name': 'experiment_type', 'type': 'str', 'value': ''},
|
|
308
|
+
{'title': 'Description:', 'name': 'description', 'type': 'text', 'value': ''}]}]
|
|
309
|
+
|
|
310
|
+
params_scan = [{'title': 'Scan information', 'name': 'scan_info', 'type': 'group', 'children': [
|
|
311
|
+
{'title': 'Author:', 'name': 'author', 'type': 'str', 'value': config_utils['user']['name']},
|
|
312
|
+
{'title': 'Date/time:', 'name': 'date_time', 'type': 'date_time', 'value': date},
|
|
313
|
+
{'title': 'Scan type:', 'name': 'scan_type', 'type': 'str', 'value': ''},
|
|
314
|
+
{'title': 'Scan subtype:', 'name': 'scan_sub_type', 'type': 'str', 'value': ''},
|
|
315
|
+
{'title': 'Scan name:', 'name': 'scan_name', 'type': 'str', 'value': '', 'readonly': True},
|
|
316
|
+
{'title': 'Description:', 'name': 'description', 'type': 'text', 'value': ''},
|
|
317
|
+
]}]
|
|
318
|
+
|
|
319
|
+
self.dataset_attributes = Parameter.create(name='Attributes', type='group', children=params_dataset)
|
|
320
|
+
self.scan_attributes = Parameter.create(name='Attributes', type='group', children=params_scan)
|
|
321
|
+
###################
|
|
322
|
+
# external modules
|
|
323
|
+
|
|
324
|
+
def show_batcher(self, menubar):
|
|
325
|
+
self.batcher = BatchScanner(self.dockarea, self.modules_manager.actuators_all,
|
|
326
|
+
self.modules_manager.detectors_all)
|
|
327
|
+
self.batcher.create_menu(menubar)
|
|
328
|
+
self.batcher.setupUI()
|
|
329
|
+
self.ui.set_action_visible('start_batch', True)
|
|
330
|
+
|
|
331
|
+
def start_scan_batch(self):
|
|
332
|
+
self.batch_started = True
|
|
333
|
+
self.ind_batch = 0
|
|
334
|
+
self.loop_scan_batch()
|
|
335
|
+
|
|
336
|
+
def loop_scan_batch(self):
|
|
337
|
+
if self.ind_batch >= len(self.batcher.scans_names):
|
|
338
|
+
self.stop_scan()
|
|
339
|
+
return
|
|
340
|
+
self.scanner = self.batcher.get_scan(self.batcher.scans_names[self.ind_batch])
|
|
341
|
+
actuators, detectors = self.batcher.get_act_dets()
|
|
342
|
+
self.set_scan_batch(actuators[self.batcher.scans_names[self.ind_batch]],
|
|
343
|
+
detectors[self.batcher.scans_names[self.ind_batch]])
|
|
344
|
+
self.start_scan()
|
|
345
|
+
|
|
346
|
+
def set_scan_batch(self, actuators, detectors):
|
|
347
|
+
self.modules_manager.selected_detectors_name = detectors
|
|
348
|
+
self.modules_manager.selected_actuators_name = actuators
|
|
349
|
+
QtWidgets.QApplication.processEvents()
|
|
350
|
+
|
|
351
|
+
def show_file_attributes(self, type_info='dataset'):
|
|
352
|
+
"""
|
|
353
|
+
Switch the type_info value.
|
|
354
|
+
|
|
355
|
+
In case of :
|
|
356
|
+
* *scan* : Set parameters showing top false
|
|
357
|
+
* *dataset* : Set parameters showing top false
|
|
358
|
+
* *managers* : Set parameters showing top false. Add the save/cancel buttons to the accept/reject dialog (to save managers parameters in a xml file).
|
|
359
|
+
|
|
360
|
+
Finally, in case of accepted managers type info, save the managers parameters in a xml file.
|
|
361
|
+
|
|
362
|
+
=============== =========== ====================================
|
|
363
|
+
**Parameters** **Type** **Description**
|
|
364
|
+
*type_info* string The file type information between
|
|
365
|
+
* scan
|
|
366
|
+
* dataset
|
|
367
|
+
* managers
|
|
368
|
+
=============== =========== ====================================
|
|
369
|
+
|
|
370
|
+
See Also
|
|
371
|
+
--------
|
|
372
|
+
custom_tree.parameter_to_xml_file, create_menu
|
|
373
|
+
"""
|
|
374
|
+
if SHOW_POPUPS:
|
|
375
|
+
dialog = QtWidgets.QDialog()
|
|
376
|
+
vlayout = QtWidgets.QVBoxLayout()
|
|
377
|
+
tree = ParameterTree()
|
|
378
|
+
tree.setMinimumWidth(400)
|
|
379
|
+
tree.setMinimumHeight(500)
|
|
380
|
+
if type_info == 'scan':
|
|
381
|
+
tree.setParameters(self.scan_attributes, showTop=False)
|
|
382
|
+
elif type_info == 'dataset':
|
|
383
|
+
tree.setParameters(self.dataset_attributes, showTop=False)
|
|
384
|
+
|
|
385
|
+
vlayout.addWidget(tree)
|
|
386
|
+
dialog.setLayout(vlayout)
|
|
387
|
+
buttonBox = QDialogButtonBox(parent=dialog)
|
|
388
|
+
buttonBox.addButton("Cancel", QDialogButtonBox.ButtonRole.RejectRole)
|
|
389
|
+
buttonBox.addButton("Apply", QDialogButtonBox.ButtonRole.AcceptRole)
|
|
390
|
+
buttonBox.rejected.connect(dialog.reject)
|
|
391
|
+
buttonBox.accepted.connect(dialog.accept)
|
|
392
|
+
|
|
393
|
+
vlayout.addWidget(buttonBox)
|
|
394
|
+
dialog.setWindowTitle('Fill in information about this {}'.format(type_info))
|
|
395
|
+
res = dialog.exec()
|
|
396
|
+
else:
|
|
397
|
+
res = True
|
|
398
|
+
return res
|
|
399
|
+
|
|
400
|
+
def show_file_content(self):
|
|
401
|
+
try:
|
|
402
|
+
self.h5saver.show_file_content()
|
|
403
|
+
except Exception as e:
|
|
404
|
+
logger.exception(str(e))
|
|
405
|
+
|
|
406
|
+
def show_navigator(self):
|
|
407
|
+
|
|
408
|
+
if self.navigator is None:
|
|
409
|
+
# loading navigator
|
|
410
|
+
self.navigator_dock = gutils.Dock('Navigator')
|
|
411
|
+
widgnav = QtWidgets.QWidget()
|
|
412
|
+
self.navigator_dock.addWidget(widgnav)
|
|
413
|
+
self.dockarea.addDock(self.navigator_dock)
|
|
414
|
+
self.navigator_dock.float()
|
|
415
|
+
|
|
416
|
+
self.navigator = Navigator(widgnav)
|
|
417
|
+
|
|
418
|
+
self.navigator.log_signal[str].connect(self.dashboard.add_status)
|
|
419
|
+
self.navigator.settings.child('settings', 'Load h5').hide()
|
|
420
|
+
self.navigator.set_action_visible('load_scan', False)
|
|
421
|
+
|
|
422
|
+
self.navigator.sig_double_clicked.connect(self.move_at)
|
|
423
|
+
self.navigator.h5saver = self.h5saver
|
|
424
|
+
self.navigator.list_2D_scans()
|
|
425
|
+
|
|
426
|
+
self.show_scan_selector()
|
|
427
|
+
|
|
428
|
+
def show_scan_selector(self):
|
|
429
|
+
viewer_items = []
|
|
430
|
+
if self.navigator is not None:
|
|
431
|
+
viewer_items.append(SelectorItem(self.navigator.viewer, name='Navigator'))
|
|
432
|
+
#
|
|
433
|
+
# for viewer in self.live_plotter.viewers:
|
|
434
|
+
# viewer_items.update({viewer.title: dict(viewers=[viewer], names=[viewer.title])})
|
|
435
|
+
self.scan_selector = ScanSelector(viewer_items)
|
|
436
|
+
|
|
437
|
+
self.ui.add_scanner_settings(self.scan_selector.settings_tree)
|
|
438
|
+
|
|
439
|
+
self.scan_selector.scan_select_signal.connect(self.scanner.update_from_scan_selector)
|
|
440
|
+
|
|
441
|
+
################
|
|
442
|
+
# LOADING SAVING
|
|
443
|
+
|
|
444
|
+
def load_file(self):
|
|
445
|
+
self.h5saver.load_file(self.h5saver.h5_file_path)
|
|
446
|
+
|
|
447
|
+
def save_file(self):
|
|
448
|
+
if not os.path.isdir(self.h5saver.settings['base_path']):
|
|
449
|
+
os.mkdir(self.h5saver.settings['base_path'])
|
|
450
|
+
filename = gutils.file_io.select_file(self.h5saver.settings['base_path'], save=True, ext='h5')
|
|
451
|
+
self.h5saver.h5_file.copy_file(str(filename), overwrite=True)
|
|
452
|
+
|
|
453
|
+
def save_metadata(self, node, type_info='dataset_info'):
|
|
454
|
+
"""
|
|
455
|
+
Switch the type_info value with :
|
|
456
|
+
* *'dataset_info'* : Give the params attributes the dataset_attributes values
|
|
457
|
+
* *'dataset'* : Give the params attributes the scan_attributes values
|
|
458
|
+
|
|
459
|
+
|
|
|
460
|
+
| Once done, course the params and add string casted date/time metadata as an element of attributes array.
|
|
461
|
+
| Save the contents of given parameter object into a xml string unde the attributes settings.
|
|
462
|
+
|
|
463
|
+
=============== =================== =========================================
|
|
464
|
+
**Parameters** **Type** **Description**
|
|
465
|
+
*node* pytables h5 node Root node to be treated
|
|
466
|
+
*type_info* string File type info between :
|
|
467
|
+
* 'dataset_info'
|
|
468
|
+
* 'scan_info'
|
|
469
|
+
=============== =================== =========================================
|
|
470
|
+
|
|
471
|
+
See Also
|
|
472
|
+
--------
|
|
473
|
+
custom_tree.parameter_to_xml_string
|
|
474
|
+
"""
|
|
475
|
+
|
|
476
|
+
attr = node.attrs
|
|
477
|
+
if type_info == 'dataset_info':
|
|
478
|
+
attr['type'] = 'dataset'
|
|
479
|
+
params = self.dataset_attributes
|
|
480
|
+
else:
|
|
481
|
+
attr['type'] = 'scan'
|
|
482
|
+
params = self.scan_attributes
|
|
483
|
+
for child in params.child(type_info).children():
|
|
484
|
+
if type(child.value()) is QDateTime:
|
|
485
|
+
attr[child.name()] = child.value().toString('dd/mm/yyyy HH:MM:ss')
|
|
486
|
+
else:
|
|
487
|
+
attr[child.name()] = child.value()
|
|
488
|
+
if type_info == 'dataset_info':
|
|
489
|
+
# save contents of given parameter object into an xml string under the attribute settings
|
|
490
|
+
settings_str = b'<All_settings title="All Settings" type="group">' + \
|
|
491
|
+
ioxml.parameter_to_xml_string(params) + \
|
|
492
|
+
ioxml.parameter_to_xml_string(self.settings)
|
|
493
|
+
# ioxml.parameter_to_xml_string(
|
|
494
|
+
# self.dashboard.preset_manager.preset_params) +\
|
|
495
|
+
settings_str += b'</All_settings>'
|
|
496
|
+
attr['settings'] = settings_str
|
|
497
|
+
|
|
498
|
+
elif type_info == 'scan_info':
|
|
499
|
+
settings_all = [ioxml.parameter_to_xml_string(params),
|
|
500
|
+
ioxml.parameter_to_xml_string(self.settings),
|
|
501
|
+
ioxml.parameter_to_xml_string(self.h5saver.settings),
|
|
502
|
+
ioxml.parameter_to_xml_string(self.scanner.settings)]
|
|
503
|
+
|
|
504
|
+
settings_str = b'<All_settings title="All Settings" type="group">'
|
|
505
|
+
for set in settings_all:
|
|
506
|
+
if len(settings_str + set) < 60000:
|
|
507
|
+
# size limit for any object header (including all the other attributes) is 64kb
|
|
508
|
+
settings_str += set
|
|
509
|
+
else:
|
|
510
|
+
break
|
|
511
|
+
settings_str += b'</All_settings>'
|
|
512
|
+
attr['settings'] = settings_str
|
|
513
|
+
|
|
514
|
+
def create_new_file(self, new_file):
|
|
515
|
+
if new_file:
|
|
516
|
+
self._metada_dataset_set = False
|
|
517
|
+
self.close_file()
|
|
518
|
+
|
|
519
|
+
self.module_and_data_saver.h5saver = self.h5saver # force it for detectors to update their h5saver
|
|
520
|
+
res = self.update_file_settings()
|
|
521
|
+
if new_file:
|
|
522
|
+
self.ui.enable_start_stop()
|
|
523
|
+
return res
|
|
524
|
+
|
|
525
|
+
@property
|
|
526
|
+
def h5saver(self):
|
|
527
|
+
if self._h5saver is None:
|
|
528
|
+
self._h5saver = H5Saver(backend=config_utils('general', 'hdf5_backend'))
|
|
529
|
+
self._h5saver.settings.child('do_save').hide()
|
|
530
|
+
self._h5saver.settings.child('custom_name').hide()
|
|
531
|
+
self._h5saver.new_file_sig.connect(self.create_new_file)
|
|
532
|
+
if self._h5saver.h5_file is None:
|
|
533
|
+
self._h5saver.init_file(update_h5=True)
|
|
534
|
+
if not self._h5saver.isopen():
|
|
535
|
+
self._h5saver.init_file(addhoc_file_path=self._h5saver.settings['current_h5_file'])
|
|
536
|
+
return self._h5saver
|
|
537
|
+
|
|
538
|
+
@h5saver.setter
|
|
539
|
+
def h5saver(self, h5saver_temp: H5Saver):
|
|
540
|
+
self._h5saver = h5saver_temp
|
|
541
|
+
|
|
542
|
+
def close_file(self):
|
|
543
|
+
self.h5saver.close_file()
|
|
544
|
+
|
|
545
|
+
@property
|
|
546
|
+
def module_and_data_saver(self):
|
|
547
|
+
if not self._module_and_data_saver.h5saver.isopen():
|
|
548
|
+
self._module_and_data_saver.h5saver = self.h5saver
|
|
549
|
+
return self._module_and_data_saver
|
|
550
|
+
|
|
551
|
+
@module_and_data_saver.setter
|
|
552
|
+
def module_and_data_saver(self, mod: module_saving.ScanSaver):
|
|
553
|
+
self._module_and_data_saver = mod
|
|
554
|
+
self._module_and_data_saver.h5saver = self.h5saver
|
|
555
|
+
|
|
556
|
+
def update_file_settings(self):
|
|
557
|
+
try:
|
|
558
|
+
res = True
|
|
559
|
+
if not self._metada_dataset_set:
|
|
560
|
+
res = self.set_metadata_about_dataset()
|
|
561
|
+
self.save_metadata(self.h5saver.raw_group, 'dataset_info')
|
|
562
|
+
|
|
563
|
+
if self.navigator is not None:
|
|
564
|
+
self.navigator.update_h5file(self.h5saver.h5_file)
|
|
565
|
+
self.navigator.settings.child('settings', 'filepath').setValue(self.h5saver.h5_file.filename)
|
|
566
|
+
|
|
567
|
+
return res
|
|
568
|
+
|
|
569
|
+
except Exception as e:
|
|
570
|
+
logger.exception(str(e))
|
|
571
|
+
|
|
572
|
+
def update_scan_info(self):
|
|
573
|
+
# set attributes to the current group, such as scan_type....
|
|
574
|
+
self.scan_attributes.child('scan_info', 'scan_type').setValue(
|
|
575
|
+
self.scanner.settings.child('scan_type').value())
|
|
576
|
+
self.scan_attributes.child('scan_info', 'scan_sub_type').setValue(
|
|
577
|
+
self.scanner.settings.child('scan_sub_type').value())
|
|
578
|
+
scan_node = self.module_and_data_saver.get_set_node(new=False)
|
|
579
|
+
if scan_node.attrs['scan_done']:
|
|
580
|
+
scan_name = self.module_and_data_saver.get_next_node_name()
|
|
581
|
+
else:
|
|
582
|
+
scan_name = scan_node.name
|
|
583
|
+
self.scan_attributes.child('scan_info', 'scan_name').setValue(scan_name)
|
|
584
|
+
self.scan_attributes.child('scan_info', 'description').setValue('')
|
|
585
|
+
self.h5saver.settings.child('current_scan_name').setValue(scan_name)
|
|
586
|
+
|
|
587
|
+
res = self.set_metadata_about_current_scan()
|
|
588
|
+
return res
|
|
589
|
+
|
|
590
|
+
# PROCESS MODIFICATIONS
|
|
591
|
+
def update_actuators(self, actuators: List[str]):
|
|
592
|
+
self.scanner.actuators = self.modules_manager.actuators
|
|
593
|
+
|
|
594
|
+
def move_to_crosshair(self, *args, **kwargs):
|
|
595
|
+
if self.ui.is_action_checked('move_at'):
|
|
596
|
+
self.modules_manager.connect_actuators()
|
|
597
|
+
self.live_plotter.connect_double_clicked(self.move_at)
|
|
598
|
+
else:
|
|
599
|
+
self.live_plotter.disconnect(self.move_at)
|
|
600
|
+
self.modules_manager.connect_actuators(False)
|
|
601
|
+
|
|
602
|
+
def move_at(self, posx: float, posy: float = None):
|
|
603
|
+
if logging.getLevelName(logger.level) == 'DEBUG':
|
|
604
|
+
print(f'clicked at: {posx}, {posy}')
|
|
605
|
+
positions = [posx, posy]
|
|
606
|
+
positions = positions[:self.scanner.n_axes]
|
|
607
|
+
actuators = self.modules_manager.actuators
|
|
608
|
+
dte = data_mod.DataToExport(name="move_at")
|
|
609
|
+
for ind, pos in enumerate(positions):
|
|
610
|
+
dte.append(DataActuator(actuators[ind].title, data=float(pos), units=actuators[ind].units))
|
|
611
|
+
|
|
612
|
+
self.modules_manager.move_actuators(dte, polling=False)
|
|
613
|
+
|
|
614
|
+
def value_changed(self, param):
|
|
615
|
+
"""
|
|
616
|
+
|
|
617
|
+
"""
|
|
618
|
+
if param.name() == 'scan_average':
|
|
619
|
+
self.ui.show_average_step(param.value() > 1)
|
|
620
|
+
elif param.name() == 'prepare_viewers':
|
|
621
|
+
self.prepare_viewers()
|
|
622
|
+
elif param.name() == 'plot_probe':
|
|
623
|
+
self.plot_from()
|
|
624
|
+
elif param.name() == 'plot_at_each_step':
|
|
625
|
+
self.settings.child('plot_options', 'refresh_live').show(not param.value())
|
|
626
|
+
|
|
627
|
+
def clear_plot_from(self):
|
|
628
|
+
self.settings.child('plot_options', 'plot_0d').setValue(dict(all_items=[], selected=[]))
|
|
629
|
+
self.settings.child('plot_options', 'plot_1d').setValue(dict(all_items=[], selected=[]))
|
|
630
|
+
|
|
631
|
+
def check_number_type_viewers(self) -> Tuple[
|
|
632
|
+
List[ViewersEnum],
|
|
633
|
+
List[str],
|
|
634
|
+
bool]:
|
|
635
|
+
""" Assert from selected options the number and type of needed viewers for live plotting
|
|
636
|
+
|
|
637
|
+
Return
|
|
638
|
+
------
|
|
639
|
+
List[ViewersEnum]: the list of needed viewers
|
|
640
|
+
List[str]: the list of data names to be plotted in the corresponding viewer
|
|
641
|
+
"""
|
|
642
|
+
viewer2D_overload = False
|
|
643
|
+
viewers_enum = [ViewersEnum.Viewer0D.increase_dim(self.scanner.n_axes)
|
|
644
|
+
for _ in range(len(self.settings['plot_options', 'plot_0d']['selected']))]
|
|
645
|
+
data_names = self.settings['plot_options', 'plot_0d']['selected'][:]
|
|
646
|
+
|
|
647
|
+
if self.settings['plot_options', 'group0D'] and len(viewers_enum) > 0 and ViewersEnum.Viewer1D in viewers_enum:
|
|
648
|
+
viewers_enum = [ViewersEnum.Viewer1D]
|
|
649
|
+
data_names = [self.live_plotter.grouped_data0D_fullname]
|
|
650
|
+
elif (self.settings['plot_options', 'group0D'] and len(viewers_enum) > 0 and
|
|
651
|
+
ViewersEnum.Viewer2D in viewers_enum):
|
|
652
|
+
viewers_enum = [ViewersEnum.Viewer2D]
|
|
653
|
+
n2Dplots = len(data_names)
|
|
654
|
+
if (self.settings['scan_options', 'scan_average'] > 1 and
|
|
655
|
+
self.settings['scan_options', 'average_on_top']):
|
|
656
|
+
n2Dplots *= 2
|
|
657
|
+
if n2Dplots > 3:
|
|
658
|
+
viewer2D_overload = True
|
|
659
|
+
data_names = [self.live_plotter.grouped_data0D_fullname]
|
|
660
|
+
|
|
661
|
+
if self.scanner.n_axes <= 1:
|
|
662
|
+
viewers_enum.extend([ViewersEnum.Viewer1D.increase_dim(self.scanner.n_axes)
|
|
663
|
+
for _ in range(len(self.settings['plot_options', 'plot_1d']['selected']))])
|
|
664
|
+
data_names.extend(self.settings['plot_options', 'plot_1d']['selected'][:])
|
|
665
|
+
if (self.settings['scan_options', 'scan_average'] > 1 and
|
|
666
|
+
not self.settings['scan_options', 'average_on_top']):
|
|
667
|
+
|
|
668
|
+
viewers_enum = viewers_enum + viewers_enum
|
|
669
|
+
data_names = data_names + [f'{data_name}_averaged' for data_name in data_names]
|
|
670
|
+
|
|
671
|
+
return viewers_enum, data_names, viewer2D_overload
|
|
672
|
+
|
|
673
|
+
def prepare_viewers(self):
|
|
674
|
+
""" Assert from selected options the number and type of needed viewers for live plotting
|
|
675
|
+
and prepare them on the live plot panel
|
|
676
|
+
"""
|
|
677
|
+
viewers_enum, data_names, _ = self.check_number_type_viewers()
|
|
678
|
+
self.live_plotter.prepare_viewers(viewers_enum, viewers_name=data_names)
|
|
679
|
+
|
|
680
|
+
def update_status(self, txt: str, wait_time=0):
|
|
681
|
+
""" Show the txt message in the status bar with a delay of wait_time ms.
|
|
682
|
+
|
|
683
|
+
add an info log in the logger
|
|
684
|
+
|
|
685
|
+
Parameters
|
|
686
|
+
----------
|
|
687
|
+
txt: str
|
|
688
|
+
the message to log
|
|
689
|
+
wait_time: int
|
|
690
|
+
leave the message apparent in the status bar for this duration in ms
|
|
691
|
+
"""
|
|
692
|
+
self.ui.display_status(txt, wait_time)
|
|
693
|
+
self.status_signal.emit(txt)
|
|
694
|
+
logger.info(txt)
|
|
695
|
+
|
|
696
|
+
def thread_status(self, status: utils.ThreadCommand):
|
|
697
|
+
""" General function to get datas/infos from child thread back to the main.
|
|
698
|
+
|
|
699
|
+
Possible commands are:
|
|
700
|
+
|
|
701
|
+
* "Update_Status"
|
|
702
|
+
* "Update_scan_index"
|
|
703
|
+
* "Scan_done"
|
|
704
|
+
* "Timeout"
|
|
705
|
+
"""
|
|
706
|
+
if status.command == "Update_Status":
|
|
707
|
+
self.update_status(status.attribute, wait_time=self.wait_time)
|
|
708
|
+
|
|
709
|
+
elif status.command == "Update_scan_index":
|
|
710
|
+
# status[1] = [ind_scan,ind_average]
|
|
711
|
+
self.ind_scan = status.attribute[0]
|
|
712
|
+
self.ui.set_scan_step(status.attribute[0] + 1)
|
|
713
|
+
self.ind_average = status.attribute[1]
|
|
714
|
+
self.ui.set_scan_step_average(status.attribute[1] + 1)
|
|
715
|
+
|
|
716
|
+
elif status.command == "Scan_done":
|
|
717
|
+
|
|
718
|
+
self.modules_manager.reset_signals()
|
|
719
|
+
self.live_timer.stop()
|
|
720
|
+
self.ui.set_scan_done()
|
|
721
|
+
scan_node = self.module_and_data_saver.get_last_node()
|
|
722
|
+
scan_node.attrs['scan_done'] = True
|
|
723
|
+
self.module_and_data_saver.flush()
|
|
724
|
+
self.close_file()
|
|
725
|
+
|
|
726
|
+
if not self.batch_started:
|
|
727
|
+
if not self.dashboard.overshoot:
|
|
728
|
+
self.set_ini_positions()
|
|
729
|
+
self.ui.set_action_enabled('ini_positions', True)
|
|
730
|
+
self.ui.set_action_enabled('start', True)
|
|
731
|
+
|
|
732
|
+
# reactivate module controls using remote_control
|
|
733
|
+
if hasattr(self.dashboard, 'remote_manager'):
|
|
734
|
+
remote_manager = getattr(self.dashboard, 'remote_manager')
|
|
735
|
+
remote_manager.activate_all(True)
|
|
736
|
+
if self.navigator is not None:
|
|
737
|
+
self.navigator.list_2D_scans()
|
|
738
|
+
else:
|
|
739
|
+
self.ind_batch += 1
|
|
740
|
+
self.loop_scan_batch()
|
|
741
|
+
|
|
742
|
+
elif status.command == "Timeout":
|
|
743
|
+
self.ui.set_permanent_status('Timeout occurred')
|
|
744
|
+
|
|
745
|
+
elif status.command == 'add_data':
|
|
746
|
+
self.module_and_data_saver.add_data(**status.attribute)
|
|
747
|
+
|
|
748
|
+
elif status.command == 'add_nav_axes':
|
|
749
|
+
self.module_and_data_saver.add_nav_axes(status.attribute)
|
|
750
|
+
|
|
751
|
+
############
|
|
752
|
+
# PLOTTING
|
|
753
|
+
|
|
754
|
+
def save_temp_live_data(self, scan_data: ScanDataTemp):
|
|
755
|
+
if scan_data.scan_index == 0:
|
|
756
|
+
nav_axes = self.scanner.get_nav_axes()
|
|
757
|
+
Naverage = self.settings['scan_options', 'scan_average']
|
|
758
|
+
if Naverage > 1:
|
|
759
|
+
for nav_axis in nav_axes:
|
|
760
|
+
nav_axis.index += 1
|
|
761
|
+
nav_axes.append(data_mod.Axis('Average',
|
|
762
|
+
data=np.linspace(0, Naverage - 1, Naverage),
|
|
763
|
+
index=0))
|
|
764
|
+
|
|
765
|
+
self.extended_saver.add_nav_axes(self.h5temp.raw_group, nav_axes)
|
|
766
|
+
|
|
767
|
+
self.extended_saver.add_data(self.h5temp.raw_group, scan_data.data, scan_data.indexes,
|
|
768
|
+
distribution=self.scanner.distribution)
|
|
769
|
+
if self.settings['plot_options', 'plot_at_each_step']:
|
|
770
|
+
self.update_live_plots()
|
|
771
|
+
|
|
772
|
+
def update_live_plots(self):
|
|
773
|
+
|
|
774
|
+
if self.settings['scan_options', 'scan_average'] > 1:
|
|
775
|
+
average_axis = 0
|
|
776
|
+
else:
|
|
777
|
+
average_axis = None
|
|
778
|
+
try:
|
|
779
|
+
self.live_plotter.load_plot_data(group_0D=self.settings['plot_options', 'group0D'],
|
|
780
|
+
average_axis=average_axis,
|
|
781
|
+
average_index=self.ind_average,
|
|
782
|
+
separate_average= not self.settings['scan_options', 'average_on_top'],
|
|
783
|
+
target_at=self.scanner.positions[self.ind_scan],
|
|
784
|
+
last_step=(self.ind_scan ==
|
|
785
|
+
self.scanner.positions.size - 1 and
|
|
786
|
+
self.ind_average ==
|
|
787
|
+
self.settings[
|
|
788
|
+
'scan_options', 'scan_average'] - 1))
|
|
789
|
+
except Exception as e:
|
|
790
|
+
logger.exception(str(e))
|
|
791
|
+
#################
|
|
792
|
+
# SCAN FLOW
|
|
793
|
+
|
|
794
|
+
def set_scan(self, scan=None) -> bool:
|
|
795
|
+
"""
|
|
796
|
+
Sets the current scan given the selected settings. Makes some checks,
|
|
797
|
+
increments the h5 file scans.
|
|
798
|
+
In case the dialog is cancelled, return False and aborts the scan
|
|
799
|
+
"""
|
|
800
|
+
try:
|
|
801
|
+
|
|
802
|
+
res = self.update_scan_info()
|
|
803
|
+
if not res:
|
|
804
|
+
return False
|
|
805
|
+
|
|
806
|
+
is_oversteps = self.scanner.set_scan()
|
|
807
|
+
if is_oversteps:
|
|
808
|
+
messagebox(
|
|
809
|
+
text=f"An error occurred when establishing the scan steps. Actual settings "
|
|
810
|
+
f"gives approximately {int(self.scanner.n_steps)} steps."
|
|
811
|
+
f" Please check the steps number "
|
|
812
|
+
f"limit in the config file ({config['scan']['steps_limit']}) or modify"
|
|
813
|
+
f" your scan settings.")
|
|
814
|
+
|
|
815
|
+
_, _, viewer2D_overload = self.check_number_type_viewers()
|
|
816
|
+
if viewer2D_overload:
|
|
817
|
+
messagebox(text=
|
|
818
|
+
'The number of live data chosen and the selected options '
|
|
819
|
+
'will not be able to render fully on the 2D live viewers. Consider changing '
|
|
820
|
+
'the options, such as "plot on top" for the averaging or "Group 0D data" '
|
|
821
|
+
'or the number of selected data')
|
|
822
|
+
return False
|
|
823
|
+
|
|
824
|
+
if self.modules_manager.Nactuators != self.scanner.n_axes:
|
|
825
|
+
messagebox(
|
|
826
|
+
text="There are not enough or too much selected move modules for this scan")
|
|
827
|
+
return False
|
|
828
|
+
|
|
829
|
+
self.ui.n_scan_steps = self.scanner.n_steps
|
|
830
|
+
|
|
831
|
+
# check if the modules are initialized
|
|
832
|
+
for module in self.modules_manager.actuators:
|
|
833
|
+
if not module.initialized_state:
|
|
834
|
+
raise DAQ_ScanException('module ' + module.title + " is not initialized")
|
|
835
|
+
|
|
836
|
+
for module in self.modules_manager.detectors:
|
|
837
|
+
if not module.initialized_state:
|
|
838
|
+
raise DAQ_ScanException('module ' + module.title + " is not initialized")
|
|
839
|
+
|
|
840
|
+
self.ui.enable_start_stop(True)
|
|
841
|
+
return True
|
|
842
|
+
|
|
843
|
+
except Exception as e:
|
|
844
|
+
logger.exception(str(e))
|
|
845
|
+
self.ui.enable_start_stop(False)
|
|
846
|
+
|
|
847
|
+
def set_metadata_about_current_scan(self):
|
|
848
|
+
"""
|
|
849
|
+
Set the date/time and author values of the scan_info child of the scan_attributes tree.
|
|
850
|
+
Show the 'scan' file attributes.
|
|
851
|
+
|
|
852
|
+
See Also
|
|
853
|
+
--------
|
|
854
|
+
show_file_attributes
|
|
855
|
+
"""
|
|
856
|
+
date = QDateTime(QDate.currentDate(), QTime.currentTime())
|
|
857
|
+
self.scan_attributes.child('scan_info', 'date_time').setValue(date)
|
|
858
|
+
self.scan_attributes.child('scan_info', 'author').setValue(
|
|
859
|
+
self.dataset_attributes.child('dataset_info', 'author').value())
|
|
860
|
+
if not self.batch_started:
|
|
861
|
+
res = self.show_file_attributes('scan')
|
|
862
|
+
else:
|
|
863
|
+
res = True
|
|
864
|
+
return res
|
|
865
|
+
|
|
866
|
+
def set_metadata_about_dataset(self):
|
|
867
|
+
"""
|
|
868
|
+
Set the date value of the data_set_info-date_time child of the data_set_attributes tree.
|
|
869
|
+
Show the 'dataset' file attributes.
|
|
870
|
+
|
|
871
|
+
See Also
|
|
872
|
+
--------
|
|
873
|
+
show_file_attributes
|
|
874
|
+
"""
|
|
875
|
+
date = QDateTime(QDate.currentDate(), QTime.currentTime())
|
|
876
|
+
self.dataset_attributes.child('dataset_info', 'date_time').setValue(date)
|
|
877
|
+
res = self.show_file_attributes('dataset')
|
|
878
|
+
self._metada_dataset_set = True
|
|
879
|
+
return res
|
|
880
|
+
|
|
881
|
+
def exit_runner_thread(self, duration : int = 5000):
|
|
882
|
+
self.runner_thread.quit()
|
|
883
|
+
terminated = self.runner_thread.wait(duration)
|
|
884
|
+
if not terminated:
|
|
885
|
+
self.runner_thread.terminate()
|
|
886
|
+
self.runner_thread.wait()
|
|
887
|
+
|
|
888
|
+
def start_scan(self):
|
|
889
|
+
"""
|
|
890
|
+
Start an acquisition calling the set_scan function.
|
|
891
|
+
Emit the command_DAQ signal "start_acquisition".
|
|
892
|
+
|
|
893
|
+
See Also
|
|
894
|
+
--------
|
|
895
|
+
set_scan
|
|
896
|
+
"""
|
|
897
|
+
self.ui.display_status('Starting acquisition')
|
|
898
|
+
self.dashboard.overshoot = False
|
|
899
|
+
#deactivate double_clicked
|
|
900
|
+
if self.ui.is_action_checked('move_at'):
|
|
901
|
+
self.ui.get_action('move_at').trigger()
|
|
902
|
+
|
|
903
|
+
self._module_and_data_saver.h5saver = self.h5saver
|
|
904
|
+
res = self.set_scan()
|
|
905
|
+
if res:
|
|
906
|
+
# deactivate module controls using remote_control
|
|
907
|
+
if hasattr(self.dashboard, 'remote_manager'):
|
|
908
|
+
remote_manager = getattr(self.dashboard, 'remote_manager')
|
|
909
|
+
remote_manager.activate_all(False)
|
|
910
|
+
|
|
911
|
+
new_scan = self.module_and_data_saver.get_last_node().attrs['scan_done'] # get_last_node
|
|
912
|
+
scan_node = self.module_and_data_saver.get_set_node(new=new_scan)
|
|
913
|
+
self.save_metadata(scan_node, 'scan_info')
|
|
914
|
+
|
|
915
|
+
self._init_live()
|
|
916
|
+
Naverage = self.settings['scan_options', 'scan_average']
|
|
917
|
+
if Naverage > 1:
|
|
918
|
+
scan_shape = [Naverage]
|
|
919
|
+
scan_shape.extend(self.scanner.get_scan_shape())
|
|
920
|
+
else:
|
|
921
|
+
scan_shape = self.scanner.get_scan_shape()
|
|
922
|
+
for det in self.modules_manager.detectors:
|
|
923
|
+
det._module_and_data_saver = (
|
|
924
|
+
module_saving.DetectorExtendedSaver(det, scan_shape))
|
|
925
|
+
self._module_and_data_saver.h5saver = self.h5saver # force the update as the h5saver will also be set on each detectors
|
|
926
|
+
|
|
927
|
+
# mandatory to deal with multithreads
|
|
928
|
+
if self.runner_thread is not None:
|
|
929
|
+
self.command_daq_signal.disconnect()
|
|
930
|
+
self.exit_runner_thread()
|
|
931
|
+
self.runner_thread = None
|
|
932
|
+
|
|
933
|
+
self.runner_thread = QThread()
|
|
934
|
+
|
|
935
|
+
scan_acquisition = DAQScanAcquisition(self.settings, self.scanner, self.modules_manager,
|
|
936
|
+
)
|
|
937
|
+
|
|
938
|
+
if config['scan']['scan_in_thread']:
|
|
939
|
+
scan_acquisition.moveToThread(self.runner_thread)
|
|
940
|
+
self.command_daq_signal[utils.ThreadCommand].connect(scan_acquisition.queue_command)
|
|
941
|
+
scan_acquisition.scan_data_tmp[ScanDataTemp].connect(self.save_temp_live_data)
|
|
942
|
+
scan_acquisition.status_sig[utils.ThreadCommand].connect(self.thread_status)
|
|
943
|
+
|
|
944
|
+
self.runner_thread.scan_acquisition = scan_acquisition
|
|
945
|
+
self.runner_thread.start()
|
|
946
|
+
|
|
947
|
+
self.ui.set_action_enabled('ini_positions', False)
|
|
948
|
+
self.ui.set_action_enabled('start', False)
|
|
949
|
+
self.ui.set_scan_done(False)
|
|
950
|
+
if not self.settings['plot_options', 'plot_at_each_step']:
|
|
951
|
+
self.live_timer.start(self.settings['plot_options', 'refresh_live'])
|
|
952
|
+
self.command_daq_signal.emit(utils.ThreadCommand('start_acquisition'))
|
|
953
|
+
self.ui.set_permanent_status('Running acquisition')
|
|
954
|
+
logger.info('Running acquisition')
|
|
955
|
+
|
|
956
|
+
def _init_live(self):
|
|
957
|
+
Naverage = self.settings['scan_options', 'scan_average']
|
|
958
|
+
if Naverage > 1:
|
|
959
|
+
scan_shape = [Naverage]
|
|
960
|
+
scan_shape.extend(self.scanner.get_scan_shape())
|
|
961
|
+
else:
|
|
962
|
+
scan_shape = self.scanner.get_scan_shape()
|
|
963
|
+
if self.temp_path is not None:
|
|
964
|
+
try:
|
|
965
|
+
self.h5temp.close()
|
|
966
|
+
self.temp_path.cleanup()
|
|
967
|
+
except Exception as e:
|
|
968
|
+
logger.exception(str(e))
|
|
969
|
+
|
|
970
|
+
self.h5temp = H5Saver()
|
|
971
|
+
self.temp_path = tempfile.TemporaryDirectory(prefix='pymo')
|
|
972
|
+
addhoc_file_path = Path(self.temp_path.name).joinpath('temp_data.h5')
|
|
973
|
+
self.h5temp.init_file(custom_naming=True, addhoc_file_path=addhoc_file_path)
|
|
974
|
+
self.extended_saver: data_saving.DataToExportExtendedSaver =\
|
|
975
|
+
data_saving.DataToExportExtendedSaver(self.h5temp, extended_shape=scan_shape)
|
|
976
|
+
self.live_plotter.h5saver = self.h5temp
|
|
977
|
+
|
|
978
|
+
self.prepare_viewers()
|
|
979
|
+
QtWidgets.QApplication.processEvents()
|
|
980
|
+
|
|
981
|
+
def set_ini_positions(self):
|
|
982
|
+
"""
|
|
983
|
+
Send the command_DAQ signal with "set_ini_positions" list item as an attribute.
|
|
984
|
+
"""
|
|
985
|
+
self.command_daq_signal.emit(utils.ThreadCommand("set_ini_positions"))
|
|
986
|
+
|
|
987
|
+
def stop_scan(self):
|
|
988
|
+
"""
|
|
989
|
+
Emit the command_DAQ signal "stop_acquisition".
|
|
990
|
+
|
|
991
|
+
See Also
|
|
992
|
+
--------
|
|
993
|
+
set_ini_positions
|
|
994
|
+
"""
|
|
995
|
+
self.ui.set_permanent_status('Stoping acquisition')
|
|
996
|
+
self.command_daq_signal.emit(utils.ThreadCommand("stop_acquisition"))
|
|
997
|
+
scan_node = self.module_and_data_saver.get_last_node()
|
|
998
|
+
if scan_node is not None:
|
|
999
|
+
scan_node.attrs['scan_done'] = True
|
|
1000
|
+
|
|
1001
|
+
if not self.dashboard.overshoot:
|
|
1002
|
+
self.set_ini_positions() # do not set ini position again in case overshoot fired
|
|
1003
|
+
status = 'Data Acquisition has been stopped by user'
|
|
1004
|
+
else:
|
|
1005
|
+
status = 'Data Acquisition has been stopped due to overshoot'
|
|
1006
|
+
|
|
1007
|
+
self.update_status(status)
|
|
1008
|
+
self.ui.set_permanent_status('')
|
|
1009
|
+
|
|
1010
|
+
self.ui.set_action_enabled('ini_positions', True)
|
|
1011
|
+
self.ui.set_action_enabled('start', True)
|
|
1012
|
+
|
|
1013
|
+
def do_scan(self, start_scan=True):
|
|
1014
|
+
"""Public method to start the scan programmatically"""
|
|
1015
|
+
if start_scan:
|
|
1016
|
+
if not self.ui.is_action_enabled('start'):
|
|
1017
|
+
self.ui.get_action('set_scan').trigger()
|
|
1018
|
+
QtWidgets.QApplication.processEvents()
|
|
1019
|
+
self.ui.get_action('start').trigger()
|
|
1020
|
+
else:
|
|
1021
|
+
self.ui.get_action('stop').trigger()
|
|
1022
|
+
|
|
1023
|
+
|
|
1024
|
+
class DAQScanAcquisition(QObject):
|
|
1025
|
+
"""
|
|
1026
|
+
=========================== ========================================
|
|
1027
|
+
|
|
1028
|
+
=========================== ========================================
|
|
1029
|
+
|
|
1030
|
+
"""
|
|
1031
|
+
scan_data_tmp = Signal(ScanDataTemp)
|
|
1032
|
+
status_sig = Signal(utils.ThreadCommand)
|
|
1033
|
+
|
|
1034
|
+
def __init__(self, scan_settings: Parameter = None, scanner: Scanner = None,
|
|
1035
|
+
modules_manager: ModulesManager = None,):
|
|
1036
|
+
|
|
1037
|
+
"""
|
|
1038
|
+
DAQScanAcquisition deal with the acquisition part of daq_scan, that is transferring commands to modules,
|
|
1039
|
+
getting back data, saviong and letting know th UI about the scan status
|
|
1040
|
+
|
|
1041
|
+
"""
|
|
1042
|
+
|
|
1043
|
+
super().__init__()
|
|
1044
|
+
|
|
1045
|
+
self.scan_settings = scan_settings
|
|
1046
|
+
self.modules_manager = modules_manager
|
|
1047
|
+
self.scanner = scanner
|
|
1048
|
+
|
|
1049
|
+
self.stop_scan_flag = False
|
|
1050
|
+
self.Naverage = self.scan_settings['scan_options', 'scan_average']
|
|
1051
|
+
self.ind_average = 0
|
|
1052
|
+
self.ind_scan = 0
|
|
1053
|
+
|
|
1054
|
+
self.isadaptive = self.scanner.scan_sub_type == 'Adaptive'
|
|
1055
|
+
|
|
1056
|
+
self.modules_manager.timeout_signal.connect(self.timeout)
|
|
1057
|
+
self.timeout_scan_flag = False
|
|
1058
|
+
|
|
1059
|
+
|
|
1060
|
+
self.move_done_flag = False
|
|
1061
|
+
self.det_done_flag = False
|
|
1062
|
+
|
|
1063
|
+
self.det_done_datas = data_mod.DataToExport('ScanData')
|
|
1064
|
+
|
|
1065
|
+
scan_shape = self.scanner.get_scan_shape()
|
|
1066
|
+
if self.Naverage > 1:
|
|
1067
|
+
self.scan_shape = [self.Naverage]
|
|
1068
|
+
self.scan_shape.extend(scan_shape)
|
|
1069
|
+
else:
|
|
1070
|
+
self.scan_shape = scan_shape
|
|
1071
|
+
|
|
1072
|
+
def queue_command(self, command: utils.ThreadCommand):
|
|
1073
|
+
"""Process the commands sent by the main ui
|
|
1074
|
+
|
|
1075
|
+
Parameters
|
|
1076
|
+
----------
|
|
1077
|
+
command: utils.ThreadCommand
|
|
1078
|
+
"""
|
|
1079
|
+
if command.command == "start_acquisition":
|
|
1080
|
+
self.start_acquisition()
|
|
1081
|
+
|
|
1082
|
+
elif command.command == "stop_acquisition":
|
|
1083
|
+
self.stop_scan_flag = True
|
|
1084
|
+
|
|
1085
|
+
elif command.command == "set_ini_positions":
|
|
1086
|
+
self.set_ini_positions()
|
|
1087
|
+
|
|
1088
|
+
elif command.command == "move_stages":
|
|
1089
|
+
self.modules_manager.move_actuators(command.attribute, polling=False)
|
|
1090
|
+
|
|
1091
|
+
def set_ini_positions(self):
|
|
1092
|
+
""" Set the actuators's positions totheir initial value as defined in the scanner """
|
|
1093
|
+
try:
|
|
1094
|
+
if self.scanner.scan_sub_type != 'Adaptive':
|
|
1095
|
+
self.modules_manager.move_actuators(self.scanner.positions_at(0), polling=False)
|
|
1096
|
+
|
|
1097
|
+
except Exception as e:
|
|
1098
|
+
logger.exception(str(e))
|
|
1099
|
+
|
|
1100
|
+
def start_acquisition(self):
|
|
1101
|
+
try:
|
|
1102
|
+
|
|
1103
|
+
self.modules_manager.connect_actuators()
|
|
1104
|
+
self.modules_manager.connect_detectors()
|
|
1105
|
+
|
|
1106
|
+
self.stop_scan_flag = False
|
|
1107
|
+
|
|
1108
|
+
Naxes = self.scanner.n_axes
|
|
1109
|
+
scan_type = self.scanner.scan_type
|
|
1110
|
+
self.navigation_axes = self.scanner.get_nav_axes()
|
|
1111
|
+
self.status_sig.emit(utils.ThreadCommand("Update_Status",
|
|
1112
|
+
attribute="Acquisition has started"))
|
|
1113
|
+
|
|
1114
|
+
self.timeout_scan_flag = False
|
|
1115
|
+
for ind_average in range(self.Naverage):
|
|
1116
|
+
self.ind_average = ind_average
|
|
1117
|
+
self.ind_scan = -1
|
|
1118
|
+
while True:
|
|
1119
|
+
self.ind_scan += 1
|
|
1120
|
+
if not self.isadaptive:
|
|
1121
|
+
if self.ind_scan >= len(self.scanner.positions):
|
|
1122
|
+
break
|
|
1123
|
+
positions = self.scanner.positions_at(self.ind_scan) # get positions
|
|
1124
|
+
else:
|
|
1125
|
+
pass
|
|
1126
|
+
|
|
1127
|
+
self.status_sig.emit(
|
|
1128
|
+
utils.ThreadCommand("Update_scan_index",
|
|
1129
|
+
attribute=[self.ind_scan, ind_average]))
|
|
1130
|
+
|
|
1131
|
+
if self.stop_scan_flag or self.timeout_scan_flag:
|
|
1132
|
+
break
|
|
1133
|
+
|
|
1134
|
+
#move motors of modules and wait for move completion
|
|
1135
|
+
positions = self.modules_manager.order_positions(self.modules_manager.move_actuators(positions))
|
|
1136
|
+
|
|
1137
|
+
QThread.msleep(self.scan_settings['time_flow', 'wait_time_between'])
|
|
1138
|
+
|
|
1139
|
+
#grab datas and wait for grab completion
|
|
1140
|
+
self.det_done(self.modules_manager.grab_data(positions=positions), positions)
|
|
1141
|
+
|
|
1142
|
+
# daq_scan wait time
|
|
1143
|
+
QThread.msleep(self.scan_settings.child('time_flow', 'wait_time').value())
|
|
1144
|
+
|
|
1145
|
+
self.modules_manager.timeout_signal.disconnect()
|
|
1146
|
+
self.modules_manager.connect_actuators(False)
|
|
1147
|
+
self.modules_manager.connect_detectors(False)
|
|
1148
|
+
|
|
1149
|
+
self.status_sig.emit(utils.ThreadCommand("Update_Status",
|
|
1150
|
+
attribute="Acquisition has finished"))
|
|
1151
|
+
self.status_sig.emit(utils.ThreadCommand("Scan_done"))
|
|
1152
|
+
|
|
1153
|
+
|
|
1154
|
+
except Exception as e:
|
|
1155
|
+
logger.exception(str(e))
|
|
1156
|
+
|
|
1157
|
+
def det_done(self, det_done_datas: data_mod.DataToExport, positions):
|
|
1158
|
+
"""
|
|
1159
|
+
|
|
1160
|
+
"""
|
|
1161
|
+
try:
|
|
1162
|
+
indexes = self.scanner.get_indexes_from_scan_index(self.ind_scan)
|
|
1163
|
+
if self.Naverage > 1:
|
|
1164
|
+
indexes = [self.ind_average] + list(indexes)
|
|
1165
|
+
indexes = tuple(indexes)
|
|
1166
|
+
if self.ind_scan == 0:
|
|
1167
|
+
nav_axes = self.scanner.get_nav_axes()
|
|
1168
|
+
if self.Naverage > 1:
|
|
1169
|
+
for nav_axis in nav_axes:
|
|
1170
|
+
nav_axis.index += 1
|
|
1171
|
+
nav_axes.append(data_mod.Axis('Average', data=np.linspace(0, self.Naverage - 1, self.Naverage),
|
|
1172
|
+
index=0))
|
|
1173
|
+
self.status_sig.emit(utils.ThreadCommand("add_nav_axes", nav_axes))
|
|
1174
|
+
|
|
1175
|
+
self.status_sig.emit(
|
|
1176
|
+
utils.ThreadCommand("add_data",
|
|
1177
|
+
dict(indexes=indexes, distribution=self.scanner.distribution)))
|
|
1178
|
+
|
|
1179
|
+
self.det_done_flag = True
|
|
1180
|
+
|
|
1181
|
+
full_names: list = self.scan_settings['plot_options', 'plot_0d']['selected'][:]
|
|
1182
|
+
full_names.extend(self.scan_settings['plot_options', 'plot_1d']['selected'][:])
|
|
1183
|
+
data_temp = det_done_datas.get_data_from_full_names(full_names, deepcopy=False)
|
|
1184
|
+
n_nav_axis_selection = 2-len(indexes) + 1 if self.Naverage > 1 else 2-len(indexes)
|
|
1185
|
+
data_temp = data_temp.get_data_with_naxes_lower_than(n_nav_axis_selection) # maximum Data2D included nav indexes
|
|
1186
|
+
|
|
1187
|
+
self.scan_data_tmp.emit(ScanDataTemp(self.ind_scan, indexes, data_temp))
|
|
1188
|
+
|
|
1189
|
+
except Exception as e:
|
|
1190
|
+
logger.exception(str(e))
|
|
1191
|
+
|
|
1192
|
+
def timeout(self):
|
|
1193
|
+
"""
|
|
1194
|
+
Send the status signal *'Time out during acquisition'*.
|
|
1195
|
+
"""
|
|
1196
|
+
self.timeout_scan_flag = True
|
|
1197
|
+
self.status_sig.emit(utils.ThreadCommand("Update_Status",
|
|
1198
|
+
attribute="Timeout during acquisition"))
|
|
1199
|
+
self.status_sig.emit(utils.ThreadCommand("Timeout"))
|
|
1200
|
+
|
|
1201
|
+
|
|
1202
|
+
def main():
|
|
1203
|
+
from pymodaq_gui.utils.utils import mkQApp
|
|
1204
|
+
from pymodaq.utils.gui_utils.loader_utils import load_dashboard_with_preset
|
|
1205
|
+
|
|
1206
|
+
app = mkQApp('DAQScan')
|
|
1207
|
+
preset_file_name = config('presets', f'default_preset_for_scan')
|
|
1208
|
+
|
|
1209
|
+
dashboard, extension, win = load_dashboard_with_preset(preset_file_name, 'DAQScan')
|
|
1210
|
+
|
|
1211
|
+
app.exec()
|
|
1212
|
+
|
|
1213
|
+
return dashboard, extension, win
|
|
1214
|
+
|
|
1215
|
+
|
|
1216
|
+
if __name__ == '__main__':
|
|
1217
|
+
main()
|
|
1218
|
+
|