horiba-sdk 0.4.0__py3-none-any.whl → 0.5.4__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/__init__.py CHANGED
@@ -1,6 +1,8 @@
1
1
  # mypy: disable-error-code="attr-defined"
2
2
  """'horiba-sdk' is a package that provides source code for the development with Horiba devices"""
3
3
 
4
+ from loguru import logger
5
+
4
6
  __version__ = '0.2.0' # It MUST match the version in pyproject.toml file
5
7
  from importlib import metadata as importlib_metadata
6
8
 
@@ -13,3 +15,7 @@ def get_version() -> str:
13
15
 
14
16
 
15
17
  version: str = get_version()
18
+
19
+ # it is good practice to disable the logging for a library, this is done here.
20
+ # on initialization of a device manager, this can be enabled.
21
+ logger.disable(__name__)
@@ -166,13 +166,13 @@ class WebsocketCommunicator(AbstractCommunicator):
166
166
 
167
167
  def register_binary_message_callback(self, callback: Callable[[bytes], Any]) -> None:
168
168
  """Registers a callback to be called with every incoming binary message."""
169
- logger.info('Binary message callback registered.')
169
+ logger.debug('Binary message callback registered.')
170
170
  self.binary_message_callback = callback
171
171
 
172
172
  async def _receive_data(self) -> None:
173
173
  try:
174
174
  async for message in self.websocket: # type: ignore
175
- logger.info(f'Received message: {message!r}')
175
+ logger.debug(f'Received message: {message!r}')
176
176
  if isinstance(message, str):
177
177
  await self.json_message_queue.put(message)
178
178
  elif isinstance(message, bytes):
@@ -4,6 +4,16 @@ from typing import final
4
4
 
5
5
  @final
6
6
  class AcquisitionFormat(Enum):
7
+ """Formats for the acquisition.
8
+
9
+ Attributes:
10
+ SPECTRA: X axis in nm, Y axis in counts
11
+ IMAGE: X axis in pixels, Y axis in counts
12
+ CROP: TBD
13
+ FAST_KINETICS: TBD
14
+
15
+ """
16
+
7
17
  SPECTRA = 0
8
18
  IMAGE = 1
9
19
  CROP = 2
@@ -4,6 +4,8 @@ from typing import final
4
4
 
5
5
  @final
6
6
  class CleanCountMode(Enum):
7
+ """TODO"""
8
+
7
9
  NEVER = 0
8
10
  FIRST_ONLY = 1
9
11
  BETWEEN_ONLY = 2
@@ -4,9 +4,11 @@ from typing import final
4
4
 
5
5
  @final
6
6
  class TimerResolution(Enum):
7
- """
8
- .. note:: The timer resolution value of 1 microsecond is not supported by all CCDs.
7
+ """Resolution for the timer for the acquisition time.
8
+
9
+ .. note:: The timer resolution value MICROSECONDS is not supported by all CCDs.
9
10
  """
10
11
 
11
- _1000_MICROSECONDS = 0
12
- _1_MICROSECOND = 1
12
+ MILLISECONDS = 0
13
+ MICROSECONDS = 1
14
+ NOTHING_EVAL = 2
@@ -5,7 +5,12 @@ from typing import final
5
5
  @final
6
6
  class XAxisConversionType(Enum):
7
7
  """
8
- Enumeration of possible XAxisConversionTypes
8
+ Conversion types for the x axis of acquired data.
9
+
10
+ Attributes:
11
+ NONE: No conversion.
12
+ FROM_CCD_FIRMWARE: CCD FIT parameters contained in the CCD firmware.
13
+ FROM_ICL_SETTINGS_INI: Mono Wavelength parameters contained in the icl_settings.ini file
9
14
  """
10
15
 
11
16
  NONE = 0
@@ -66,6 +66,7 @@ class DeviceManager(AbstractDeviceManager):
66
66
  icl_ip: str = '127.0.0.1',
67
67
  icl_port: str = '25010',
68
68
  enable_binary_messages: bool = True,
69
+ enable_logging: bool = False
69
70
  ):
70
71
  """
71
72
  Initializes the DeviceManager with the specified communicator class.
@@ -75,7 +76,15 @@ class DeviceManager(AbstractDeviceManager):
75
76
  icl_ip (str) = '127.0.0.1': websocket IP
76
77
  icl_port (str) = '25010': websocket port
77
78
  enable_binary_messages (bool) = True: If True, binary messages are enabled.
79
+ enable_logging (bool) = True: If True, logging with the loguru library is enabled.
78
80
  """
81
+ # By default, logging is disabled for a library, so if desired it can be enabled
82
+ root_name_space: str = __name__.split('.')[0]
83
+ if enable_logging:
84
+ logger.info(f"Initializing logger for namespace: {root_name_space}")
85
+ logger.enable(root_name_space)
86
+ else:
87
+ logger.disable(root_name_space)
79
88
  super().__init__()
80
89
  self._start_icl = start_icl
81
90
  self._icl_communicator: WebsocketCommunicator = WebsocketCommunicator('ws://' + icl_ip + ':' + str(icl_port))
@@ -129,6 +138,8 @@ class DeviceManager(AbstractDeviceManager):
129
138
  logger.info('icl not running, starting it...')
130
139
  # subprocess.Popen([r'C:\Program Files\HORIBA Scientific\SDK\icl.exe'])
131
140
  self._icl_process = await asyncio.create_subprocess_exec(r'C:\Program Files\HORIBA Scientific\SDK\icl.exe')
141
+ await asyncio.sleep(4)
142
+
132
143
  # except subprocess.CalledProcessError:
133
144
  # logger.error('Failed to start ICL software.')
134
145
  # TODO: [saga] is this the best way handle exceptions?
@@ -1,5 +1,5 @@
1
1
  from abc import ABC, abstractmethod
2
- from typing import Any
2
+ from typing import Any, List, Optional
3
3
 
4
4
  from horiba_sdk.communication import AbstractCommunicator, Command, Response
5
5
  from horiba_sdk.icl_error import AbstractError, AbstractErrorDB
@@ -51,6 +51,43 @@ class AbstractDevice(ABC):
51
51
  """
52
52
  pass
53
53
 
54
+ async def pass_command(
55
+ self, command: str, params: Optional[List[Any]] = None, values: Optional[List[Any]] = None
56
+ ) -> Response:
57
+ """command to pass user input strings to ICL
58
+
59
+ .. note: used for internal usage
60
+ """
61
+
62
+ parameters = params if params else []
63
+ parameter_values = values if values else []
64
+
65
+ def convert_to_float(s):
66
+ try:
67
+ float(s)
68
+ return True
69
+ except ValueError:
70
+ return False
71
+
72
+ j = 0
73
+ for i in parameter_values:
74
+ if convert_to_float(i):
75
+ parameter_values[j] = float(parameter_values[j])
76
+ print(parameter_values[j])
77
+ print(type(parameter_values[j]))
78
+ j += 1
79
+ else:
80
+ j += 1
81
+
82
+ params_dict = dict(zip(parameters, parameter_values))
83
+ params_dict['index'] = self._id
84
+
85
+ if params[0] == '':
86
+ response: Response = await self._execute_command(str(command), {'index': self._id})
87
+ else:
88
+ response: Response = await self._execute_command(str(command), params_dict)
89
+ return response
90
+
54
91
  async def _execute_command(self, command_name: str, parameters: dict[Any, Any], timeout: int = 5) -> Response:
55
92
  """
56
93
  Creates a command from the command name, and it's parameters
@@ -1,5 +1,5 @@
1
1
  from types import TracebackType
2
- from typing import Any, List, Optional, final
2
+ from typing import Any, Optional, final
3
3
 
4
4
  from loguru import logger
5
5
  from overrides import override
@@ -155,6 +155,27 @@ class ChargeCoupledDevice(AbstractDevice):
155
155
  """
156
156
  await super()._execute_command('ccd_setSpeed', {'index': self._id, 'token': speed_token})
157
157
 
158
+ async def get_parallel_speed(self) -> int:
159
+ """Gets the current parallel speed token
160
+
161
+ Returns:
162
+ int: current parallel speed token
163
+
164
+ Raises:
165
+ Exception: When an error occurred on the device side
166
+ """
167
+ response: Response = await super()._execute_command('ccd_getParallelSpeed', {'index': self._id})
168
+ parallel_speed_token: int = int(response.results['token'])
169
+ return parallel_speed_token
170
+
171
+ async def set_parallel_speed(self, parallel_speed_token: int) -> None:
172
+ """Sets the desired parallel speed token
173
+
174
+ Raises:
175
+ Exception: When an error occurred on the device side
176
+ """
177
+ await super()._execute_command('ccd_setParallelSpeed', {'index': self._id, 'token': parallel_speed_token})
178
+
158
179
  async def get_fit_parameters(self) -> list[int]:
159
180
  """Returns the fit parameters of the CCD
160
181
 
@@ -337,7 +358,7 @@ class ChargeCoupledDevice(AbstractDevice):
337
358
  response: Response = await super()._execute_command('ccd_getDataSize', {'index': self._id})
338
359
  return int(response.results['size'])
339
360
 
340
- async def get_temperature(self) -> float:
361
+ async def get_chip_temperature(self) -> float:
341
362
  """Chip temperature of the CCD.
342
363
 
343
364
  Returns:
@@ -558,7 +579,7 @@ class ChargeCoupledDevice(AbstractDevice):
558
579
  response: Response = await super()._execute_command('ccd_getAcquisitionReady', {'index': self._id})
559
580
  return bool(response.results['ready'])
560
581
 
561
- async def set_acquisition_start(self, open_shutter: bool) -> None:
582
+ async def acquisition_start(self, open_shutter: bool) -> None:
562
583
  """Starts an acquisition that has been set up according to the previously defined acquisition parameters.
563
584
 
564
585
  Note: To specify the acquisiton parameters please see set_region_of_interest, set_x_axis_conversion_type.
@@ -569,16 +590,16 @@ class ChargeCoupledDevice(AbstractDevice):
569
590
  Raises:
570
591
  Exception: When an error occurred on the device side
571
592
  """
572
- await super()._execute_command('ccd_setAcquisitionStart', {'index': self._id, 'openShutter': open_shutter})
593
+ await super()._execute_command('ccd_acquisitionStart', {'index': self._id, 'openShutter': open_shutter})
573
594
 
574
595
  async def get_acquisition_busy(self) -> bool:
575
596
  """Returns true if the CCD is busy with the acquisition"""
576
597
  response: Response = await super()._execute_command('ccd_getAcquisitionBusy', {'index': self._id})
577
598
  return bool(response.results['isBusy'])
578
599
 
579
- async def set_acquisition_abort(self, reset_port: bool = True) -> None:
600
+ async def acquisition_abort(self) -> None:
580
601
  """Stops the acquisition of the CCD"""
581
- await super()._execute_command('ccd_setAcquisitionAbort', {'index': self._id, 'resetPort': reset_port})
602
+ await super()._execute_command('ccd_acquisitionAbort', {'index': self._id})
582
603
 
583
604
  async def get_acquisition_data(self) -> dict[Any, Any]:
584
605
  """Retrieves data from the last acquisition.
@@ -599,12 +620,13 @@ class ChargeCoupledDevice(AbstractDevice):
599
620
  response: Response = await super()._execute_command('ccd_getAcquisitionData', {'index': self._id})
600
621
  return response.results['acquisition']
601
622
 
602
- async def set_center_wavelength(self, center_wavelength: float) -> None:
623
+ async def set_center_wavelength(self, mono_index: float, center_wavelength: float) -> None:
603
624
  """Sets the center wavelength value to be used in the grating equation.
604
625
 
605
626
  Used when X axis conversion is XAxisConversionType.FROM_ICL_SETTINGS_INI
606
627
 
607
628
  Args:
629
+ mono_index (float): Index for which mono to pull info from icl_settings.ini for
608
630
  center_wavelength (float): Center wavelength
609
631
 
610
632
  Raises:
@@ -612,12 +634,12 @@ class ChargeCoupledDevice(AbstractDevice):
612
634
  """
613
635
  await super()._execute_command(
614
636
  'ccd_setCenterWavelength',
615
- {'index': self._id, 'wavelength': center_wavelength},
637
+ {'index': self._id, 'monoIndex': mono_index, 'wavelength': center_wavelength},
616
638
  )
617
639
 
618
640
  async def range_mode_center_wavelengths(
619
641
  self, monochromator_index: int, start_wavelength: float, end_wavelength: float, pixel_overlap: int
620
- ) -> List[float]:
642
+ ) -> list[float]:
621
643
  """Finds the center wavelength positions based on the input range and pixel overlap.
622
644
 
623
645
  The following commands are prerequisites and should be called prior to using this command:
@@ -647,3 +669,20 @@ class ChargeCoupledDevice(AbstractDevice):
647
669
  },
648
670
  )
649
671
  return response.results['centerWavelengths']
672
+
673
+ @staticmethod
674
+ async def raman_convert(spectrum: list[float], excitation_wavelength: float) -> list[float]:
675
+ """Calculates the raman shift for every wavelength in the list relative to the excitation wavelength.
676
+
677
+ Args:
678
+ spectrum (list[float]): Wavelengths
679
+ excitation_wavelength: Excitation wavelength
680
+
681
+ Returns:
682
+ list[float]: Wavelengths converted to raman shifts
683
+ """
684
+ raman_values = []
685
+ for wave_length in spectrum:
686
+ raman_shift = ((1 / excitation_wavelength) - (1 / wave_length)) * (10**7)
687
+ raman_values.append(raman_shift)
688
+ return raman_values
@@ -145,7 +145,7 @@ class Monochromator(AbstractDevice):
145
145
  response: Response = await super()._execute_command('mono_isBusy', {'index': self._id})
146
146
  return bool(response.results['busy'])
147
147
 
148
- async def home(self) -> None:
148
+ async def initialize(self) -> None:
149
149
  """Starts the monochromator initialization process called "homing".
150
150
 
151
151
  Use :func:`Monochromator.is_busy()` to know if the operation is still taking place.
@@ -155,6 +155,19 @@ class Monochromator(AbstractDevice):
155
155
  """
156
156
  await super()._execute_command('mono_init', {'index': self._id})
157
157
 
158
+ async def is_initialized(self) -> bool:
159
+ """This command returns true when the mono is initialized. Otherwise, it returns false.
160
+ Note: This command may also return false when the mono is busy with another command.
161
+
162
+ Returns:
163
+ bool: If the monochromator is initialized or not
164
+
165
+ Raises:
166
+ Exception: When an error occurred on the device side
167
+ """
168
+ response: Response = await super()._execute_command('mono_isInitialized', {'index': self._id})
169
+ return bool(response.results['initialized'])
170
+
158
171
  async def configuration(self) -> dict[str, Any]:
159
172
  """Returns the configuration of the monochromator.
160
173
 
@@ -169,7 +169,7 @@ class WebsocketCommunicator(AbstractCommunicator):
169
169
  raise CommunicationException(None, 'Binary message callback already registered')
170
170
 
171
171
  self.binary_message_callback = callback
172
- logger.info('Binary message callback registered.')
172
+ logger.debug('Binary message callback registered.')
173
173
  self.running_binary_message_handling_thread = True
174
174
  self.binary_message_handling_thread = Thread(target=self._run_binary_message_callback)
175
175
  self.binary_message_handling_thread.start()
@@ -3,6 +3,7 @@ Synchronous Device Manager Module
3
3
 
4
4
  This Device Manager uses threads instead of asyncio
5
5
  """
6
+
6
7
  import importlib.resources
7
8
  import platform
8
9
  import subprocess
@@ -1,5 +1,5 @@
1
1
  from types import TracebackType
2
- from typing import Any, List, Optional, final
2
+ from typing import Any, Optional, final
3
3
 
4
4
  from loguru import logger
5
5
  from overrides import override
@@ -142,6 +142,27 @@ class ChargeCoupledDevice(AbstractDevice):
142
142
  """
143
143
  super()._execute_command('ccd_setSpeed', {'index': self._id, 'token': speed_token})
144
144
 
145
+ def get_parallel_speed(self) -> int:
146
+ """Gets the current parallel speed token
147
+
148
+ Returns:
149
+ int: current parallel speed token
150
+
151
+ Raises:
152
+ Exception: When an error occurred on the device side
153
+ """
154
+ response: Response = super()._execute_command('ccd_getParallelSpeed', {'index': self._id})
155
+ parallel_speed_token: int = int(response.results['token'])
156
+ return parallel_speed_token
157
+
158
+ def set_parallel_speed(self, parallel_speed_token: int) -> None:
159
+ """Sets the desired parallel speed token
160
+
161
+ Raises:
162
+ Exception: When an error occurred on the device side
163
+ """
164
+ super()._execute_command('ccd_setParallelSpeed', {'index': self._id, 'token': parallel_speed_token})
165
+
145
166
  def get_fit_parameters(self) -> list[int]:
146
167
  """Returns the fit parameters of the CCD
147
168
 
@@ -324,7 +345,7 @@ class ChargeCoupledDevice(AbstractDevice):
324
345
  response: Response = super()._execute_command('ccd_getDataSize', {'index': self._id})
325
346
  return int(response.results['size'])
326
347
 
327
- def get_temperature(self) -> float:
348
+ def get_chip_temperature(self) -> float:
328
349
  """Chip temperature of the CCD.
329
350
 
330
351
  Returns:
@@ -545,7 +566,7 @@ class ChargeCoupledDevice(AbstractDevice):
545
566
  response: Response = super()._execute_command('ccd_getAcquisitionReady', {'index': self._id})
546
567
  return bool(response.results['ready'])
547
568
 
548
- def set_acquisition_start(self, open_shutter: bool) -> None:
569
+ def acquisition_start(self, open_shutter: bool) -> None:
549
570
  """Starts an acquisition that has been set up according to the previously defined acquisition parameters.
550
571
 
551
572
  Note: To specify the acquisiton parameters please see set_region_of_interest, set_x_axis_conversion_type.
@@ -556,16 +577,16 @@ class ChargeCoupledDevice(AbstractDevice):
556
577
  Raises:
557
578
  Exception: When an error occurred on the device side
558
579
  """
559
- super()._execute_command('ccd_setAcquisitionStart', {'index': self._id, 'openShutter': open_shutter})
580
+ super()._execute_command('ccd_acquisitionStart', {'index': self._id, 'openShutter': open_shutter})
560
581
 
561
582
  def get_acquisition_busy(self) -> bool:
562
583
  """Returns true if the CCD is busy with the acquisition"""
563
584
  response: Response = super()._execute_command('ccd_getAcquisitionBusy', {'index': self._id})
564
585
  return bool(response.results['isBusy'])
565
586
 
566
- def set_acquisition_abort(self, reset_port: bool = True) -> None:
587
+ def acquisition_abort(self) -> None:
567
588
  """Stops the acquisition of the CCD"""
568
- super()._execute_command('ccd_setAcquisitionAbort', {'index': self._id, 'resetPort': reset_port})
589
+ super()._execute_command('ccd_acquisitionStart', {'index': self._id})
569
590
 
570
591
  def get_acquisition_data(self) -> dict[Any, Any]:
571
592
  """Retrieves data from the last acquisition.
@@ -604,7 +625,7 @@ class ChargeCoupledDevice(AbstractDevice):
604
625
 
605
626
  def range_mode_center_wavelengths(
606
627
  self, monochromator_index: int, start_wavelength: float, end_wavelength: float, pixel_overlap: int
607
- ) -> List[float]:
628
+ ) -> list[float]:
608
629
  """Finds the center wavelength positions based on the input range and pixel overlap.
609
630
 
610
631
  The following commands are prerequisites and should be called prior to using this command:
@@ -634,3 +655,20 @@ class ChargeCoupledDevice(AbstractDevice):
634
655
  },
635
656
  )
636
657
  return response.results['centerWavelengths']
658
+
659
+ @staticmethod
660
+ def raman_convert(spectrum: list[float], excitation_wavelength: float) -> list[float]:
661
+ """Calculates the raman shift for every wavelength in the list relative to the excitation wavelength.
662
+
663
+ Args:
664
+ spectrum (list[float]): Wavelengths
665
+ excitation_wavelength: Excitation wavelength
666
+
667
+ Returns:
668
+ list[float]: Wavelengths converted to raman shifts
669
+ """
670
+ raman_values = []
671
+ for wave_length in spectrum:
672
+ raman_shift = ((1 / excitation_wavelength) - (1 / wave_length)) * (10**7)
673
+ raman_values.append(raman_shift)
674
+ return raman_values
@@ -147,7 +147,7 @@ class Monochromator(AbstractDevice):
147
147
  response: Response = super()._execute_command('mono_isBusy', {'index': self._id})
148
148
  return bool(response.results['busy'])
149
149
 
150
- def home(self) -> None:
150
+ def initialize(self) -> None:
151
151
  """Starts the monochromator initialization process called "homing".
152
152
 
153
153
  Use :func:`Monochromator.is_busy()` to know if the operation is still taking place.
@@ -157,6 +157,19 @@ class Monochromator(AbstractDevice):
157
157
  """
158
158
  super()._execute_command('mono_init', {'index': self._id})
159
159
 
160
+ def is_initialized(self) -> bool:
161
+ """This command returns true when the mono is initialized. Otherwise, it returns false.
162
+ Note: This command may also return false when the mono is busy with another command.
163
+
164
+ Returns:
165
+ bool: If the monochromator is initialized or not
166
+
167
+ Raises:
168
+ Exception: When an error occurred on the device side
169
+ """
170
+ response: Response = super()._execute_command('mono_isInitialized', {'index': self._id})
171
+ return bool(response.results['initialized'])
172
+
160
173
  def configuration(self) -> dict[str, Any]:
161
174
  """Returns the configuration of the monochromator.
162
175
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: horiba-sdk
3
- Version: 0.4.0
3
+ Version: 0.5.4
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
@@ -25,24 +25,23 @@ Requires-Dist: websockets (>=12.0,<13.0)
25
25
  Project-URL: Repository, https://github.com/ThatsTheEnd/horiba-python-sdk
26
26
  Description-Content-Type: text/markdown
27
27
 
28
- # horiba-python-sdk
28
+ # HORIBA Python SDK
29
29
 
30
30
  <div align="center">
31
31
 
32
- [![build](https://github.com/ThatsTheEnd/horiba-python-sdk/actions/workflows/build.yml/badge.svg)](https://github.com/ThatsTheEnd/horiba-python-sdk/actions/workflows/build.yml)
32
+ [![build](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/actions/workflows/build.yml/badge.svg)](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/actions/workflows/build.yml)
33
33
  [![PyPI - Version](https://img.shields.io/pypi/v/horiba-sdk)](https://pypi.org/project/horiba-sdk/)
34
34
  [![Python Version](https://img.shields.io/pypi/pyversions/horiba-sdk.svg)](https://pypi.org/project/horiba-sdk/)
35
- [![Dependencies Status](https://img.shields.io/badge/dependencies-up%20to%20date-brightgreen.svg)](https://github.com/ThatsTheEnd/horiba-python-sdk/pulls?utf8=%E2%9C%93&q=is%3Apr%20author%3Aapp%2Fdependabot)
35
+ [![Dependencies Status](https://img.shields.io/badge/dependencies-up%20to%20date-brightgreen.svg)](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/pulls?utf8=%E2%9C%93&q=is%3Apr%20author%3Aapp%2Fdependabot)
36
36
 
37
37
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
38
38
  [![Security: bandit](https://img.shields.io/badge/security-bandit-green.svg)](https://github.com/PyCQA/bandit)
39
- [![Pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/.pre-commit-config.yaml)
40
- [![Semantic Versions](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--versions-e10079.svg)](https://github.com/ThatsTheEnd/horiba-python-sdk/releases)
41
- [![License](https://img.shields.io/github/license/ThatsTheEnd/horiba-python-sdk)](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/LICENSE)
39
+ [![Pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/blob/master/.pre-commit-config.yaml)
40
+ [![Semantic Versions](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--versions-e10079.svg)](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/releases)
41
+ [![License](https://img.shields.io/github/license/HORIBAEzSpecSDK/horiba-python-sdk)](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/blob/master/LICENSE)
42
42
  ![Coverage Report](assets/images/coverage.svg)
43
43
  [![Documentation Status](https://readthedocs.org/projects/horiba-python-sdk/badge/?version=latest)](https://horiba-python-sdk.readthedocs.io/en/latest/?badge=latest)
44
44
 
45
- 'horiba-sdk' is a package that provides source code for the development with Horiba devices
46
45
 
47
46
  </div>
48
47
 
@@ -61,11 +60,20 @@ ___
61
60
 
62
61
  ___
63
62
 
64
- **📦 Prerequisites**
63
+ # 📦 About this repository
64
+ `horiba-sdk` is a package that provides source code for the development of custom applications that include interaction with Horiba devices, namely monochromators and multichannel detectors (e.g. CCD cameras).
65
+ Future versions of this package will include access to more devices. The SDK exists for several programming languages:
66
+ - Python (this repo)
67
+ - [C#](https://github.com/HORIBAEzSpecSDK/dotnet-sdk)
68
+ - [C++](https://github.com/HORIBAEzSpecSDK/cpp-sdk)
69
+ - [LabVIEW](https://github.com/HORIBAEzSpecSDK/labview-sdk)
65
70
 
66
- * Python `>=3.9`
67
- * ICL.exe installed as part of the Horiba SDK, licensed and activated
71
+ # ☑️ Prerequisites
68
72
 
73
+ To use this package, the following conditions must be met:
74
+ * Python `>=3.9` is installed
75
+ * ICL.exe installed as part of the `Horiba SDK`, licensed and activated. The Horiba SDK can be purchased by contacting the [Horiba Support](https://www.horiba.com/int/scientific/contact/) and sending a message to the `Scientific` business segment, specifying `no division` and selecting the `sales` department
76
+ *
69
77
  <details>
70
78
  <summary>To make sure that the USB devices do not get disconnected, uncheck the following boxes in the properties</summary>
71
79
 
@@ -73,7 +81,7 @@ ___
73
81
 
74
82
  </details>
75
83
 
76
- ## 🛠️ Usage
84
+ # 🛠️ Getting Started
77
85
 
78
86
  <details>
79
87
  <summary>Video of the steps below</summary>
@@ -82,7 +90,7 @@ ___
82
90
 
83
91
  </details>
84
92
 
85
- 0. (Optional but recommended) Work in a virtual environment:
93
+ 1. (Optional but recommended) Work in a virtual environment:
86
94
 
87
95
  Navigate to the (empty) project folder you want to work and run:
88
96
 
@@ -143,17 +151,40 @@ ___
143
151
  ```bash
144
152
  python center_scan.py
145
153
  ```
154
+ # 🔗 Examples
155
+ ## Getting Started
156
+ The files in the folder [examples/asynchronous_examples/](examples/asynchronous_examples) can be used as a starting point for a custom application.
146
157
 
147
- ## 👩‍💻 First steps as contributor
158
+ ## Tests
159
+ The files in the folder [tests/test_single_devices](tests/test_single_devices) can be used to explore further functionality.
148
160
 
149
- ### Clone and setup the repo
161
+ ## Asynchronous vs Synchronous Examples
162
+ This SDK is based on Websocket communication. The nature of this communication is asynchronous by its design.
163
+ The way this SDK uses websockets is to send requests to the underlying `instrument control layer (ICL)` and get a (nearly immediate) reply back.
164
+ This is true even for commands that longer to execute, e.g. to move the mono or to acquire data from the CCD. The way this is handled is by sending an acknowledgement back that the command is received and being processed. The user has then to inquire if `mono_isBusy` to see when the hardware is free to receive the next command.
165
+ That means that every `async` function can be `awaited` immediately and handled just like any other function.
166
+ To learn more about async and await, [check out this introduction](https://www.pythontutorial.net/python-concurrency/python-async-await/) or [take a deeper dive here](https://medium.com/@danielwume/an-in-depth-guide-to-asyncio-and-await-in-python-059c3ecc9d96).
150
167
 
151
- 1. Clone the repo:
152
168
 
153
- ```bash
154
- git clone https://github.com/ThatsTheEnd/horiba-python-sdk.git
155
- cd horiba-python-sdk
156
- ```
169
+
170
+ # 🏗️ Architecture
171
+ The functionality is distributed over two parts, the `instrument control layer (ICL)` and the `github source code`. This split is shown in the following image:
172
+ ![SDK Split](docs/SDK_Overview_Dark.png#gh-dark-mode-only "SDK Split")
173
+ ![SDK Split](docs/SDK_Overview_Dark.png#gh-light-mode-only "SDK Split")
174
+
175
+ The ICL itself is sold and distributed by Horiba. The source code to communicate with the ICL and drive the instruments is located in this repo for Python, but can be also found for C#, C++ and LabVIEW as described above.
176
+ The communication between SDK and ICL is websocket based. I.e. in essence the ICL is steered by a `command and control` pattern where commands and their replies are JSON commands.
177
+
178
+ # 👩‍💻 First steps as contributor
179
+
180
+ ## Clone and set up the repo
181
+
182
+ 1. Clone the repo:
183
+
184
+ ```bash
185
+ git clone https://github.com/HORIBAEzSpecSDK/horiba-python-sdk.git
186
+ cd horiba-python-sdk
187
+ ```
157
188
 
158
189
  2. If you don't have `Poetry` installed run:
159
190
 
@@ -187,7 +218,7 @@ git push
187
218
  <!-- - Set up [Dependabot](https://docs.github.com/en/github/administering-a-repository/enabling-and-disabling-version-updates#enabling-github-dependabot-version-updates) to ensure you have the latest dependencies. -->
188
219
  <!-- - Set up [Stale bot](https://github.com/apps/stale) for automatic issue closing. -->
189
220
 
190
- ### Poetry
221
+ ## Poetry
191
222
 
192
223
  Want to know more about Poetry? Check [its documentation](https://python-poetry.org/docs/).
193
224
 
@@ -205,7 +236,7 @@ etc
205
236
  </p>
206
237
  </details>
207
238
 
208
- ### Building and releasing your package
239
+ ## Building and releasing your package
209
240
 
210
241
  Building a new version of the application contains steps:
211
242
 
@@ -219,12 +250,12 @@ Building a new version of the application contains steps:
219
250
  git push --tags
220
251
  ```
221
252
 
222
- ### Makefile usage
253
+ ## Makefile usage
223
254
 
224
- [`Makefile`](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/Makefile) contains a lot of functions for faster development.
255
+ [`Makefile`](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/blob/master/Makefile) contains a lot of functions for faster development.
225
256
 
226
257
  <details>
227
- <summary>1. Download and remove Poetry</summary>
258
+ <summary>1. Installation of Poetry</summary>
228
259
  <p>
229
260
 
230
261
  To download and install Poetry run:
@@ -252,7 +283,7 @@ Install requirements:
252
283
  make install
253
284
  ```
254
285
 
255
- Pre-commit hooks coulb be installed after `git init` via
286
+ Pre-commit hooks could be installed after `git init` via
256
287
 
257
288
  ```bash
258
289
  make pre-commit-install
@@ -287,6 +318,8 @@ Update all dev libraries to the latest version using one comand
287
318
  ```bash
288
319
  make update-dev-deps
289
320
  ```
321
+ </p>
322
+ </details>
290
323
 
291
324
  <details>
292
325
  <summary>4. Code security</summary>
@@ -302,8 +335,6 @@ This command launches `Poetry` integrity checks as well as identifies security i
302
335
  make check-safety
303
336
  ```
304
337
 
305
- </p>
306
- </details>
307
338
 
308
339
  </p>
309
340
  </details>
@@ -406,7 +437,7 @@ Remove docker image with
406
437
  make docker-remove
407
438
  ```
408
439
 
409
- More information [about docker](https://github.com/ThatsTheEnd/horiba-python-sdk/tree/master/docker).
440
+ More information [about docker](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/tree/master/docker).
410
441
 
411
442
  </p>
412
443
  </details>
@@ -443,11 +474,10 @@ Or to remove all above run:
443
474
  ```bash
444
475
  make cleanup
445
476
  ```
446
-
447
477
  </p>
448
478
  </details>
449
479
 
450
- ## 📚 Documentation
480
+ # 📚 Documentation
451
481
 
452
482
  The latest documentation can be found at
453
483
  [horiba-python-sdk.readthedocs.io](https://horiba-python-sdk.readthedocs.io/en/latest/).
@@ -463,63 +493,12 @@ Documentation is built each time a commit is pushed on `main` or for pull
463
493
  requests. When release tags are created in the repo, readthedocs will also tag
464
494
  the documentation accordingly
465
495
 
466
- ## 🚀 Features
467
-
468
- ### Development features
469
-
470
- - Supports for `Python 3.9` and higher.
471
- - [`Poetry`](https://python-poetry.org/) as the dependencies manager. See configuration in [`pyproject.toml`](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/pyproject.toml).
472
- - Automatic codestyle with [`ruff`](https://github.com/astral-sh/ruff)
473
- - Ready-to-use [`pre-commit`](https://pre-commit.com/) hooks with code-formatting.
474
- - Type checks with [`mypy`](https://mypy.readthedocs.io); security checks with [`safety`](https://github.com/pyupio/safety) and [`bandit`](https://github.com/PyCQA/bandit)
475
- - Testing with [`pytest`](https://docs.pytest.org/en/latest/).
476
- - Ready-to-use [`.editorconfig`](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/.editorconfig), [`.dockerignore`](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/.dockerignore), and [`.gitignore`](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/.gitignore). You don't have to worry about those things.
477
-
478
- ### Deployment features
479
-
480
- - `GitHub` integration: issue and pr templates.
481
- - `Github Actions` with predefined [build workflow](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/.github/workflows/build.yml) as the default CI/CD.
482
- - Everything is already set up for security checks, codestyle checks, code formatting, testing, linting, docker builds, etc with [`Makefile`](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/Makefile#L89). More details in [makefile-usage](#makefile-usage).
483
- - [Dockerfile](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/docker/Dockerfile) for your package.
484
- - Always up-to-date dependencies with [`@dependabot`](https://dependabot.com/). You will only [enable it](https://docs.github.com/en/github/administering-a-repository/enabling-and-disabling-version-updates#enabling-github-dependabot-version-updates).
485
- - Automatic drafts of new releases with [`Release Drafter`](https://github.com/marketplace/actions/release-drafter). You may see the list of labels in [`release-drafter.yml`](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/.github/release-drafter.yml). Works perfectly with [Semantic Versions](https://semver.org/) specification.
486
-
487
- ### Open source community features
488
-
489
- - Ready-to-use [Pull Requests templates](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/.github/PULL_REQUEST_TEMPLATE.md) and several [Issue templates](https://github.com/ThatsTheEnd/horiba-python-sdk/tree/master/.github/ISSUE_TEMPLATE).
490
- - Files such as: `LICENSE`, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, and `SECURITY.md` are generated automatically.
491
- - [Semantic Versions](https://semver.org/) specification with [`Release Drafter`](https://github.com/marketplace/actions/release-drafter).
492
- <!-- - [`Stale bot`](https://github.com/apps/stale) that closes abandoned issues after a period of inactivity. (You will only [need to setup free plan](https://github.com/marketplace/stale)). Configuration is [here](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/.github/.stale.yml). -->
493
-
494
-
495
- ## 📈 Releases
496
-
497
- You can see the list of available releases on the [GitHub Releases](https://github.com/ThatsTheEnd/horiba-python-sdk/releases) page.
498
-
499
- We follow [Semantic Versions](https://semver.org/) specification.
500
-
501
- <!-- We use [`Release Drafter`](https://github.com/marketplace/actions/release-drafter). As pull requests are merged, a draft release is kept up-to-date listing the changes, ready to publish when you’re ready. With the categories option, you can categorize pull requests in release notes using labels. -->
502
-
503
- ### List of labels and corresponding titles
504
-
505
- | **Label** | **Title in Releases** |
506
- | :-----------------------------------: | :---------------------: |
507
- | `enhancement`, `feature` | 🚀 Features |
508
- | `bug`, `refactoring`, `bugfix`, `fix` | 🔧 Fixes & Refactoring |
509
- | `build`, `ci`, `testing` | 📦 Build System & CI/CD |
510
- | `breaking` | 💥 Breaking Changes |
511
- | `documentation` | 📝 Documentation |
512
- | `dependencies` | ⬆️ Dependencies updates |
513
-
514
- <!-- You can update it in [`release-drafter.yml`](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/.github/release-drafter.yml). -->
515
-
516
- <!-- GitHub creates the `bug`, `enhancement`, and `documentation` labels for you. Dependabot creates the `dependencies` label. Create the remaining labels on the Issues tab of your GitHub repository, when you need them. -->
517
496
 
518
497
  ## 🛡 License
519
498
 
520
- [![License](https://img.shields.io/github/license/ThatsTheEnd/horiba-python-sdk)](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/LICENSE)
499
+ [![License](https://img.shields.io/github/license/HORIBAEzSpecSDK/horiba-python-sdk)](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/blob/master/LICENSE)
521
500
 
522
- This project is licensed under the terms of the `MIT` license. See [LICENSE](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/LICENSE) for more details.
501
+ This project is licensed under the terms of the `MIT` license. See [LICENSE](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/blob/master/LICENSE) for more details.
523
502
 
524
503
  ## 📃 Citation
525
504
 
@@ -530,10 +509,11 @@ This project is licensed under the terms of the `MIT` license. See [LICENSE](htt
530
509
  year = {2023},
531
510
  publisher = {GitHub},
532
511
  journal = {GitHub repository},
533
- howpublished = {\url{https://github.com/ThatsTheEnd/horiba-python-sdk}}
512
+ howpublished = {\url{https://github.com/HORIBAEzSpecSDK/horiba-python-sdk}}
534
513
  }
535
514
  ```
536
515
 
516
+
537
517
  ## Credits [![🚀 Your next Python package needs a bleeding-edge project structure.](https://img.shields.io/badge/python--package--template-%F0%9F%9A%80-brightgreen)](https://github.com/TezRomacH/python-package-template)
538
518
 
539
519
  This project was generated with [`python-package-template`](https://github.com/TezRomacH/python-package-template)
@@ -1,20 +1,20 @@
1
- horiba_sdk/__init__.py,sha256=QS1eJSOd6_DgyxAQE3FoKhvHgWRG3JVhkKHpQ1Za8yM,485
1
+ horiba_sdk/__init__.py,sha256=J8jCV90BJ2SCDFSpacDeAAsJa8PFqBUDpREOJeAgXug,679
2
2
  horiba_sdk/communication/__init__.py,sha256=nhS1rw1ZojM8zmRIx0VEFtYge0IH-B_L4zNBUNBJZYE,1564
3
3
  horiba_sdk/communication/abstract_communicator.py,sha256=E80dfOjrfuNay3W5jeSn4T2tUas6vygRcF46kSoP5j8,1498
4
4
  horiba_sdk/communication/communication_exception.py,sha256=d2ouOoVI6Q69_JL1bUEjjQOmjiO0CEm8K20WsaUuhB0,583
5
5
  horiba_sdk/communication/messages.py,sha256=Gxkw01JI0U5ShILANT0pezywcoSO2aHo06j1ixzZPEQ,2661
6
- horiba_sdk/communication/websocket_communicator.py,sha256=LzFaklePVAMkXKsdCQLFcx1XlOrJAAm1_Q9h2nAxeWY,8298
6
+ horiba_sdk/communication/websocket_communicator.py,sha256=wQxGRtj3htQ8n_hMvcoBmZdD18iea0uiCEsTZYdR2Jg,8300
7
7
  horiba_sdk/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- horiba_sdk/core/acquisition_format.py,sha256=fpACwGAL8qXEdsWGPTY0HYt708XSw0AtZ2FYb0RhpRk,152
9
- horiba_sdk/core/clean_count_mode.py,sha256=jAiYvTBGnffgZpZAArifPINHd09884FaQ_x4Tq6uFzI,169
8
+ horiba_sdk/core/acquisition_format.py,sha256=pHcHZck75BbJ4nn2FOZ9cUXq9juZUjoNxKdLUhIIINI,358
9
+ horiba_sdk/core/clean_count_mode.py,sha256=7guLd3eUV3Y2Cnj3w0JJ7ry74I8a6MFQiqbK5pTb3oU,185
10
10
  horiba_sdk/core/resolution.py,sha256=VOQonjPBqZ2TbtiNVDBT3TI1Aymj1pPYWMoobFaXjG8,1029
11
- horiba_sdk/core/timer_resolution.py,sha256=sKu_Y2Ev0jfR_UbD3AwzFRAWAJmR6rYXTtdt37HE-Tw,240
12
- horiba_sdk/core/x_axis_conversion_type.py,sha256=w44y6NUu_O0klltbIMj_pwFZBIL0MIZ4ouryh65Rzyw,224
11
+ horiba_sdk/core/timer_resolution.py,sha256=V6pXEZMVvUUoqrJmLFFI45pFt_7po6-5r1DmC8jY6gs,300
12
+ horiba_sdk/core/x_axis_conversion_type.py,sha256=SQrdbciTR8x5UWOqLDgxWPy12o0-yIVLrkm7N3gpWpg,449
13
13
  horiba_sdk/devices/__init__.py,sha256=YYsJL2CSJwc2vsAjFQEKXjA5_QAnKlxSwlx32EgzFME,336
14
14
  horiba_sdk/devices/abstract_device_discovery.py,sha256=04ZCEB5IZkUuJxSisS78eW6lAQNXG4uaDoPv-eBccBA,178
15
15
  horiba_sdk/devices/abstract_device_manager.py,sha256=RXj5_pzFHpiolJe3ZfFsofwpD1hlwUMwYD1DyWMmlI0,1717
16
16
  horiba_sdk/devices/ccd_discovery.py,sha256=nGuskZ07Y9myI__dU8LeLnrNiJEBpGPby21EEwgSVUk,2376
17
- horiba_sdk/devices/device_manager.py,sha256=S5iIxnvJivXdMLUmEq0rmoG899ENE-BdL_4TLQWQWsg,10327
17
+ horiba_sdk/devices/device_manager.py,sha256=ZmZjQjTLxJvs5NqWQLTfDcCNb3PFwBYbu1mg_qmMCVo,10847
18
18
  horiba_sdk/devices/fake_device_manager.py,sha256=Tr4Z067smYfy-ya29PO4PK4EWF6Sa1R2UQFZCWfPePE,5015
19
19
  horiba_sdk/devices/fake_icl_server.py,sha256=Yh9oh0YCbw-AXMnCHFWsZvJ7ZFjsnm1JG1y0ix1b-9Q,2348
20
20
  horiba_sdk/devices/fake_responses/ccd.json,sha256=sMg-UqU6W1I01n2kEbkFc5J0XF8E3JVdOjB-1MhufzM,17020
@@ -22,9 +22,9 @@ horiba_sdk/devices/fake_responses/icl.json,sha256=ZPKhjx0BmwmqNGwbb7reXSStddgVZe
22
22
  horiba_sdk/devices/fake_responses/monochromator.json,sha256=I9yBMsJyXXVCLF1J8mNPsfgXasIg60IL-4ToECcoGhw,4308
23
23
  horiba_sdk/devices/monochromator_discovery.py,sha256=KoZ8vfPsI6QRuiD4BiE0YvzO42dQzFZqvoxygDSllE8,2365
24
24
  horiba_sdk/devices/single_devices/__init__.py,sha256=Ai6HLstvMqT1c6A0yKLAH6227TUdS4ZMsC6zFrsmJic,192
25
- horiba_sdk/devices/single_devices/abstract_device.py,sha256=0OW4RnZi0NQhS6DsN5BRbbbqbqOGajnYsHz9Nu0L-ws,2800
26
- horiba_sdk/devices/single_devices/ccd.py,sha256=TN9sU2re2yhi6yxjtV0tDif7TnbCaOccKgf7n5sepyI,28466
27
- horiba_sdk/devices/single_devices/monochromator.py,sha256=kUn2OizpItjrTfB-2AAq8bYz35pdwZpXV8a75LEH150,13969
25
+ horiba_sdk/devices/single_devices/abstract_device.py,sha256=CwCjHJWSp8WGrYsyq0ZhTQVnvYdoCG-kb4BU9SZXJm8,3994
26
+ horiba_sdk/devices/single_devices/ccd.py,sha256=mnx0keMePP7JAxCs8ZgCm_05tyu89efjAcBn2FPv4FI,29995
27
+ horiba_sdk/devices/single_devices/monochromator.py,sha256=r-9bI-ne_iGdfTWBvUnYSxxp1DQNlEt7wBN31cGfZ1E,14538
28
28
  horiba_sdk/icl_error/__init__.py,sha256=EtqHaUeoaLilpb7L7juuVamJgIZ3Anfnca0AO2o46rg,1053
29
29
  horiba_sdk/icl_error/abstract_error.py,sha256=5nQDI6MNfCi4xUx6taJcEWGwANOo8mPEb4kVxrO-DW8,1301
30
30
  horiba_sdk/icl_error/abstract_error_db.py,sha256=IJ3wGb6DWxnY35VIUwZX0X80IPi5k4QoQXnVjDy8lH8,589
@@ -35,19 +35,19 @@ horiba_sdk/sync/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
35
  horiba_sdk/sync/communication/__init__.py,sha256=iVHHW0cQ9w_J5dGRkwdBnw7vYgRLn5MVsdIFWGZvJoA,186
36
36
  horiba_sdk/sync/communication/abstract_communicator.py,sha256=CEkGj2qVNoWYkFD9HOlxV82kls3gsJ517gWPK-9rUBw,1185
37
37
  horiba_sdk/sync/communication/test_client.py,sha256=7N5sM2Y63GSuFDmOEFRXeTHeAY7Nq7yX5MvbdPXeDko,651
38
- horiba_sdk/sync/communication/websocket_communicator.py,sha256=ThM2seSrMldQpX6pdacfi5_JCR05tF8aS8FgHy6V7RU,9233
38
+ horiba_sdk/sync/communication/websocket_communicator.py,sha256=rCa0RVwl9Evwtr8VJUhkhTiIX2yG6pVtblhV3BGwnxU,9234
39
39
  horiba_sdk/sync/devices/__init__.py,sha256=NDHi3zfDC52aTO1RpaPIBfyp7PEXfTqiYdvY_ThkyRE,469
40
40
  horiba_sdk/sync/devices/abstract_device_discovery.py,sha256=hKDWbI8-I3r5mrU3O1Y1ve6vV7sg6KpUa5d3EfcLIE8,447
41
41
  horiba_sdk/sync/devices/abstract_device_manager.py,sha256=hF9BGgVqi6EK0n6hdUDu7dnKI1SfZ5mCweGvm6vCJ5o,1709
42
42
  horiba_sdk/sync/devices/device_discovery.py,sha256=eLErCF3mFYK5Za0jRg2xBLQnWJTwce7LcWdm8OQcI0A,2723
43
- horiba_sdk/sync/devices/device_manager.py,sha256=cK5roPvBMzxm9qI3sB9WN_UXmxm5ER89CyDSLP22WY4,8428
43
+ horiba_sdk/sync/devices/device_manager.py,sha256=yGoijQnxJn7xyI3TpkBf-DpN3JGPNrQDkDtvvSLKNQk,8429
44
44
  horiba_sdk/sync/devices/fake_device_manager.py,sha256=myj1GwxjcOmv9jZbaTk8QwC6qYluNaZfuP9xPXrw3do,2809
45
45
  horiba_sdk/sync/devices/fake_icl_server.py,sha256=DlQOC54p7UCYBTdZOZCD9TgDdK4MarnbaWFUXRo7NP4,3298
46
46
  horiba_sdk/sync/devices/single_devices/__init__.py,sha256=Ai6HLstvMqT1c6A0yKLAH6227TUdS4ZMsC6zFrsmJic,192
47
47
  horiba_sdk/sync/devices/single_devices/abstract_device.py,sha256=ABQpU6wU5trH0j8OVjDP6MeGTj41e1ey5K5sw7tCCdQ,2883
48
- horiba_sdk/sync/devices/single_devices/ccd.py,sha256=Ig3aUFcKSZ3IAyir0onSioVtz08aG6eTp46iPnMOw4M,26833
49
- horiba_sdk/sync/devices/single_devices/monochromator.py,sha256=uix3ffDMfSHCHFtKb6aX_SgF4urRaUsRdUTRqg9NqtA,13794
50
- horiba_sdk-0.4.0.dist-info/LICENSE,sha256=JD-7TpNZoT7emooLwaTU9bJZbFbeM1ba90b8gpCYxzM,1083
51
- horiba_sdk-0.4.0.dist-info/METADATA,sha256=q3J-KfVJSxpHm-y9yF_fVhkRln1BRu_VfAnQtOEiSfA,16035
52
- horiba_sdk-0.4.0.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
53
- horiba_sdk-0.4.0.dist-info/RECORD,,
48
+ horiba_sdk/sync/devices/single_devices/ccd.py,sha256=fHj3lU8pZJKCoo1W3xdLhucNd_7JM4XRRxbbHfAO5Os,28196
49
+ horiba_sdk/sync/devices/single_devices/monochromator.py,sha256=ur0scHOnnX0f_xhxh4Wzz4CNyeltFPuGg49PmPKEFwg,14351
50
+ horiba_sdk-0.5.4.dist-info/LICENSE,sha256=JD-7TpNZoT7emooLwaTU9bJZbFbeM1ba90b8gpCYxzM,1083
51
+ horiba_sdk-0.5.4.dist-info/METADATA,sha256=MGbBOP5E9ZWO8D-gGsU8TInWUUWannU3v1K5D1t4CY0,14349
52
+ horiba_sdk-0.5.4.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
53
+ horiba_sdk-0.5.4.dist-info/RECORD,,