Skip to content

Commit c71bf2a

Browse files
kameyer226pre-commit-ci[bot]ddkohler
authored
Fake has transformed position (#61)
* updated CI yamls * fixes has_transformed and adds fake_has_tranformed for testing * mod changelog * removed redundant method, added the fake-has-transformed-position files * tweaked workflows, modded set_position back to original super * tweaked workflow again * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added test folder and files for testing fake-has-transformed-position * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update .pre-commit-config.yaml * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update _has_transformed_position.py * Update _has_limits.py * Update _has_limits.py * Update _has_transformed_position.py * Update _has_limits.py * Update _has_transformed_position.py * refactor get_limits - reduce code redundancy - fix bug where native limits will not work with negative sloped transforms * handle changing transform rely on native coordinates tests pass * new htp tests set_relative change_native_reference * cleanup * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix tests * Update _has_limits.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update _has_limits.py * add fake-htp config param customizable transform slope * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * refactor * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * more testing test transform with negative slope * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * use tomli * kameyer fixes --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Daniel Kohler <11864045+ddkohler@users.noreply.github.com>
1 parent 660fca8 commit c71bf2a

14 files changed

Lines changed: 665 additions & 20 deletions

.github/workflows/python-pytest.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,3 @@ jobs:
3131
cd tests
3232
pytest
3333
34-

.github/workflows/run-entry-points.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,3 @@ jobs:
3838
yaqd-fake-sensor --help
3939
yaqd-fake-spectrometer --version
4040
yaqd-fake-spectrometer --help
41-
42-

tests/fake_furnace/test_fake_furnace.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ def test_ramp():
1919
c = yaqc.Client(39426)
2020
c.set_ramp_time(0)
2121
c.set_position(0)
22+
time.sleep(0.1)
2223
assert c.get_position() == 0
2324
c.set_ramp_time(1)
2425
c.set_position(100)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[fake-transformed1]
2+
port=38001
3+
velocity=100.0
4+
factor=2.0
5+
limits=[1.0,4.0]
6+
7+
[fake-transformed2]
8+
port=38002
9+
velocity=100.0
10+
factor=-1.0
11+
limits=[-1.0,-4.0]
12+
native_limits=[0.0,3.0]
13+
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import pathlib
2+
import subprocess
3+
import sys
4+
import time
5+
import numpy as np
6+
import tomli
7+
8+
import pytest
9+
10+
import yaqc
11+
import yaqd_core
12+
from yaqd_core import testing
13+
14+
15+
config = pathlib.Path(__file__).parent / "config.toml"
16+
17+
18+
@testing.run_daemon_entry_point("fake-has-transformed-position", config=config)
19+
def test_transform():
20+
c = yaqc.Client(38001)
21+
c.set_native_reference(1.0)
22+
val = 3.0
23+
bb = c.to_transformed(c.to_native(val))
24+
assert np.isclose(val, bb)
25+
26+
27+
@testing.run_daemon_entry_point("fake-has-transformed-position", config=config)
28+
def test_limits():
29+
for port in [38001, 38002]:
30+
c = yaqc.Client(port)
31+
config = tomli.loads(c.get_config())
32+
33+
limits = c.get_limits()
34+
lims2 = list(map(c.to_transformed, c.get_native_limits()))
35+
assert np.isclose(limits, lims2).all()
36+
37+
for setpoint, actual in zip(
38+
[limits[0] - 0.5, 0.5 * sum(limits), limits[1] + 0.5],
39+
[limits[0], 0.5 * sum(limits), limits[1]],
40+
):
41+
c.set_position(setpoint)
42+
time.sleep(0.10)
43+
assert np.isclose(c.get_position(), actual)
44+
assert np.isclose(c.get_destination(), actual)
45+
assert np.isclose(c.get_native_position(), c.to_native(actual))
46+
assert np.isclose(c.get_native_destination(), c.to_native(actual))
47+
48+
49+
@testing.run_daemon_entry_point("fake-has-transformed-position", config=config)
50+
def test_change_native_reference():
51+
for port in [38001, 38002]:
52+
c = yaqc.Client(port)
53+
factor = tomli.loads(c.get_config())["factor"]
54+
55+
midrange = 0.5 * sum(c.get_limits())
56+
57+
c.set_native_reference(1)
58+
c.set_position(midrange)
59+
time.sleep(0.1)
60+
native_position = c.get_native_position()
61+
assert np.isclose(native_position, c.to_native(midrange))
62+
c.set_native_reference(1.5)
63+
64+
assert np.isclose(native_position, c.get_native_position())
65+
assert np.isclose(
66+
c.get_position(), c.to_transformed(c.to_native(midrange) - 0.5)
67+
)
68+
69+
70+
@testing.run_daemon_entry_point("fake-has-transformed-position", config=config)
71+
def test_set_relative():
72+
c = yaqc.Client(38001)
73+
c.set_position(c.get_limits()[0])
74+
initial = c.get_position()
75+
time.sleep(0.1)
76+
destination = c.set_relative(1.0)
77+
time.sleep(0.1)
78+
79+
assert np.isclose(destination, initial + 1.0)
80+
81+
82+
if __name__ == "__main__":
83+
test_change_native_reference()
84+
test_limits()
85+
test_set_relative()
86+
test_transform()

yaqd-core/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
55

66
## [Unreleased]
77

8+
### Added
9+
- fake-has-transformed-position for testing purposes
10+
- has-transformed-position can read native_limits as a config option
11+
12+
### Fixed
13+
- Fixes has_transformed_position which had inheiritance issue
14+
815
### Changed
916
- Upgraded appdirs to platformdirs
1017
- moved to hatchling build system

yaqd-core/yaqd_core/_has_limits.py

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,40 @@ def __init__(
1515
self._out_of_limits = config["out_of_limits"]
1616

1717
def get_limits(self) -> List[float]:
18-
assert self._state["hw_limits"][0] < self._state["hw_limits"][1]
19-
config_limits = self._config["limits"]
20-
assert config_limits[0] < config_limits[1]
21-
out = [
22-
max(self._state["hw_limits"][0], config_limits[0]),
23-
min(self._state["hw_limits"][1], config_limits[1]),
24-
]
18+
# wrapper for client
19+
return self.limits
20+
21+
@property
22+
def limits(self) -> List[float]:
23+
# for internal use
24+
return self._joint_limit(self._state["hw_limits"], self._config["limits"])
25+
26+
@classmethod
27+
def _joint_limit(cls, *limits: List[float]):
28+
mins, maxes = [*zip(*limits)]
29+
assert all([mini < maxi for mini, maxi in zip(mins, maxes)])
30+
out = [max(*mins), min(*maxes)]
2531
assert out[0] < out[1]
2632
return out
2733

2834
def in_limits(self, position: float) -> bool:
29-
low, upp = self.get_limits()
35+
return self._in_limits(position)
36+
37+
def _in_limits(self, position):
38+
# for internal use
39+
low, upp = self.limits
3040
return low <= position <= upp
3141

3242
def set_position(self, position: float) -> None:
33-
if not self.in_limits(position):
43+
if not self._in_limits(position):
3444
if self._out_of_limits == "closest":
35-
low, upp = self.get_limits()
45+
low, upp = self.limits
3646
if position > upp:
3747
position = upp
3848
elif position < low:
3949
position = low
4050
elif self._out_of_limits == "ignore":
4151
return
4252
else:
43-
raise ValueError(f"{position} not in ranges {self.get_limits()}")
53+
raise ValueError(f"{position} not in ranges {self.limits}")
4454
super().set_position(position)

yaqd-core/yaqd_core/_has_transformed_position.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
import pathlib
55
from typing import Dict, Any, Optional, List
6-
76
from yaqd_core import HasLimits, HasPosition, IsDaemon
87

98

@@ -52,13 +51,20 @@ def _transformed_to_relative(self, transformed_position):
5251
"""
5352
return transformed_position
5453

55-
# --- methods for transformed positions -------------------------------------------------------
54+
# --- wrap parent messages that deal with position --------------------------------------------
55+
# --- clients use transformed position, but parent messages except/return native position.
5656

5757
def set_position(self, position: float) -> None:
5858
super().set_position(self.to_native(position))
5959

60+
def set_relative(self, distance: float) -> float:
61+
new = self.to_transformed(self._state["destination"]) + distance
62+
self.set_position(new)
63+
return new
64+
6065
def get_position(self) -> float:
61-
return self.to_transformed(super().get_position())
66+
position = super().get_position()
67+
return self.to_transformed(position)
6268

6369
def get_destination(self) -> float:
6470
return self.to_transformed(super().get_destination())
@@ -67,9 +73,18 @@ def in_limits(self, position: float) -> bool:
6773
return super().in_limits(self.to_native(position))
6874

6975
def get_limits(self) -> List[float]:
70-
return [self.to_transformed(lim) for lim in super().get_limits()]
76+
return sorted(map(self.to_transformed, self.limits))
77+
78+
# --- setting or returning native coordinates -------------------------------------------------
79+
# --- new messages introduce by this trait
7180

72-
# --- native properties -----------------------------------------------------------------------
81+
@property
82+
def limits(self) -> List[float]:
83+
return self._joint_limit(
84+
self._state["hw_limits"],
85+
sorted(map(self.to_native, self._config["limits"])),
86+
self._config["native_limits"],
87+
)
7388

7489
def get_native_reference(self) -> float:
7590
return self._state["native_reference_position"]
@@ -87,7 +102,7 @@ def get_native_destination(self) -> float:
87102
return self._state["destination"]
88103

89104
def get_native_limits(self) -> List[float]:
90-
return super().get_limits()
105+
return self.limits
91106

92107
def get_native_units(self) -> Optional[str]:
93108
return self._native_units

yaqd-fakes/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
77

88
### Added
99
- new fake-furnace daemon, useful for prototyping chemical engineering setups
10+
- new fake-has-transformed-position daemon
1011

1112
### Changed
1213
- moved to hatchling build system

yaqd-fakes/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This package contains the following daemon(s):
1313

1414
- [fake-camera](https://yaq.fyi/daemons/fake-camera)
1515
- [fake-continuous-hardware](https://yaq.fyi/daemons/fake-continuous-hardware)
16+
- [fake-has-transformed-position](https://yaq.fyi/daemons/fake-has-transformed-position)
1617
- [fake-discrete-hardware](https://yaq.fyi/daemons/fake-discrete-hardware)
1718
- [fake-furnace](https://yaq.fyi/daemons/fake-furnace)
1819
- [fake-has-turret](https://yaq.fyi/daemons/fake-has-turret)

0 commit comments

Comments
 (0)