horiba-sdk 0.5.4__py3-none-any.whl → 0.7.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.
Files changed (38) hide show
  1. horiba_sdk/communication/messages.py +7 -3
  2. horiba_sdk/communication/websocket_communicator.py +1 -1
  3. horiba_sdk/core/acquisition_format.py +1 -2
  4. horiba_sdk/core/stitching/__init__.py +6 -0
  5. horiba_sdk/core/stitching/labspec6_spectra_stitch.py +90 -0
  6. horiba_sdk/core/stitching/linear_spectra_stitch.py +107 -0
  7. horiba_sdk/core/stitching/simple_cut_spectra_stitch.py +84 -0
  8. horiba_sdk/core/stitching/spectra_stitch.py +16 -0
  9. horiba_sdk/core/stitching/y_displacement_spectra_stitch.py +87 -0
  10. horiba_sdk/core/trigger_input_polarity.py +6 -0
  11. horiba_sdk/devices/device_manager.py +19 -3
  12. horiba_sdk/devices/fake_icl_server.py +7 -0
  13. horiba_sdk/devices/fake_responses/spectracq3.json +217 -0
  14. horiba_sdk/devices/single_devices/__init__.py +2 -1
  15. horiba_sdk/devices/single_devices/ccd.py +4 -2
  16. horiba_sdk/devices/single_devices/spectracq3.py +392 -0
  17. horiba_sdk/devices/spectracq3_discovery.py +55 -0
  18. {horiba_sdk-0.5.4.dist-info → horiba_sdk-0.7.0.dist-info}/METADATA +2 -1
  19. horiba_sdk-0.7.0.dist-info/RECORD +47 -0
  20. horiba_sdk/sync/__init__.py +0 -0
  21. horiba_sdk/sync/communication/__init__.py +0 -7
  22. horiba_sdk/sync/communication/abstract_communicator.py +0 -47
  23. horiba_sdk/sync/communication/test_client.py +0 -16
  24. horiba_sdk/sync/communication/websocket_communicator.py +0 -232
  25. horiba_sdk/sync/devices/__init__.py +0 -15
  26. horiba_sdk/sync/devices/abstract_device_discovery.py +0 -17
  27. horiba_sdk/sync/devices/abstract_device_manager.py +0 -68
  28. horiba_sdk/sync/devices/device_discovery.py +0 -58
  29. horiba_sdk/sync/devices/device_manager.py +0 -213
  30. horiba_sdk/sync/devices/fake_device_manager.py +0 -91
  31. horiba_sdk/sync/devices/fake_icl_server.py +0 -82
  32. horiba_sdk/sync/devices/single_devices/__init__.py +0 -5
  33. horiba_sdk/sync/devices/single_devices/abstract_device.py +0 -87
  34. horiba_sdk/sync/devices/single_devices/ccd.py +0 -674
  35. horiba_sdk/sync/devices/single_devices/monochromator.py +0 -413
  36. horiba_sdk-0.5.4.dist-info/RECORD +0 -53
  37. {horiba_sdk-0.5.4.dist-info → horiba_sdk-0.7.0.dist-info}/LICENSE +0 -0
  38. {horiba_sdk-0.5.4.dist-info → horiba_sdk-0.7.0.dist-info}/WHEEL +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: horiba-sdk
3
- Version: 0.5.4
3
+ Version: 0.7.0
4
4
  Summary: 'horiba-sdk' is a package that provides source code for the development with Horiba devices
5
5
  Home-page: https://github.com/ThatsTheEnd/horiba-python-sdk
6
6
  License: MIT
@@ -21,6 +21,7 @@ Requires-Dist: loguru (>=0.7.2,<0.8.0)
21
21
  Requires-Dist: overrides (>=7.4.0,<8.0.0)
22
22
  Requires-Dist: pint (>=0.23,<0.24)
23
23
  Requires-Dist: psutil (>=5.9.7,<6.0.0)
24
+ Requires-Dist: pydantic (>=2.10.6,<3.0.0)
24
25
  Requires-Dist: websockets (>=12.0,<13.0)
25
26
  Project-URL: Repository, https://github.com/ThatsTheEnd/horiba-python-sdk
26
27
  Description-Content-Type: text/markdown
@@ -0,0 +1,47 @@
1
+ horiba_sdk/__init__.py,sha256=J8jCV90BJ2SCDFSpacDeAAsJa8PFqBUDpREOJeAgXug,679
2
+ horiba_sdk/communication/__init__.py,sha256=nhS1rw1ZojM8zmRIx0VEFtYge0IH-B_L4zNBUNBJZYE,1564
3
+ horiba_sdk/communication/abstract_communicator.py,sha256=E80dfOjrfuNay3W5jeSn4T2tUas6vygRcF46kSoP5j8,1498
4
+ horiba_sdk/communication/communication_exception.py,sha256=d2ouOoVI6Q69_JL1bUEjjQOmjiO0CEm8K20WsaUuhB0,583
5
+ horiba_sdk/communication/messages.py,sha256=7PCCkIpicq0CbN95nLoxIpsXlPd0c5lKbW92rn8h-6w,2701
6
+ horiba_sdk/communication/websocket_communicator.py,sha256=IFP6dfuqiwGsNymPtT4Hqmii_8n2pO1Jv_k31GfrM8s,8331
7
+ horiba_sdk/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ horiba_sdk/core/acquisition_format.py,sha256=CWyLgCotx60-vBAv6ZzMoCpKDXFtS43Q5reiKcYmF6U,350
9
+ horiba_sdk/core/clean_count_mode.py,sha256=7guLd3eUV3Y2Cnj3w0JJ7ry74I8a6MFQiqbK5pTb3oU,185
10
+ horiba_sdk/core/resolution.py,sha256=VOQonjPBqZ2TbtiNVDBT3TI1Aymj1pPYWMoobFaXjG8,1029
11
+ horiba_sdk/core/stitching/__init__.py,sha256=Ih8CHdb9VnoM1DHAO-T3w7IGxZHZqSlnI2AeAuJq7qc,337
12
+ horiba_sdk/core/stitching/labspec6_spectra_stitch.py,sha256=8oc1DoEtnBhYaOXfLFQBBc12DEh_wRVDI_lu9ELnP6Q,3093
13
+ horiba_sdk/core/stitching/linear_spectra_stitch.py,sha256=epA2_pMlJVJ83oBn_4W1IWb3ImIzag61E2XrR-0MpCI,3968
14
+ horiba_sdk/core/stitching/simple_cut_spectra_stitch.py,sha256=yMqy1T_GwViG4pBwZIDg557gFPR8qMhJv8v1-9eQnTY,2920
15
+ horiba_sdk/core/stitching/spectra_stitch.py,sha256=Y63e4_sm2fU6GKYphopCWec1n29yMX-fDIyIrjKRuoc,447
16
+ horiba_sdk/core/stitching/y_displacement_spectra_stitch.py,sha256=YA1RefdJCe5U22gkqQQNi5mLgZ4A7dZxBiezkP0FsAI,3189
17
+ horiba_sdk/core/timer_resolution.py,sha256=V6pXEZMVvUUoqrJmLFFI45pFt_7po6-5r1DmC8jY6gs,300
18
+ horiba_sdk/core/trigger_input_polarity.py,sha256=UDns386W5ecKOHAdEru4dMObX9EF7pZetzY8IvmNufM,97
19
+ horiba_sdk/core/x_axis_conversion_type.py,sha256=SQrdbciTR8x5UWOqLDgxWPy12o0-yIVLrkm7N3gpWpg,449
20
+ horiba_sdk/devices/__init__.py,sha256=YYsJL2CSJwc2vsAjFQEKXjA5_QAnKlxSwlx32EgzFME,336
21
+ horiba_sdk/devices/abstract_device_discovery.py,sha256=04ZCEB5IZkUuJxSisS78eW6lAQNXG4uaDoPv-eBccBA,178
22
+ horiba_sdk/devices/abstract_device_manager.py,sha256=RXj5_pzFHpiolJe3ZfFsofwpD1hlwUMwYD1DyWMmlI0,1717
23
+ horiba_sdk/devices/ccd_discovery.py,sha256=nGuskZ07Y9myI__dU8LeLnrNiJEBpGPby21EEwgSVUk,2376
24
+ horiba_sdk/devices/device_manager.py,sha256=0C5qZK7ekZL7A68yXWiUHRYCVqQ1fjioL3861DP1gkY,11548
25
+ horiba_sdk/devices/fake_device_manager.py,sha256=Tr4Z067smYfy-ya29PO4PK4EWF6Sa1R2UQFZCWfPePE,5015
26
+ horiba_sdk/devices/fake_icl_server.py,sha256=aVmUZfBddz_w6HX2L6KS_9ei-a4SKtkh5Q8MuUV3szA,2757
27
+ horiba_sdk/devices/fake_responses/ccd.json,sha256=sMg-UqU6W1I01n2kEbkFc5J0XF8E3JVdOjB-1MhufzM,17020
28
+ horiba_sdk/devices/fake_responses/icl.json,sha256=ZPKhjx0BmwmqNGwbb7reXSStddgVZetCQQYVeNr9CSU,572
29
+ horiba_sdk/devices/fake_responses/monochromator.json,sha256=I9yBMsJyXXVCLF1J8mNPsfgXasIg60IL-4ToECcoGhw,4308
30
+ horiba_sdk/devices/fake_responses/spectracq3.json,sha256=gnGWGtz4MzifY4NaKtjQ6XaWL73d5lMJM3mOlpdmDtE,4099
31
+ horiba_sdk/devices/monochromator_discovery.py,sha256=KoZ8vfPsI6QRuiD4BiE0YvzO42dQzFZqvoxygDSllE8,2365
32
+ horiba_sdk/devices/single_devices/__init__.py,sha256=JtQYr26ZrSqi3uaralLipw6u3e9f7z-QsXPIrSfdAoQ,241
33
+ horiba_sdk/devices/single_devices/abstract_device.py,sha256=CwCjHJWSp8WGrYsyq0ZhTQVnvYdoCG-kb4BU9SZXJm8,3994
34
+ horiba_sdk/devices/single_devices/ccd.py,sha256=qtXMzEhYs3UJhOsDA2hIajdN1nyvP7eFZaiaBYZ3piY,30083
35
+ horiba_sdk/devices/single_devices/monochromator.py,sha256=r-9bI-ne_iGdfTWBvUnYSxxp1DQNlEt7wBN31cGfZ1E,14538
36
+ horiba_sdk/devices/single_devices/spectracq3.py,sha256=cFNFDbGLQB9HkKfg3TwYTEaLx8gcNu1O_nPjXOjH1hk,13667
37
+ horiba_sdk/devices/spectracq3_discovery.py,sha256=5mrjmZ30F35Jswl3u3yuMnu5Hu5LDOqYZ4rlOJeRvO8,2389
38
+ horiba_sdk/icl_error/__init__.py,sha256=EtqHaUeoaLilpb7L7juuVamJgIZ3Anfnca0AO2o46rg,1053
39
+ horiba_sdk/icl_error/abstract_error.py,sha256=5nQDI6MNfCi4xUx6taJcEWGwANOo8mPEb4kVxrO-DW8,1301
40
+ horiba_sdk/icl_error/abstract_error_db.py,sha256=IJ3wGb6DWxnY35VIUwZX0X80IPi5k4QoQXnVjDy8lH8,589
41
+ horiba_sdk/icl_error/error_list.json,sha256=wyDYZPeH2sYMRPoL-JBoDul4uaYvoqp7SJg0zypfl40,5977
42
+ horiba_sdk/icl_error/icl_error.py,sha256=IElsKl5nvLjprIV_ub9MacJmdSS327iNwOXhSiJXTc0,755
43
+ horiba_sdk/icl_error/icl_error_db.py,sha256=5Jbs_ZlMp8Bjr4u8reIxxfOSFYYkzWTfKIqOQ4oUSXQ,2603
44
+ horiba_sdk-0.7.0.dist-info/LICENSE,sha256=JD-7TpNZoT7emooLwaTU9bJZbFbeM1ba90b8gpCYxzM,1083
45
+ horiba_sdk-0.7.0.dist-info/METADATA,sha256=l-MRWVpfMYjW3dovj-kzSGahIZTe8mQsT5nxh1aaOfw,14391
46
+ horiba_sdk-0.7.0.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
47
+ horiba_sdk-0.7.0.dist-info/RECORD,,
File without changes
@@ -1,7 +0,0 @@
1
- from .abstract_communicator import AbstractCommunicator
2
- from .websocket_communicator import WebsocketCommunicator
3
-
4
- __all__ = [
5
- 'AbstractCommunicator',
6
- 'WebsocketCommunicator',
7
- ]
@@ -1,47 +0,0 @@
1
- from abc import ABC, abstractmethod
2
-
3
- from horiba_sdk.communication.messages import Command, Response
4
-
5
-
6
- class AbstractCommunicator(ABC):
7
- """
8
- Abstract base class for communication protocols.
9
- """
10
-
11
- @abstractmethod
12
- def open(self) -> None:
13
- """
14
- Abstract method to establish a connection.
15
- """
16
- pass
17
-
18
- @abstractmethod
19
- def opened(self) -> bool:
20
- """
21
- Abstract method that says if the connection is open
22
-
23
- Returns:
24
- bool: True if the connection is open, False otherwise
25
- """
26
- pass
27
-
28
- @abstractmethod
29
- def request_with_response(self, command: Command, response_timeout_s: float = 5) -> Response:
30
- """
31
- Abstract method to fetch a response from a command.
32
-
33
- Args:
34
- command (Command): Command for which a response is desired
35
- response_timeout_s (float, optional): Timeout in seconds. Defaults to 5.
36
-
37
- Returns:
38
- Response: The response corresponding to the sent command.
39
- """
40
- pass
41
-
42
- @abstractmethod
43
- def close(self) -> None:
44
- """
45
- Abstract method to close the connection.
46
- """
47
- pass
@@ -1,16 +0,0 @@
1
- # Closing frame is apparently not sent. Here is a minimal example to prove the point:
2
- # 1. First run icl.exe
3
- # 2. Then execute this script:
4
- # poetry run python ./horiba_sdk/sync/communication/test_client.py
5
- from websockets.exceptions import ConnectionClosedError, ConnectionClosedOK
6
- from websockets.sync.client import connect
7
-
8
- websocket = connect(uri='ws://localhost:25010')
9
- try:
10
- websocket.send('{"command":"icl_shutdown"}')
11
- for message in websocket:
12
- print(message)
13
- except ConnectionClosedOK as e:
14
- print(f'Connection normally closed: {e}')
15
- except ConnectionClosedError as e:
16
- print(f'Protocol error or network failure: {e}')
@@ -1,232 +0,0 @@
1
- import time
2
- from queue import Queue
3
- from threading import Thread
4
- from types import TracebackType
5
- from typing import Any, Callable, Optional, final
6
-
7
- import websockets
8
- from loguru import logger
9
- from overrides import override
10
- from websockets.sync.client import ClientConnection
11
-
12
- from horiba_sdk.communication.communication_exception import CommunicationException
13
- from horiba_sdk.communication.messages import Command, JSONResponse, Response
14
- from horiba_sdk.sync.communication.abstract_communicator import AbstractCommunicator
15
-
16
-
17
- @final
18
- class WebsocketCommunicator(AbstractCommunicator):
19
- """
20
- The WebsocketCommunicator implements the `horiba_sdk.sync.communication.AbstractCommunicator` via websockets.
21
- A background thread listens continuously for incoming binary data.
22
- """
23
-
24
- def __init__(self, uri: str = 'ws://127.0.0.1:25010') -> None:
25
- self.uri: str = uri
26
- self.websocket: Optional[ClientConnection] = None
27
- self.running_listen_thread: bool = False
28
- self.listen_thread: Optional[Thread] = None
29
- self.running_binary_message_handling_thread: bool = False
30
- self.binary_message_handling_thread: Optional[Thread] = None
31
- self.json_message_dict: dict[int, JSONResponse] = {}
32
- self.binary_message_queue: Queue[bytes] = Queue()
33
- self.binary_message_callback: Optional[Callable[[bytes], Any]] = None
34
- self.icl_info: dict[str, Any] = {}
35
-
36
- def __enter__(self) -> 'WebsocketCommunicator':
37
- self.open()
38
- return self
39
-
40
- def __exit__(
41
- self,
42
- exc_type: Optional[type[BaseException]],
43
- exc_value: Optional[BaseException],
44
- traceback: Optional[TracebackType],
45
- ) -> None:
46
- self.close()
47
-
48
- @override
49
- def open(self) -> None:
50
- """
51
- Opens the WebSocket connection and starts listening for binary data.
52
-
53
- Raises:
54
- CommunicationException: When the websocket is already opened or
55
- there is an issue with the underlying websockets connection attempt.
56
- """
57
- if self.opened():
58
- raise CommunicationException(None, 'websocket already opened')
59
-
60
- try:
61
- self.websocket = websockets.sync.client.connect(self.uri)
62
- except websockets.WebSocketException as e:
63
- raise CommunicationException(None, 'websocket connection issue') from e
64
-
65
- self.running_listen_thread = True
66
- self.listen_thread = Thread(target=self._receive_data)
67
- self.listen_thread.start()
68
-
69
- logger.debug(f'Websocket connection established to {self.uri}')
70
-
71
- def send(self, command: Command) -> None:
72
- """
73
- Sends a command to the WebSocket server.
74
-
75
- Args:
76
- command (Command): The command to send to the server.
77
-
78
- Raises:
79
- CommunicationException: When trying to send a command while the websocket is closed.
80
-
81
- """
82
- if not self.opened():
83
- raise CommunicationException(None, 'WebSocket is not opened.')
84
-
85
- try:
86
- # mypy cannot infer the check from self.opened() done above
87
- logger.debug(f'Sending JSON command: {command.json()}')
88
- self.websocket.send(command.json()) # type: ignore
89
- except websockets.exceptions.ConnectionClosed as e:
90
- raise CommunicationException(None, 'Trying to send data while websocket is closed') from e
91
-
92
- @override
93
- def opened(self) -> bool:
94
- """
95
- Returns if the websocket connection is open or not
96
-
97
- Returns:
98
- bool: True if the websocket connection is open, False otherwise
99
- """
100
- return self.websocket is not None
101
-
102
- def response(self, command_id: int, timeout_s: float = 5.0) -> Response:
103
- """Fetches the response belonging to the command_id.
104
-
105
- Args:
106
- command_id (int): The command id of the command.
107
- timeout_s (float): The timeout in seconds.
108
-
109
- Returns:
110
- Response: The response from the server
111
-
112
- Raises:
113
- CommunicationException: When the connection terminated with an error
114
- """
115
- waited_time_in_s: float = 0.0
116
- sleep_time_in_s: float = 0.1
117
- while waited_time_in_s < timeout_s and (
118
- not self.json_message_dict
119
- or len(self.json_message_dict) == 0
120
- or self.json_message_dict.get(command_id) is None
121
- ):
122
- time.sleep(sleep_time_in_s)
123
- waited_time_in_s += sleep_time_in_s
124
-
125
- if not self.json_message_dict or len(self.json_message_dict) == 0:
126
- raise CommunicationException(None, 'no message to be received.')
127
-
128
- if self.json_message_dict.get(command_id) is None:
129
- raise CommunicationException(None, f'no response with id {command_id}')
130
-
131
- logger.debug(f'#{len(self.json_message_dict)} messages, taking the one with id:{command_id}')
132
- response: JSONResponse = self.json_message_dict[command_id]
133
- del self.json_message_dict[command_id]
134
- logger.debug('retrieved message in dict')
135
- return response
136
-
137
- @override
138
- def close(self) -> None:
139
- """
140
- Closes the WebSocket connection.
141
-
142
- Raises:
143
- CommunicationException: When the websocket is already closed
144
- """
145
- if not self.opened():
146
- raise CommunicationException(None, 'cannot close already closed websocket')
147
- if self.websocket:
148
- logger.debug('Waiting websocket close...')
149
- self.websocket.close()
150
-
151
- if self.binary_message_handling_thread:
152
- logger.debug('Canceling binary listening thread...')
153
- self.running_binary_message_handling_thread = False
154
- self.binary_message_handling_thread.join()
155
- self.binary_message_handling_thread = None
156
-
157
- if self.listen_thread:
158
- logger.debug('Canceling listening thread...')
159
- self.running_listen_thread = False
160
- self.listen_thread.join()
161
- self.listen_thread = None
162
-
163
- self.websocket = None
164
- logger.debug('Websocket connection closed')
165
-
166
- def register_binary_message_callback(self, callback: Callable[[bytes], Any]) -> None:
167
- """Registers a callback to be called with every incoming binary message."""
168
- if self.binary_message_callback:
169
- raise CommunicationException(None, 'Binary message callback already registered')
170
-
171
- self.binary_message_callback = callback
172
- logger.debug('Binary message callback registered.')
173
- self.running_binary_message_handling_thread = True
174
- self.binary_message_handling_thread = Thread(target=self._run_binary_message_callback)
175
- self.binary_message_handling_thread.start()
176
- logger.info('Started binary message thread')
177
-
178
- def _receive_data(self) -> None:
179
- if not self.websocket:
180
- raise CommunicationException(None, 'Websocket is not open')
181
-
182
- while self.running_listen_thread:
183
- try:
184
- for message in self.websocket:
185
- logger.debug(f'Received message: {message!r}')
186
- if isinstance(message, str):
187
- response: JSONResponse = JSONResponse(message)
188
- self.json_message_dict[response.id] = response
189
- elif isinstance(message, bytes) and self.binary_message_callback:
190
- self.binary_message_queue.put(message)
191
- else:
192
- raise CommunicationException(None, f'Unknown type of message {type(message)}')
193
- except websockets.ConnectionClosedOK:
194
- logger.debug('websocket connection terminated properly')
195
- except websockets.ConnectionClosedError as e:
196
- raise CommunicationException(None, 'connection terminated with error') from e
197
- except Exception as e:
198
- raise CommunicationException(None, 'failure to process binary data') from e
199
-
200
- def _run_binary_message_callback(self) -> None:
201
- if not self.binary_message_callback:
202
- raise CommunicationException(None, 'No binary message callback registered')
203
-
204
- while self.running_binary_message_handling_thread:
205
- while self.binary_message_queue.empty():
206
- time.sleep(0.5)
207
- if not self.running_binary_message_handling_thread:
208
- return
209
-
210
- binary_message = self.binary_message_queue.get()
211
- self.binary_message_callback(binary_message)
212
-
213
- @override
214
- def request_with_response(self, command: Command, response_timeout_s: float = 5) -> Response:
215
- """
216
- Concrete method to fetch a response from a command.
217
-
218
- Args:
219
- command (Command): Command for which a response is desired
220
- response_timeout_s (float, optional): Timeout in seconds. Defaults to 5.
221
-
222
- Returns:
223
- Response: The response corresponding to the sent command.
224
- """
225
- self.send(command)
226
- response: Response = self.response(command.id, response_timeout_s)
227
-
228
- if response.id != command.id:
229
- logger.error(f'got wrong response id: {response.id}, command id: {command.id}')
230
- raise Exception('got wrong response id')
231
-
232
- return response
@@ -1,15 +0,0 @@
1
- from .abstract_device_discovery import AbstractDeviceDiscovery
2
- from .abstract_device_manager import AbstractDeviceManager
3
- from .device_discovery import DeviceDiscovery
4
- from .device_manager import DeviceManager
5
- from .fake_device_manager import FakeDeviceManager
6
- from .fake_icl_server import FakeICLServer
7
-
8
- __all__ = [
9
- 'AbstractDeviceDiscovery',
10
- 'AbstractDeviceManager',
11
- 'DeviceDiscovery',
12
- 'DeviceManager',
13
- 'FakeDeviceManager',
14
- 'FakeICLServer',
15
- ]
@@ -1,17 +0,0 @@
1
- from abc import ABC, abstractmethod
2
-
3
- from horiba_sdk.sync.devices.single_devices import ChargeCoupledDevice, Monochromator
4
-
5
-
6
- class AbstractDeviceDiscovery(ABC):
7
- @abstractmethod
8
- def execute(self, error_on_no_device: bool = False) -> None:
9
- pass
10
-
11
- @abstractmethod
12
- def charge_coupled_devices(self) -> list[ChargeCoupledDevice]:
13
- pass
14
-
15
- @abstractmethod
16
- def monochromators(self) -> list[Monochromator]:
17
- pass
@@ -1,68 +0,0 @@
1
- from abc import ABC, abstractmethod
2
-
3
- from horiba_sdk.sync.communication import AbstractCommunicator
4
- from horiba_sdk.sync.devices.single_devices import ChargeCoupledDevice, Monochromator
5
-
6
-
7
- class AbstractDeviceManager(ABC):
8
- """
9
- DeviceManager class manages the lifecycle and interactions with devices.
10
-
11
- """
12
-
13
- @abstractmethod
14
- def start(self) -> None:
15
- """
16
- Abstract method to start the device manager.
17
- """
18
- pass
19
-
20
- @abstractmethod
21
- def stop(self) -> None:
22
- """
23
- Abstract method to stop the device manager.
24
- """
25
- pass
26
-
27
- @abstractmethod
28
- def discover_devices(self, error_on_no_device: bool = False) -> None:
29
- """
30
- Abstract method that discovers and registers devices.
31
-
32
- Args:
33
- error_on_no_device (bool): If True, an exception is raised if no device is connected.
34
- """
35
- pass
36
-
37
- @property
38
- @abstractmethod
39
- def communicator(self) -> AbstractCommunicator:
40
- """
41
- Abstract method to get the communicator attribute.
42
-
43
- Returns:
44
- AbstractCommunicator: Returns the internal communicator instance.
45
- """
46
- pass
47
-
48
- @property
49
- @abstractmethod
50
- def monochromators(self) -> list[Monochromator]:
51
- """
52
- Abstract method to get the detected monochromators.
53
-
54
- Returns:
55
- List[Monochromator]: The detected monochromators
56
- """
57
- pass
58
-
59
- @property
60
- @abstractmethod
61
- def charge_coupled_devices(self) -> list[ChargeCoupledDevice]:
62
- """
63
- Abstract method to get the detected CCDs.
64
-
65
- Returns:
66
- List[ChargeCoupledDevice]: The detected CCDS.
67
- """
68
- pass
@@ -1,58 +0,0 @@
1
- from typing import final
2
-
3
- from loguru import logger
4
- from overrides import override
5
-
6
- from horiba_sdk.communication import Command, Response
7
- from horiba_sdk.icl_error import AbstractErrorDB
8
- from horiba_sdk.sync.communication import AbstractCommunicator
9
- from horiba_sdk.sync.devices.abstract_device_discovery import AbstractDeviceDiscovery
10
- from horiba_sdk.sync.devices.single_devices import ChargeCoupledDevice, Monochromator
11
-
12
-
13
- @final
14
- class DeviceDiscovery(AbstractDeviceDiscovery):
15
- def __init__(self, communicator: AbstractCommunicator, error_db: AbstractErrorDB):
16
- self._communicator: AbstractCommunicator = communicator
17
- self._charge_coupled_devices: list[ChargeCoupledDevice] = []
18
- self._monochromators: list[Monochromator] = []
19
- self._error_db: AbstractErrorDB = error_db
20
- self._discovered_devices: bool = False
21
-
22
- @override
23
- def execute(self, error_on_no_device: bool = False) -> None:
24
- """
25
- Discovers the connected devices and saves them internally.
26
- """
27
- if not self._communicator.opened():
28
- self._communicator.open()
29
-
30
- # Define the commands and device types in a list of tuples for iteration
31
- commands_and_types = [('ccd_discover', 'ccd_list', 'CCD'), ('mono_discover', 'mono_list', 'Monochromator')]
32
-
33
- for discover_command, list_command, device_type in commands_and_types:
34
- response: Response = self._communicator.request_with_response(Command(discover_command, {}))
35
- if response.results.get('count', 0) == 0 and error_on_no_device:
36
- raise Exception(f'No {device_type} connected')
37
- response = self._communicator.request_with_response(Command(list_command, {}))
38
-
39
- for device in response.results['devices']:
40
- if device_type == 'CCD':
41
- ccd = ChargeCoupledDevice(device['index'], self._communicator, self._error_db)
42
- logger.info(f'Detected CCD: {device["deviceType"]}')
43
- self._charge_coupled_devices.append(ccd)
44
- elif device_type == 'Monochromator':
45
- mono = Monochromator(device['index'], self._communicator, self._error_db)
46
- logger.info(f'Detected Monochromator: {device["deviceType"]}')
47
- self._monochromators.append(mono)
48
-
49
- logger.info(f'Found {len(self._monochromators)} Monochromator devices')
50
- logger.info(f'Found {len(self._charge_coupled_devices)} CCD devices')
51
-
52
- @override
53
- def charge_coupled_devices(self) -> list[ChargeCoupledDevice]:
54
- return self._charge_coupled_devices
55
-
56
- @override
57
- def monochromators(self) -> list[Monochromator]:
58
- return self._monochromators