scietex.hal.qcm 1.0.0__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 (28) hide show
  1. scietex_hal_qcm-1.0.0/LICENSE +21 -0
  2. scietex_hal_qcm-1.0.0/PKG-INFO +90 -0
  3. scietex_hal_qcm-1.0.0/README.md +45 -0
  4. scietex_hal_qcm-1.0.0/pyproject.toml +52 -0
  5. scietex_hal_qcm-1.0.0/setup.cfg +4 -0
  6. scietex_hal_qcm-1.0.0/src/scietex/hal/qcm/__init__.py +4 -0
  7. scietex_hal_qcm-1.0.0/src/scietex/hal/qcm/base/__init__.py +3 -0
  8. scietex_hal_qcm-1.0.0/src/scietex/hal/qcm/base/data.py +84 -0
  9. scietex_hal_qcm-1.0.0/src/scietex/hal/qcm/base/ftm_calculation.py +21 -0
  10. scietex_hal_qcm-1.0.0/src/scietex/hal/qcm/base/ftm_materials_data.py +63 -0
  11. scietex_hal_qcm-1.0.0/src/scietex/hal/qcm/base/gated_ftm.py +235 -0
  12. scietex_hal_qcm-1.0.0/src/scietex/hal/qcm/base/rs485/__init__.py +3 -0
  13. scietex_hal_qcm-1.0.0/src/scietex/hal/qcm/base/rs485/gated_ftm.py +326 -0
  14. scietex_hal_qcm-1.0.0/src/scietex/hal/qcm/base/serial/__init__.py +3 -0
  15. scietex_hal_qcm-1.0.0/src/scietex/hal/qcm/base/serial/gated_ftm.py +323 -0
  16. scietex_hal_qcm-1.0.0/src/scietex/hal/qcm/cyky/__init__.py +3 -0
  17. scietex_hal_qcm-1.0.0/src/scietex/hal/qcm/cyky/tm106b.py +411 -0
  18. scietex_hal_qcm-1.0.0/src/scietex/hal/qcm/scietex/__init__.py +4 -0
  19. scietex_hal_qcm-1.0.0/src/scietex/hal/qcm/scietex/ftm_one.py +480 -0
  20. scietex_hal_qcm-1.0.0/src/scietex/hal/qcm/scietex/ftm_one_usb.py +618 -0
  21. scietex_hal_qcm-1.0.0/src/scietex/hal/qcm/scietex/utils.py +21 -0
  22. scietex_hal_qcm-1.0.0/src/scietex/hal/qcm/version.py +8 -0
  23. scietex_hal_qcm-1.0.0/src/scietex.hal.qcm.egg-info/PKG-INFO +90 -0
  24. scietex_hal_qcm-1.0.0/src/scietex.hal.qcm.egg-info/SOURCES.txt +26 -0
  25. scietex_hal_qcm-1.0.0/src/scietex.hal.qcm.egg-info/dependency_links.txt +1 -0
  26. scietex_hal_qcm-1.0.0/src/scietex.hal.qcm.egg-info/requires.txt +12 -0
  27. scietex_hal_qcm-1.0.0/src/scietex.hal.qcm.egg-info/top_level.txt +1 -0
  28. scietex_hal_qcm-1.0.0/tests/test_version.py +47 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Anton Bondarenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,90 @@
1
+ Metadata-Version: 2.4
2
+ Name: scietex.hal.qcm
3
+ Version: 1.0.0
4
+ Summary: Scietex Quatz Microbalance HAL package
5
+ Author-email: Anton Bondarenko <bond.anton@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Anton Bondarenko
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ Project-URL: Homepage, https://github.com/bond-anton/scietex.hal.qcm
28
+ Project-URL: Bug Tracker, https://github.com/bond-anton/scietex.hal.qcm/issues
29
+ Classifier: Programming Language :: Python :: 3
30
+ Classifier: License :: OSI Approved :: MIT License
31
+ Classifier: Operating System :: OS Independent
32
+ Requires-Python: >=3.9
33
+ Description-Content-Type: text/markdown
34
+ License-File: LICENSE
35
+ Requires-Dist: msgspec[yaml]~=0.19.0
36
+ Requires-Dist: scietex.hal.serial<2.0.0,>=1.0.5
37
+ Provides-Extra: dev
38
+ Provides-Extra: test
39
+ Requires-Dist: flake8; extra == "test"
40
+ Requires-Dist: pytest; extra == "test"
41
+ Requires-Dist: pytest-asyncio; extra == "test"
42
+ Provides-Extra: lint
43
+ Requires-Dist: pylint; extra == "lint"
44
+ Dynamic: license-file
45
+
46
+ # scietex.hal.qcm
47
+
48
+ The `scietex.hal.qcm` package is a Python library for interfacing with Quartz Microbalance (QCM).
49
+ It provides an abstract base class (`QCM`), which in its turn inherits `scietex.hal.serial.RS485Client`.
50
+ The concrete implementations for two models of QCM manufactured by Scietex and CYKY are provided.
51
+
52
+ ## Features
53
+ - Abstract base class (`VFD`) for consistent VFD implementations.
54
+ - Implemented VFD models:
55
+ - `Scietex ftmONE`.
56
+ - `CYKY TM106B`.
57
+
58
+ ## Installation
59
+ Install the package via pip (assuming it’s published to PyPI):
60
+ ```bash
61
+ pip install scietex.hal.qcm
62
+ ```
63
+
64
+ Alternatively, clone the repository and install locally:
65
+ ```bash
66
+ git clone https://github.com/bond-anton/scietex.hal.qcm.git
67
+ cd scietex.hal.qcm
68
+ pip install .
69
+ ```
70
+
71
+ ## Requirements
72
+
73
+ - Python 3.9 or higher.
74
+ - `scietex.hal.serial` (For RS485 communication support).
75
+
76
+ ## Contributing
77
+
78
+ Contributions are welcome! To contribute:
79
+
80
+ 1. Fork the repository.
81
+ 2. Create a feature branch (`git checkout -b feature/YourFeature`).
82
+ 3. Commit your changes (`git commit -m "Add your message"`).
83
+ 4. Push to the branch (`git push origin feature/YourFeature`).
84
+ 5. Open a Pull Request.
85
+
86
+ Please include tests (if applicable) and follow PEP 8 style guidelines.
87
+
88
+ ## License
89
+
90
+ This project is licensed under the **MIT License** - see the LICENSE file for details.
@@ -0,0 +1,45 @@
1
+ # scietex.hal.qcm
2
+
3
+ The `scietex.hal.qcm` package is a Python library for interfacing with Quartz Microbalance (QCM).
4
+ It provides an abstract base class (`QCM`), which in its turn inherits `scietex.hal.serial.RS485Client`.
5
+ The concrete implementations for two models of QCM manufactured by Scietex and CYKY are provided.
6
+
7
+ ## Features
8
+ - Abstract base class (`VFD`) for consistent VFD implementations.
9
+ - Implemented VFD models:
10
+ - `Scietex ftmONE`.
11
+ - `CYKY TM106B`.
12
+
13
+ ## Installation
14
+ Install the package via pip (assuming it’s published to PyPI):
15
+ ```bash
16
+ pip install scietex.hal.qcm
17
+ ```
18
+
19
+ Alternatively, clone the repository and install locally:
20
+ ```bash
21
+ git clone https://github.com/bond-anton/scietex.hal.qcm.git
22
+ cd scietex.hal.qcm
23
+ pip install .
24
+ ```
25
+
26
+ ## Requirements
27
+
28
+ - Python 3.9 or higher.
29
+ - `scietex.hal.serial` (For RS485 communication support).
30
+
31
+ ## Contributing
32
+
33
+ Contributions are welcome! To contribute:
34
+
35
+ 1. Fork the repository.
36
+ 2. Create a feature branch (`git checkout -b feature/YourFeature`).
37
+ 3. Commit your changes (`git commit -m "Add your message"`).
38
+ 4. Push to the branch (`git push origin feature/YourFeature`).
39
+ 5. Open a Pull Request.
40
+
41
+ Please include tests (if applicable) and follow PEP 8 style guidelines.
42
+
43
+ ## License
44
+
45
+ This project is licensed under the **MIT License** - see the LICENSE file for details.
@@ -0,0 +1,52 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "scietex.hal.qcm"
7
+ dynamic = ["version"]
8
+ description = "Scietex Quatz Microbalance HAL package"
9
+ authors = [
10
+ { name = "Anton Bondarenko", email = "bond.anton@gmail.com" },
11
+ ]
12
+ license = { file = "LICENSE" }
13
+ readme = "README.md"
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ ]
19
+ requires-python = ">=3.9"
20
+ dependencies = [
21
+ "msgspec[yaml] ~= 0.19.0",
22
+ "scietex.hal.serial >= 1.0.5, < 2.0.0",
23
+ ]
24
+
25
+ [project.urls]
26
+ "Homepage" = "https://github.com/bond-anton/scietex.hal.qcm"
27
+ "Bug Tracker" = "https://github.com/bond-anton/scietex.hal.qcm/issues"
28
+
29
+ [project.optional-dependencies]
30
+ dev = []
31
+ test = ["flake8", "pytest", "pytest-asyncio"]
32
+ lint = ["pylint"]
33
+
34
+ [tool.setuptools.dynamic]
35
+ version = {attr = "scietex.hal.qcm.version.__version__"}
36
+
37
+ [tool.setuptools.package-data]
38
+ "scietex.hal.qcm" = ["py.typed"]
39
+
40
+ [tool.setuptools.packages.find]
41
+ where = ["src"]
42
+
43
+ [tool.black]
44
+ line-length = 100
45
+
46
+ [tool.mypy]
47
+ python_version = "3.9"
48
+
49
+ [tool.pytest.ini_options]
50
+ pythonpath = [
51
+ "src"
52
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,4 @@
1
+ """QCM HAL module"""
2
+
3
+ from .version import __version__
4
+ from .base import GatedFTM
@@ -0,0 +1,3 @@
1
+ """Base QCM classes to be inherited in particular device implementation."""
2
+
3
+ from .gated_ftm import GatedFTM
@@ -0,0 +1,84 @@
1
+ """Data models for the QCM."""
2
+
3
+ from enum import Enum
4
+ import msgspec
5
+
6
+
7
+ class OutCTRLMode(Enum):
8
+ """Modes of the control output."""
9
+
10
+ HIGH = 1
11
+ LOW = 2
12
+ MANUAL = 3
13
+ DISABLED = 4
14
+
15
+
16
+ class PwmCTRLMode(Enum):
17
+ """Modes of the PWM output."""
18
+
19
+ ABSOLUTE = 1
20
+ RELATIVE = 2
21
+ MANUAL = 3
22
+ DISABLED = 4
23
+
24
+
25
+ # pylint: disable=too-few-public-methods
26
+ class QCMError(msgspec.Struct, frozen=True):
27
+ """Model of a QCM error with code and message."""
28
+
29
+ code: int
30
+ message: str = "Unknown error"
31
+
32
+ def __str__(self) -> str:
33
+ return f"QCMError(code={self.code}, message='{self.message}')"
34
+
35
+
36
+ # pylint: disable=too-few-public-methods
37
+ class FTMParameters(msgspec.Struct, frozen=True):
38
+ """Model for FTM operational parameters."""
39
+
40
+ frequency: float = 0.0 # Sensor frequency
41
+ frequency_std: float = 0.0 # Sensor frequency standard deviation
42
+ averaging_window: int = 1 # Averaging window size
43
+ averaging_progress: int = 1 # Averaging accumulation progress
44
+ rate: float = 0.0 # Deposition rate A/s
45
+ thickness: float = 0.0 # Film thickness A
46
+ thickness_std: float = 0.0 # Film thickness standard deviation A
47
+ target: float = 0.0 # Film target thickness A
48
+ material_density: float = 0.0 # Material density g/cm^3
49
+ material_z_ratio: float = 0.0 # Material Z-ratio
50
+ running: bool = False # Current FTM measurement running state
51
+ scale: float = 1.0 # Scale (tooling) factor
52
+
53
+ def __str__(self) -> str:
54
+ return (
55
+ f"FTMParameters(frequency={self.frequency:g} +- {self.frequency_std:g} Hz, "
56
+ f"averaging={self.averaging_progress}/{self.averaging_window}, "
57
+ f"deposition rate={self.rate} A/s, "
58
+ f"film thickness={self.thickness} +- {self.thickness_std} A, "
59
+ f"target thickness={self.target} A, "
60
+ f"material density={self.material_density} g/cm^3, "
61
+ f"material Z-ratio={self.material_z_ratio}, "
62
+ f"running={self.running})"
63
+ )
64
+
65
+
66
+ # pylint: disable=too-few-public-methods
67
+ class FTMStartCMD(msgspec.Struct, frozen=True):
68
+ """FTM start command with parameters."""
69
+
70
+ reset: bool = True # Reset thickness at start
71
+
72
+ def __str__(self) -> str:
73
+ return f"FTMStartCMD(reset={self.reset})"
74
+
75
+
76
+ # pylint: disable=too-few-public-methods
77
+ class Material(msgspec.Struct, frozen=True):
78
+ """Material parameters."""
79
+
80
+ density: float # Density g/cm^3
81
+ z_ratio: float # Z-ratio
82
+
83
+ def __str__(self) -> str:
84
+ return f"Material(density={self.density}, Z-ratio={self.z_ratio})"
@@ -0,0 +1,21 @@
1
+ """Functions for film thickness calculation"""
2
+
3
+ import math as m
4
+
5
+
6
+ NQ = 1.668e5 # Hz*cm Frequency const of AT-cut quartz
7
+ RQ = 2.648 # g/cm^3 Density of quartz
8
+ MQ = 2.947e11 # g/(cm*s^2) Shear modulus of quartz
9
+
10
+ NRQ = 140593.14771292824 # NQ * RQ / m.pi [Hz * g / cm^2]
11
+
12
+
13
+ def freq_change_to_mass_per_cm2(f0, f1, z):
14
+ """Calculate film mass per cm2 (g / cm^2)"""
15
+ return NRQ / (z * f1) * m.atan(z * m.tan(m.pi * (f0 - f1) / f0)) # g / cm^2
16
+
17
+
18
+ def freq_change_to_thickness(f0, f1, rho, z):
19
+ """Calculate film thickness (A)"""
20
+ m_per_cm2 = freq_change_to_mass_per_cm2(f0, f1, z)
21
+ return m_per_cm2 / rho * 1e8 # Angstrom
@@ -0,0 +1,63 @@
1
+ """
2
+ Reference dict for different materials.
3
+ Density is given in g/cm^3.
4
+ """
5
+
6
+ materials = {
7
+ "Ag": {
8
+ "density": 10.5,
9
+ "z_ratio": 0.53,
10
+ },
11
+ "Al": {
12
+ "density": 2.7,
13
+ "z_ratio": 1.08,
14
+ },
15
+ "Au": {
16
+ "density": 19.3,
17
+ "z_ratio": 0.38,
18
+ },
19
+ "C": {
20
+ "density": 2.25,
21
+ "z_ratio": 3.26,
22
+ },
23
+ "Cr": {
24
+ "density": 7.2,
25
+ "z_ratio": 0.31,
26
+ },
27
+ "Cu": {
28
+ "density": 8.93,
29
+ "z_ratio": 0.44,
30
+ },
31
+ "Mo": {
32
+ "density": 10.2,
33
+ "z_ratio": 0.26,
34
+ },
35
+ "Ni": {
36
+ "density": 8.91,
37
+ "z_ratio": 0.33,
38
+ },
39
+ "Pd": {
40
+ "density": 12.04,
41
+ "z_ratio": 0.36,
42
+ },
43
+ "Pt": {
44
+ "density": 21.4,
45
+ "z_ratio": 0.25,
46
+ },
47
+ "Ti": {
48
+ "density": 4.5,
49
+ "z_ratio": 0.63,
50
+ },
51
+ "V": {
52
+ "density": 5.96,
53
+ "z_ratio": 0.53,
54
+ },
55
+ "W": {
56
+ "density": 19.3,
57
+ "z_ratio": 0.16,
58
+ },
59
+ "Zn": {
60
+ "density": 7.04,
61
+ "z_ratio": 0.51,
62
+ },
63
+ }
@@ -0,0 +1,235 @@
1
+ """Gated FTM abstract interface."""
2
+
3
+ from abc import ABC, abstractmethod
4
+
5
+ from .data import QCMError, PwmCTRLMode, OutCTRLMode, FTMParameters, Material
6
+
7
+
8
+ # pylint: disable=too-many-public-methods
9
+ class GatedFTM(ABC):
10
+ """Gated Film Thickness Monitor interface class."""
11
+
12
+ error_codes: dict[int, QCMError] = {0: QCMError(code=0, message="No error")}
13
+ error_codes_com: dict[int, QCMError] = {0: QCMError(code=0, message="No error")}
14
+
15
+ # Error processing methods
16
+ @abstractmethod
17
+ async def get_error_code(self, comm: bool = False) -> int:
18
+ """Read error code from the FTM"""
19
+
20
+ def parse_error_code(self, code: int, comm: bool = False) -> QCMError:
21
+ """Parse error code from the FTM"""
22
+ if comm:
23
+ if code in self.error_codes_com:
24
+ return self.error_codes_com[code]
25
+ else:
26
+ if code in self.error_codes:
27
+ return self.error_codes[code]
28
+ return QCMError(code=code, message="Unknown error")
29
+
30
+ @abstractmethod
31
+ async def clear_error(self, comm: bool = False) -> int:
32
+ """Clear error from the FTM"""
33
+
34
+ # Device information methods
35
+ @abstractmethod
36
+ async def get_vendor(self) -> str:
37
+ """Get QCM vendor name."""
38
+
39
+ @abstractmethod
40
+ async def get_product_name(self) -> str:
41
+ """Get QCM product name."""
42
+
43
+ @abstractmethod
44
+ async def get_serial_number(self) -> str:
45
+ """Get QCM serial number."""
46
+
47
+ @abstractmethod
48
+ async def get_version(self) -> str:
49
+ """get QCM firmware version"""
50
+
51
+ # FTM measurement setup
52
+
53
+ @abstractmethod
54
+ async def get_mcu_frequency(self) -> int:
55
+ """GET MCU frequency."""
56
+
57
+ @abstractmethod
58
+ async def get_gate_time(self) -> float:
59
+ """Get gate time in ms"""
60
+
61
+ @abstractmethod
62
+ async def set_gate_time(self, gate_time_ms: float) -> float:
63
+ """Set gate time in ms"""
64
+
65
+ @abstractmethod
66
+ async def get_averaging(self) -> int:
67
+ """Get averaging window"""
68
+
69
+ @abstractmethod
70
+ async def set_averaging(self, averaging_window: int) -> int:
71
+ """Set averaging window."""
72
+
73
+ # FTM frequency measurement data access methods
74
+
75
+ @abstractmethod
76
+ async def get_counter(self) -> int:
77
+ """Get FTM raw counts."""
78
+
79
+ @abstractmethod
80
+ async def get_frequency_instant(self) -> float:
81
+ """Get instant frequency."""
82
+
83
+ @abstractmethod
84
+ async def get_averaging_progress(self) -> int:
85
+ """Get averaging accumulation progress."""
86
+
87
+ @abstractmethod
88
+ async def get_frequency(self) -> float:
89
+ """Get frequency."""
90
+
91
+ @abstractmethod
92
+ async def get_frequency_std(self) -> float:
93
+ """Get frequency standard deviation."""
94
+
95
+ # Thickness measurement methods
96
+
97
+ @abstractmethod
98
+ async def get_thickness(self) -> float:
99
+ """Get thickness (Angstrom) value."""
100
+
101
+ @abstractmethod
102
+ async def get_thickness_std(self) -> float:
103
+ """Parse thickness (Angstrom) standard deviation."""
104
+
105
+ @abstractmethod
106
+ async def get_rate(self) -> float:
107
+ """Parse rate (Angstrom/s) value from register data"""
108
+
109
+ @abstractmethod
110
+ async def get_material_density(self) -> float:
111
+ """Get material density g/cm3"""
112
+
113
+ @abstractmethod
114
+ async def set_material_density(self, density: float) -> float:
115
+ """Set material density g/cm3."""
116
+
117
+ @abstractmethod
118
+ async def get_material_z_ratio(self) -> float:
119
+ """Get material Z-ratio."""
120
+
121
+ @abstractmethod
122
+ async def set_material_z_ratio(self, z_ratio: float) -> float:
123
+ """Set material Z-ratio."""
124
+
125
+ @abstractmethod
126
+ async def get_material(self) -> Material:
127
+ """Get deposition material density and Z-ratio"""
128
+
129
+ @abstractmethod
130
+ async def set_material(self, material: Material) -> Material:
131
+ """Set deposition material density and Z-ratio"""
132
+
133
+ @abstractmethod
134
+ async def get_ftm_scale(self) -> float:
135
+ """Get FTM scale factor."""
136
+
137
+ @abstractmethod
138
+ async def set_ftm_scale(self, scale: float) -> float:
139
+ """Set FTM scale factor."""
140
+
141
+ @abstractmethod
142
+ async def get_target_thickness(self) -> float:
143
+ """Get target thickness (Angstrom)."""
144
+
145
+ @abstractmethod
146
+ async def set_target_thickness(self, target: float) -> float:
147
+ """Set target thickness (Angstrom)."""
148
+
149
+ async def get_target_nm(self) -> float:
150
+ """Get target thickness (nm)."""
151
+ return await self.get_target_thickness() / 10
152
+
153
+ async def set_target_nm(self, target: float) -> float:
154
+ """Set target thickness (nm)."""
155
+ return await self.set_target_thickness(target * 10)
156
+
157
+ async def get_target_um(self) -> float:
158
+ """Get target thickness (nm)."""
159
+ return await self.get_target_thickness() / 10_000
160
+
161
+ async def set_target_um(self, target: float) -> float:
162
+ """Set target thickness (um)."""
163
+ return await self.set_target_thickness(target * 10_000)
164
+
165
+ @abstractmethod
166
+ async def reset_thickness(self) -> None:
167
+ """Reset measured thickness."""
168
+
169
+ # CTRL OUT parameters
170
+
171
+ @abstractmethod
172
+ async def get_ctrl_out_mode(self) -> OutCTRLMode:
173
+ """Get the mode of the control output."""
174
+
175
+ @abstractmethod
176
+ async def set_ctrl_out_mode(self, mode: OutCTRLMode) -> OutCTRLMode:
177
+ """Set the mode of the control output."""
178
+
179
+ @abstractmethod
180
+ async def get_ctrl_out_value(self) -> bool:
181
+ """Get control output value."""
182
+
183
+ @abstractmethod
184
+ async def set_ctrl_out_value(self, value: bool) -> bool:
185
+ """Set control output value."""
186
+
187
+ @abstractmethod
188
+ async def get_ctrl_pwm_mode(self) -> PwmCTRLMode:
189
+ """Get the mode of the PWM output."""
190
+
191
+ @abstractmethod
192
+ async def set_ctrl_pwm_mode(self, mode: PwmCTRLMode) -> PwmCTRLMode:
193
+ """Set the mode of the PWM output."""
194
+
195
+ @abstractmethod
196
+ async def get_ctrl_pwm_value(self) -> float:
197
+ """Get PWM output value."""
198
+
199
+ @abstractmethod
200
+ async def set_ctrl_pwm_value(self, value: float) -> float:
201
+ """Set PWM output value."""
202
+
203
+ @abstractmethod
204
+ async def get_ctrl_pwm_scale(self) -> int:
205
+ """Get PWM output scale."""
206
+
207
+ @abstractmethod
208
+ async def set_ctrl_pwm_scale(self, scale: int) -> int:
209
+ """Set PWM output scale."""
210
+
211
+ # MEASUREMENT START/STOP
212
+
213
+ @abstractmethod
214
+ async def get_running_state(self) -> bool:
215
+ """Get running state."""
216
+
217
+ @abstractmethod
218
+ async def set_running_state(self, running: bool) -> bool:
219
+ """Set running state."""
220
+
221
+ async def start_measurement(self, reset: bool = True) -> bool:
222
+ """Start measurement"""
223
+ if reset:
224
+ await self.reset_thickness()
225
+ return await self.set_running_state(True)
226
+
227
+ async def stop_measurement(self) -> bool:
228
+ """Stop measurement."""
229
+ return await self.set_running_state(False)
230
+
231
+ # Read FTM parameters in single call
232
+
233
+ @abstractmethod
234
+ async def read_parameters(self) -> FTMParameters:
235
+ """Read FTM Parameters"""
@@ -0,0 +1,3 @@
1
+ """RS485 devices."""
2
+
3
+ from .gated_ftm import RS485GatedFTM