Skip to content

Commit

Permalink
Merge c18d860 into 047daf4
Browse files Browse the repository at this point in the history
  • Loading branch information
prjemian committed Apr 4, 2024
2 parents 047daf4 + c18d860 commit 5dde9a1
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 32 deletions.
3 changes: 2 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@ describe future plans.
1.6.19
******

release expected by 2024-04-02
release expected by 2024-04-12

Fixes
-----

* lineup2() should work with low intensity peaks.
* lineup2() would raise ZeroDivideError in some cases.
* Increase minimum aps-dm-api version to 8.
* PVPositionerSoftDone should set 'done' to False at start of a move.

Maintenance
-----------
Expand Down
24 changes: 10 additions & 14 deletions apstools/devices/positioner_soft_done.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,6 @@ class PVPositionerSoftDone(PVPositioner):

target = Component(Signal, value="None", kind="config")

_rb_count = 0
_sp_count = 0

def __init__(
self,
prefix="",
Expand Down Expand Up @@ -171,8 +168,6 @@ def cb_readback(self, *args, **kwargs):
if idle:
return

self._rb_count += 1

if self.inposition:
self.done.put(self.done_value)
if self.report_dmov_changes.get():
Expand All @@ -184,15 +179,11 @@ def cb_setpoint(self, *args, **kwargs):
When the setpoint is changed, force`` done=False``. For any move, ``done``
**must** transition to ``!= done_value``, then back to ``done_value``.
Without this response, a small move (within tolerance) will not return.
The ``cb_readback()`` method will compute ``done``.
Since other code will also call this method, check the keys in kwargs
and do not react to the "wrong" signature.
"""
if "value" in kwargs and "status" not in kwargs:
self._sp_count += 1
# Only update when the expected kwargs are received.
self.done.put(not self.done_value)
logger.debug("cb_setpoint: done=%s, setpoint=%s", self.done.get(), self.setpoint.get())

Expand Down Expand Up @@ -226,14 +217,19 @@ def _setup_move(self, position):
if issubclass(self.target.__class__, EpicsSignalBase):
kwargs["wait"] = True # Signal.put() warns if kwargs are given
self.target.put(position, **kwargs)

# Write the setpoint value.
self.setpoint.put(position, wait=True)
# A new move requires done to become unset (here).
# Move is finished (in cb_readback()) when done is reset.
self.done.put(not self.done_value)

if self.actuate is not None:
self.log.debug("%s.actuate = %s", self.name, self.actuate_value)
self.actuate.put(self.actuate_value, wait=False)
# This is needed because in a special case the setpoint.put does not
# run the "sub_value" subscriptions.
self.cb_setpoint()
self.cb_readback() # This is needed to force the first check.

# Force the first check for done.
self.cb_readback()


class PVPositionerSoftDoneWithStop(PVPositionerSoftDone):
Expand Down
27 changes: 10 additions & 17 deletions apstools/devices/tests/test_positioner_soft_done.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,8 @@ def confirm_in_position(p, dt):
p.readback.get(use_monitor=False) # force a read from the IOC
p.cb_readback() # update self.done

# collect these values at one instant (as close as possible).
c_rb = p._rb_count # do not expect any new callbacks during this method
c_sp = p._sp_count
# Collect these values at one instant (as close in time as possible).
# Do not expect any new callbacks during this function.
dmov = p.done.get()
rb = p.readback.get()
sp = p.setpoint.get()
Expand All @@ -106,18 +105,13 @@ def confirm_in_position(p, dt):
f"{p.name=}"
f" {rb=:.5f} {sp=:.5f} {tol=}"
f" {dt=:.4f}s"
f" {p._sp_count=}"
f" {p._rb_count=}"
f" {p.done=}"
f" {p.done_value=}"
f" {time.time()=:.4f}"
)
# fmt: on

assert p._rb_count == c_rb, diagnostics
assert p._sp_count == c_sp, diagnostics
assert dmov == p.done_value, diagnostics
# assert math.isclose(rb, sp, abs_tol=tol), diagnostics


@run_in_thread
Expand Down Expand Up @@ -206,16 +200,13 @@ def test_structure(device, has_inposition):
assert pos.tolerance.get() == -1


@pytest.mark.local
def test_put_and_stop(rbv, prec, pos):
assert pos.tolerance.get() == -1
assert pos.precision == prec.get()

def motion(rb_initial, target, rb_mid=None):
rbv.put(rb_initial) # make the readback to different
c_sp = pos._sp_count
pos.setpoint.put(target)
assert pos._sp_count == c_sp + 1
assert math.isclose(pos.readback.get(use_monitor=False), rb_initial, abs_tol=0.02)
assert math.isclose(pos.setpoint.get(use_monitor=False), target, abs_tol=0.02)
assert pos.done.get() != pos.done_value
Expand All @@ -230,7 +221,7 @@ def motion(rb_initial, target, rb_mid=None):
# force a stop now
pos.stop()
pos.cb_readback()
assert pos.setpoint.get(use_monitor=False) == rb_mid
assert pos.setpoint.get(use_monitor=False) == rb_mid # FIXME: fails sometimes 1.0 == 0.5
assert pos.readback.get(use_monitor=False) == rb_mid
assert pos.position == rb_mid
else: # interrupted move
Expand All @@ -248,7 +239,6 @@ def motion(rb_initial, target, rb_mid=None):
motion(1, 0, 0.5) # interrupted move


@pytest.mark.local
def test_move_and_stop_nonzero(rbv, pos):
timed_pause()

Expand All @@ -271,9 +261,14 @@ def test_move_and_stop_nonzero(rbv, pos):
assert pos.inposition


@pytest.mark.local
def test_move_and_stopped_early(rbv, pos):
def motion(target, delay, interrupt=False):
"""
Test moving pos to target. Update rbv after delay.
If interrupt is True, stop the move before it is done
(at a time that is less than the 'delay' value).
"""
timed_pause(0.1) # allow previous activities to settle down

t0 = time.time()
Expand All @@ -286,7 +281,7 @@ def motion(target, delay, interrupt=False):
rb_new = pos.readback.get(use_monitor=False)
arrived = math.isclose(rb_new, target, abs_tol=pos.actual_tolerance)
# fmt: on
if interrupt:
if interrupt and not status.done:
assert not status.done
assert not status.success
assert not arrived, f"{dt=:.3f}"
Expand Down Expand Up @@ -350,11 +345,9 @@ def test_position_sequence_calcpos(target, calcpos):
def motion(p, goal):
timed_pause(0.1) # allow previous activities to settle down

c_sp = p._sp_count
t0 = time.time()
status = p.move(goal)
dt = time.time() - t0
assert p._sp_count == c_sp + 1
assert status.elapsed > 0, str(status)
assert status.done, str(status)

Expand Down

0 comments on commit 5dde9a1

Please sign in to comment.