horiba-sdk 0.5.2__py3-none-any.whl → 0.6.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 (36) hide show
  1. horiba_sdk/communication/messages.py +5 -2
  2. horiba_sdk/communication/websocket_communicator.py +1 -1
  3. horiba_sdk/core/stitching/__init__.py +6 -0
  4. horiba_sdk/core/stitching/labspec6_spectra_stitch.py +90 -0
  5. horiba_sdk/core/stitching/linear_spectra_stitch.py +107 -0
  6. horiba_sdk/core/stitching/simple_cut_spectra_stitch.py +84 -0
  7. horiba_sdk/core/stitching/spectra_stitch.py +16 -0
  8. horiba_sdk/core/stitching/y_displacement_spectra_stitch.py +87 -0
  9. horiba_sdk/core/trigger_input_polarity.py +6 -0
  10. horiba_sdk/devices/device_manager.py +19 -1
  11. horiba_sdk/devices/fake_icl_server.py +7 -0
  12. horiba_sdk/devices/fake_responses/spectracq3.json +217 -0
  13. horiba_sdk/devices/single_devices/__init__.py +2 -1
  14. horiba_sdk/devices/single_devices/ccd.py +4 -2
  15. horiba_sdk/devices/single_devices/spectracq3.py +392 -0
  16. horiba_sdk/devices/spectracq3_discovery.py +55 -0
  17. {horiba_sdk-0.5.2.dist-info → horiba_sdk-0.6.0.dist-info}/METADATA +3 -1
  18. {horiba_sdk-0.5.2.dist-info → horiba_sdk-0.6.0.dist-info}/RECORD +20 -26
  19. horiba_sdk/sync/__init__.py +0 -0
  20. horiba_sdk/sync/communication/__init__.py +0 -7
  21. horiba_sdk/sync/communication/abstract_communicator.py +0 -47
  22. horiba_sdk/sync/communication/test_client.py +0 -16
  23. horiba_sdk/sync/communication/websocket_communicator.py +0 -232
  24. horiba_sdk/sync/devices/__init__.py +0 -15
  25. horiba_sdk/sync/devices/abstract_device_discovery.py +0 -17
  26. horiba_sdk/sync/devices/abstract_device_manager.py +0 -68
  27. horiba_sdk/sync/devices/device_discovery.py +0 -58
  28. horiba_sdk/sync/devices/device_manager.py +0 -213
  29. horiba_sdk/sync/devices/fake_device_manager.py +0 -91
  30. horiba_sdk/sync/devices/fake_icl_server.py +0 -82
  31. horiba_sdk/sync/devices/single_devices/__init__.py +0 -5
  32. horiba_sdk/sync/devices/single_devices/abstract_device.py +0 -87
  33. horiba_sdk/sync/devices/single_devices/ccd.py +0 -674
  34. horiba_sdk/sync/devices/single_devices/monochromator.py +0 -413
  35. {horiba_sdk-0.5.2.dist-info → horiba_sdk-0.6.0.dist-info}/LICENSE +0 -0
  36. {horiba_sdk-0.5.2.dist-info → horiba_sdk-0.6.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,217 @@
1
+ {
2
+ "saq3_discover": {
3
+ "id": 1234,
4
+ "command": "saq3_discover",
5
+ "results": {
6
+ "count": 1
7
+ },
8
+ "errors": []
9
+ },
10
+ "saq3_list": {
11
+ "id": 1234,
12
+ "command": "saq3_list",
13
+ "results": {
14
+ "devices": [
15
+ {
16
+ "deviceType": "SpectrAcq3",
17
+ "index": 0,
18
+ "serialNumber": "SAQ3-2025-001"
19
+ }
20
+ ]
21
+ },
22
+ "errors": []
23
+ },
24
+ "saq3_listCount": {
25
+ "id": 1234,
26
+ "command": "saq3_listCount",
27
+ "results": {
28
+ "count": 1
29
+ },
30
+ "errors": []
31
+ },
32
+ "saq3_open": {
33
+ "id": 1234,
34
+ "command": "saq3_open",
35
+ "errors": []
36
+ },
37
+ "saq3_close": {
38
+ "id": 1234,
39
+ "command": "saq3_close",
40
+ "errors": []
41
+ },
42
+ "saq3_isOpen": {
43
+ "id": 1234,
44
+ "command": "saq3_isOpen",
45
+ "results": {
46
+ "open": true
47
+ },
48
+ "errors": []
49
+ },
50
+ "saq3_isBusy": {
51
+ "id": 1234,
52
+ "command": "saq3_isBusy",
53
+ "results": {
54
+ "busy": false
55
+ },
56
+ "errors": []
57
+ },
58
+ "saq3_getFirmwareVersion": {
59
+ "id": 1234,
60
+ "command": "saq3_getFirmwareVersion",
61
+ "results": {
62
+ "version": "1.0.0"
63
+ },
64
+ "errors": []
65
+ },
66
+ "saq3_getFPGAVersion": {
67
+ "id": 1234,
68
+ "command": "saq3_getFPGAVersion",
69
+ "results": {
70
+ "version": "1.0.0"
71
+ },
72
+ "errors": []
73
+ },
74
+ "saq3_getBoardRevision": {
75
+ "id": 1234,
76
+ "command": "saq3_getBoardRevision",
77
+ "results": {
78
+ "revision": "Rev A"
79
+ },
80
+ "errors": []
81
+ },
82
+ "saq3_getSerialNumber": {
83
+ "id": 1234,
84
+ "command": "saq3_getSerialNumber",
85
+ "results": {
86
+ "serialNumber": "SAQ3-2025-001"
87
+ },
88
+ "errors": []
89
+ },
90
+ "saq3_setIntegrationTime": {
91
+ "id": 1234,
92
+ "command": "saq3_setIntegrationTime",
93
+ "errors": []
94
+ },
95
+ "saq3_getIntegrationTime": {
96
+ "id": 1234,
97
+ "command": "saq3_getIntegrationTime",
98
+ "results": {
99
+ "time": 100
100
+ },
101
+ "errors": []
102
+ },
103
+ "saq3_setHVBiasVoltage": {
104
+ "id": 1234,
105
+ "command": "saq3_setHVBiasVoltage",
106
+ "errors": []
107
+ },
108
+ "saq3_getHVBiasVoltage": {
109
+ "id": 1234,
110
+ "command": "saq3_getHVBiasVoltage",
111
+ "results": {
112
+ "voltage": 5.0
113
+ },
114
+ "errors": []
115
+ },
116
+ "saq3_getMaxHVVoltageAllowed": {
117
+ "id": 1234,
118
+ "command": "saq3_getMaxHVVoltageAllowed",
119
+ "results": {
120
+ "maxVoltage": 10.0
121
+ },
122
+ "errors": []
123
+ },
124
+ "saq3_defineAcqSet": {
125
+ "id": 1234,
126
+ "command": "saq3_defineAcqSet",
127
+ "errors": []
128
+ },
129
+ "saq3_acqStart": {
130
+ "id": 1234,
131
+ "command": "saq3_acqStart",
132
+ "errors": []
133
+ },
134
+ "saq3_acqStop": {
135
+ "id": 1234,
136
+ "command": "saq3_acqStop",
137
+ "errors": []
138
+ },
139
+ "saq3_acqPause": {
140
+ "id": 1234,
141
+ "command": "saq3_acqPause",
142
+ "errors": []
143
+ },
144
+ "saq3_acqContinue": {
145
+ "id": 1234,
146
+ "command": "saq3_acqContinue",
147
+ "errors": []
148
+ },
149
+ "saq3_getAvailableData": {
150
+ "id": 1234,
151
+ "command": "saq3_getAvailableData",
152
+ "results": {
153
+ "data": [0, 1, 2, 3, 4]
154
+ },
155
+ "errors": []
156
+ },
157
+ "saq3_isDataAvailable": {
158
+ "id": 1234,
159
+ "command": "saq3_isDataAvailable",
160
+ "results": {
161
+ "available": true
162
+ },
163
+ "errors": []
164
+ },
165
+ "saq3_forceTrigger": {
166
+ "id": 1234,
167
+ "command": "saq3_forceTrigger",
168
+ "errors": []
169
+ },
170
+ "saq3_setTriggerInPolarity": {
171
+ "id": 1234,
172
+ "command": "saq3_setTriggerInPolarity",
173
+ "errors": []
174
+ },
175
+ "saq3_getTriggerInPolarity": {
176
+ "id": 1234,
177
+ "command": "saq3_getTriggerInPolarity",
178
+ "results": {
179
+ "polarity": "active_high"
180
+ },
181
+ "errors": []
182
+ },
183
+ "saq3_setInTriggerMode": {
184
+ "id": 1234,
185
+ "command": "saq3_setInTriggerMode",
186
+ "errors": []
187
+ },
188
+ "saq3_getInTriggerMode": {
189
+ "id": 1234,
190
+ "command": "saq3_getInTriggerMode",
191
+ "results": {
192
+ "mode": "continuous"
193
+ },
194
+ "errors": []
195
+ },
196
+ "saq3_getLastError": {
197
+ "id": 1234,
198
+ "command": "saq3_getLastError",
199
+ "results": {
200
+ "error": "No error"
201
+ },
202
+ "errors": []
203
+ },
204
+ "saq3_getErrorLog": {
205
+ "id": 1234,
206
+ "command": "saq3_getErrorLog",
207
+ "results": {
208
+ "log": []
209
+ },
210
+ "errors": []
211
+ },
212
+ "saq3_clearErrorLog": {
213
+ "id": 1234,
214
+ "command": "saq3_clearErrorLog",
215
+ "errors": []
216
+ }
217
+ }
@@ -1,5 +1,6 @@
1
1
  from .abstract_device import AbstractDevice
2
2
  from .ccd import ChargeCoupledDevice
3
3
  from .monochromator import Monochromator
4
+ from .spectracq3 import SpectrAcq3
4
5
 
5
- __all__ = ['AbstractDevice', 'Monochromator', 'ChargeCoupledDevice']
6
+ __all__ = ['AbstractDevice', 'Monochromator', 'ChargeCoupledDevice', 'SpectrAcq3']
@@ -618,7 +618,8 @@ class ChargeCoupledDevice(AbstractDevice):
618
618
  acquisitions have completed, therefore the same timestamp is used for all acquisitions.
619
619
  """
620
620
  response: Response = await super()._execute_command('ccd_getAcquisitionData', {'index': self._id})
621
- return response.results['acquisition']
621
+ results: dict[Any, Any] = response.results
622
+ return results
622
623
 
623
624
  async def set_center_wavelength(self, mono_index: float, center_wavelength: float) -> None:
624
625
  """Sets the center wavelength value to be used in the grating equation.
@@ -668,7 +669,8 @@ class ChargeCoupledDevice(AbstractDevice):
668
669
  'overlap': pixel_overlap,
669
670
  },
670
671
  )
671
- return response.results['centerWavelengths']
672
+ center_wavelengths: list[float] = response.results['centerWavelengths']
673
+ return center_wavelengths
672
674
 
673
675
  @staticmethod
674
676
  async def raman_convert(spectrum: list[float], excitation_wavelength: float) -> list[float]:
@@ -0,0 +1,392 @@
1
+ from types import TracebackType
2
+ from typing import Optional, final
3
+
4
+ from loguru import logger
5
+ from pydantic import BaseModel
6
+
7
+ from horiba_sdk.communication import AbstractCommunicator, Response
8
+
9
+ from ...core.trigger_input_polarity import TriggerInputPolarity
10
+ from ...icl_error import AbstractErrorDB
11
+ from .abstract_device import AbstractDevice
12
+
13
+
14
+ class BaseResults(BaseModel):
15
+ pass
16
+
17
+
18
+ # Define a model for the saq3_getAvailableData results
19
+ class Signal(BaseModel):
20
+ unit: str
21
+ value: float
22
+
23
+
24
+ class DataPoint(BaseModel):
25
+ currentSignal: Signal
26
+ elapsedTime: int
27
+ eventMarker: bool
28
+ overscaleCurrentChannel: bool
29
+ overscaleVoltageChannel: bool
30
+ pmtSignal: Signal
31
+ pointNumber: int
32
+ ppdSignal: Signal
33
+ voltageSignal: Signal
34
+
35
+
36
+ class AvailableDataResults(BaseModel):
37
+ data: list[DataPoint]
38
+
39
+
40
+ class AcqSetResults(BaseResults):
41
+ scanCount: int
42
+ timeStep: int
43
+ integrationTime: int
44
+ externalParam: int
45
+
46
+
47
+ @final
48
+ class SpectrAcq3(AbstractDevice):
49
+ """
50
+ SpectrAcq3 device class.
51
+
52
+ This class represents the SpectrAcq3 - Single Channel Detector Interface. It provides methods to open and close
53
+ the device connection and retrieve the device's serial number. The focus is on ensuring reliable communication
54
+ with the device and handling any potential errors gracefully.
55
+ """
56
+
57
+ def __init__(self, device_id: int, communicator: AbstractCommunicator, error_db: AbstractErrorDB) -> None:
58
+ super().__init__(device_id, communicator, error_db)
59
+
60
+ async def __aenter__(self) -> 'SpectrAcq3':
61
+ await self.open()
62
+ return self
63
+
64
+ async def __aexit__(
65
+ self, exc_type: type[BaseException], exc_value: BaseException, traceback: Optional[TracebackType]
66
+ ) -> None:
67
+ is_open = await self.is_open()
68
+ if not is_open:
69
+ logger.debug('SpectrAcq3 is already closed')
70
+ return
71
+
72
+ await self.close()
73
+
74
+ async def open(self) -> None:
75
+ """
76
+ Open a connection to the SpectrAcq3 device.
77
+
78
+ This method sends a command to the device to establish a connection. It is crucial to ensure that the device
79
+ is ready for communication before attempting any operations, to prevent errors and data loss.
80
+ """
81
+ await self._execute_command('saq3_open', {'index': self._id})
82
+
83
+ async def close(self) -> None:
84
+ """
85
+ Close the connection to the SpectrAcq3 device.
86
+
87
+ This method sends a command to safely terminate the connection with the device. Properly closing the connection
88
+ helps in freeing up resources and maintaining the device's integrity for future operations.
89
+ """
90
+ await self._execute_command('saq3_close', {'index': self._id})
91
+
92
+ async def is_open(self) -> bool:
93
+ """
94
+ Check if the connection to the SpectrAcq3 device is open.
95
+
96
+ This method checks the status of the device connection to determine if it is open or closed. It is useful for
97
+ verifying the device's state before performing any operations that require an active connection.
98
+
99
+ Returns:
100
+ bool: True if the connection is open, False otherwise.
101
+ """
102
+ response: Response = await self._execute_command('saq3_isOpen', {'index': self._id})
103
+ is_open: bool = response.results['open']
104
+ return is_open
105
+
106
+ async def is_busy(self) -> bool:
107
+ """
108
+ Check if the device is busy.
109
+
110
+ Returns:
111
+ bool: True if the device is busy, False otherwise.
112
+ """
113
+ response: Response = await self._execute_command('saq3_isBusy', {'index': self._id})
114
+ is_busy: bool = response.results['isBusy']
115
+ return is_busy
116
+
117
+ async def get_serial_number(self) -> str:
118
+ """
119
+ Retrieve the serial number of the SpectrAcq3 device.
120
+
121
+ This method sends a command to the device to fetch its serial number. Knowing the serial number is essential
122
+ for device identification and tracking, especially in environments with multiple devices.
123
+
124
+ Returns:
125
+ str: The serial number of the device.
126
+ """
127
+ response: Response = await self._execute_command('saq3_getSerialNumber', {'index': self._id})
128
+ serial_number: str = response.results['serialNumber']
129
+ return serial_number
130
+
131
+ async def get_firmware_version(self) -> str:
132
+ """
133
+ Get the firmware version of the device.
134
+
135
+ This method sends a command to the device to fetch its firmware version.
136
+
137
+ Returns:
138
+ str: The firmware version of the device.
139
+ """
140
+ response: Response = await self._execute_command('saq3_getFirmwareVersion', {'index': self._id})
141
+ firmware_version: str = response.results['firmwareVersion']
142
+ return firmware_version
143
+
144
+ async def get_fpga_version(self) -> str:
145
+ """
146
+ Get the FPGA version of the device.
147
+
148
+ This method sends a command to the device to fetch its FPGA version.
149
+
150
+ Returns:
151
+ str: The FPGA version of the device.
152
+ """
153
+ response: Response = await self._execute_command('saq3_getFPGAVersion', {'index': self._id})
154
+ fpga_version: str = response.results['FpgaVersion']
155
+ return fpga_version
156
+
157
+ async def get_board_revision(self) -> str:
158
+ """
159
+ Get the board revision of the device.
160
+
161
+ This method sends a command to the device to fetch its board revision.
162
+
163
+ Returns:
164
+ str: The board revision of the device.
165
+ """
166
+ response: Response = await self._execute_command('saq3_getBoardRevision', {'index': self._id})
167
+ board_revision: str = response.results['boardRevision']
168
+ return board_revision
169
+
170
+ async def set_hv_bias_voltage(self, bias_voltage: int) -> None:
171
+ """
172
+ Set the high bias voltage.
173
+
174
+ This method sends a command to the device to set the high bias voltage.
175
+
176
+ Args:
177
+ bias_voltage (int): The high bias voltage in volts.
178
+ """
179
+ await self._execute_command('saq3_setHVBiasVoltage', {'index': self._id, 'biasVoltage': bias_voltage})
180
+
181
+ async def get_hv_bias_voltage(self) -> int:
182
+ """
183
+ Get the high bias voltage that was previously set.
184
+
185
+ This method sends a command to the device to fetch the high bias voltage.
186
+
187
+ Returns:
188
+ int: The high bias voltage in volts.
189
+ """
190
+ response: Response = await self._execute_command('saq3_getHVBiasVoltage', {'index': self._id})
191
+ hv_bias_voltage: int = response.results['biasVoltage']
192
+ return hv_bias_voltage
193
+
194
+ async def get_max_hv_voltage_allowed(self) -> int:
195
+ """
196
+ Get the maximum high bias voltage allowed.
197
+
198
+ This method sends a command to the device to fetch the maximum high bias voltage allowed.
199
+
200
+ Returns:
201
+ int: The maximum high bias voltage in volts.
202
+ """
203
+ response: Response = await self._execute_command('saq3_getMaxHVVoltageAllowed', {'index': self._id})
204
+ max_hv_voltage: int = response.results['biasVoltage']
205
+ return max_hv_voltage
206
+
207
+ async def set_acq_set(self, scan_count: int, time_step: int, integration_time: int, external_param: int) -> None:
208
+ """
209
+ Define the acquisition set parameters.
210
+
211
+ This method sends a command to the device to define the acquisition set parameters.
212
+
213
+ Args:
214
+ scan_count (int): Number of acquisitions to perform.
215
+ time_step (int): Time interval in seconds between acquisitions.
216
+ integration_time (int): Integration time in seconds.
217
+ external_param (int): User defined value.
218
+ """
219
+ await self._execute_command(
220
+ 'saq3_setAcqSet',
221
+ {
222
+ 'index': self._id,
223
+ 'scanCount': scan_count,
224
+ 'timeStep': time_step,
225
+ 'integrationTime': integration_time,
226
+ 'externalParam': external_param,
227
+ },
228
+ )
229
+
230
+ async def get_acq_set(self) -> dict:
231
+ """
232
+ Get the acquisition set parameters.
233
+
234
+ This method sends a command to the device to fetch the acquisition set parameters.
235
+
236
+ Returns:
237
+ dict: The acquisition set parameters.
238
+ """
239
+ response: Response = await self._execute_command('saq3_getAcqSet', {'index': self._id})
240
+ acq_set: dict = response.results
241
+ return acq_set
242
+
243
+ async def acq_start(self, trigger: int) -> None:
244
+ """
245
+ Start the acquisition.
246
+
247
+ Trigger Modes:
248
+ >| Mode | description |
249
+ >|1| 1st data started on Start command, all subsequent data acquired based on interval time|
250
+ >|2| 1st data started by Trigger after start Command, all subsequent data acquired based on interval time |
251
+ >|3| Each data acquisition waits for Trigger
252
+
253
+ Args:
254
+ trigger (int): Integer indicating the trigger mode.
255
+ """
256
+ await self._execute_command('saq3_acqStart', {'index': self._id, 'trigger': trigger})
257
+
258
+ async def acq_stop(self) -> None:
259
+ """
260
+ Stop the current acquisition.
261
+
262
+ This method sends a command to the device to stop the current acquisition.
263
+ """
264
+ await self._execute_command('saq3_acqStop', {'index': self._id})
265
+
266
+ async def acq_pause(self) -> None:
267
+ """
268
+ Pause the active acquisition.
269
+
270
+ This method sends a command to the device to pause the active acquisition.
271
+ """
272
+ await self._execute_command('saq3_acqPause', {'index': self._id})
273
+
274
+ async def acq_continue(self) -> None:
275
+ """
276
+ Continue a paused acquisition.
277
+
278
+ This method sends a command to the device to continue a paused acquisition.
279
+ """
280
+ await self._execute_command('saq3_acqContinue', {'index': self._id})
281
+
282
+ async def is_data_available(self) -> bool:
283
+ """
284
+ Check whether the acquired data is available.
285
+
286
+ This method sends a command to the device to check if the acquired data is available.
287
+
288
+ Returns:
289
+ bool: True if data is available, False otherwise.
290
+ """
291
+ response: Response = await self._execute_command('saq3_isDataAvailable', {'index': self._id})
292
+ is_data_available: bool = response.results['isDataAvailable']
293
+ return is_data_available
294
+
295
+ async def get_available_data(self) -> AvailableDataResults:
296
+ """
297
+ Retrieve the acquired data that is available so far.
298
+
299
+ This method sends a command to the device to retrieve the acquired data.
300
+
301
+ Returns:
302
+ list: The acquired data.
303
+ """
304
+ response: Response = await self._execute_command('saq3_getAvailableData', {'index': self._id})
305
+ available_data: AvailableDataResults = response.results['data']
306
+ return available_data
307
+
308
+ async def force_trigger(self) -> None:
309
+ """
310
+ Force a software trigger.
311
+
312
+ This method sends a command to the device to force a software trigger.
313
+ """
314
+ await self._execute_command('saq3_forceTrigger', {'index': self._id})
315
+
316
+ async def set_trigger_in_polarity(self, polarity: TriggerInputPolarity) -> None:
317
+ """
318
+ Set the input trigger polarity.
319
+
320
+ Args:
321
+ polarity (TriggerInputPolarity): Input trigger polarity (ACTIVE_LOW or ACTIVE_HIGH).
322
+ """
323
+ await self._execute_command('saq3_setTriggerInPolarity', {'index': self._id, 'polarity': polarity.value})
324
+
325
+ async def get_trigger_in_polarity(self) -> int:
326
+ """
327
+ Get the input trigger polarity.
328
+
329
+ Returns:
330
+ int: Input trigger polarity (0: Active Low, 1: Active High).
331
+ """
332
+ response: Response = await self._execute_command('saq3_getTriggerInPolarity', {'index': self._id})
333
+ polarity: int = response.results['polarity']
334
+ return polarity
335
+
336
+ async def set_in_trigger_mode(self, mode: int) -> None:
337
+ """
338
+ Set the hardware trigger pin mode.
339
+
340
+ This method sends a command to the device to set the hardware trigger pin mode.
341
+
342
+ Args:
343
+ mode (int): Mode of hardware trigger pin.
344
+ """
345
+ await self._execute_command('saq3_setInTriggerMode', {'index': self._id, 'mode': mode})
346
+
347
+ async def get_trigger_mode(self) -> dict:
348
+ """
349
+ Get the trigger mode.
350
+
351
+ This method sends a command to the device to fetch the trigger mode.
352
+
353
+ Returns:
354
+ dict: The trigger mode settings.
355
+ """
356
+ response: Response = await self._execute_command('saq3_getInTriggerMode', {'index': self._id})
357
+ trigger_mode: dict = response.results
358
+ return trigger_mode
359
+
360
+ async def get_last_error(self) -> str:
361
+ """
362
+ Get the last error.
363
+
364
+ This method sends a command to the device to fetch the last error.
365
+
366
+ Returns:
367
+ str: The last error message.
368
+ """
369
+ response: Response = await self._execute_command('saq3_getLastError', {'index': self._id})
370
+ last_error: str = response.results['error']
371
+ return last_error
372
+
373
+ async def get_error_log(self) -> list[str]:
374
+ """
375
+ Get the error log.
376
+
377
+ This method sends a command to the device to fetch the error log.
378
+
379
+ Returns:
380
+ str: The error log.
381
+ """
382
+ response: Response = await self._execute_command('saq3_getErrorLog', {'index': self._id})
383
+ error_log: list[str] = response.results['errors']
384
+ return error_log
385
+
386
+ async def clear_error_log(self) -> None:
387
+ """
388
+ Clear all the errors in the log.
389
+
390
+ This method sends a command to the device to clear the error log.
391
+ """
392
+ await self._execute_command('saq3_clearErrorLog', {'index': self._id})
@@ -0,0 +1,55 @@
1
+ from typing import Any, final
2
+
3
+ from loguru import logger
4
+ from overrides import override
5
+
6
+ from horiba_sdk.communication import AbstractCommunicator, Command, Response
7
+ from horiba_sdk.devices.abstract_device_discovery import AbstractDeviceDiscovery
8
+ from horiba_sdk.devices.single_devices import SpectrAcq3
9
+ from horiba_sdk.icl_error import AbstractErrorDB
10
+
11
+
12
+ @final
13
+ class SpectrAcq3Discovery(AbstractDeviceDiscovery):
14
+ def __init__(self, communicator: AbstractCommunicator, error_db: AbstractErrorDB):
15
+ self._communicator: AbstractCommunicator = communicator
16
+ self._spectracq3_devices: list[SpectrAcq3] = []
17
+ self._error_db: AbstractErrorDB = error_db
18
+
19
+ @override
20
+ async def execute(self, error_on_no_device: bool = False) -> None:
21
+ """
22
+ Discovers the connected SpectrAcq3 devices and saves them internally.
23
+
24
+ Raises:
25
+ Exception: When no SpectrAcq3 devices are discovered and that `error_on_no_device` is set.
26
+ Or when there is an issue parsing the SpectrAcq3 devices list
27
+ """
28
+ if not self._communicator.opened():
29
+ await self._communicator.open()
30
+
31
+ response: Response = await self._communicator.request_with_response(Command('saq3_discover', {}))
32
+ if response.results.get('count', 0) == 0 and error_on_no_device:
33
+ raise Exception('No SpectrAcq3 devices connected')
34
+
35
+ response = await self._communicator.request_with_response(Command('saq3_list', {}))
36
+
37
+ raw_device_list = response.results
38
+ self._spectracq3_devices = self._parse_devices(raw_device_list)
39
+ logger.info(f'Found {len(self._spectracq3_devices)} SpectrAcq3 devices')
40
+
41
+ def _parse_devices(self, raw_device_list: dict[str, Any]) -> list[SpectrAcq3]:
42
+ detected_devices: list[SpectrAcq3] = []
43
+ for device in raw_device_list['devices']:
44
+ try:
45
+ logger.debug(f'Parsing SpectrAcq3: {device}')
46
+ spectracq3 = SpectrAcq3(device['index'], self._communicator, self._error_db)
47
+ logger.info(f'Detected SpectrAcq3: {device["deviceType"]}')
48
+ detected_devices.append(spectracq3)
49
+ except Exception as e:
50
+ logger.error(f'Error while parsing SpectrAcq3: {e}')
51
+
52
+ return detected_devices
53
+
54
+ def spectracq3_devices(self) -> list[SpectrAcq3]:
55
+ return self._spectracq3_devices