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,219 @@
1
+ from enum import Enum
2
+ from types import TracebackType
3
+ from typing import Any, Optional, Union, final
4
+
5
+ import pint
6
+ from loguru import logger
7
+ from overrides import override
8
+
9
+ from horiba_sdk import ureg
10
+ from horiba_sdk.communication import Response
11
+ from horiba_sdk.core.resolution import Resolution
12
+ from horiba_sdk.icl_error import AbstractErrorDB
13
+ from horiba_sdk.sync.communication.abstract_communicator import AbstractCommunicator
14
+ from horiba_sdk.sync.devices.single_devices.abstract_device import AbstractDevice
15
+
16
+
17
+ @final
18
+ class ChargeCoupledDevice(AbstractDevice):
19
+ """Charge Coupled Device
20
+
21
+ This class should not be instanced by the end user. Instead, the :class:`horiba_sdk.devices.DeviceManager`
22
+ should be used to access the detected CCDs on the system.
23
+ """
24
+
25
+ @final
26
+ class XAxisConversionType(Enum):
27
+ NONE = 0
28
+ FROM_CCD_FIRMWARE = 1
29
+ FROM_ICL_SETTINGS_INI = 2
30
+
31
+ def __init__(self, device_id: int, communicator: AbstractCommunicator, error_db: AbstractErrorDB) -> None:
32
+ super().__init__(device_id, communicator, error_db)
33
+
34
+ def __enter__(self) -> 'ChargeCoupledDevice':
35
+ self.open()
36
+ return self
37
+
38
+ def __exit__(
39
+ self,
40
+ exc_type: Optional[type[BaseException]],
41
+ exc_value: Optional[BaseException],
42
+ traceback: Optional[TracebackType],
43
+ ) -> None:
44
+ is_open = self.is_open()
45
+ if not is_open:
46
+ logger.debug('CCD is already closed')
47
+ return
48
+
49
+ self.close()
50
+
51
+ @override
52
+ def open(self) -> None:
53
+ """Opens the connection to the Charge Coupled Device
54
+
55
+ Raises:
56
+ Exception: When an error occurred on the device side
57
+ """
58
+ super().open()
59
+ super()._execute_command('ccd_open', {'index': self._id})
60
+
61
+ @override
62
+ def close(self) -> None:
63
+ """Closes the connection to the ChargeCoupledDevice
64
+
65
+ Raises:
66
+ Exception: When an error occurred on the device side
67
+ """
68
+ super()._execute_command('ccd_close', {'index': self._id})
69
+
70
+ def is_open(self) -> bool:
71
+ """Checks if the connection to the charge coupled device is open.
72
+
73
+ Raises:
74
+ Exception: When an error occurred on the device side
75
+ """
76
+ response: Response = super()._execute_command('ccd_isOpen', {'index': self._id})
77
+ return bool(response.results['open'])
78
+
79
+ def get_temperature(self) -> pint.Quantity:
80
+ """Chip temperature of the CCD.
81
+
82
+ Returns:
83
+ pint.Quantity: chip's temperature in degree Celsius
84
+
85
+ Raises:
86
+ Exception: When an error occurred on the device side
87
+ """
88
+ response: Response = super()._execute_command('ccd_getChipTemperature', {'index': self._id})
89
+ return ureg.Quantity(response.results['temperature'], ureg.degC) # type: ignore
90
+
91
+ def get_chip_size(self) -> Resolution:
92
+ """Chip resolution of the CCD.
93
+
94
+ Returns:
95
+ Resolution: chip resolution (width, height)
96
+
97
+ Raises:
98
+ Exception: When an error occurred on the device side
99
+ """
100
+ response: Response = super()._execute_command('ccd_getChipSize', {'index': self._id})
101
+ width: int = response.results['x']
102
+ height: int = response.results['y']
103
+ resolution: Resolution = Resolution(width, height)
104
+ return resolution
105
+
106
+ def get_speed(self) -> Union[pint.Quantity, None]:
107
+ """Chip transfer speed in kHz
108
+
109
+ Returns:
110
+ pint.Quantity: Transfer speed in kilo Hertz
111
+
112
+ Raises:
113
+ Exception: When an error occurred on the device side
114
+ """
115
+ response: Response = super()._execute_command('ccd_getSpeed', {'index': self._id})
116
+ return ureg(response.results['info'])
117
+
118
+ def get_exposure_time(self) -> Union[pint.Quantity, None]:
119
+ """Returns the exposure time in ms
120
+
121
+ Returns:
122
+ pint.Quantity: Exposure time in ms
123
+ Raises:
124
+ Exception: When an error occurred on the device side
125
+ """
126
+ response: Response = super()._execute_command('ccd_getExposureTime', {'index': self._id})
127
+ exposure = ureg.Quantity(response.results['time'], 'ms')
128
+ return exposure
129
+
130
+ def set_exposure_time(self, exposure_time_ms: int) -> None:
131
+ """Sets the exposure time in ms
132
+
133
+ Args:
134
+ exposure_time_ms (int): Exposure time in ms
135
+ Raises:
136
+ Exception: When an error occurred on the device side
137
+ """
138
+
139
+ super()._execute_command('ccd_setExposureTime', {'index': self._id, 'time': exposure_time_ms})
140
+
141
+ def get_acquisition_ready(self) -> bool:
142
+ """Returns true if the CCD is ready to acquire
143
+
144
+ Returns:
145
+ bool: True if the CCD is ready to acquire
146
+ Raises:
147
+ Exception: When an error occurred on the device side
148
+ """
149
+ response: Response = super()._execute_command('ccd_getAcquisitionReady', {'index': self._id})
150
+ return bool(response.results['ready'])
151
+
152
+ def set_acquisition_start(self, open_shutter: bool) -> None:
153
+ """Starts the acquisition of the CCD
154
+
155
+ Args:
156
+ open_shutter (bool): Whether the shutter of the camera should be open
157
+ Raises:
158
+ Exception: When an error occurred on the device side
159
+ """
160
+ super()._execute_command('ccd_setAcquisitionStart', {'index': self._id, 'openShutter': open_shutter})
161
+
162
+ def set_region_of_interest(
163
+ self,
164
+ roi_index: int = 1,
165
+ x_origin: int = 0,
166
+ y_origin: int = 0,
167
+ x_size: int = 1024,
168
+ y_size: int = 256,
169
+ x_bin: int = 1,
170
+ y_bin: int = 256,
171
+ ) -> None:
172
+ """Sets the region of interest of the CCD
173
+ an example json command looks like this:
174
+
175
+ Args:
176
+ roi_index (int, optional): Index of the region of interest. Defaults to 1.
177
+ x_origin (int, optional): X origin of the region of interest. Defaults to 0.
178
+ y_origin (int, optional): Y origin of the region of interest. Defaults to 0.
179
+ x_size (int, optional): X size of the region of interest. Defaults to 1024.
180
+ y_size (int, optional): Y size of the region of interest. Defaults to 256.
181
+ x_bin (int, optional): X bin of the region of interest. Defaults to 1.
182
+ y_bin (int, optional): Y bin of the region of interest. Defaults to 256.
183
+
184
+ Raises:
185
+ Exception: When an error occurred on the device side
186
+ """
187
+ super()._execute_command(
188
+ 'ccd_setRoi',
189
+ {
190
+ 'index': self._id,
191
+ 'roiIndex': roi_index,
192
+ 'xOrigin': x_origin,
193
+ 'yOrigin': y_origin,
194
+ 'xSize': x_size,
195
+ 'ySize': y_size,
196
+ 'xBin': x_bin,
197
+ 'yBin': y_bin,
198
+ },
199
+ )
200
+
201
+ def get_acquisition_data(self) -> dict[Any, Any]:
202
+ """Returns the acquisition data of the CCD
203
+ nina: atm this returns data still formatted for telnet communication, not formatted as json"""
204
+ response: Response = super()._execute_command('ccd_getAcquisitionData', {'index': self._id})
205
+ return response.results
206
+
207
+ def get_acquisition_busy(self) -> bool:
208
+ """Returns true if the CCD is busy with the acquisition"""
209
+ response: Response = super()._execute_command('ccd_getAcquisitionBusy', {'index': self._id})
210
+ return bool(response.results['isBusy'])
211
+
212
+ def set_x_axis_conversion_type(self, conversion_type: XAxisConversionType) -> None:
213
+ """Sets the conversion type of the x axis"""
214
+ super()._execute_command('ccd_setXAxisConversionType', {'index': self._id, 'type': conversion_type.value})
215
+
216
+ def get_x_axis_conversion_type(self) -> XAxisConversionType:
217
+ """Gets the conversion type of the x axis"""
218
+ response: Response = super()._execute_command('ccd_getXAxisConversionType', {'index': self._id})
219
+ return self.XAxisConversionType(response.results['type'])
@@ -0,0 +1,150 @@
1
+ from types import TracebackType
2
+ from typing import Optional, final
3
+
4
+ from loguru import logger
5
+ from numericalunits import nm
6
+ from overrides import override
7
+
8
+ from horiba_sdk.communication import Response
9
+ from horiba_sdk.icl_error import AbstractErrorDB
10
+ from horiba_sdk.sync.communication.abstract_communicator import AbstractCommunicator
11
+ from horiba_sdk.sync.devices.single_devices.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.sync.devices.DeviceManager`
19
+ should be used to access the detected Monochromators on the system.
20
+ """
21
+
22
+ def __init__(self, device_id: int, communicator: AbstractCommunicator, error_db: AbstractErrorDB) -> None:
23
+ super().__init__(device_id, communicator, error_db)
24
+
25
+ def __enter__(self) -> 'Monochromator':
26
+ self.open()
27
+ return self
28
+
29
+ def __exit__(
30
+ self,
31
+ exc_type: Optional[type[BaseException]],
32
+ exc_value: Optional[BaseException],
33
+ traceback: Optional[TracebackType],
34
+ ) -> None:
35
+ if not self.is_open():
36
+ logger.debug('Monochromator is already closed')
37
+ return
38
+
39
+ self.close()
40
+
41
+ @override
42
+ def open(self) -> None:
43
+ """Opens the connection to the Monochromator
44
+
45
+ Raises:
46
+ Exception: When an error occured on the device side
47
+ """
48
+ super().open()
49
+ super()._execute_command('mono_open', {'index': self._id}, 0.5)
50
+
51
+ @override
52
+ def close(self) -> None:
53
+ """Closes the connection to the Monochromator
54
+
55
+ Raises:
56
+ Exception: When an error occured on the device side
57
+ """
58
+ super()._execute_command('mono_close', {'index': self._id})
59
+
60
+ def is_open(self) -> bool:
61
+ """Checks if the connection to the monochromator is open.
62
+
63
+ Raises:
64
+ Exception: When an error occured on the device side
65
+ """
66
+ response: Response = super()._execute_command('mono_isOpen', {'index': self._id})
67
+ return bool(response.results['open'])
68
+
69
+ @property
70
+ def is_busy(self) -> bool:
71
+ """Checks if the monochromator is busy.
72
+
73
+ Raises:
74
+ Exception: When an error occured on the device side
75
+ """
76
+ response: Response = super()._execute_command('mono_isBusy', {'index': self._id})
77
+ return bool(response.results['busy'])
78
+
79
+ def home(self) -> None:
80
+ """Starts the monochromator initialization process called "homing".
81
+
82
+ Use :func:`Monochromator.is_busy()` to know if the operation is still taking place.
83
+
84
+ Raises:
85
+ Exception: When an error occured on the device side
86
+ """
87
+ super()._execute_command('mono_init', {'index': self._id})
88
+
89
+ @property
90
+ def wavelength(self) -> nm:
91
+ """Current wavelength of the monochromator's position in nm.
92
+
93
+ Raises:
94
+ Exception: When an error occured on the device side
95
+ """
96
+ response = super()._execute_command('mono_getPosition', {'index': self._id})
97
+ return float(response.results['wavelength']) * nm
98
+
99
+ def set_current_wavelength(self, wavelength: int) -> None:
100
+ """This command sets the wavelength value of the current grating position of the monochromator.
101
+
102
+ .. warning:: This could potentially uncalibrate the monochromator and report an incorrect wavelength compared to
103
+ the actual output wavelength.
104
+
105
+ Args:
106
+ wavelength (nm): wavelength
107
+
108
+ Raises:
109
+ Exception: When an error occured on the device side
110
+ """
111
+ super()._execute_command('mono_setPosition', {'index': self._id, 'wavelength': wavelength})
112
+
113
+ def move_to_wavelength(self, wavelength: nm) -> None:
114
+ """Orders the monochromator to move to the requested wavelength.
115
+
116
+ Use :func:`Monochromator.is_busy()` to know if the operation is still taking place.
117
+
118
+ Args:
119
+ wavelength (nm): wavelength
120
+
121
+ Raises:
122
+ Exception: When an error occured on the device side
123
+ """
124
+ super()._execute_command('mono_moveToPosition', {'index': self._id, 'wavelength': wavelength / nm})
125
+
126
+ @property
127
+ def turret_grating_position(self) -> int:
128
+ """Grating turret position.
129
+
130
+ Returns:
131
+ int: current grating turret position
132
+
133
+ Raises:
134
+ Exception: When an error occured on the device side
135
+ """
136
+ response: Response = super()._execute_command('mono_getGratingPosition', {'index': self._id})
137
+ return int(response.results['position'])
138
+
139
+ def move_turret_to_grating(self, position: int) -> None:
140
+ """Move turret to grating position
141
+
142
+ .. todo:: Get more information about how it works and clarify veracity of returned data
143
+
144
+ Args:
145
+ position (int): new grating position
146
+
147
+ Raises:
148
+ Exception: When an error occured on the device side
149
+ """
150
+ super()._execute_command('mono_getPosition', {'index': self._id, 'position': position})
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+ Copyright (c) 2023 ZühlkeEngineering
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
20
+ OR OTHER DEALINGS IN THE SOFTWARE.