cgse-common 2025.0.8.dev3__py3-none-any.whl → 2025.0.9__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cgse-common
3
- Version: 2025.0.8.dev3
3
+ Version: 2025.0.9
4
4
  Summary: Software framework to support hardware testing
5
5
  Author: IVS KU Leuven
6
6
  Maintainer-email: Rik Huygen <rik.huygen@kuleuven.be>, Sara Regibo <sara.regibo@kuleuven.be>
@@ -4,26 +4,26 @@ egse/bits.py,sha256=1DPcC5R-8w-KLfbr2WLx-4Ezfwm6jpRMz-E5QfQ_YKw,11138
4
4
  egse/calibration.py,sha256=DZ-LgEwamTWDukMqTr2JLCPt3M1lw6W0SfpHQfG7cXg,8989
5
5
  egse/command.py,sha256=su35PU_0Bru1Es4up1lJFps9FTQpJ7xcxl9Y4w8suxA,22880
6
6
  egse/config.py,sha256=Ib1Ddt0wFwRrioBvMs7Md-wgEEPdK3eaXCt0H7HJEow,14888
7
- egse/control.py,sha256=cH0EA5mqaS3nQc35x1Sa0o2iM2Q4_hsrak6GOdcRMMU,12793
7
+ egse/control.py,sha256=Z4aeYfZkzdULMWFgPilAR9AlZu5I0vAUizCPSdINJaY,16567
8
8
  egse/decorators.py,sha256=YDoFNn7Fe-voKX2iui85POciXuLs0P5rW1TNtxpejUM,13629
9
- egse/device.py,sha256=SIMROwB9YdNdFowTcZp1HW_el8LWVWDJ5tDUxs58YuY,8500
9
+ egse/device.py,sha256=sc4cx-xOXjmewVZuXSSzROv1gqP8BqUycojiEmqV6ck,8501
10
10
  egse/env.py,sha256=IzT_DYTHKlqtXDR8fWI9n7xj7KnL58UE6_mZUCdQMss,28279
11
11
  egse/exceptions.py,sha256=Tc1xqUrWxV6SXxc9xB9viK_O-GVa_SpzqZUVEhZkwzA,1801
12
12
  egse/hk.py,sha256=5WMOGlx043XmEPj7ML1TkDzA8ZaTYLD-i5OcNUxE268,31163
13
13
  egse/metrics.py,sha256=cZKMEe3OTT2uomj7vXjEl54JD0CStfEC4nCgS6U5YSM,3794
14
- egse/mixin.py,sha256=J3yu5lPnm0grJqIm5LiacBUCZujJsdcKBNRaOQcMnNo,17345
14
+ egse/mixin.py,sha256=E7GvuAYAr-KGjCOsYojIcicdmcHoETwBcCeVjP5EkMc,17471
15
15
  egse/monitoring.py,sha256=-pwXqPoiNKzQYKQSpKddFlaPkCTJZYdxvG1d2MBN3l0,3033
16
16
  egse/observer.py,sha256=6faaLHqgpOQs_oEvdBygQ5HF7mGneKJEfyQEFUFA5VY,1069
17
17
  egse/obsid.py,sha256=-HPuHApZrr3Nj1J2-qqnIiE814C-gm4FSHdM2afKdRY,5883
18
18
  egse/persistence.py,sha256=Lx6LMJ1-dh8N43XF7tTM6RwD0sSETiGQ9DNqug-G-zQ,2160
19
19
  egse/plugin.py,sha256=Vuy4WkymGEt-_fibUIoVxdF1pcCS3FaXwtiYhUIdjcE,4820
20
20
  egse/process.py,sha256=mQ2ojeL_9oE_QkMJlQDPd1290z0j2mOrGXrlrWtOtzI,16615
21
- egse/protocol.py,sha256=G1HTU7xfS4xE1Oi1diDOTWQUTkPLyuooawWCOY6p-b0,23828
21
+ egse/protocol.py,sha256=dQVQ8U0AitjvkOxJRrJJ7dvYAn0vxOfi5IL6gxmCyeQ,23837
22
22
  egse/proxy.py,sha256=pMKdnF62SXm0quLoKfgvK9GFkH2mLMB5fWNrZenfqQQ,18100
23
23
  egse/reload.py,sha256=rDT0bC6RFeRhW38wSgFcxr30h8FvaKkoGp_OE-AwBB4,4388
24
24
  egse/resource.py,sha256=VoB7BVrQULT_SJ1XioDzB59-uH47nUcN-KNVLvFxiFE,15163
25
25
  egse/response.py,sha256=WFtWftovrmmn92NW4mhJjibMtCWteZmAzNuPLVsV-lg,2716
26
- egse/services.py,sha256=FV6BUEotkooGALq1EzypZjHg0BDRJixSghp3ak7o_z8,7712
26
+ egse/services.py,sha256=nobKsApRxBIg7Vxy67MJWH2qT47mJfDOaxMIpc0n7fg,7716
27
27
  egse/services.yaml,sha256=p8QBF56zLI21iJ9skt65VlNz4rIqRoFfBTZxOIUZCZ4,1853
28
28
  egse/settings.py,sha256=lmIfg3lwIuHrI8JL5qfiAaXHjblC87_W0c2mjbj-vRA,15562
29
29
  egse/settings.yaml,sha256=mz9O2QqmiptezsMvxJRLhnC1ROwIHENX0nbnhMaXUpE,190
@@ -32,7 +32,7 @@ egse/state.py,sha256=ekcCZu_DZKkKYn-5iWG7ij7Aif2WYMNVs5h3cia-cVc,5352
32
32
  egse/system.py,sha256=KCXz8eH3PNwLaB7ww3OCkK_tQoaTYhh2FEwX1mvN8RQ,48996
33
33
  egse/version.py,sha256=-EMuiSn2eEp8QJX6csmBEu1m2yGqlXHJj2Hj49w6a2k,6217
34
34
  egse/zmq_ser.py,sha256=2-nwVUBWZ3vvosKNmlWobHJrIJA2HlM3V5a63Gz2JY0,1819
35
- cgse_common-2025.0.8.dev3.dist-info/METADATA,sha256=0i907msDC2Sa7vvRsmPeYLvJUTdxyA2F1lf_x4JQWks,2579
36
- cgse_common-2025.0.8.dev3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
37
- cgse_common-2025.0.8.dev3.dist-info/entry_points.txt,sha256=MtsCYBzfuc3afzsjbdGJ-TZRSS8rhof5fI9w86_nGkQ,91
38
- cgse_common-2025.0.8.dev3.dist-info/RECORD,,
35
+ cgse_common-2025.0.9.dist-info/METADATA,sha256=Vi5NH0UJeeVC0eeJufawREAoIdl50PgjqvGsJYUwusU,2574
36
+ cgse_common-2025.0.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
37
+ cgse_common-2025.0.9.dist-info/entry_points.txt,sha256=MtsCYBzfuc3afzsjbdGJ-TZRSS8rhof5fI9w86_nGkQ,91
38
+ cgse_common-2025.0.9.dist-info/RECORD,,
egse/control.py CHANGED
@@ -1,10 +1,12 @@
1
1
  """
2
- This module defines the abstract class for any control server and some convenience functions.
2
+ This module defines the abstract class for any Control Server and some convenience functions.
3
3
  """
4
+
4
5
  import abc
5
6
  import logging
6
7
  import pickle
7
8
  import threading
9
+ from typing import Union
8
10
 
9
11
  import zmq
10
12
 
@@ -30,16 +32,18 @@ PROCESS_SETTINGS = Settings.load("PROCESS")
30
32
 
31
33
 
32
34
  def is_control_server_active(endpoint: str = None, timeout: float = 0.5) -> bool:
33
- """
34
- Check if the control server is running. This function sends a *Ping* message to the
35
- control server and expects a *Pong* answer back within the timeout period.
35
+ """ Checks if the Control Server is running.
36
+
37
+ This function sends a *Ping* message to the Control Server and expects a *Pong* answer back within the timeout
38
+ period.
36
39
 
37
40
  Args:
38
- endpoint (str): the endpoint to connect to, i.e. <protocol>://<address>:<port>
39
- timeout (float): timeout when waiting for a reply [seconds, default=0.5]
40
- Returns:
41
- True if the Control Server is running and replied with the expected answer.
41
+ endpoint (str): Endpoint to connect to, i.e. <protocol>://<address>:<port>
42
+ timeout (float): Timeout when waiting for a reply [s, default=0.5]
43
+
44
+ Returns: True if the Control Server is running and replied with the expected answer; False otherwise.
42
45
  """
46
+
43
47
  ctx = zmq.Context.instance()
44
48
 
45
49
  return_code = False
@@ -50,6 +54,7 @@ def is_control_server_active(endpoint: str = None, timeout: float = 0.5) -> bool
50
54
  data = pickle.dumps("Ping")
51
55
  socket.send(data)
52
56
  rlist, _, _ = zmq.select([socket], [], [], timeout=timeout)
57
+
53
58
  if socket in rlist:
54
59
  data = socket.recv()
55
60
  response = pickle.loads(data)
@@ -62,20 +67,22 @@ def is_control_server_active(endpoint: str = None, timeout: float = 0.5) -> bool
62
67
 
63
68
 
64
69
  class ControlServer(metaclass=abc.ABCMeta):
65
- """
66
- The base class for all device control servers and for the Storage Manager and Configuration
67
- Manager. A Control Server reads commands from a ZeroMQ socket and executes these commands by
68
- calling the `execute()` method of the commanding protocol class.
70
+ """ Base class for all device control servers and for the Storage Manager and Configuration Manager.
71
+
72
+ A Control Server reads commands from a ZeroMQ socket and executes these commands by calling the `execute()` method
73
+ of the commanding protocol class.
69
74
 
70
75
  The sub-class shall define the following:
71
76
 
72
- * Define the device protocol class -> `self.device_protocol`
73
- * Bind the command socket to the device protocol -> `self.dev_ctrl_cmd_sock`
74
- * Register the command socket in the poll set -> `self.poller`
77
+ - Define the device protocol class -> `self.device_protocol`
78
+ - Bind the command socket to the device protocol -> `self.dev_ctrl_cmd_sock`
79
+ - Register the command socket in the poll set -> `self.poller`
75
80
 
76
81
  """
77
82
 
78
83
  def __init__(self):
84
+ """ Initialisation of a new Control Server."""
85
+
79
86
  from egse.monitoring import MonitoringProtocol
80
87
  from egse.services import ServiceProtocol
81
88
 
@@ -86,15 +93,14 @@ class ControlServer(metaclass=abc.ABCMeta):
86
93
  self._timer_thread.daemon = True
87
94
  self._timer_thread.start()
88
95
 
89
- # The logger will be overwritten by the sub-class, if not, then we use this logger
90
- # with the name of the sub-class. That will help us to identify which sub-class did not
91
- # overwrite the logger attribute.
96
+ # The logger will be overwritten by the sub-class, if not, we use this logger with the name of the sub-class.
97
+ # That will help us to identify which sub-class did not overwrite the logger attribute.
92
98
 
93
99
  self.logger = logging.getLogger(get_full_classname(self))
94
100
 
95
101
  self.interrupted = False
96
- self.delay = 1000 # delay between publish status information [milliseconds]
97
- self.hk_delay = 1000 # delay between saving housekeeping information [milliseconds]
102
+ self.mon_delay = 1000 # Delay between publish status information [ms]
103
+ self.hk_delay = 1000 # Delay between saving housekeeping information [ms]
98
104
 
99
105
  self.zcontext = zmq.Context.instance()
100
106
  self.poller = zmq.Poller()
@@ -103,118 +109,206 @@ class ControlServer(metaclass=abc.ABCMeta):
103
109
  self.service_protocol = ServiceProtocol(self)
104
110
  self.monitoring_protocol = MonitoringProtocol(self)
105
111
 
106
- # Setup the control server waiting for service requests
112
+ # Set up the Control Server waiting for service requests
107
113
 
108
114
  self.dev_ctrl_service_sock = self.zcontext.socket(zmq.REP)
109
115
  self.service_protocol.bind(self.dev_ctrl_service_sock)
110
116
 
111
- # Setup the control server for sending monitoring info
117
+ # Set up the Control Server for sending monitoring info
112
118
 
113
119
  self.dev_ctrl_mon_sock = self.zcontext.socket(zmq.PUB)
114
120
  self.monitoring_protocol.bind(self.dev_ctrl_mon_sock)
115
121
 
116
- # Setup the control server waiting for device commands.
122
+ # Set up the Control Server waiting for device commands.
117
123
  # The device protocol shall bind the socket in the sub-class
118
124
 
119
125
  self.dev_ctrl_cmd_sock = self.zcontext.socket(zmq.REP)
120
126
 
121
- # Initialize the poll set
127
+ # Initialise the poll set
122
128
 
123
129
  self.poller.register(self.dev_ctrl_service_sock, zmq.POLLIN)
124
130
  self.poller.register(self.dev_ctrl_mon_sock, zmq.POLLIN)
125
131
 
126
132
  @abc.abstractmethod
127
- def get_communication_protocol(self):
133
+ def get_communication_protocol(self) -> str:
134
+ """ Returns the communication protocol used by the Control Server.
135
+
136
+ Returns: Communication protocol used by the Control Server, as specified in the settings.
137
+ """
138
+
128
139
  pass
129
140
 
130
141
  @abc.abstractmethod
131
- def get_commanding_port(self):
142
+ def get_commanding_port(self) -> int:
143
+ """ Returns the commanding port used by the Control Server.
144
+
145
+ Returns: Commanding port used by the Control Server, as specified in the settings.
146
+ """
147
+
132
148
  pass
133
149
 
134
150
  @abc.abstractmethod
135
- def get_service_port(self):
151
+ def get_service_port(self) -> int:
152
+ """ Returns the service port used by the Control Server.
153
+
154
+ Returns: Service port used by the Control Server, as specified in the settings.
155
+ """
156
+
136
157
  pass
137
158
 
138
159
  @abc.abstractmethod
139
- def get_monitoring_port(self):
160
+ def get_monitoring_port(self) -> int:
161
+ """ Returns the monitoring port used by the Control Server.
162
+
163
+ Returns: Monitoring port used by the Control Server, as specified in the settings.
164
+ """
165
+
140
166
  pass
141
167
 
142
- def get_ip_address(self):
168
+ def get_ip_address(self) -> str:
169
+
143
170
  return get_host_ip()
144
171
 
145
- def get_storage_mnemonic(self):
172
+ def get_storage_mnemonic(self) -> str:
173
+ """ Returns the storage mnemonics used by the Control Server.
174
+
175
+ This is a string that will appear in the filename with the housekeeping information of the device, as a way of
176
+ identifying the device. If this is not implemented in the sub-class, then the class name will be used.
177
+
178
+ Returns: Storage mnemonics used by the Control Server, as specified in the settings.
179
+ """
180
+
146
181
  return self.__class__.__name__
147
182
 
148
- def get_process_status(self):
183
+ def get_process_status(self) -> dict:
184
+ """ Returns the process status of the Control Server.
185
+
186
+ Returns: Dictionary with the process status of the Control Server.
187
+ """
188
+
149
189
  return self._process_status.as_dict()
150
190
 
151
- def get_average_execution_times(self):
152
- return get_average_execution_times()
191
+ def get_average_execution_times(self) -> dict:
192
+ """ Returns the average execution times of all functions that have been monitored by this process.
153
193
 
154
- def set_delay(self, seconds: float) -> float:
194
+ Returns: Dictionary with the average execution times of all functions that have been monitored by this process.
195
+ The dictionary keys are the function names, and the values are the average execution times in ms.
155
196
  """
156
- Sets the delay time for monitoring. The delay time is the time between two successive executions of the
157
- `get_status()` function of the device protocol.
158
197
 
159
- It might happen that the delay time that is set is longer than what you requested. That is the case when
160
- the execution of the `get_status()` function takes longer than the requested delay time. That should
161
- prevent the server from blocking when a too short delay time is requested.
198
+ return get_average_execution_times()
199
+
200
+ def set_mon_delay(self, seconds: float) -> float:
201
+ """ Sets the delay time for monitoring.
202
+
203
+ The delay time is the time between two successive executions of the `get_status()` function of the device
204
+ protocol.
205
+
206
+ It might happen that the delay time that is set is longer than what you requested. That is the case when the
207
+ execution of the `get_status()` function takes longer than the requested delay time. That should prevent the
208
+ server from blocking when a too short delay time is requested.
162
209
 
163
210
  Args:
164
- seconds: the number of seconds between the monitoring calls.
165
- Returns:
166
- The delay that was set in milliseconds.
211
+ seconds (float): Number of seconds between the monitoring calls
212
+
213
+ Returns: Delay that was set [ms].
167
214
  """
215
+
168
216
  execution_time = get_average_execution_time(self.device_protocol.get_status)
169
- self.delay = max(seconds * 1000, (execution_time + 0.2) * 1000)
170
- return self.delay
217
+ self.mon_delay = max(seconds * 1000, (execution_time + 0.2) * 1000)
171
218
 
172
- def set_hk_delay(self, seconds) -> float:
173
- """
174
- Sets the delay time for housekeeping. The delay time is the time between two successive executions of the
175
- `get_housekeeping()` function of the device protocol.
219
+ return self.mon_delay
220
+
221
+ def set_hk_delay(self, seconds: float) -> float:
222
+ """ Sets the delay time for housekeeping.
176
223
 
177
- It might happen that the delay time that is set is longer than what you requested. That is the case when
178
- the execution of the `get_housekeeping()` function takes longer than the requested delay time. That should
179
- prevent the server from blocking when a too short delay time is requested.
224
+ The delay time is the time between two successive executions of the `get_housekeeping()` function of the device
225
+ protocol.
226
+
227
+ It might happen that the delay time that is set is longer than what you requested. That is the case when the
228
+ execution of the `get_housekeeping()` function takes longer than the requested delay time. That should prevent
229
+ the server from blocking when a too short delay time is requested.
180
230
 
181
231
  Args:
182
- seconds: the number of seconds between the housekeeping calls.
183
- Returns:
184
- The delay that was set in milliseconds.
232
+ seconds (float): Number of seconds between the housekeeping calls
233
+
234
+ Returns: Delay that was set [ms].
185
235
  """
236
+
186
237
  execution_time = get_average_execution_time(self.device_protocol.get_housekeeping)
187
238
  self.hk_delay = max(seconds * 1000, (execution_time + 0.2) * 1000)
239
+
188
240
  return self.hk_delay
189
241
 
190
- def set_logging_level(self, level):
242
+ def set_logging_level(self, level: Union[int, str]) -> None:
243
+ """ Sets the logging level to the given level.
244
+
245
+ Allowed logging levels are:
246
+ - "CRITICAL" or 50
247
+ - "FATAL" or CRITICAL
248
+ - "ERROR" or 40
249
+ - "WARNING" or 30
250
+ - "WARN" or WARNING
251
+ - "INFO" or 20
252
+ - "DEBUG" or 10
253
+ - "NOTSET" or 0
254
+
255
+ Args:
256
+ level (int | str): Logging level to use, specified as either a string or an integer
257
+ """
258
+
191
259
  self.logger.setLevel(level=level)
192
260
 
193
- def quit(self):
261
+ def quit(self) -> None:
262
+ """ Interrupts the Control Server."""
263
+
194
264
  self.interrupted = True
195
265
 
196
- def before_serve(self):
266
+ def before_serve(self) -> None:
267
+ """ Steps to take before the Control Server is activated."""
268
+
197
269
  pass
198
270
 
199
- def after_serve(self):
271
+ def after_serve(self) -> None:
272
+ """ Steps to take after the Control Server has been deactivated."""
273
+
200
274
  pass
201
275
 
202
- def is_storage_manager_active(self):
203
- """
204
- This method needs to be implemented by the subclass if you need to store information.
276
+ def is_storage_manager_active(self) -> bool:
277
+ """ Checks if the Storage Manager is active.
278
+
279
+ This method has to be implemented by the sub-class if you need to store information.
205
280
 
206
- Note: you might want to set a specific timeout when checking for the Storage Manager.
281
+ Note: You might want to set a specific timeout when checking for the Storage Manager.
207
282
 
208
- Note: If this method returns True, the following methods shall also be implemented by the subclass:
283
+ Note: If this method returns True, the following methods shall also be implemented by the sub-class:
209
284
 
210
- * register_to_storage_manager()
211
- * unregister_from_storage_manager()
212
- * store_housekeeping_information()
285
+ - register_to_storage_manager()
286
+ - unregister_from_storage_manager()
287
+ - store_housekeeping_information()
213
288
 
289
+ Returns: True if the Storage Manager is active; False otherwise.
214
290
  """
291
+
215
292
  return False
216
293
 
217
- def serve(self):
294
+ def serve(self) -> None:
295
+ """ Activation of the Control Server.
296
+
297
+ This comprises the following steps:
298
+
299
+ - Executing the `before_serve` method;
300
+ - Checking if the Storage Manager is active and registering the Control Server to it;
301
+ - Start listening for keyboard interrupts;
302
+ - Start accepting (listening to) commands;
303
+ - Start sending out monitoring information;
304
+ - Start sending out housekeeping information;
305
+ - Start listening for quit commands;
306
+ - After a quit command has been received:
307
+ - Unregister from the Storage Manager;
308
+ - Execute the `after_serve` method;
309
+ - Close all sockets;
310
+ - Clean up all threads.
311
+ """
218
312
 
219
313
  self.before_serve()
220
314
 
@@ -239,8 +333,7 @@ class ControlServer(metaclass=abc.ABCMeta):
239
333
  except KeyboardInterrupt:
240
334
  self.logger.warning("Keyboard interrupt caught!")
241
335
  self.logger.warning(
242
- "The ControlServer can not be interrupted with CTRL-C, "
243
- "send a quit command to the server."
336
+ "The ControlServer can not be interrupted with CTRL-C, send a quit command to the server instead."
244
337
  )
245
338
  continue
246
339
 
@@ -250,17 +343,19 @@ class ControlServer(metaclass=abc.ABCMeta):
250
343
  if self.dev_ctrl_service_sock in socks:
251
344
  self.service_protocol.execute()
252
345
 
253
- # Now handle the periodic sending out of status information. A dictionary with the
254
- # status or HK info is sent out periodically based on the DELAY time that is in the
255
- # YAML config file.
346
+ # Handle sending out monitoring information periodically, based on the MON_DELAY time that is specified in
347
+ # the YAML configuration file for the device
256
348
 
257
- if time_in_ms() - last_time >= self.delay:
349
+ if time_in_ms() - last_time >= self.mon_delay:
258
350
  last_time = time_in_ms()
259
351
  # self.logger.debug("Sending status to monitoring processes.")
260
352
  self.monitoring_protocol.send_status(
261
353
  save_average_execution_time(self.device_protocol.get_status)
262
354
  )
263
355
 
356
+ # Handle sending out housekeeping information periodically, based on the HK_DELAY time that is specified in
357
+ # the YAML configuration file for the device
358
+
264
359
  if time_in_ms() - last_time_hk >= self.hk_delay:
265
360
  last_time_hk = time_in_ms()
266
361
  if storage_manager:
@@ -275,14 +370,12 @@ class ControlServer(metaclass=abc.ABCMeta):
275
370
  )
276
371
  break
277
372
 
278
- # Some device protocol subclasses might start a number of threads or processes to
279
- # support the commanding. Check if these threads/processes are still alive and
280
- # terminate gracefully if they are not.
373
+ # Some device protocol sub-classes might start a number of threads or processes to support the commanding.
374
+ # Check if these threads/processes are still alive and terminate gracefully if they are not.
281
375
 
282
376
  if not self.device_protocol.is_alive():
283
377
  self.logger.error(
284
- "Some Thread or sub-process that was started by Protocol has "
285
- "died, terminating..."
378
+ "Some Thread or sub-process that was started by Protocol has died, terminating..."
286
379
  )
287
380
  break
288
381
 
@@ -300,11 +393,11 @@ class ControlServer(metaclass=abc.ABCMeta):
300
393
 
301
394
  self.zcontext.term()
302
395
 
303
- def store_housekeeping_information(self, data: dict):
304
- """
305
- Send housekeeping information to the Storage manager.
396
+ def store_housekeeping_information(self, data: dict) -> None:
397
+ """ Sends housekeeping information to the Storage Manager.
306
398
 
307
- Subclasses need to overwrite this method if they want the device housekeeping information to be saved.
399
+ This method has to be overwritten by the sub-classes if they want the device housekeeping information to be
400
+ saved.
308
401
 
309
402
  Args:
310
403
  data (dict): a dictionary containing parameter name and value of all device housekeeping. There is also
@@ -312,16 +405,21 @@ class ControlServer(metaclass=abc.ABCMeta):
312
405
  """
313
406
  pass
314
407
 
315
- def register_to_storage_manager(self):
316
- """
317
- Register this ControlServer to the Storage Manager so the housekeeping information of the device can be saved.
408
+ def register_to_storage_manager(self) -> None:
409
+ """ Registers this Control Server to the Storage Manager.
410
+
411
+ By doing so, the housekeeping information of the device will be sent to the Storage Manager, which will store
412
+ the information in a dedicated CSV file.
413
+
414
+ This method has to be overwritten by the sub-classes if they have housekeeping information that must be stored.
415
+
416
+ Subclasses need to overwrite this method if they have housekeeping information to be stored.
318
417
 
319
- Subclasses need to overwrite this method if they have housekeeping information to be stored. The following
320
- information is required for the registration:
418
+ The following information is required for the registration:
321
419
 
322
- * origin: can be retrieved from `self.get_storage_mnemonic()`
323
- * persistence_class: one of the TYPES in egse.storage.persistence
324
- * prep: depending on the type of the persistence class (see respective documentation)
420
+ - origin: Storage mnemonic, which can be retrieved from `self.get_storage_mnemonic()`
421
+ - persistence_class: Persistence layer (one of the TYPES in egse.storage.persistence)
422
+ - prep: depending on the type of the persistence class (see respective documentation)
325
423
 
326
424
  The `egse.storage` module provides a convenience method that can be called from the method in the subclass:
327
425
 
@@ -331,18 +429,20 @@ class ControlServer(metaclass=abc.ABCMeta):
331
429
  """
332
430
  pass
333
431
 
334
- def unregister_from_storage_manager(self):
335
- """
336
- Unregister this ControlServer from the Storage manager.
432
+ def unregister_from_storage_manager(self) -> None:
433
+ """ Unregisters the Control Server from the Storage Manager.
337
434
 
338
- Subclasses need to overwrite this method. The following information is required for the registration:
435
+ This method has to be overwritten by the sub-classes.
339
436
 
340
- * origin: can be retrieved from `self.get_storage_mnemonic()`
437
+ The following information is required for the registration:
341
438
 
342
- The `egse.storage` module provides a convenience method that can be called from the method in the subclass:
439
+ - origin: Storage mnemonic, which can be retrieved from `self.get_storage_mnemonic()`
440
+
441
+ The `egse.storage` module provides a convenience method that can be called from the method in the sub-class:
343
442
 
344
443
  >>> from egse.storage import unregister_from_storage_manager # noqa
345
444
 
346
445
  Note: the `egse.storage` module might not be available, it is provided by the `cgse-core` package.
347
446
  """
447
+
348
448
  pass
egse/device.py CHANGED
@@ -152,6 +152,7 @@ class DeviceConnectionInterface(DeviceConnectionObservable):
152
152
  Raises:
153
153
  ConnectionError: when the connection can not be opened.
154
154
  """
155
+
155
156
  raise NotImplementedError
156
157
 
157
158
  @dynamic_interface
@@ -187,13 +188,13 @@ class DeviceInterface(DeviceConnectionInterface):
187
188
 
188
189
  @dynamic_interface
189
190
  def is_simulator(self) -> bool:
190
- """Ask if the device class is a Simulator instead of the real Controller.
191
+ """ Checks whether the device is a simulator rather than a real hardware controller.
191
192
 
192
193
  This can be useful for testing purposes or when doing actual movement simulations.
193
194
 
194
- Returns:
195
- True if the Device is a Simulator, False if the Device is connected to real hardware.
195
+ Returns: True if the Device is a Simulator; False if the Device is connected to real hardware.
196
196
  """
197
+
197
198
  raise NotImplementedError
198
199
 
199
200
 
egse/mixin.py CHANGED
@@ -28,6 +28,7 @@ __all__ = [
28
28
  "add_cr_lf",
29
29
  "dynamic_command",
30
30
  "DynamicCommandMixin",
31
+ "CommandType"
31
32
  ]
32
33
 
33
34
  # ----- Mixin for dynamic commanding ---------------------------------------------------------------
@@ -108,6 +109,13 @@ def expand_kwargs(kwargs: Dict):
108
109
  return " ".join(f"{k}={v}" for k, v in kwargs.items())
109
110
 
110
111
 
112
+ class CommandType(str, enum.Enum):
113
+
114
+ READ = "read"
115
+ WRITE = "write"
116
+ TRANSACTION = "transaction"
117
+
118
+
111
119
  def dynamic_command(
112
120
  *,
113
121
  cmd_type: str, # required keyword-only argument
egse/protocol.py CHANGED
@@ -163,7 +163,7 @@ class BaseCommandProtocol(DeviceConnectionObserver):
163
163
  """
164
164
  status = {
165
165
  "timestamp": format_datetime(),
166
- "delay": self.__control_server.delay,
166
+ "delay": self.__control_server.mon_delay,
167
167
  }
168
168
  status.update(self.__control_server.get_process_status())
169
169
  return status
@@ -386,7 +386,7 @@ class CommandProtocol(DeviceConnectionObserver, metaclass=abc.ABCMeta):
386
386
 
387
387
  def quit(self):
388
388
  """
389
- This method can be overridden by a sub-class to cleanup and stop threads that it
389
+ This method can be overridden by a sub-class to clean up and stop threads that it
390
390
  started.
391
391
  """
392
392
 
@@ -444,7 +444,7 @@ class CommandProtocol(DeviceConnectionObserver, metaclass=abc.ABCMeta):
444
444
  """
445
445
  status = {
446
446
  "timestamp": format_datetime(),
447
- "delay": self.control_server.delay,
447
+ "delay": self.control_server.mon_delay,
448
448
  }
449
449
  status.update(self.control_server.get_process_status())
450
450
  return status
egse/services.py CHANGED
@@ -57,7 +57,7 @@ class ServiceProtocol(CommandProtocol):
57
57
  Returns:
58
58
  Sends back the selected delay time in milliseconds.
59
59
  """
60
- delay = self.control_server.set_delay(1.0 / freq)
60
+ delay = self.control_server.set_mon_delay(1.0 / freq)
61
61
 
62
62
  LOGGER.debug(f"Set monitoring frequency to {freq}Hz, ± every {delay:.0f}ms.")
63
63