Skip to content

Commit

Permalink
add first-class support for discrete hardware (#98)
Browse files Browse the repository at this point in the history
* add first-class support for discrete hardware

* add

* setter not getter (#94)

* setter not getter

* tests

* rm print

* add first-class support for discrete hardware

* add

* don't double count describe
  • Loading branch information
untzag authored Aug 14, 2023
1 parent c77686d commit 0ca5517
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 4 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).

## [Unreleased]

### Added
- daemons supporting is-discrete now can be set according to their identifiers

### Changed
- properties now appear as sub-devices

Expand Down
2 changes: 2 additions & 0 deletions tests/is_discrete/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[spectrometer]
port = 38383
67 changes: 67 additions & 0 deletions tests/is_discrete/test_is_discrete.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import pathlib
import numpy as np
import time
import subprocess
from yaqd_core import testing
import yaqc_bluesky
import bluesky
from bluesky import plan_stubs as bps


config = pathlib.Path(__file__).parent / "config.toml"


@testing.run_daemon_entry_point("fake-discrete-hardware", config=config)
def test_identifier_is_in_read():
d = yaqc_bluesky.Device(38383)
read_keys = list(d.read().keys())
assert f"{d.name}_position_identifier" in read_keys


@testing.run_daemon_entry_point("fake-discrete-hardware", config=config)
def test_identifier_is_in_describe():
d = yaqc_bluesky.Device(38383)
describe_keys = list(d.describe().keys())
assert f"{d.name}_position_identifier" in describe_keys


@testing.run_daemon_entry_point("fake-discrete-hardware", config=config)
def test_describe_read():
d = yaqc_bluesky.Device(38383)
describe_keys = list(d.describe().keys())
read_keys = list(d.read().keys())
assert describe_keys == read_keys


@testing.run_daemon_entry_point("fake-discrete-hardware", config=config)
def test_hint_fields():
d = yaqc_bluesky.Device(38383)
fields = d.hints["fields"]
for field in fields:
assert field in d.describe().keys()
assert field in d.read().keys()


@testing.run_daemon_entry_point("fake-discrete-hardware", config=config)
def test_set_read():
d = yaqc_bluesky.Device(38383)
d.set(470)
time.sleep(1)
out = d.read()
assert out[f"{d.name}_position_identifier"]["value"] == "blue"
d.set("green")
time.sleep(1)
out = d.read()
assert np.isclose(out[d.name]["value"], 540.0)


@testing.run_daemon_entry_point("fake-discrete-hardware", config=config)
def test_mv():
def plan():
d = yaqc_bluesky.Device(38383)
for identifier, position in d.yaq_client.get_position_identifiers().items():
yield from bps.mv(d, identifier)
assert np.isclose(d.read()[d.name]["value"], position)

RE = bluesky.RunEngine()
RE(plan())
2 changes: 1 addition & 1 deletion yaqc_bluesky/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def __init__(self, yaq_client, *, name=None):
for key, prop in self.yaq_client.properties.items():
if key in ["destination", "position"]:
continue
if prop.type not in ["double"]:
if prop.type not in ["double", "string"]:
continue
self.children.append(PropertyDevice(self, key))
if not hasattr(self, key): # don't overwrite please
Expand Down
4 changes: 3 additions & 1 deletion yaqc_bluesky/_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
from ._has_measure_trigger import HasMeasureTrigger
from ._has_mapping import HasMapping
from ._has_dependent import HasDependent
from ._is_discrete import IsDiscrete


traits = [
traits = [ # MRO goes from top to bottom
("is-discrete", IsDiscrete),
("has-position", HasPosition),
("has-measure-trigger", HasMeasureTrigger),
("has-mapping", HasMapping),
Expand Down
15 changes: 15 additions & 0 deletions yaqc_bluesky/_is_discrete.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from collections import OrderedDict
import time
import warnings

from ._has_position import HasPosition


class IsDiscrete(HasPosition):

def set(self, value):
try:
self.yaq_client.set_position(value)
except TypeError:
self.yaq_client.set_identifier(value)
return self._wait_until_still()
11 changes: 9 additions & 2 deletions yaqc_bluesky/_property.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ def __init__(self, parent, name):
self.parent = parent
self.name = name
self._yaq_property = self.parent.yaq_client.properties[self.name]
if self._yaq_property.type in ["int", "float", "double"]:
self._dtype = "number"
elif self._yaq_property.type == "string":
self._dtype = "string"
else:
self._dtype = "array"

self._setpoint = float("nan")

def set(self, value) -> Status:
Expand All @@ -30,10 +37,10 @@ def set(self, value) -> Status:

def describe(self) -> dict:
out = dict()
out[f"{self.parent.name}_{self.name}_readback"] = {"dtype": "number", "shape": [], "source": f"yaq:{self.parent.yaq_name}"
out[f"{self.parent.name}_{self.name}_readback"] = {"dtype": self._dtype, "shape": [], "source": f"yaq:{self.parent.yaq_name}"
}
if self._yaq_property._property["getter"]:
out[f"{self.parent.name}_{self.name}_setpoint"] = {"dtype": "number", "shape": [], "source": f"yaq:{self.parent.yaq_name}"}
out[f"{self.parent.name}_{self.name}_setpoint"] = {"dtype": self._dtype, "shape": [], "source": f"yaq:{self.parent.yaq_name}"}
return out

def read(self) -> dict:
Expand Down

0 comments on commit 0ca5517

Please sign in to comment.