horiba-sdk 0.4.0__tar.gz → 0.5.4__tar.gz

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 (56) hide show
  1. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/PKG-INFO +65 -85
  2. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/README.md +64 -84
  3. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/__init__.py +6 -0
  4. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/communication/websocket_communicator.py +2 -2
  5. horiba_sdk-0.5.4/horiba_sdk/core/acquisition_format.py +20 -0
  6. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/core/clean_count_mode.py +2 -0
  7. horiba_sdk-0.5.4/horiba_sdk/core/timer_resolution.py +14 -0
  8. horiba_sdk-0.5.4/horiba_sdk/core/x_axis_conversion_type.py +18 -0
  9. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/devices/device_manager.py +11 -0
  10. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/devices/single_devices/abstract_device.py +38 -1
  11. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/devices/single_devices/ccd.py +48 -9
  12. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/devices/single_devices/monochromator.py +14 -1
  13. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/sync/communication/websocket_communicator.py +1 -1
  14. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/sync/devices/device_manager.py +1 -0
  15. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/sync/devices/single_devices/ccd.py +45 -7
  16. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/sync/devices/single_devices/monochromator.py +14 -1
  17. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/pyproject.toml +1 -1
  18. horiba_sdk-0.4.0/horiba_sdk/core/acquisition_format.py +0 -10
  19. horiba_sdk-0.4.0/horiba_sdk/core/timer_resolution.py +0 -12
  20. horiba_sdk-0.4.0/horiba_sdk/core/x_axis_conversion_type.py +0 -13
  21. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/LICENSE +0 -0
  22. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/communication/__init__.py +0 -0
  23. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/communication/abstract_communicator.py +0 -0
  24. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/communication/communication_exception.py +0 -0
  25. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/communication/messages.py +0 -0
  26. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/core/__init__.py +0 -0
  27. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/core/resolution.py +0 -0
  28. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/devices/__init__.py +0 -0
  29. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/devices/abstract_device_discovery.py +0 -0
  30. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/devices/abstract_device_manager.py +0 -0
  31. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/devices/ccd_discovery.py +0 -0
  32. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/devices/fake_device_manager.py +0 -0
  33. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/devices/fake_icl_server.py +0 -0
  34. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/devices/fake_responses/ccd.json +0 -0
  35. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/devices/fake_responses/icl.json +0 -0
  36. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/devices/fake_responses/monochromator.json +0 -0
  37. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/devices/monochromator_discovery.py +0 -0
  38. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/devices/single_devices/__init__.py +0 -0
  39. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/icl_error/__init__.py +0 -0
  40. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/icl_error/abstract_error.py +0 -0
  41. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/icl_error/abstract_error_db.py +0 -0
  42. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/icl_error/error_list.json +0 -0
  43. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/icl_error/icl_error.py +0 -0
  44. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/icl_error/icl_error_db.py +0 -0
  45. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/sync/__init__.py +0 -0
  46. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/sync/communication/__init__.py +0 -0
  47. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/sync/communication/abstract_communicator.py +0 -0
  48. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/sync/communication/test_client.py +0 -0
  49. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/sync/devices/__init__.py +0 -0
  50. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/sync/devices/abstract_device_discovery.py +0 -0
  51. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/sync/devices/abstract_device_manager.py +0 -0
  52. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/sync/devices/device_discovery.py +0 -0
  53. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/sync/devices/fake_device_manager.py +0 -0
  54. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/sync/devices/fake_icl_server.py +0 -0
  55. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/sync/devices/single_devices/__init__.py +0 -0
  56. {horiba_sdk-0.4.0 → horiba_sdk-0.5.4}/horiba_sdk/sync/devices/single_devices/abstract_device.py +0 -0
@@ -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,21 +1,20 @@
1
- # horiba-python-sdk
1
+ # HORIBA Python SDK
2
2
 
3
3
  <div align="center">
4
4
 
5
- [![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)
5
+ [![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)
6
6
  [![PyPI - Version](https://img.shields.io/pypi/v/horiba-sdk)](https://pypi.org/project/horiba-sdk/)
7
7
  [![Python Version](https://img.shields.io/pypi/pyversions/horiba-sdk.svg)](https://pypi.org/project/horiba-sdk/)
8
- [![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)
8
+ [![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)
9
9
 
10
10
  [![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)
11
11
  [![Security: bandit](https://img.shields.io/badge/security-bandit-green.svg)](https://github.com/PyCQA/bandit)
12
- [![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)
13
- [![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)
14
- [![License](https://img.shields.io/github/license/ThatsTheEnd/horiba-python-sdk)](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/LICENSE)
12
+ [![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)
13
+ [![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)
14
+ [![License](https://img.shields.io/github/license/HORIBAEzSpecSDK/horiba-python-sdk)](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/blob/master/LICENSE)
15
15
  ![Coverage Report](assets/images/coverage.svg)
16
16
  [![Documentation Status](https://readthedocs.org/projects/horiba-python-sdk/badge/?version=latest)](https://horiba-python-sdk.readthedocs.io/en/latest/?badge=latest)
17
17
 
18
- 'horiba-sdk' is a package that provides source code for the development with Horiba devices
19
18
 
20
19
  </div>
21
20
 
@@ -34,11 +33,20 @@ ___
34
33
 
35
34
  ___
36
35
 
37
- **📦 Prerequisites**
36
+ # 📦 About this repository
37
+ `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).
38
+ Future versions of this package will include access to more devices. The SDK exists for several programming languages:
39
+ - Python (this repo)
40
+ - [C#](https://github.com/HORIBAEzSpecSDK/dotnet-sdk)
41
+ - [C++](https://github.com/HORIBAEzSpecSDK/cpp-sdk)
42
+ - [LabVIEW](https://github.com/HORIBAEzSpecSDK/labview-sdk)
38
43
 
39
- * Python `>=3.9`
40
- * ICL.exe installed as part of the Horiba SDK, licensed and activated
44
+ # ☑️ Prerequisites
41
45
 
46
+ To use this package, the following conditions must be met:
47
+ * Python `>=3.9` is installed
48
+ * 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
49
+ *
42
50
  <details>
43
51
  <summary>To make sure that the USB devices do not get disconnected, uncheck the following boxes in the properties</summary>
44
52
 
@@ -46,7 +54,7 @@ ___
46
54
 
47
55
  </details>
48
56
 
49
- ## 🛠️ Usage
57
+ # 🛠️ Getting Started
50
58
 
51
59
  <details>
52
60
  <summary>Video of the steps below</summary>
@@ -55,7 +63,7 @@ ___
55
63
 
56
64
  </details>
57
65
 
58
- 0. (Optional but recommended) Work in a virtual environment:
66
+ 1. (Optional but recommended) Work in a virtual environment:
59
67
 
60
68
  Navigate to the (empty) project folder you want to work and run:
61
69
 
@@ -116,17 +124,40 @@ ___
116
124
  ```bash
117
125
  python center_scan.py
118
126
  ```
127
+ # 🔗 Examples
128
+ ## Getting Started
129
+ The files in the folder [examples/asynchronous_examples/](examples/asynchronous_examples) can be used as a starting point for a custom application.
119
130
 
120
- ## 👩‍💻 First steps as contributor
131
+ ## Tests
132
+ The files in the folder [tests/test_single_devices](tests/test_single_devices) can be used to explore further functionality.
121
133
 
122
- ### Clone and setup the repo
134
+ ## Asynchronous vs Synchronous Examples
135
+ This SDK is based on Websocket communication. The nature of this communication is asynchronous by its design.
136
+ The way this SDK uses websockets is to send requests to the underlying `instrument control layer (ICL)` and get a (nearly immediate) reply back.
137
+ 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.
138
+ That means that every `async` function can be `awaited` immediately and handled just like any other function.
139
+ 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).
123
140
 
124
- 1. Clone the repo:
125
141
 
126
- ```bash
127
- git clone https://github.com/ThatsTheEnd/horiba-python-sdk.git
128
- cd horiba-python-sdk
129
- ```
142
+
143
+ # 🏗️ Architecture
144
+ 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:
145
+ ![SDK Split](docs/SDK_Overview_Dark.png#gh-dark-mode-only "SDK Split")
146
+ ![SDK Split](docs/SDK_Overview_Dark.png#gh-light-mode-only "SDK Split")
147
+
148
+ 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.
149
+ 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.
150
+
151
+ # 👩‍💻 First steps as contributor
152
+
153
+ ## Clone and set up the repo
154
+
155
+ 1. Clone the repo:
156
+
157
+ ```bash
158
+ git clone https://github.com/HORIBAEzSpecSDK/horiba-python-sdk.git
159
+ cd horiba-python-sdk
160
+ ```
130
161
 
131
162
  2. If you don't have `Poetry` installed run:
132
163
 
@@ -160,7 +191,7 @@ git push
160
191
  <!-- - 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. -->
161
192
  <!-- - Set up [Stale bot](https://github.com/apps/stale) for automatic issue closing. -->
162
193
 
163
- ### Poetry
194
+ ## Poetry
164
195
 
165
196
  Want to know more about Poetry? Check [its documentation](https://python-poetry.org/docs/).
166
197
 
@@ -178,7 +209,7 @@ etc
178
209
  </p>
179
210
  </details>
180
211
 
181
- ### Building and releasing your package
212
+ ## Building and releasing your package
182
213
 
183
214
  Building a new version of the application contains steps:
184
215
 
@@ -192,12 +223,12 @@ Building a new version of the application contains steps:
192
223
  git push --tags
193
224
  ```
194
225
 
195
- ### Makefile usage
226
+ ## Makefile usage
196
227
 
197
- [`Makefile`](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/Makefile) contains a lot of functions for faster development.
228
+ [`Makefile`](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/blob/master/Makefile) contains a lot of functions for faster development.
198
229
 
199
230
  <details>
200
- <summary>1. Download and remove Poetry</summary>
231
+ <summary>1. Installation of Poetry</summary>
201
232
  <p>
202
233
 
203
234
  To download and install Poetry run:
@@ -225,7 +256,7 @@ Install requirements:
225
256
  make install
226
257
  ```
227
258
 
228
- Pre-commit hooks coulb be installed after `git init` via
259
+ Pre-commit hooks could be installed after `git init` via
229
260
 
230
261
  ```bash
231
262
  make pre-commit-install
@@ -260,6 +291,8 @@ Update all dev libraries to the latest version using one comand
260
291
  ```bash
261
292
  make update-dev-deps
262
293
  ```
294
+ </p>
295
+ </details>
263
296
 
264
297
  <details>
265
298
  <summary>4. Code security</summary>
@@ -275,8 +308,6 @@ This command launches `Poetry` integrity checks as well as identifies security i
275
308
  make check-safety
276
309
  ```
277
310
 
278
- </p>
279
- </details>
280
311
 
281
312
  </p>
282
313
  </details>
@@ -379,7 +410,7 @@ Remove docker image with
379
410
  make docker-remove
380
411
  ```
381
412
 
382
- More information [about docker](https://github.com/ThatsTheEnd/horiba-python-sdk/tree/master/docker).
413
+ More information [about docker](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/tree/master/docker).
383
414
 
384
415
  </p>
385
416
  </details>
@@ -416,11 +447,10 @@ Or to remove all above run:
416
447
  ```bash
417
448
  make cleanup
418
449
  ```
419
-
420
450
  </p>
421
451
  </details>
422
452
 
423
- ## 📚 Documentation
453
+ # 📚 Documentation
424
454
 
425
455
  The latest documentation can be found at
426
456
  [horiba-python-sdk.readthedocs.io](https://horiba-python-sdk.readthedocs.io/en/latest/).
@@ -436,63 +466,12 @@ Documentation is built each time a commit is pushed on `main` or for pull
436
466
  requests. When release tags are created in the repo, readthedocs will also tag
437
467
  the documentation accordingly
438
468
 
439
- ## 🚀 Features
440
-
441
- ### Development features
442
-
443
- - Supports for `Python 3.9` and higher.
444
- - [`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).
445
- - Automatic codestyle with [`ruff`](https://github.com/astral-sh/ruff)
446
- - Ready-to-use [`pre-commit`](https://pre-commit.com/) hooks with code-formatting.
447
- - Type checks with [`mypy`](https://mypy.readthedocs.io); security checks with [`safety`](https://github.com/pyupio/safety) and [`bandit`](https://github.com/PyCQA/bandit)
448
- - Testing with [`pytest`](https://docs.pytest.org/en/latest/).
449
- - 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.
450
-
451
- ### Deployment features
452
-
453
- - `GitHub` integration: issue and pr templates.
454
- - `Github Actions` with predefined [build workflow](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/.github/workflows/build.yml) as the default CI/CD.
455
- - 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).
456
- - [Dockerfile](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/docker/Dockerfile) for your package.
457
- - 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).
458
- - 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.
459
-
460
- ### Open source community features
461
-
462
- - 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).
463
- - Files such as: `LICENSE`, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, and `SECURITY.md` are generated automatically.
464
- - [Semantic Versions](https://semver.org/) specification with [`Release Drafter`](https://github.com/marketplace/actions/release-drafter).
465
- <!-- - [`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). -->
466
-
467
-
468
- ## 📈 Releases
469
-
470
- You can see the list of available releases on the [GitHub Releases](https://github.com/ThatsTheEnd/horiba-python-sdk/releases) page.
471
-
472
- We follow [Semantic Versions](https://semver.org/) specification.
473
-
474
- <!-- 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. -->
475
-
476
- ### List of labels and corresponding titles
477
-
478
- | **Label** | **Title in Releases** |
479
- | :-----------------------------------: | :---------------------: |
480
- | `enhancement`, `feature` | 🚀 Features |
481
- | `bug`, `refactoring`, `bugfix`, `fix` | 🔧 Fixes & Refactoring |
482
- | `build`, `ci`, `testing` | 📦 Build System & CI/CD |
483
- | `breaking` | 💥 Breaking Changes |
484
- | `documentation` | 📝 Documentation |
485
- | `dependencies` | ⬆️ Dependencies updates |
486
-
487
- <!-- You can update it in [`release-drafter.yml`](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/.github/release-drafter.yml). -->
488
-
489
- <!-- 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. -->
490
469
 
491
470
  ## 🛡 License
492
471
 
493
- [![License](https://img.shields.io/github/license/ThatsTheEnd/horiba-python-sdk)](https://github.com/ThatsTheEnd/horiba-python-sdk/blob/master/LICENSE)
472
+ [![License](https://img.shields.io/github/license/HORIBAEzSpecSDK/horiba-python-sdk)](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/blob/master/LICENSE)
494
473
 
495
- 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.
474
+ 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.
496
475
 
497
476
  ## 📃 Citation
498
477
 
@@ -503,10 +482,11 @@ This project is licensed under the terms of the `MIT` license. See [LICENSE](htt
503
482
  year = {2023},
504
483
  publisher = {GitHub},
505
484
  journal = {GitHub repository},
506
- howpublished = {\url{https://github.com/ThatsTheEnd/horiba-python-sdk}}
485
+ howpublished = {\url{https://github.com/HORIBAEzSpecSDK/horiba-python-sdk}}
507
486
  }
508
487
  ```
509
488
 
489
+
510
490
  ## 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)
511
491
 
512
492
  This project was generated with [`python-package-template`](https://github.com/TezRomacH/python-package-template)
@@ -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):
@@ -0,0 +1,20 @@
1
+ from enum import Enum
2
+ from typing import final
3
+
4
+
5
+ @final
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
+
17
+ SPECTRA = 0
18
+ IMAGE = 1
19
+ CROP = 2
20
+ FAST_KINETICS = 3
@@ -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
@@ -0,0 +1,14 @@
1
+ from enum import Enum
2
+ from typing import final
3
+
4
+
5
+ @final
6
+ class TimerResolution(Enum):
7
+ """Resolution for the timer for the acquisition time.
8
+
9
+ .. note:: The timer resolution value MICROSECONDS is not supported by all CCDs.
10
+ """
11
+
12
+ MILLISECONDS = 0
13
+ MICROSECONDS = 1
14
+ NOTHING_EVAL = 2
@@ -0,0 +1,18 @@
1
+ from enum import Enum
2
+ from typing import final
3
+
4
+
5
+ @final
6
+ class XAxisConversionType(Enum):
7
+ """
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
14
+ """
15
+
16
+ NONE = 0
17
+ FROM_CCD_FIRMWARE = 1
18
+ FROM_ICL_SETTINGS_INI = 2
@@ -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
 
@@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"
5
5
 
6
6
  [tool.poetry]
7
7
  name = "horiba-sdk"
8
- version = "0.4.0"
8
+ version = "0.5.4"
9
9
  description = "'horiba-sdk' is a package that provides source code for the development with Horiba devices"
10
10
  readme = "README.md"
11
11
  authors = ["ZühlkeEngineering <nikolaus.naredi-rainer@zuehlke.com>"]
@@ -1,10 +0,0 @@
1
- from enum import Enum
2
- from typing import final
3
-
4
-
5
- @final
6
- class AcquisitionFormat(Enum):
7
- SPECTRA = 0
8
- IMAGE = 1
9
- CROP = 2
10
- FAST_KINETICS = 3
@@ -1,12 +0,0 @@
1
- from enum import Enum
2
- from typing import final
3
-
4
-
5
- @final
6
- class TimerResolution(Enum):
7
- """
8
- .. note:: The timer resolution value of 1 microsecond is not supported by all CCDs.
9
- """
10
-
11
- _1000_MICROSECONDS = 0
12
- _1_MICROSECOND = 1
@@ -1,13 +0,0 @@
1
- from enum import Enum
2
- from typing import final
3
-
4
-
5
- @final
6
- class XAxisConversionType(Enum):
7
- """
8
- Enumeration of possible XAxisConversionTypes
9
- """
10
-
11
- NONE = 0
12
- FROM_CCD_FIRMWARE = 1
13
- FROM_ICL_SETTINGS_INI = 2
File without changes