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.
- horiba_sdk/communication/messages.py +5 -2
- horiba_sdk/communication/websocket_communicator.py +1 -1
- horiba_sdk/core/stitching/__init__.py +6 -0
- horiba_sdk/core/stitching/labspec6_spectra_stitch.py +90 -0
- horiba_sdk/core/stitching/linear_spectra_stitch.py +107 -0
- horiba_sdk/core/stitching/simple_cut_spectra_stitch.py +84 -0
- horiba_sdk/core/stitching/spectra_stitch.py +16 -0
- horiba_sdk/core/stitching/y_displacement_spectra_stitch.py +87 -0
- horiba_sdk/core/trigger_input_polarity.py +6 -0
- horiba_sdk/devices/device_manager.py +19 -1
- horiba_sdk/devices/fake_icl_server.py +7 -0
- horiba_sdk/devices/fake_responses/spectracq3.json +217 -0
- horiba_sdk/devices/single_devices/__init__.py +2 -1
- horiba_sdk/devices/single_devices/ccd.py +4 -2
- horiba_sdk/devices/single_devices/spectracq3.py +392 -0
- horiba_sdk/devices/spectracq3_discovery.py +55 -0
- {horiba_sdk-0.5.2.dist-info → horiba_sdk-0.6.0.dist-info}/METADATA +3 -1
- {horiba_sdk-0.5.2.dist-info → horiba_sdk-0.6.0.dist-info}/RECORD +20 -26
- horiba_sdk/sync/__init__.py +0 -0
- horiba_sdk/sync/communication/__init__.py +0 -7
- horiba_sdk/sync/communication/abstract_communicator.py +0 -47
- horiba_sdk/sync/communication/test_client.py +0 -16
- horiba_sdk/sync/communication/websocket_communicator.py +0 -232
- horiba_sdk/sync/devices/__init__.py +0 -15
- horiba_sdk/sync/devices/abstract_device_discovery.py +0 -17
- horiba_sdk/sync/devices/abstract_device_manager.py +0 -68
- horiba_sdk/sync/devices/device_discovery.py +0 -58
- horiba_sdk/sync/devices/device_manager.py +0 -213
- horiba_sdk/sync/devices/fake_device_manager.py +0 -91
- horiba_sdk/sync/devices/fake_icl_server.py +0 -82
- horiba_sdk/sync/devices/single_devices/__init__.py +0 -5
- horiba_sdk/sync/devices/single_devices/abstract_device.py +0 -87
- horiba_sdk/sync/devices/single_devices/ccd.py +0 -674
- horiba_sdk/sync/devices/single_devices/monochromator.py +0 -413
- {horiba_sdk-0.5.2.dist-info → horiba_sdk-0.6.0.dist-info}/LICENSE +0 -0
- {horiba_sdk-0.5.2.dist-info → horiba_sdk-0.6.0.dist-info}/WHEEL +0 -0
@@ -1,213 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Synchronous Device Manager Module
|
3
|
-
|
4
|
-
This Device Manager uses threads instead of asyncio
|
5
|
-
"""
|
6
|
-
|
7
|
-
import importlib.resources
|
8
|
-
import platform
|
9
|
-
import subprocess
|
10
|
-
from pathlib import Path
|
11
|
-
from subprocess import Popen
|
12
|
-
from typing import Optional, final
|
13
|
-
|
14
|
-
import psutil
|
15
|
-
from loguru import logger
|
16
|
-
from overrides import override
|
17
|
-
|
18
|
-
from horiba_sdk.communication import Command, CommunicationException, Response
|
19
|
-
from horiba_sdk.icl_error import AbstractError, AbstractErrorDB, ICLErrorDB
|
20
|
-
from horiba_sdk.sync.communication import AbstractCommunicator, WebsocketCommunicator
|
21
|
-
from horiba_sdk.sync.devices import AbstractDeviceManager, DeviceDiscovery
|
22
|
-
from horiba_sdk.sync.devices.single_devices import ChargeCoupledDevice, Monochromator
|
23
|
-
|
24
|
-
|
25
|
-
@final
|
26
|
-
class DeviceManager(AbstractDeviceManager):
|
27
|
-
"""
|
28
|
-
DeviceManager class manages the lifecycle and interactions with devices.
|
29
|
-
"""
|
30
|
-
|
31
|
-
def __init__(
|
32
|
-
self,
|
33
|
-
start_icl: bool = True,
|
34
|
-
icl_ip: str = '127.0.0.1',
|
35
|
-
icl_port: str = '25010',
|
36
|
-
enable_binary_messages: bool = True,
|
37
|
-
):
|
38
|
-
"""
|
39
|
-
Initializes the DeviceManager with the specified communicator class.
|
40
|
-
|
41
|
-
Args:
|
42
|
-
start_icl (bool) = True: If True, the ICL software is started and communication is established.
|
43
|
-
icl_ip (str) = '127.0.0.1': websocket IP
|
44
|
-
icl_port (str) = '25010': websocket port
|
45
|
-
enable_binary_messages (bool) = True: If True, binary messages are enabled.
|
46
|
-
"""
|
47
|
-
super().__init__()
|
48
|
-
self._start_icl = start_icl
|
49
|
-
self._icl_communicator: WebsocketCommunicator = WebsocketCommunicator('ws://' + icl_ip + ':' + str(icl_port))
|
50
|
-
self._icl_websocket_ip: str = icl_ip
|
51
|
-
self._icl_websocket_port: str = icl_port
|
52
|
-
self._icl_process: Optional[Popen[bytes]] = None
|
53
|
-
self._binary_messages: bool = enable_binary_messages
|
54
|
-
self._charge_coupled_devices: list[ChargeCoupledDevice] = []
|
55
|
-
self._monochromators: list[Monochromator] = []
|
56
|
-
|
57
|
-
error_list_path: Path = Path(str(importlib.resources.files('horiba_sdk.icl_error') / 'error_list.json'))
|
58
|
-
self._icl_error_db: AbstractErrorDB = ICLErrorDB(error_list_path)
|
59
|
-
|
60
|
-
@override
|
61
|
-
def start(self) -> None:
|
62
|
-
if self._start_icl:
|
63
|
-
self.start_icl()
|
64
|
-
|
65
|
-
self._icl_communicator.register_binary_message_callback(self._binary_message_callback)
|
66
|
-
self._icl_communicator.open()
|
67
|
-
|
68
|
-
icl_info: Response = self._icl_communicator.request_with_response(Command('icl_info', {}))
|
69
|
-
logger.info(f'ICL info: {icl_info.results}')
|
70
|
-
|
71
|
-
if self._binary_messages:
|
72
|
-
self._enable_binary_messages()
|
73
|
-
|
74
|
-
self.discover_devices()
|
75
|
-
|
76
|
-
@override
|
77
|
-
def stop(self) -> None:
|
78
|
-
if self._start_icl:
|
79
|
-
self.stop_icl()
|
80
|
-
return
|
81
|
-
|
82
|
-
if self._icl_communicator.opened():
|
83
|
-
self._icl_communicator.close()
|
84
|
-
|
85
|
-
def start_icl(self) -> None:
|
86
|
-
"""
|
87
|
-
Starts the ICL software and establishes communication.
|
88
|
-
"""
|
89
|
-
logger.info('Starting ICL software...')
|
90
|
-
# try:
|
91
|
-
if platform.system() != 'Windows':
|
92
|
-
logger.info('Only Windows is supported for ICL software. Skip starting of ICL...')
|
93
|
-
return
|
94
|
-
|
95
|
-
icl_running = 'icl.exe' in (p.name() for p in psutil.process_iter())
|
96
|
-
if not icl_running:
|
97
|
-
logger.info('icl not running, starting it...')
|
98
|
-
try:
|
99
|
-
self._icl_process = subprocess.Popen([r'C:\Program Files\HORIBA Scientific\SDK\icl.exe'])
|
100
|
-
except subprocess.CalledProcessError as error:
|
101
|
-
logger.error('Failed to start ICL software.')
|
102
|
-
raise Exception('Failed to start ICL software') from error
|
103
|
-
|
104
|
-
def _enable_binary_messages(self) -> None:
|
105
|
-
bin_mode_command: Command = Command('icl_binMode', {'mode': 'all'})
|
106
|
-
response: Response = self._icl_communicator.request_with_response(bin_mode_command)
|
107
|
-
|
108
|
-
if response.errors:
|
109
|
-
self._handle_errors(response.errors)
|
110
|
-
|
111
|
-
def _handle_errors(self, errors: list[str]) -> None:
|
112
|
-
for error in errors:
|
113
|
-
icl_error: AbstractError = self._icl_error_db.error_from(error)
|
114
|
-
icl_error.log()
|
115
|
-
# TODO: [saga] only throw depending on the log level, tbd
|
116
|
-
raise Exception(f'Error from the ICL: {icl_error.message()}')
|
117
|
-
|
118
|
-
def stop_icl(self) -> None:
|
119
|
-
"""
|
120
|
-
Stops the communication and cleans up resources.
|
121
|
-
"""
|
122
|
-
logger.info('Requesting shutdown of ICL...')
|
123
|
-
|
124
|
-
if not self._icl_communicator.opened():
|
125
|
-
self._icl_communicator.open()
|
126
|
-
|
127
|
-
try:
|
128
|
-
info_command: Command = Command('icl_info', {})
|
129
|
-
response: Response = self._icl_communicator.request_with_response(info_command)
|
130
|
-
|
131
|
-
logger.info(f'ICL info: {response.results}')
|
132
|
-
shutdown_command: Command = Command('icl_shutdown', {})
|
133
|
-
# _response: Response = self._icl_communicator.request_with_response(shutdown_command, timeout=10)
|
134
|
-
_response: Response = self._icl_communicator.request_with_response(shutdown_command)
|
135
|
-
except CommunicationException as e:
|
136
|
-
logger.debug(f'CommunicationException: {e.message}')
|
137
|
-
|
138
|
-
if self._icl_communicator.opened():
|
139
|
-
self._icl_communicator.close()
|
140
|
-
|
141
|
-
if self._icl_process is not None:
|
142
|
-
self._icl_process.terminate()
|
143
|
-
icl_running = 'icl.exe' in (p.name() for p in psutil.process_iter())
|
144
|
-
if icl_running:
|
145
|
-
raise Exception('Failed to shutdown ICL software.')
|
146
|
-
|
147
|
-
logger.info('icl_shutdown command sent')
|
148
|
-
|
149
|
-
def _format_icl_binary_to_string(self, message: bytes) -> str:
|
150
|
-
return ' '.join(format(byte, '02x') for byte in message[::-1])
|
151
|
-
|
152
|
-
def _binary_message_callback(self, message: bytes) -> None:
|
153
|
-
# hex_data = ' '.join(format(byte, '02x') for byte in message)
|
154
|
-
# if len(message) < 18:
|
155
|
-
# logger.warning(f'binary message not valid: {len(message)} < 16')
|
156
|
-
# logger.info(f'Received binary message: {hex_data}')
|
157
|
-
# logger.info(f'magic number: {self._format_icl_binary_to_string(message[:2])}')
|
158
|
-
# logger.info(f'message type: {self._format_icl_binary_to_string(message[2:4])}')
|
159
|
-
# logger.info(f'element type: {self._format_icl_binary_to_string(message[4:6])}')
|
160
|
-
# logger.info(f'element count: {self._format_icl_binary_to_string(message[6:10])}')
|
161
|
-
# logger.info(f'tag 1: {self._format_icl_binary_to_string(message[10:12])}')
|
162
|
-
# logger.info(f'tag 2: {self._format_icl_binary_to_string(message[12:14])}')
|
163
|
-
# logger.info(f'tag 3: {self._format_icl_binary_to_string(message[14:16])}')
|
164
|
-
# logger.info(f'tag 4: {self._format_icl_binary_to_string(message[16:18])}')
|
165
|
-
# logger.info(f'payload: {self._format_icl_binary_to_string(message[18:])}')
|
166
|
-
# logger.info(f'payload as string: {str(message[18:])}')
|
167
|
-
pass
|
168
|
-
|
169
|
-
@override
|
170
|
-
def discover_devices(self, error_on_no_device: bool = False) -> None:
|
171
|
-
"""
|
172
|
-
Discovers the connected devices and saves them internally.
|
173
|
-
|
174
|
-
Args:
|
175
|
-
error_on_no_device (bool): If True, an exception is raised if no device is connected.
|
176
|
-
"""
|
177
|
-
device_discovery: DeviceDiscovery = DeviceDiscovery(self._icl_communicator, self._icl_error_db)
|
178
|
-
device_discovery.execute(error_on_no_device)
|
179
|
-
self._charge_coupled_devices = device_discovery.charge_coupled_devices()
|
180
|
-
self._monochromators = device_discovery.monochromators()
|
181
|
-
|
182
|
-
@property
|
183
|
-
@override
|
184
|
-
def communicator(self) -> AbstractCommunicator:
|
185
|
-
"""
|
186
|
-
Getter method for the communicator attribute.
|
187
|
-
|
188
|
-
Returns:
|
189
|
-
horiba_sdk.communication.AbstractCommunicator: Returns a new communicator instance.
|
190
|
-
"""
|
191
|
-
return self._icl_communicator
|
192
|
-
|
193
|
-
@property
|
194
|
-
@override
|
195
|
-
def monochromators(self) -> list[Monochromator]:
|
196
|
-
"""
|
197
|
-
The detected monochromators, should be called after :meth:`discover_devices`
|
198
|
-
|
199
|
-
Returns:
|
200
|
-
List[Monochromator]: The detected monochromators
|
201
|
-
"""
|
202
|
-
return self._monochromators
|
203
|
-
|
204
|
-
@property
|
205
|
-
@override
|
206
|
-
def charge_coupled_devices(self) -> list[ChargeCoupledDevice]:
|
207
|
-
"""
|
208
|
-
The detected CCDs, should be called after :meth:`discover_devices`
|
209
|
-
|
210
|
-
Returns:
|
211
|
-
List[ChargeCoupledDevice]: The detected CCDS.
|
212
|
-
"""
|
213
|
-
return self._charge_coupled_devices
|
@@ -1,91 +0,0 @@
|
|
1
|
-
from typing import final
|
2
|
-
|
3
|
-
from overrides import override
|
4
|
-
|
5
|
-
from horiba_sdk.icl_error import FakeErrorDB
|
6
|
-
from horiba_sdk.sync.communication.websocket_communicator import WebsocketCommunicator
|
7
|
-
from horiba_sdk.sync.devices.abstract_device_manager import AbstractDeviceManager
|
8
|
-
from horiba_sdk.sync.devices.single_devices import ChargeCoupledDevice, Monochromator
|
9
|
-
|
10
|
-
|
11
|
-
@final
|
12
|
-
class FakeDeviceManager(AbstractDeviceManager):
|
13
|
-
"""
|
14
|
-
The FakeDeviceManager represents a `horiba_sdk.sync.devices.DeviceManager` that can be used in the unit tests.
|
15
|
-
|
16
|
-
The class should be used in a pytest fixture as follows::
|
17
|
-
|
18
|
-
fake_icl_host: str = 'localhost'
|
19
|
-
fake_icl_port: int = 8765
|
20
|
-
|
21
|
-
@pytest.fixture(scope='module')
|
22
|
-
def fake_sync_icl_exe(): # noqa: ARG001
|
23
|
-
sync_server = FakeSyncICLServer(fake_icl_host=fake_icl_host, fake_icl_port=fake_icl_port)
|
24
|
-
thread = threading.Thread(target=sync_server.start)
|
25
|
-
thread.start()
|
26
|
-
|
27
|
-
yield thread
|
28
|
-
|
29
|
-
sync_server.stop()
|
30
|
-
thread.join()
|
31
|
-
|
32
|
-
|
33
|
-
@pytest.fixture(scope='module')
|
34
|
-
def fake_sync_device_manager(): # noqa: ARG001
|
35
|
-
fake_device_manager = FakeSyncDeviceManager(host=fake_icl_host, port=fake_icl_port)
|
36
|
-
fake_device_manager.start()
|
37
|
-
|
38
|
-
yield fake_device_manager
|
39
|
-
fake_device_manager.stop()
|
40
|
-
|
41
|
-
def your_unit_test(fake_sync_icl_exe, fake_sync_device_manager):
|
42
|
-
pass
|
43
|
-
|
44
|
-
"""
|
45
|
-
|
46
|
-
def __init__(self, host: str = '127.0.0.1', port: int = 25011):
|
47
|
-
self.host = host
|
48
|
-
self.port = port
|
49
|
-
self.error_db: FakeErrorDB = FakeErrorDB()
|
50
|
-
self.websocket_communicator = WebsocketCommunicator('ws://' + self.host + ':' + str(self.port))
|
51
|
-
|
52
|
-
def start(self) -> None:
|
53
|
-
self.websocket_communicator.open()
|
54
|
-
|
55
|
-
def stop(self) -> None:
|
56
|
-
self.websocket_communicator.close()
|
57
|
-
|
58
|
-
@override
|
59
|
-
def discover_devices(self, error_on_no_device: bool = False) -> None:
|
60
|
-
"""
|
61
|
-
Does nothing.
|
62
|
-
"""
|
63
|
-
pass
|
64
|
-
|
65
|
-
@property
|
66
|
-
@override
|
67
|
-
def communicator(self) -> WebsocketCommunicator:
|
68
|
-
"""Communicator"""
|
69
|
-
return self.websocket_communicator
|
70
|
-
|
71
|
-
@property
|
72
|
-
@override
|
73
|
-
def monochromators(self) -> list[Monochromator]:
|
74
|
-
"""
|
75
|
-
Abstract method to get the detected monochromators.
|
76
|
-
|
77
|
-
Returns:
|
78
|
-
List[Monochromator]: The detected monochromators
|
79
|
-
"""
|
80
|
-
return [Monochromator(0, self.communicator, self.error_db)]
|
81
|
-
|
82
|
-
@property
|
83
|
-
@override
|
84
|
-
def charge_coupled_devices(self) -> list[ChargeCoupledDevice]:
|
85
|
-
"""
|
86
|
-
Abstract method to get the detected CCDs.
|
87
|
-
|
88
|
-
Returns:
|
89
|
-
List[ChargeCoupledDevice]: The detected CCDS.
|
90
|
-
"""
|
91
|
-
return [ChargeCoupledDevice(0, self.communicator, self.error_db)]
|
@@ -1,82 +0,0 @@
|
|
1
|
-
import importlib.resources
|
2
|
-
import json
|
3
|
-
from pathlib import Path
|
4
|
-
from typing import Optional
|
5
|
-
|
6
|
-
from loguru import logger
|
7
|
-
from websockets.sync.server import WebSocketServer, serve
|
8
|
-
|
9
|
-
|
10
|
-
class FakeICLServer:
|
11
|
-
"""The FakeICLServer is a synchronous fake ICL server that sends only dummy data.
|
12
|
-
|
13
|
-
It starts a local websocket server and returns the predefined response located in
|
14
|
-
`horiba_sdk/devices/fake_responses/*.json`.
|
15
|
-
Currently supported devices for fake responses are:
|
16
|
-
- The ICL itself
|
17
|
-
- The :class:`Monochromator`
|
18
|
-
- The :class:`ChargeCoupledDevice`
|
19
|
-
|
20
|
-
For other unsupported devices, it just responds with the sent command.
|
21
|
-
|
22
|
-
"""
|
23
|
-
|
24
|
-
def __init__(self, fake_icl_host: str = 'localhost', fake_icl_port: int = 8765):
|
25
|
-
self._fake_icl_host: str = fake_icl_host
|
26
|
-
self._fake_icl_port: int = fake_icl_port
|
27
|
-
self._server: Optional[WebSocketServer] = None
|
28
|
-
|
29
|
-
fake_responses_path: Path = Path(str(importlib.resources.files('horiba_sdk.devices'))) / Path('fake_responses')
|
30
|
-
|
31
|
-
icl_fake_responses_path = fake_responses_path / 'icl.json'
|
32
|
-
with open(icl_fake_responses_path) as json_file:
|
33
|
-
self.icl_responses = json.load(json_file)
|
34
|
-
|
35
|
-
monochromator_fake_responses_path = fake_responses_path / 'monochromator.json'
|
36
|
-
with open(monochromator_fake_responses_path) as json_file:
|
37
|
-
self.monochromator_responses = json.load(json_file)
|
38
|
-
|
39
|
-
ccd_fake_responses_path = fake_responses_path / 'ccd.json'
|
40
|
-
with open(ccd_fake_responses_path) as json_file:
|
41
|
-
self.ccd_responses = json.load(json_file)
|
42
|
-
|
43
|
-
def echo(self, websocket):
|
44
|
-
for message in websocket:
|
45
|
-
logger.info('received: {message}', message=message)
|
46
|
-
command = json.loads(message)
|
47
|
-
|
48
|
-
if 'shutdown' in command['command']:
|
49
|
-
logger.info('Shutting down websocket')
|
50
|
-
websocket.close()
|
51
|
-
continue
|
52
|
-
|
53
|
-
if 'command' not in command:
|
54
|
-
logger.info('unknown message format, responding with message')
|
55
|
-
websocket.send(message)
|
56
|
-
continue
|
57
|
-
if command['command'].startswith('icl_'):
|
58
|
-
response = self.icl_responses[command['command']]
|
59
|
-
response['id'] = command['id']
|
60
|
-
websocket.send(json.dumps(response))
|
61
|
-
elif command['command'].startswith('mono_'):
|
62
|
-
response = self.monochromator_responses[command['command']]
|
63
|
-
response['id'] = command['id']
|
64
|
-
websocket.send(json.dumps(response))
|
65
|
-
elif command['command'].startswith('ccd_'):
|
66
|
-
response = self.ccd_responses[command['command']]
|
67
|
-
response['id'] = command['id']
|
68
|
-
websocket.send(json.dumps(response))
|
69
|
-
else:
|
70
|
-
logger.info('unknown command, responding with message')
|
71
|
-
websocket.send(message)
|
72
|
-
|
73
|
-
def start(self):
|
74
|
-
self._server = serve(self.echo, host=self._fake_icl_host, port=self._fake_icl_port)
|
75
|
-
self._server.serve_forever()
|
76
|
-
|
77
|
-
def stop(self):
|
78
|
-
if self._server:
|
79
|
-
logger.info('shutting down websocket server...')
|
80
|
-
self._server.shutdown()
|
81
|
-
self._server = None
|
82
|
-
logger.info('shutdown websocket server')
|
@@ -1,87 +0,0 @@
|
|
1
|
-
from abc import ABC, abstractmethod
|
2
|
-
from typing import Any
|
3
|
-
|
4
|
-
from horiba_sdk.communication import Command, Response
|
5
|
-
from horiba_sdk.icl_error import AbstractError, AbstractErrorDB
|
6
|
-
from horiba_sdk.sync.communication.abstract_communicator import AbstractCommunicator
|
7
|
-
|
8
|
-
|
9
|
-
class AbstractDevice(ABC):
|
10
|
-
"""
|
11
|
-
Abstract base class representing a generic device.
|
12
|
-
|
13
|
-
This class provides an interface for device-specific operations. Concrete implementations should provide specific
|
14
|
-
functionalities for each of the abstract methods.
|
15
|
-
|
16
|
-
Attributes:
|
17
|
-
_id (int):
|
18
|
-
_communicator (WebsocketCommunicator):
|
19
|
-
"""
|
20
|
-
|
21
|
-
def __init__(self, device_id: int, communicator: AbstractCommunicator, error_db: AbstractErrorDB) -> None:
|
22
|
-
self._id: int = device_id
|
23
|
-
self._error_db: AbstractErrorDB = error_db
|
24
|
-
self._communicator: AbstractCommunicator = communicator
|
25
|
-
|
26
|
-
def id(self) -> int:
|
27
|
-
"""Return the ID of the device.
|
28
|
-
|
29
|
-
Returns:
|
30
|
-
int: ID of the device.
|
31
|
-
"""
|
32
|
-
return self._id
|
33
|
-
|
34
|
-
@abstractmethod
|
35
|
-
def open(self) -> None:
|
36
|
-
"""
|
37
|
-
Open a connection to the device.
|
38
|
-
|
39
|
-
Returns:
|
40
|
-
Result: Result object indicating success or failure.
|
41
|
-
"""
|
42
|
-
if not self._communicator.opened():
|
43
|
-
self._communicator.open()
|
44
|
-
|
45
|
-
@abstractmethod
|
46
|
-
def close(self) -> None:
|
47
|
-
"""
|
48
|
-
Close the connection to the device.
|
49
|
-
|
50
|
-
Returns:
|
51
|
-
Result: Result object indicating success or failure.
|
52
|
-
"""
|
53
|
-
pass
|
54
|
-
|
55
|
-
def _execute_command(self, command_name: str, parameters: dict[Any, Any], timeout_in_s: float = 5) -> Response:
|
56
|
-
"""
|
57
|
-
Creates a command from the command name, and it's parameters
|
58
|
-
Executes a command and handles the response.
|
59
|
-
|
60
|
-
Args:
|
61
|
-
command_name (str): The name of the command to execute.
|
62
|
-
parameters (dict): The parameters for the command.
|
63
|
-
timeout_in_s (float, optional): The timeout in seconds.
|
64
|
-
|
65
|
-
Returns:
|
66
|
-
Response: The response from the device.
|
67
|
-
|
68
|
-
Raises:
|
69
|
-
Exception: When an error occurred on the device side.
|
70
|
-
"""
|
71
|
-
response: Response = self._communicator.request_with_response(Command(command_name, parameters), timeout_in_s)
|
72
|
-
if response.errors:
|
73
|
-
self._handle_errors(response.errors)
|
74
|
-
return response
|
75
|
-
|
76
|
-
def _handle_errors(self, errors: list[str]) -> None:
|
77
|
-
"""
|
78
|
-
Handles errors, logs them, and may take corrective actions.
|
79
|
-
|
80
|
-
Args:
|
81
|
-
errors (Exception): The exception or error to handle.
|
82
|
-
"""
|
83
|
-
for error in errors:
|
84
|
-
icl_error: AbstractError = self._error_db.error_from(error)
|
85
|
-
icl_error.log()
|
86
|
-
# TODO: [saga] only throw depending on the log level, tbd
|
87
|
-
raise Exception(f'Error from the ICL: {icl_error.message()}')
|