pymodaq 5.0.17__py3-none-any.whl → 5.1.0__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.

Potentially problematic release.


This version of pymodaq might be problematic. Click here for more details.

Files changed (92) hide show
  1. pymodaq/__init__.py +23 -11
  2. pymodaq/control_modules/__init__.py +1 -0
  3. pymodaq/control_modules/daq_move.py +458 -246
  4. pymodaq/control_modules/daq_move_ui/__init__.py +0 -0
  5. pymodaq/control_modules/daq_move_ui/factory.py +48 -0
  6. pymodaq/control_modules/{daq_move_ui.py → daq_move_ui/ui_base.py} +168 -210
  7. pymodaq/control_modules/daq_move_ui/uis/__init__.py +0 -0
  8. pymodaq/control_modules/daq_move_ui/uis/binary.py +139 -0
  9. pymodaq/control_modules/daq_move_ui/uis/original.py +120 -0
  10. pymodaq/control_modules/daq_move_ui/uis/relative.py +124 -0
  11. pymodaq/control_modules/daq_move_ui/uis/simple.py +126 -0
  12. pymodaq/control_modules/daq_viewer.py +113 -101
  13. pymodaq/control_modules/daq_viewer_ui.py +41 -31
  14. pymodaq/control_modules/mocks.py +2 -2
  15. pymodaq/control_modules/move_utility_classes.py +113 -41
  16. pymodaq/control_modules/thread_commands.py +137 -0
  17. pymodaq/control_modules/ui_utils.py +72 -0
  18. pymodaq/control_modules/utils.py +107 -63
  19. pymodaq/control_modules/viewer_utility_classes.py +13 -17
  20. pymodaq/dashboard.py +1294 -625
  21. pymodaq/examples/qt_less_standalone_module.py +48 -11
  22. pymodaq/extensions/__init__.py +8 -3
  23. pymodaq/extensions/adaptive/__init__.py +2 -0
  24. pymodaq/extensions/adaptive/adaptive_optimization.py +179 -0
  25. pymodaq/extensions/adaptive/loss_function/_1d_loss_functions.py +73 -0
  26. pymodaq/extensions/adaptive/loss_function/_2d_loss_functions.py +73 -0
  27. pymodaq/extensions/adaptive/loss_function/__init__.py +3 -0
  28. pymodaq/extensions/adaptive/loss_function/loss_factory.py +110 -0
  29. pymodaq/extensions/adaptive/utils.py +123 -0
  30. pymodaq/extensions/bayesian/__init__.py +1 -1
  31. pymodaq/extensions/bayesian/acquisition/__init__.py +2 -0
  32. pymodaq/extensions/bayesian/acquisition/acquisition_function_factory.py +80 -0
  33. pymodaq/extensions/bayesian/acquisition/base_acquisition_function.py +105 -0
  34. pymodaq/extensions/bayesian/bayesian_optimization.py +143 -0
  35. pymodaq/extensions/bayesian/utils.py +71 -297
  36. pymodaq/extensions/daq_logger/daq_logger.py +7 -12
  37. pymodaq/extensions/daq_logger/h5logging.py +1 -1
  38. pymodaq/extensions/daq_scan.py +30 -55
  39. pymodaq/extensions/data_mixer/__init__.py +0 -0
  40. pymodaq/extensions/data_mixer/daq_0Dviewer_DataMixer.py +97 -0
  41. pymodaq/extensions/data_mixer/data_mixer.py +262 -0
  42. pymodaq/extensions/data_mixer/model.py +108 -0
  43. pymodaq/extensions/data_mixer/models/__init__.py +0 -0
  44. pymodaq/extensions/data_mixer/models/equation_model.py +91 -0
  45. pymodaq/extensions/data_mixer/models/gaussian_fit_model.py +65 -0
  46. pymodaq/extensions/data_mixer/parser.py +53 -0
  47. pymodaq/extensions/data_mixer/utils.py +23 -0
  48. pymodaq/extensions/h5browser.py +3 -34
  49. pymodaq/extensions/optimizers_base/__init__.py +0 -0
  50. pymodaq/extensions/optimizers_base/optimizer.py +1016 -0
  51. pymodaq/extensions/optimizers_base/thread_commands.py +22 -0
  52. pymodaq/extensions/optimizers_base/utils.py +427 -0
  53. pymodaq/extensions/pid/actuator_controller.py +3 -2
  54. pymodaq/extensions/pid/daq_move_PID.py +107 -30
  55. pymodaq/extensions/pid/pid_controller.py +613 -287
  56. pymodaq/extensions/pid/utils.py +8 -5
  57. pymodaq/extensions/utils.py +17 -2
  58. pymodaq/resources/config_template.toml +57 -0
  59. pymodaq/resources/preset_default.xml +1 -1
  60. pymodaq/utils/config.py +13 -4
  61. pymodaq/utils/daq_utils.py +14 -0
  62. pymodaq/utils/data.py +1 -0
  63. pymodaq/utils/gui_utils/loader_utils.py +25 -15
  64. pymodaq/utils/h5modules/module_saving.py +134 -22
  65. pymodaq/utils/leco/daq_move_LECODirector.py +123 -84
  66. pymodaq/utils/leco/daq_xDviewer_LECODirector.py +84 -97
  67. pymodaq/utils/leco/director_utils.py +32 -16
  68. pymodaq/utils/leco/leco_director.py +104 -27
  69. pymodaq/utils/leco/pymodaq_listener.py +186 -97
  70. pymodaq/utils/leco/rpc_method_definitions.py +43 -0
  71. pymodaq/utils/leco/utils.py +25 -25
  72. pymodaq/utils/managers/batchscan_manager.py +12 -11
  73. pymodaq/utils/managers/modules_manager.py +74 -33
  74. pymodaq/utils/managers/overshoot_manager.py +11 -10
  75. pymodaq/utils/managers/preset_manager.py +100 -64
  76. pymodaq/utils/managers/preset_manager_utils.py +163 -107
  77. pymodaq/utils/managers/remote_manager.py +21 -16
  78. pymodaq/utils/scanner/scan_factory.py +18 -4
  79. pymodaq/utils/scanner/scan_selector.py +1 -3
  80. pymodaq/utils/scanner/scanner.py +35 -6
  81. pymodaq/utils/scanner/scanners/_1d_scanners.py +15 -46
  82. pymodaq/utils/scanner/scanners/_2d_scanners.py +21 -68
  83. pymodaq/utils/scanner/scanners/sequential.py +50 -31
  84. pymodaq/utils/scanner/scanners/tabular.py +45 -28
  85. {pymodaq-5.0.17.dist-info → pymodaq-5.1.0.dist-info}/METADATA +7 -6
  86. pymodaq-5.1.0.dist-info/RECORD +154 -0
  87. {pymodaq-5.0.17.dist-info → pymodaq-5.1.0.dist-info}/entry_points.txt +0 -2
  88. pymodaq/extensions/bayesian/bayesian_optimisation.py +0 -685
  89. pymodaq/utils/leco/desktop.ini +0 -2
  90. pymodaq-5.0.17.dist-info/RECORD +0 -121
  91. {pymodaq-5.0.17.dist-info → pymodaq-5.1.0.dist-info}/WHEEL +0 -0
  92. {pymodaq-5.0.17.dist-info → pymodaq-5.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,17 +1,23 @@
1
1
  from __future__ import annotations
2
2
  from typing import Optional, Union
3
3
 
4
- from easydict import EasyDict as edict
5
-
6
4
  from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters, main
7
-
5
+ from pymodaq.control_modules.thread_commands import ThreadStatus, ThreadStatusViewer
6
+ from pymodaq.utils.data import DataFromPlugins, Axis
7
+ from pymodaq_data import DataToExport
8
+ from pymodaq_utils.serialize.factory import SerializableFactory
8
9
  from pymodaq_utils.utils import ThreadCommand, getLineInfo
9
10
 
11
+ from pymodaq.utils import data # for serialization factory registration # noqa: F401
10
12
  from pymodaq_gui.parameter import Parameter
11
- from pymodaq_utils.serialize.serializer_legacy import DeSerializer
12
13
 
13
14
  from pymodaq.utils.leco.leco_director import LECODirector, leco_parameters
14
15
  from pymodaq.utils.leco.director_utils import DetectorDirector
16
+ from pymodaq_utils.logger import set_logger, get_module_name
17
+
18
+ import numpy as np
19
+
20
+ logger = set_logger(get_module_name(__file__))
15
21
 
16
22
 
17
23
  class DAQ_xDViewer_LECODirector(LECODirector, DAQ_Viewer_base):
@@ -25,23 +31,27 @@ class DAQ_xDViewer_LECODirector(LECODirector, DAQ_Viewer_base):
25
31
 
26
32
  params_GRABBER = []
27
33
 
28
- message_list = LECODirector.message_list + ["Quit", "Send Data 0D", "Send Data 1D",
29
- "Send Data 2D", "Send Data ND",
30
- "Status", "Done", "Server Closed",
31
- "Info", "Infos", "Info_xml", 'x_axis', 'y_axis']
32
34
  socket_types = ["GRABBER"]
33
35
  params = comon_parameters + leco_parameters
34
-
35
- def __init__(self, parent=None, params_state=None, grabber_type: str = "0D", **kwargs) -> None:
36
- super().__init__(parent=parent, params_state=params_state, **kwargs)
37
- self.register_rpc_methods((
38
- self.set_x_axis,
39
- self.set_y_axis,
40
- ))
41
- for method in (
42
- self.set_data,
43
- ):
44
- self.listener.register_binary_rpc_method(method, accept_binary_input=True)
36
+ live_mode_available = True
37
+
38
+ def __init__(
39
+ self,
40
+ parent=None,
41
+ params_state=None,
42
+ grabber_type: str = "0D",
43
+ host: Optional[str] = None,
44
+ port: Optional[int] = None,
45
+ **kwargs,
46
+ ) -> None:
47
+ DAQ_Viewer_base.__init__(self, parent=parent, params_state=params_state)
48
+ if host is not None:
49
+ self.settings["host"] = host
50
+ if port is not None:
51
+ self.settings["port"] = port
52
+ LECODirector.__init__(self, host=self.settings["host"], port=self.settings["port"])
53
+
54
+ self.register_binary_rpc_methods((self.set_data,))
45
55
 
46
56
  self.client_type = "GRABBER"
47
57
  self.x_axis = None
@@ -50,6 +60,7 @@ class DAQ_xDViewer_LECODirector(LECODirector, DAQ_Viewer_base):
50
60
  self.grabber_type = grabber_type
51
61
  self.ind_data = 0
52
62
  self.data_mock = None
63
+ self.start_timer()
53
64
 
54
65
  def ini_detector(self, controller=None):
55
66
  """
@@ -62,54 +73,23 @@ class DAQ_xDViewer_LECODirector(LECODirector, DAQ_Viewer_base):
62
73
  --------
63
74
  utility_classes.DAQ_TCP_server.init_server, get_xaxis, get_yaxis
64
75
  """
65
- self.status.update(edict(initialized=False, info="", x_axis=None, y_axis=None,
66
- controller=None))
67
- actor_name = self.settings.child("actor_name").value()
68
- self.controller = self.ini_detector_init( # type: ignore
69
- old_controller=controller,
70
- new_controller=DetectorDirector(actor=actor_name, communicator=self.communicator),
71
- )
72
- self.controller.set_remote_name(self.communicator.full_name) # type: ignore
73
- try:
74
- # self.settings.child(('infos')).addChildren(self.params_GRABBER)
75
-
76
- # init axes from image , here returns only None values (to tricky to di it with the
77
- # server and not really necessary for images anyway)
78
- self.x_axis = self.get_xaxis()
79
- self.y_axis = self.get_yaxis()
80
- self.status.x_axis = self.x_axis
81
- self.status.y_axis = self.y_axis
82
- self.status.initialized = True
83
- return self.status
84
-
85
- except Exception as e:
86
- self.status.info = getLineInfo() + str(e)
87
- self.status.initialized = False
88
- return self.status
89
-
90
- def get_xaxis(self):
91
- """
92
- Obtain the horizontal axis of the image.
93
76
 
94
- Returns
95
- -------
96
- 1D numpy array
97
- Contains a vector of integer corresponding to the horizontal camera pixels.
98
- """
99
- pass
100
- return self.x_axis
77
+ actor_name = self.settings["actor_name"]
78
+ if self.is_master:
79
+ self.controller = DetectorDirector(actor=actor_name,
80
+ communicator=self.communicator)
81
+ try:
82
+ self.controller.set_remote_name(self.communicator.full_name)
83
+ except TimeoutError:
84
+ logger.warning("Timeout setting remote name.")
85
+ else:
86
+ self.controller = controller
101
87
 
102
- def get_yaxis(self):
103
- """
104
- Obtain the vertical axis of the image.
88
+ self.controller.get_settings()
105
89
 
106
- Returns
107
- -------
108
- 1D numpy array
109
- Contains a vector of integer corresponding to the vertical camera pixels.
110
- """
111
- pass
112
- return self.y_axis
90
+ initialized = True
91
+ info = 'Viewer Director ready'
92
+ return info, initialized
113
93
 
114
94
  def grab_data(self, Naverage=1, **kwargs):
115
95
  """
@@ -126,35 +106,20 @@ class DAQ_xDViewer_LECODirector(LECODirector, DAQ_Viewer_base):
126
106
  --------
127
107
  utility_classes.DAQ_TCP_server.process_cmds
128
108
  """
129
- try:
130
- self.ind_grabbed = 0 # to keep track of the current image in the average
131
- self.Naverage = Naverage
132
- self.controller.set_remote_name(self.communicator.full_name)
133
- self.controller.send_data(grabber_type=self.grabber_type)
134
109
 
135
- except Exception as e:
136
- self.emit_status(ThreadCommand('Update_Status', [getLineInfo() + str(e), "log"]))
110
+ self.ind_grabbed = 0 # to keep track of the current image in the average
111
+ self.Naverage = Naverage
112
+ if kwargs.get('live', False):
113
+ self.controller.send_data_grab()
114
+ else:
115
+ self.controller.send_data_snap()
137
116
 
138
117
  def stop(self):
139
- """
140
- not implemented.
141
- """
142
- pass
143
- return ""
144
-
145
- # Methods for RPC calls
146
- def set_x_axis(self, data, label: str = "", units: str = ""):
147
- # TODO make to work
148
- self.x_axis = dict(data=data, label=label, units=units)
149
- self.emit_x_axis()
150
-
151
- def set_y_axis(self, data, label: str = "", units: str = ""):
152
- # TODO make to work
153
- self.y_axis = dict(data=data, label=label, units=units)
154
- self.emit_y_axis()
155
-
156
- def set_data(self, data: Union[list, str, None],
157
- additional_payload: Optional[list[bytes]]=None) -> None:
118
+ """Stop grabbing."""
119
+ self.controller.stop_grab()
120
+
121
+ def set_data(self, data: Union[dict,list, str, float, None],
122
+ additional_payload: Optional[list[bytes]] = None) -> None:
158
123
  """
159
124
  Set the grabbed data signal.
160
125
 
@@ -162,15 +127,37 @@ class DAQ_xDViewer_LECODirector(LECODirector, DAQ_Viewer_base):
162
127
 
163
128
  :param data: If None, look for the additional object
164
129
  """
165
- if isinstance(data, str):
166
- deserializer = DeSerializer.from_b64_string(data)
167
- elif additional_payload is not None:
168
- deserializer = DeSerializer(additional_payload[0])
130
+ if additional_payload:
131
+ dte = SerializableFactory().get_apply_deserializer(additional_payload[0])
132
+ elif data is not None:
133
+ axes = []
134
+ labels = []
135
+ multichannel = False
136
+ if isinstance(data, dict):
137
+ axes = [
138
+ Axis( label=axis.get('label', ''),
139
+ units=axis.get('units', ''),
140
+ data=np.array(axis.get('data', [])),
141
+ index=ind
142
+ ) for ind, axis in enumerate(data.get('axes', []))
143
+ ]
144
+ labels = data.get('labels', [])
145
+ multichannel = data.get('multichannel', False)
146
+ data = data.get('data', [])
147
+ if multichannel:
148
+ # data[0] may fail if data is empty, but it shouldn't happen
149
+ ndim = np.array(data[0]).ndim
150
+ data = [np.atleast_1d(d) for d in data]
151
+ else:
152
+ ndim = np.array(data).ndim
153
+ data = [np.atleast_1d(data)]
154
+
155
+ dfp = DataFromPlugins(self.controller.actor, data=data, axes=axes[:ndim], labels=labels)
156
+ dte = DataToExport('Copy', data=[dfp])
169
157
  else:
170
- raise NotImplementedError("Not implemented to set a list of values.")
171
- dte = deserializer.dte_deserialization()
158
+ raise ValueError("Can't set_data when data is None")
172
159
  self.dte_signal.emit(dte)
173
160
 
174
161
 
175
162
  if __name__ == '__main__':
176
- main(__file__)
163
+ main(__file__, init=False)
@@ -10,8 +10,12 @@ from pyleco.directors.director import Director
10
10
 
11
11
  import pymodaq_gui.parameter.utils as putils
12
12
  from pymodaq_gui.parameter import Parameter, ioxml
13
- from pymodaq.control_modules.move_utility_classes import DataActuator
14
- from pymodaq.utils.leco.utils import serialize_object
13
+ from pymodaq.utils.data import DataActuator
14
+ from pymodaq.utils.leco.utils import binary_serialization_to_kwargs, SerializableFactory
15
+ from pymodaq.utils.leco.rpc_method_definitions import GenericMethods, MoveMethods, ViewerMethods
16
+
17
+ from pymodaq_gui.parameter.utils import ParameterWithPath
18
+
15
19
 
16
20
 
17
21
  class GenericDirector(Director):
@@ -19,31 +23,42 @@ class GenericDirector(Director):
19
23
 
20
24
  def set_remote_name(self, name: Optional[str] = None):
21
25
  """Set the remote name of the Module (i.e. where it should send responses to)."""
22
- self.ask_rpc(method="set_remote_name", name=name or self.communicator.name)
26
+ self.ask_rpc(method=GenericMethods.SET_REMOTE_NAME, name=name or self.communicator.name)
23
27
 
24
28
  def set_info(self, param: Parameter):
25
29
  # It removes the first two parts (main_settings and detector_settings?)
26
- self.set_info_str(path=putils.get_param_path(param)[2:],
27
- param_dict_str=ioxml.parameter_to_xml_string(param).decode())
30
+ pwp = ParameterWithPath(param, putils.get_param_path(param)[2:])
31
+ self.ask_rpc(method=GenericMethods.SET_INFO,
32
+ **binary_serialization_to_kwargs(pwp, data_key='parameter'))
28
33
 
29
- def set_info_str(self, path: List[str], param_dict_str: str) -> None:
30
- self.ask_rpc(method="sef_info", path=path, param_dict_str=param_dict_str)
34
+ def get_settings(self,) -> None:
35
+ self.ask_rpc(GenericMethods.GET_SETTINGS)
31
36
 
32
37
 
33
38
  class DetectorDirector(GenericDirector):
34
- def send_data(self, grabber_type: str = "") -> None:
35
- self.ask_rpc("send_data", grabber_type=grabber_type)
39
+ def send_data_grab(self) -> None:
40
+ self.ask_rpc(ViewerMethods.GRAB)
41
+
42
+ def send_data_snap(self) -> None:
43
+ self.ask_rpc(ViewerMethods.SNAP)
44
+
45
+ def stop_grab(self) -> None:
46
+ self.ask_rpc(ViewerMethods.STOP)
36
47
 
37
48
 
38
49
  class ActuatorDirector(GenericDirector):
39
- def move_abs(self, position: Union[float, DataActuator]) -> None:
40
- self.ask_rpc("move_abs", position=serialize_object(position))
50
+ def move_abs(self, position: Union[list, float, DataActuator]) -> None:
51
+ self.ask_rpc(
52
+ MoveMethods.MOVE_ABS, **binary_serialization_to_kwargs(position, data_key="position")
53
+ )
41
54
 
42
- def move_rel(self, position: Union[float, DataActuator]) -> None:
43
- self.ask_rpc("move_rel", position=serialize_object(position))
55
+ def move_rel(self, position: Union[list, float, DataActuator]) -> None:
56
+ self.ask_rpc(
57
+ MoveMethods.MOVE_REL, **binary_serialization_to_kwargs(position, data_key="position")
58
+ )
44
59
 
45
60
  def move_home(self) -> None:
46
- self.ask_rpc("move_home")
61
+ self.ask_rpc(MoveMethods.MOVE_HOME)
47
62
 
48
63
  def get_actuator_value(self) -> None:
49
64
  """Request that the actuator value is sent later on.
@@ -51,8 +66,9 @@ class ActuatorDirector(GenericDirector):
51
66
  Later the `set_data` method will be called.
52
67
  """
53
68
  # according to DAQ_Move, this supersedes "check_position"
54
- self.ask_rpc("get_actuator_value")
69
+ self.ask_rpc(MoveMethods.GET_ACTUATOR_VALUE)
55
70
 
56
71
  def stop_motion(self,) -> None:
57
72
  # not implemented in DAQ_Move!
58
- self.ask_rpc("stop_motion")
73
+ self.ask_rpc(MoveMethods.STOP_MOTION)
74
+
@@ -1,21 +1,49 @@
1
+
1
2
  import random
2
- from typing import Callable, Sequence, List
3
3
 
4
+ from pyleco.core import COORDINATOR_PORT
4
5
  from pymodaq_utils.enums import StrEnum
5
- from typing import Callable, Sequence, List, Optional, Union
6
+ from typing import Callable, cast, Sequence, List, Optional, Union
6
7
 
8
+ from pyleco.json_utils.errors import JSONRPCError, RECEIVER_UNKNOWN
7
9
  import pymodaq_gui.parameter.utils as putils
10
+ from pymodaq_utils.config import Config
8
11
  # object used to send info back to the main thread:
9
12
  from pymodaq_utils.utils import ThreadCommand
10
13
  from pymodaq_gui.parameter import Parameter
14
+ from pymodaq_gui.parameter import ioxml
15
+ from pymodaq_gui.parameter.utils import ParameterWithPath
16
+ from qtpy.QtCore import QTimer
11
17
 
12
18
  from pymodaq.utils.leco.director_utils import GenericDirector
13
19
  from pymodaq.utils.leco.pymodaq_listener import PymodaqListener
20
+ from pymodaq.utils.leco.rpc_method_definitions import GenericDirectorMethods, MoveDirectorMethods
21
+ from pymodaq_utils.serialize.factory import SerializableFactory
22
+ from pymodaq.control_modules.thread_commands import ThreadStatus, ThreadStatusMove
23
+
24
+ config = Config()
25
+
26
+ class DirectorCommands(StrEnum):
27
+ SET_SETTINGS = GenericDirectorMethods.SET_DIRECTOR_SETTINGS
28
+ SET_INFO = GenericDirectorMethods.SET_DIRECTOR_INFO
29
+
30
+ SEND_POSITION = MoveDirectorMethods.SEND_POSITION # to display the actor position
31
+ SET_MOVE_DONE = MoveDirectorMethods.SET_MOVE_DONE
32
+ SET_UNITS = MoveDirectorMethods.SET_UNITS # to set units accordingly to the one of the actor
14
33
 
15
34
 
35
+ class DirectorReceivedCommands(StrEnum):
36
+ MOVE_DONE = ThreadStatusMove.MOVE_DONE
37
+ GET_ACTUATOR_VALUE = ThreadStatusMove.GET_ACTUATOR_VALUE
38
+
39
+ config = Config()
40
+
16
41
  leco_parameters = [
17
42
  {'title': 'Actor name:', 'name': 'actor_name', 'type': 'str', 'value': "actor_name",
18
43
  'tip': 'Name of the actor plugin to communicate with.'},
44
+ {'title': 'Coordinator Host:', 'name': 'host', 'type': 'str', 'value': config('network', "leco-server", "host")},
45
+ {'title': 'Coordinator Port:', 'name': 'port', 'type': 'int', 'value': config('network', "leco-server", "port")},
46
+ {'title': 'Settings PyMoDAQ Client:', 'name': 'settings_client', 'type': 'group', 'children': []},
19
47
  ]
20
48
 
21
49
 
@@ -23,45 +51,40 @@ class LECODirector:
23
51
  """
24
52
  This is a mixin for a Control module to direct another, remote module (analogous to TCP Server).
25
53
 
26
- ================= ==============================
27
- **Attributes** **Type**
28
- *command_server* instance of Signal
29
- *x_axis* 1D numpy array
30
- *y_axis* 1D numpy array
31
- *data* double precision float array
32
- ================= ==============================
33
-
34
- See Also
35
- --------
36
- utility_classes.DAQ_TCP_server
54
+
37
55
  """
38
- message_list = ["Quit", "Status", "Done", "Server Closed", "Info", "Infos", "Info_xml",
39
- "move_abs", 'move_home', 'move_rel', 'get_actuator_value', 'stop_motion',
40
- 'position_is', 'move_done',
41
- ]
42
- socket_types: List[str]
43
56
 
44
57
  controller: GenericDirector
45
58
  settings: Parameter
59
+ _title: str
46
60
 
47
- def __init__(self, **kwargs) -> None:
48
- super().__init__(**kwargs)
61
+ def __init__(self, host: str = 'localhost', port : int = COORDINATOR_PORT, **kwargs) -> None:
49
62
 
50
63
  name = f'{self._title}_{random.randrange(0, 10000)}_director'
51
- # TODO use the same Listener instance as the LECOActorModule
52
- self.listener = PymodaqListener(name=name)
64
+
65
+ self.listener = PymodaqListener(name=name, host=host, port=port)
53
66
  self.listener.start_listen()
67
+
54
68
  self.communicator = self.listener.get_communicator()
69
+
70
+ #registering rpc methods common to all Directors
55
71
  self.register_rpc_methods((
56
- self.set_info,
72
+ self.set_director_settings,
73
+ ))
74
+ self.register_binary_rpc_methods((
75
+ self.set_director_info,
57
76
  ))
58
77
 
78
+ def register_binary_rpc_methods(self, methods: Sequence[Callable]) -> None:
79
+ for method in methods:
80
+ self.listener.register_binary_rpc_method(method, accept_binary_input=True)
81
+
59
82
  def register_rpc_methods(self, methods: Sequence[Callable]) -> None:
60
83
  for method in methods:
61
84
  self.communicator.register_rpc_method(method=method)
62
85
 
63
- def commit_settings(self, param: Parameter) -> None:
64
- raise NotImplementedError
86
+ def commit_settings(self, param) -> None:
87
+ self.commit_leco_settings(param=param)
65
88
 
66
89
  def commit_leco_settings(self, param: Parameter) -> None:
67
90
  if param.name() == "actor_name":
@@ -70,8 +93,35 @@ class LECODirector:
70
93
  self.controller.set_info(param=param)
71
94
 
72
95
  def close(self) -> None:
96
+ """ Clear the content of the settings_clients setting"""
97
+ self.settings.child('settings_client').clearChildren()
73
98
  self.listener.stop_listen()
74
99
 
100
+ def start_timer(self) -> None:
101
+ """To be called in child classes."""
102
+ self.timer = QTimer()
103
+ self.timer.timeout.connect(self.check_actor_connection)
104
+ try:
105
+ # cast is used by the type checker to infer the returned type (when many are possible)
106
+ timeout = cast(int, config("network", "leco-server", "heartbeat-timeout"))
107
+ except KeyError:
108
+ timeout = 1000
109
+ self.timer.start(timeout) # in milli seconds
110
+
111
+ def check_actor_connection(self) -> None:
112
+ try:
113
+ self.controller.ask_rpc("pong", timeout=0.1)
114
+ except JSONRPCError as exc:
115
+ if exc.rpc_error.code == RECEIVER_UNKNOWN.code:
116
+ self.emit_status(ThreadCommand(ThreadStatus.UPDATE_UI, "do_init", args=[False]))
117
+ else:
118
+ self.emit_status(
119
+ ThreadCommand(
120
+ ThreadStatus.UPDATE_STATUS,
121
+ f"Connection error to actor: {exc.rpc_error.message}",
122
+ )
123
+ )
124
+
75
125
  def stop(self):
76
126
  """
77
127
  not implemented.
@@ -85,5 +135,32 @@ class LECODirector:
85
135
  super().emit_status(status=status) # type: ignore
86
136
 
87
137
  # Methods accessible via remote calls
88
- def set_info(self, path: List[str], param_dict_str: str) -> None:
89
- self.emit_status(ThreadCommand("set_info", attribute=[path, param_dict_str]))
138
+ def set_director_info(self,
139
+ parameter: Optional[Union[float, str]],
140
+ additional_payload: Optional[List[bytes]] = None,
141
+ ) -> None:
142
+ """ Write the value of a param updated from the actor to here in the
143
+ Parameter with path: ('move_settings', 'settings_client')
144
+ """
145
+ GenericDirectorMethods.SET_DIRECTOR_INFO # defined here
146
+ assert additional_payload
147
+ # cast is used by the type checker to infer the returned type (when many are possible)
148
+ param = cast(
149
+ ParameterWithPath, SerializableFactory().get_apply_deserializer(additional_payload[0])
150
+ )
151
+
152
+ try:
153
+ path = ['settings_client']
154
+ path.extend(param.path[1:])
155
+
156
+ self.settings.child(*path).setValue(param.value())
157
+ except Exception as e:
158
+ print(f'could not set the param {param} in the director:\n'
159
+ f'{str(e)}')
160
+
161
+ def set_director_settings(self, settings: bytes):
162
+ """ Get the content of the actor settings to pe populated in this plugin
163
+ 'settings_client' parameter"""
164
+ GenericDirectorMethods.SET_DIRECTOR_INFO # defined here
165
+ params = ioxml.XML_string_to_parameter(settings)
166
+ self.settings.child('settings_client').addChildren(params)