horiba-sdk 0.3.2__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.
Files changed (48) hide show
  1. horiba_sdk/__init__.py +19 -0
  2. horiba_sdk/communication/__init__.py +44 -0
  3. horiba_sdk/communication/abstract_communicator.py +59 -0
  4. horiba_sdk/communication/communication_exception.py +19 -0
  5. horiba_sdk/communication/messages.py +87 -0
  6. horiba_sdk/communication/websocket_communicator.py +213 -0
  7. horiba_sdk/core/resolution.py +45 -0
  8. horiba_sdk/devices/__init__.py +11 -0
  9. horiba_sdk/devices/abstract_device_discovery.py +7 -0
  10. horiba_sdk/devices/abstract_device_manager.py +68 -0
  11. horiba_sdk/devices/ccd_discovery.py +57 -0
  12. horiba_sdk/devices/device_manager.py +250 -0
  13. horiba_sdk/devices/fake_device_manager.py +133 -0
  14. horiba_sdk/devices/fake_icl_server.py +56 -0
  15. horiba_sdk/devices/fake_responses/ccd.json +168 -0
  16. horiba_sdk/devices/fake_responses/icl.json +29 -0
  17. horiba_sdk/devices/fake_responses/monochromator.json +187 -0
  18. horiba_sdk/devices/monochromator_discovery.py +48 -0
  19. horiba_sdk/devices/single_devices/__init__.py +5 -0
  20. horiba_sdk/devices/single_devices/abstract_device.py +79 -0
  21. horiba_sdk/devices/single_devices/ccd.py +443 -0
  22. horiba_sdk/devices/single_devices/monochromator.py +395 -0
  23. horiba_sdk/icl_error/__init__.py +34 -0
  24. horiba_sdk/icl_error/abstract_error.py +65 -0
  25. horiba_sdk/icl_error/abstract_error_db.py +25 -0
  26. horiba_sdk/icl_error/error_list.json +265 -0
  27. horiba_sdk/icl_error/icl_error.py +30 -0
  28. horiba_sdk/icl_error/icl_error_db.py +81 -0
  29. horiba_sdk/sync/__init__.py +0 -0
  30. horiba_sdk/sync/communication/__init__.py +7 -0
  31. horiba_sdk/sync/communication/abstract_communicator.py +48 -0
  32. horiba_sdk/sync/communication/test_client.py +16 -0
  33. horiba_sdk/sync/communication/websocket_communicator.py +212 -0
  34. horiba_sdk/sync/devices/__init__.py +15 -0
  35. horiba_sdk/sync/devices/abstract_device_discovery.py +17 -0
  36. horiba_sdk/sync/devices/abstract_device_manager.py +68 -0
  37. horiba_sdk/sync/devices/device_discovery.py +82 -0
  38. horiba_sdk/sync/devices/device_manager.py +209 -0
  39. horiba_sdk/sync/devices/fake_device_manager.py +91 -0
  40. horiba_sdk/sync/devices/fake_icl_server.py +79 -0
  41. horiba_sdk/sync/devices/single_devices/__init__.py +5 -0
  42. horiba_sdk/sync/devices/single_devices/abstract_device.py +83 -0
  43. horiba_sdk/sync/devices/single_devices/ccd.py +219 -0
  44. horiba_sdk/sync/devices/single_devices/monochromator.py +150 -0
  45. horiba_sdk-0.3.2.dist-info/LICENSE +20 -0
  46. horiba_sdk-0.3.2.dist-info/METADATA +438 -0
  47. horiba_sdk-0.3.2.dist-info/RECORD +48 -0
  48. horiba_sdk-0.3.2.dist-info/WHEEL +4 -0
@@ -0,0 +1,395 @@
1
+ from enum import Enum
2
+ from types import TracebackType
3
+ from typing import Optional, final
4
+
5
+ from loguru import logger
6
+ from overrides import override
7
+
8
+ from horiba_sdk.communication import AbstractCommunicator, Response
9
+ from horiba_sdk.icl_error import AbstractErrorDB
10
+
11
+ from .abstract_device import AbstractDevice
12
+
13
+
14
+ @final
15
+ class Monochromator(AbstractDevice):
16
+ """Monochromator device
17
+
18
+ This class should not be instanced by the end user. Instead, the :class:`horiba_sdk.devices.DeviceManager`
19
+ should be used to access the detected Monochromators on the system.
20
+ """
21
+
22
+ @final
23
+ class ShutterPosition(Enum):
24
+ """Position of the shutter."""
25
+
26
+ CLOSED = 0
27
+ OPENED = 1
28
+
29
+ @final
30
+ class Grating(Enum):
31
+ """Gratings installed in the monochromator"""
32
+
33
+ FIRST = 0
34
+ SECOND = 1
35
+ THIRD = 2
36
+
37
+ @final
38
+ class FilterWheelPosition(Enum):
39
+ """Positions of the filter wheel installed in the monochromator.
40
+
41
+ .. note:: the filter wheel is an optional module
42
+
43
+ """
44
+
45
+ # TODO: clarify naming of filter wheel positions
46
+ RED = 0
47
+ GREEN = 1
48
+ BLUE = 2
49
+ YELLOW = 3
50
+
51
+ @final
52
+ class Mirror(Enum):
53
+ """Mirrors installed in the monochromator"""
54
+
55
+ # TODO: clarify how the mirrors are called
56
+ FIRST = 0
57
+ SECOND = 1
58
+
59
+ @final
60
+ class MirrorPosition(Enum):
61
+ """Possible positions of the mirrors"""
62
+
63
+ # TODO: clarify what possible position there are
64
+ A = 0
65
+ B = 1
66
+
67
+ @final
68
+ class Slit(Enum):
69
+ """Slits available on the monochromator."""
70
+
71
+ # TODO: clarify how the slits are called
72
+ A = 0
73
+ B = 1
74
+ C = 2
75
+ D = 3
76
+
77
+ @final
78
+ class SlitStepPosition(Enum):
79
+ """Slits steps available on the monochromator."""
80
+
81
+ # TODO: clarify how the slits are called
82
+ A = 0
83
+ B = 1
84
+ C = 2
85
+ D = 3
86
+
87
+ def __init__(self, device_id: int, communicator: AbstractCommunicator, error_db: AbstractErrorDB) -> None:
88
+ super().__init__(device_id, communicator, error_db)
89
+
90
+ async def __aenter__(self) -> 'Monochromator':
91
+ await self.open()
92
+ return self
93
+
94
+ async def __aexit__(
95
+ self, exc_type: type[BaseException], exc_value: BaseException, traceback: Optional[TracebackType]
96
+ ) -> None:
97
+ is_open = await self.is_open()
98
+ if not is_open:
99
+ logger.debug('Monochromator is already closed')
100
+ return
101
+
102
+ await self.close()
103
+
104
+ @override
105
+ async def open(self) -> None:
106
+ """Opens the connection to the Monochromator
107
+
108
+ Raises:
109
+ Exception: When an error occured on the device side
110
+ """
111
+ await super().open()
112
+ await super()._execute_command('mono_open', {'index': self._id})
113
+
114
+ @override
115
+ async def close(self) -> None:
116
+ """Closes the connection to the Monochromator
117
+
118
+ Raises:
119
+ Exception: When an error occured on the device side
120
+ """
121
+ await super()._execute_command('mono_close', {'index': self._id})
122
+
123
+ async def is_open(self) -> bool:
124
+ """Checks if the connection to the monochromator is open.
125
+
126
+ Raises:
127
+ Exception: When an error occured on the device side
128
+ """
129
+ response: Response = await super()._execute_command('mono_isOpen', {'index': self._id})
130
+ return bool(response.results['open'])
131
+
132
+ async def is_busy(self) -> bool:
133
+ """Checks if the monochromator is busy.
134
+
135
+ Raises:
136
+ Exception: When an error occured on the device side
137
+ """
138
+ response: Response = await super()._execute_command('mono_isBusy', {'index': self._id})
139
+ return bool(response.results['busy'])
140
+
141
+ async def home(self) -> None:
142
+ """Starts the monochromator initialization process called "homing".
143
+
144
+ Use :func:`Monochromator.is_busy()` to know if the operation is still taking place.
145
+
146
+ Raises:
147
+ Exception: When an error occured on the device side
148
+ """
149
+ await super()._execute_command('mono_init', {'index': self._id})
150
+
151
+ async def configuration(self) -> str:
152
+ """Returns the configuration of the monochromator.
153
+
154
+ Returns:
155
+ str: configuration of the monochromator
156
+ """
157
+ response: Response = await super()._execute_command('mono_getConfig', {'index': self._id, 'compact': False})
158
+ return str(response.results)
159
+
160
+ async def get_current_wavelength(self) -> float:
161
+ """Current wavelength of the monochromator's position in nm.
162
+
163
+ Returns:
164
+ float: The current wavelength in nm
165
+
166
+ Raises:
167
+ Exception: When an error occurred on the device side
168
+ """
169
+ response = await super()._execute_command('mono_getPosition', {'index': self._id})
170
+ return float(response.results['wavelength'])
171
+
172
+ async def calibrate_wavelength(self, wavelength: float) -> None:
173
+ """This command sets the wavelength value of the current grating position of the monochromator.
174
+
175
+ .. warning:: This could potentially un-calibrate the monochromator and report an incorrect wavelength
176
+ compared to the actual output wavelength.
177
+
178
+ Args:
179
+ wavelength (float): wavelength in nm
180
+
181
+ Raises:
182
+ Exception: When an error occurred on the device side
183
+ """
184
+ await super()._execute_command('mono_setPosition', {'index': self._id, 'wavelength': wavelength})
185
+
186
+ async def move_to_target_wavelength(self, wavelength: float) -> None:
187
+ """Orders the monochromator to move to the requested wavelength.
188
+
189
+ Use :func:`Monochromator.is_busy()` to know if the operation is still taking place.
190
+
191
+ Args:
192
+ wavelength (nm): wavelength
193
+
194
+ Raises:
195
+ Exception: When an error occurred on the device side
196
+ """
197
+ await super()._execute_command('mono_moveToPosition', {'index': self._id, 'wavelength': wavelength}, 60)
198
+
199
+ async def get_turret_grating(self) -> Grating:
200
+ """Current grating of the turret
201
+
202
+ Returns:
203
+ Grating: current grating of turret. See :class:`Monochromator.Grating` for possible values.
204
+
205
+ Raises:
206
+ Exception: When an error occured on the device side
207
+ """
208
+ response: Response = await super()._execute_command('mono_getGratingPosition', {'index': self._id})
209
+ return self.Grating(response.results['position'])
210
+
211
+ async def set_turret_grating(self, grating: Grating) -> None:
212
+ """Select turret grating
213
+
214
+ Args:
215
+ position (Grating): new grating of the turret. See :class:`Monochromator.Grating` for possible values.
216
+
217
+ Raises:
218
+ Exception: When an error occured on the device side
219
+ """
220
+ await super()._execute_command('mono_moveGrating', {'index': self._id, 'position': grating.value})
221
+
222
+ async def get_filter_wheel_position(self) -> FilterWheelPosition:
223
+ """Current position of the filter wheel.
224
+
225
+ Returns:
226
+ FilterWheelPosition: current position of the filter wheel. See :class:`Monochromator.FilterWheelPosition`
227
+ for possible values.
228
+
229
+ Raises:
230
+ Exception: When an error occured on the device side
231
+ """
232
+ # TODO: refactor in case there can be more than one filter wheel. What should be done if no filter wheel is
233
+ # installed?
234
+ response: Response = await super()._execute_command(
235
+ 'mono_getFilterWheelPosition', {'index': self._id, 'type': 1}
236
+ )
237
+ return self.FilterWheelPosition(response.results['position'])
238
+
239
+ async def set_filter_wheel_position(self, position: FilterWheelPosition) -> None:
240
+ """Sets the current position of the filter wheel.
241
+
242
+ Returns:
243
+ FilterWheelPosition: current position of the filter wheel. See :class:`Monochromator.FilterWheelPosition`,
244
+ for possible values.
245
+
246
+ Raises:
247
+ Exception: When an error occured on the device side
248
+ """
249
+ # TODO: refactor in case there can be more than one filter wheel. What should be done if no filter wheel is
250
+ # installed?
251
+ await super()._execute_command(
252
+ 'mono_moveFilterWheel', {'index': self._id, 'type': 1, 'position': position.value}
253
+ )
254
+
255
+ async def get_mirror_position(self, mirror: Mirror) -> MirrorPosition:
256
+ """Position of the selected mirror.
257
+
258
+ .. todo:: Get more information about possible values and explain elements contained in monochromator at top
259
+ of this class.
260
+
261
+ Args:
262
+ mirror (Mirror): desired mirror to get the position from.
263
+
264
+ Returns:
265
+ MirrorPosition: current mirror position. See :class:`Monochromator.MirrorPosition` for possible values
266
+
267
+ Raises:
268
+ Exception: When an error occurred on the device side
269
+ """
270
+ response: Response = await super()._execute_command(
271
+ 'mono_getMirrorPosition', {'index': self._id, 'type': mirror.value}
272
+ )
273
+ return self.MirrorPosition(response.results['position'])
274
+
275
+ async def set_mirror_position(self, mirror: Mirror, position: MirrorPosition) -> None:
276
+ """Sets the position of the selected mirror.
277
+
278
+ .. todo:: Get more information about possible values and explain elements contained in monochromator at top
279
+ of this class.
280
+
281
+ Args:
282
+ mirror (Mirror): desired mirror to set the position.
283
+ position (MirrorPosition): position to set. See :class:`Monochromator.MirrorPosition` for possible values
284
+
285
+ Raises:
286
+ Exception: When an error occurred on the device side
287
+ """
288
+ await super()._execute_command(
289
+ 'mono_moveMirror', {'index': self._id, 'type': mirror.value, 'position': position.value}
290
+ )
291
+
292
+ async def get_slit_position_in_mm(self, slit: Slit) -> float:
293
+ """Returns the position in millimeters [mm] of the selected slit.
294
+
295
+ .. todo:: Get more information about possible values and explain elements contained in monochromator at top
296
+ of this class.
297
+
298
+ Args:
299
+ slit (Slit): desired slit to get the position from. See :class:`Monochromator.Slit` for possible
300
+ values
301
+ Returns:
302
+ float: position in mm
303
+
304
+ Raises:
305
+ Exception: When an error occurred on the device side
306
+ """
307
+
308
+ response: Response = await super()._execute_command(
309
+ 'mono_getSlitPositionInMM', {'index': self._id, 'type': slit.value}
310
+ )
311
+ return float(response.results['position'])
312
+
313
+ async def set_slit_position(self, slit: Slit, position_in_mm: float) -> None:
314
+ """Sets the position of the selected slit.
315
+
316
+ .. todo:: Get more information about possible values and explain elements contained in monochromator at top
317
+ of this class.
318
+
319
+ Args:
320
+ slit (Slit): desired slit to set the position. See :class:`Monochromator.Slit` for possible values.
321
+ position (float): position to set in millimeters [mm].
322
+
323
+ Raises:
324
+ Exception: When an error occurred on the device side
325
+ """
326
+ await super()._execute_command(
327
+ 'mono_moveSlitMM', {'index': self._id, 'type': slit.value, 'position': position_in_mm}
328
+ )
329
+
330
+ async def get_slit_step_position(self, slit: Slit) -> SlitStepPosition:
331
+ """Returns the step position of the selected slit.
332
+
333
+ .. todo:: Get more information about possible values and explain elements contained in monochromator at top
334
+ of this class.
335
+
336
+ Args:
337
+ slit (Slit): desired slit to get the position from. See :class:`Monochromator.Slit` for possible
338
+ values
339
+ Returns:
340
+ SlitStepPosition: step position. See :class:`Monochromator.SlitStepPosition` for possible values
341
+
342
+ Raises:
343
+ Exception: When an error occurred on the device side
344
+ """
345
+
346
+ response: Response = await super()._execute_command(
347
+ 'mono_getSlitStepPosition', {'index': self._id, 'type': slit.value}
348
+ )
349
+ return self.SlitStepPosition(response.results['position'])
350
+
351
+ async def set_slit_step_position(self, slit: Slit, step_position: SlitStepPosition) -> None:
352
+ """Sets the step position of the selected slit.
353
+
354
+ .. todo:: Get more information about possible values and explain elements contained in monochromator at top
355
+ of this class.
356
+
357
+ Args:
358
+ slit (Slit): desired slit to set the step position. See :class:`Monochromator.Slit` for possible values.
359
+ step_position (SlitStepPosition): the step position. See :class:`Monochromator.SlitStepPosition` for
360
+ possible values
361
+
362
+ Raises:
363
+ Exception: When an error occurred on the device side
364
+ """
365
+ await super()._execute_command(
366
+ 'mono_moveSlit', {'index': self._id, 'type': slit.value, 'position': step_position.value}
367
+ )
368
+
369
+ async def open_shutter(self) -> None:
370
+ """Opens the shutter.
371
+
372
+ Raises:
373
+ Exception: When an error occurred on the device side
374
+ """
375
+ await super()._execute_command('mono_shutterOpen', {'index': self._id})
376
+
377
+ async def close_shutter(self) -> None:
378
+ """Closes the shutter.
379
+
380
+ Raises:
381
+ Exception: When an error occurred on the device side
382
+ """
383
+ await super()._execute_command('mono_shutterClose', {'index': self._id})
384
+
385
+ async def get_shutter_position(self) -> ShutterPosition:
386
+ """Returns the shutter position.
387
+
388
+ Returns:
389
+ ShutterPosition: OPEN or CLOSED
390
+
391
+ Raises:
392
+ Exception: When an error occurred on the device side
393
+ """
394
+ response: Response = await super()._execute_command('mono_getShutterStatus', {'index': self._id})
395
+ return self.ShutterPosition(response.results['position'])
@@ -0,0 +1,34 @@
1
+ """
2
+ icl_error
3
+
4
+ A package that provides error handling for the ICL.
5
+
6
+ Errors comming from the ICL have the following format: `"[E];<error code>;<error string>"`. A list of known ICL errors
7
+ exists in the `error_list.json` file. Each error is defined by:
8
+
9
+ - number: the error code
10
+ - text: the text to be logged or put in an exception
11
+ - level: the level of severity
12
+
13
+ There is an internal mapping between the `level` defined in the json file and the internal horiba_sdk.icl_error.Severity
14
+ enum. This mapping exists because the library uses loguru as a logger lib, which has other names for the logging
15
+ severities.
16
+
17
+ """
18
+
19
+ # Necessary to make Python treat the directory as a package
20
+ from .abstract_error import AbstractError, FakeError, Severity, StringAsSeverity
21
+ from .abstract_error_db import AbstractErrorDB, FakeErrorDB
22
+ from .icl_error import ICLError
23
+ from .icl_error_db import ICLErrorDB
24
+
25
+ __all__ = [
26
+ 'AbstractError',
27
+ 'AbstractErrorDB',
28
+ 'FakeError',
29
+ 'FakeErrorDB',
30
+ 'ICLError',
31
+ 'ICLErrorDB',
32
+ 'Severity',
33
+ 'StringAsSeverity',
34
+ ]
@@ -0,0 +1,65 @@
1
+ from abc import ABC, abstractmethod
2
+ from enum import Enum
3
+ from typing import final
4
+
5
+ from overrides import override
6
+
7
+
8
+ class AbstractError(ABC):
9
+ """Represents an abstract error."""
10
+
11
+ @abstractmethod
12
+ def log(self) -> None:
13
+ """Logs the error."""
14
+ pass
15
+
16
+ @abstractmethod
17
+ def message(self) -> str:
18
+ """Returns the message of the error."""
19
+ pass
20
+
21
+
22
+ @final
23
+ class FakeError(AbstractError):
24
+ """Fake error"""
25
+
26
+ def __init__(self, message: str) -> None:
27
+ self._error_message = message
28
+
29
+ @override
30
+ def log(self) -> None:
31
+ """Logs nowhere"""
32
+ pass
33
+
34
+ @override
35
+ def message(self) -> str:
36
+ """Returns the message of the error."""
37
+ return self._error_message
38
+
39
+
40
+ class Severity(Enum):
41
+ TRACE = 'TRACE'
42
+ DEBUG = 'DEBUG'
43
+ INFO = 'INFO'
44
+ SUCCESS = 'SUCCESS'
45
+ WARNING = 'WARNING'
46
+ ERROR = 'ERROR'
47
+ CRITICAL = 'CRITICAL'
48
+
49
+
50
+ class StringAsSeverity:
51
+ """StringAsSeverity."""
52
+
53
+ def __init__(self, string: str) -> None:
54
+ self._string = string
55
+
56
+ def to_severity(self) -> Severity:
57
+ """to_severity.
58
+
59
+ Args:
60
+
61
+ Returns:
62
+ Severity:
63
+ """
64
+ mapping_dict = {'fatal': Severity.CRITICAL}
65
+ return mapping_dict.get(self._string.lower(), Severity.INFO)
@@ -0,0 +1,25 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import final
3
+
4
+ from overrides import override
5
+
6
+ from horiba_sdk.icl_error import AbstractError, FakeError
7
+
8
+
9
+ class AbstractErrorDB(ABC):
10
+ """Abstract error database."""
11
+
12
+ @abstractmethod
13
+ def error_from(self, string: str) -> AbstractError:
14
+ """Searches an error in the database based on a string.
15
+
16
+ When successfull, returns a corresponding error.
17
+ """
18
+ pass
19
+
20
+
21
+ @final
22
+ class FakeErrorDB(AbstractErrorDB):
23
+ @override
24
+ def error_from(self, string: str) -> AbstractError:
25
+ return FakeError(string)