Skip to content

Commit

Permalink
Add run control on motor blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
Tom-Willemsen committed Aug 14, 2024
1 parent 768c935 commit f1f643e
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 106 deletions.
17 changes: 9 additions & 8 deletions doc/blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ value of the block.
A simple constructor, `block_r`, is available, which assumes the current instrument's PV
prefix.
The underlying type is `BlockR`
### `block_rw` (read, write)
This is a read-write block. It supports all of the same protocols as `BlockR`, with the
Expand All @@ -59,8 +57,6 @@ It can also be used as a `Movable` in full plans like `bluesky.plans.scan()`.
> This is simply a matter of terminology - bluesky fully supports moving things which
> are not motors, even if the documentation tends to use motors as the examples.
The underlying type is `BlockRw`
### `block_rw_rbv` (read, write, setpoint readback)
This is a block with full support for reading and writing as per `BlockRw`, but with
Expand All @@ -69,8 +65,6 @@ current setpoint. Where possible, the setpoint will be read back from hardware.
This object is suitable for use in plan stubs such as `bluesky.plan_stubs.locate()`.
The underlying type is `BlockRwRbv`
### `block_mot` (motor-specific)
This represents a block pointing at a motor record. This has support for:
Expand All @@ -83,12 +77,19 @@ This represents a block pointing at a motor record. This has support for:
This type is recommended to be used if the underlying block is a motor record. It always has
type `float`, and as such does not take a type argument (unlike the other block types).
The underlying type is `ophyd_async.epics.motor.Motor`
## Configuring block write behaviour
`BlockRw` and `BlockRwRbv` both take a `write_config` argument, which can be used to configure
the behaviour on writing to a block, for example tolerances and settle times.
See the docstring on `ibex_bluesky_core.devices.block.BlockWriteConfig` for a detailed
description of all the options which are available.
## Run control
Run control information is available via the `block.run_control` sub-device.
Both configuring and reading the current status of run control are permitted.
> **_ℹ️_**
> Run control limits are always `float`, regardless of the datatype of the block.
91 changes: 55 additions & 36 deletions src/ibex_bluesky_core/devices/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"block_rw",
"block_rw_rbv",
"block_mot",
"BlockMot",
]


Expand Down Expand Up @@ -219,6 +220,57 @@ async def locate(self) -> Location[T]:
}


class BlockMot(Motor):
"""Device representing an IBEX block pointing at a motor."""

def __init__(
self,
prefix: str,
block_name: str,
) -> None:
"""Create a new motor-record block.
The 'BlockMot' object supports motion-specific functionality such as:
- Stopping if a scan is aborted (supports the bluesky 'Stoppable' protocol)
- Limit checking (before a move starts - supports the bluesky 'Checkable' protocol)
- Automatic calculation of move timeouts based on motor velocity
- Fly scanning
However, it generally relies on the underlying motor being "well-behaved". For example, a
motor which does many retries may exceed the simple default timeout based on velocity (it
is possible to explicitly specify a timeout on set() to override this).
Blocks pointing at motors do not take a BlockWriteConfiguration parameter, as these
parameters duplicate functionality which already exists in the motor record. The mapping is:
use_completion_callback:
Motors always use completion callbacks to check whether motion has completed. Whether to
wait on that completion callback can be configured by the 'wait' keyword argument on
set().
set_success_func:
Use .RDBD and .RTRY to control motor retries if the position has not been reached to
within a specified tolerance. Note that motors which retry a lot may exceed the default
motion timeout which is calculated based on velocity, distance and acceleration.
set_timeout_s:
A suitable timeout is calculated automatically based on velocity, distance and
acceleration as defined on the motor record. This may be overridden by the 'timeout'
keyword-argument on set().
settle_time_s:
Use .DLY on the motor record to configure this.
"""
self.run_control = _RunControl(f"{prefix}CS:SB:{block_name}:RC:")

# GWBLOCK aliases .VAL to .RBV on a motor record for a block pointing at MOT:MTRxxxx.RBV,
# which is what we have recommended to our users for motor blocks... That means that you
# can't write to .VAL on a motor block. ophyd_async (reasonably) assumes you can write to
# .VAL for a motor which you want to move.
#
# However, we also have motor record aliases for :SP and :SP:RBV, which *don't* get mangled
# by GWBLOCK in that way. So by pointing at CS:SB:blockname:SP:RBV rather than
# CS:SB:blockname here, we avoid a write access exception when moving a motor block.
super().__init__(f"{prefix}CS:SB:{block_name}:SP:RBV", name=block_name)


def block_r(datatype: Type[T], block_name: str) -> BlockR[T]:
"""Get a local read-only block for the current instrument.
Expand Down Expand Up @@ -251,42 +303,9 @@ def block_rw_rbv(
)


def block_mot(block_name: str) -> Motor:
def block_mot(block_name: str) -> BlockMot:
"""Get a local block pointing at a motor record for the local instrument.
The 'Motor' object supports motion-specific functionality such as:
- Stopping if a scan is aborted (supports the bluesky 'Stoppable' protocol)
- Limit checking (before a move starts - supports the bluesky 'Checkable' protocol)
- Automatic calculation of move timeouts based on motor velocity
- Fly scanning
However, it generally relies on the underlying motor being "well-behaved". For example, a motor
which does many retries may exceed the simple default timeout based on velocity (it is possible
to explicitly specify a timeout on set() to override this).
Blocks pointing at motors do not take a BlockWriteConfiguration parameter, as these parameters
duplicate functionality which already exists in the motor record. The mapping is:
use_completion_callback:
Motors always use completion callbacks to check whether motion has completed. Whether to
wait on that completion callback can be configured by the 'wait' keyword argument on set().
set_success_func:
Use .RDBD and .RTRY to control motor retries if the position has not been reached to within
a specified tolerance. Note that motors which retry a lot may exceed the default motion
timeout which is calculated based on velocity, distance and acceleration.
set_timeout_s:
A suitable timeout is calculated automatically based on velocity, distance and acceleration
as defined on the motor record. This may be overridden by the 'timeout' keyword-argument on
set().
settle_time_s:
Use .DLY on the motor record to configure this.
See documentation of BlockMot for more information.
"""
# GWBLOCK aliases .VAL to .RBV on a motor record for a block pointing at MOT:MTRxxxx.RBV, which
# is what we have recommended to our users for motor blocks... That means that you can't write
# to .VAL on a motor block. ophyd_async (reasonably) assumes you can write to .VAL for a motor
# which you want to move.
#
# However, we also have motor record aliases for :SP and :SP:RBV, which *don't* get mangled by
# GWBLOCK in that way. So by pointing at CS:SB:blockname:SP:RBV rather than CS:SB:blockname
# here, we avoid a write access exception when moving a motor block.
return Motor(f"{get_pv_prefix()}CS:SB:{block_name}:SP:RBV", name=block_name)
return BlockMot(prefix=get_pv_prefix(), block_name=block_name)
Loading

0 comments on commit f1f643e

Please sign in to comment.