horiba-sdk 0.4.0__tar.gz → 0.5.2__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.
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/PKG-INFO +64 -85
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/README.md +63 -84
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/__init__.py +6 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/communication/websocket_communicator.py +2 -2
- horiba_sdk-0.5.2/horiba_sdk/core/acquisition_format.py +20 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/core/clean_count_mode.py +2 -0
- horiba_sdk-0.5.2/horiba_sdk/core/timer_resolution.py +14 -0
- horiba_sdk-0.5.2/horiba_sdk/core/x_axis_conversion_type.py +18 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/devices/device_manager.py +11 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/devices/single_devices/abstract_device.py +38 -1
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/devices/single_devices/ccd.py +48 -9
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/devices/single_devices/monochromator.py +14 -1
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/sync/communication/websocket_communicator.py +1 -1
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/sync/devices/device_manager.py +1 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/sync/devices/single_devices/ccd.py +45 -7
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/sync/devices/single_devices/monochromator.py +14 -1
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/pyproject.toml +1 -1
- horiba_sdk-0.4.0/horiba_sdk/core/acquisition_format.py +0 -10
- horiba_sdk-0.4.0/horiba_sdk/core/timer_resolution.py +0 -12
- horiba_sdk-0.4.0/horiba_sdk/core/x_axis_conversion_type.py +0 -13
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/LICENSE +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/communication/__init__.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/communication/abstract_communicator.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/communication/communication_exception.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/communication/messages.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/core/__init__.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/core/resolution.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/devices/__init__.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/devices/abstract_device_discovery.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/devices/abstract_device_manager.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/devices/ccd_discovery.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/devices/fake_device_manager.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/devices/fake_icl_server.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/devices/fake_responses/ccd.json +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/devices/fake_responses/icl.json +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/devices/fake_responses/monochromator.json +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/devices/monochromator_discovery.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/devices/single_devices/__init__.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/icl_error/__init__.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/icl_error/abstract_error.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/icl_error/abstract_error_db.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/icl_error/error_list.json +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/icl_error/icl_error.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/icl_error/icl_error_db.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/sync/__init__.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/sync/communication/__init__.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/sync/communication/abstract_communicator.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/sync/communication/test_client.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/sync/devices/__init__.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/sync/devices/abstract_device_discovery.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/sync/devices/abstract_device_manager.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/sync/devices/device_discovery.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/sync/devices/fake_device_manager.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/sync/devices/fake_icl_server.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/sync/devices/single_devices/__init__.py +0 -0
- {horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/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.
|
3
|
+
Version: 0.5.2
|
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
|
-
#
|
28
|
+
# HORIBA Python SDK
|
29
29
|
|
30
30
|
<div align="center">
|
31
31
|
|
32
|
-
[](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/actions/workflows/build.yml)
|
33
33
|
[](https://pypi.org/project/horiba-sdk/)
|
34
34
|
[](https://pypi.org/project/horiba-sdk/)
|
35
|
-
[](https://github.com/
|
35
|
+
[](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/pulls?utf8=%E2%9C%93&q=is%3Apr%20author%3Aapp%2Fdependabot)
|
36
36
|
|
37
37
|
[](https://github.com/astral-sh/ruff)
|
38
38
|
[](https://github.com/PyCQA/bandit)
|
39
|
-
[](https://github.com/
|
40
|
-
[](https://github.com/
|
41
|
-
[](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/blob/master/.pre-commit-config.yaml)
|
40
|
+
[](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/releases)
|
41
|
+
[](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/blob/master/LICENSE)
|
42
42
|

|
43
43
|
[](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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
##
|
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
|
-
|
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
|
-
|
154
|
-
|
155
|
-
|
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
|
+

|
173
|
+

|
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
|
-
|
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
|
-
|
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
|
-
|
253
|
+
## Makefile usage
|
223
254
|
|
224
|
-
[`Makefile`](https://github.com/
|
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.
|
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
|
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/
|
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
|
-
|
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
|
-
[](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/
|
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,7 +509,7 @@ 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/
|
512
|
+
howpublished = {\url{https://github.com/HORIBAEzSpecSDK/horiba-python-sdk}}
|
534
513
|
}
|
535
514
|
```
|
536
515
|
|
@@ -1,21 +1,20 @@
|
|
1
|
-
#
|
1
|
+
# HORIBA Python SDK
|
2
2
|
|
3
3
|
<div align="center">
|
4
4
|
|
5
|
-
[](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/actions/workflows/build.yml)
|
6
6
|
[](https://pypi.org/project/horiba-sdk/)
|
7
7
|
[](https://pypi.org/project/horiba-sdk/)
|
8
|
-
[](https://github.com/
|
8
|
+
[](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/pulls?utf8=%E2%9C%93&q=is%3Apr%20author%3Aapp%2Fdependabot)
|
9
9
|
|
10
10
|
[](https://github.com/astral-sh/ruff)
|
11
11
|
[](https://github.com/PyCQA/bandit)
|
12
|
-
[](https://github.com/
|
13
|
-
[](https://github.com/
|
14
|
-
[](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/blob/master/.pre-commit-config.yaml)
|
13
|
+
[](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/releases)
|
14
|
+
[](https://github.com/HORIBAEzSpecSDK/horiba-python-sdk/blob/master/LICENSE)
|
15
15
|

|
16
16
|
[](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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
##
|
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
|
-
|
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
|
-
|
127
|
-
|
128
|
-
|
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
|
+

|
146
|
+

|
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
|
-
|
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
|
-
|
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
|
-
|
226
|
+
## Makefile usage
|
196
227
|
|
197
|
-
[`Makefile`](https://github.com/
|
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.
|
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
|
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/
|
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
|
-
|
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
|
-
[](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/
|
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,7 +482,7 @@ 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/
|
485
|
+
howpublished = {\url{https://github.com/HORIBAEzSpecSDK/horiba-python-sdk}}
|
507
486
|
}
|
508
487
|
```
|
509
488
|
|
@@ -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.
|
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.
|
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
|
@@ -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,
|
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
|
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
|
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('
|
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
|
600
|
+
async def acquisition_abort(self) -> None:
|
580
601
|
"""Stops the acquisition of the CCD"""
|
581
|
-
await super()._execute_command('
|
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
|
-
) ->
|
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
|
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
|
|
{horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/sync/communication/websocket_communicator.py
RENAMED
@@ -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.
|
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()
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from types import TracebackType
|
2
|
-
from typing import Any,
|
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
|
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
|
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('
|
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
|
587
|
+
def acquisition_abort(self) -> None:
|
567
588
|
"""Stops the acquisition of the CCD"""
|
568
|
-
super()._execute_command('
|
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
|
-
) ->
|
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
|
{horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/sync/devices/single_devices/monochromator.py
RENAMED
@@ -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
|
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.
|
8
|
+
version = "0.5.2"
|
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>"]
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/sync/communication/abstract_communicator.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{horiba_sdk-0.4.0 → horiba_sdk-0.5.2}/horiba_sdk/sync/devices/single_devices/abstract_device.py
RENAMED
File without changes
|