Skip to content

Commit 625ecc7

Browse files
authored
Merge pull request #176 from ISISComputingGroup/block_write_config_fail
Allow specifying that timeout is not an error
2 parents 2fbbcd2 + 35bce78 commit 625ecc7

File tree

2 files changed

+41
-2
lines changed

2 files changed

+41
-2
lines changed

src/ibex_bluesky_core/devices/block.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import asyncio
44
import logging
5+
import sys
56
from collections.abc import Callable
67
from dataclasses import dataclass
78
from typing import Generic, TypeVar
@@ -57,6 +58,8 @@
5758
# looking at the global moving flag.
5859
GLOBAL_MOVING_FLAG_PRE_WAIT = 0.1
5960

61+
aio_timeout_error = asyncio.exceptions.TimeoutError if sys.version_info < (3, 11) else TimeoutError
62+
6063

6164
@dataclass(kw_only=True, frozen=True)
6265
class BlockWriteConfig(Generic[T]):
@@ -101,13 +104,19 @@ def check(setpoint: T, actual: T) -> bool:
101104
move, and all movement needs to be complete before the set is considered complete. Defaults
102105
to False.
103106
107+
timeout_is_error:
108+
Whether a write timeout is considered an error. Defaults to True. If False, a set will be
109+
marked as complete without error even if the block has not given a completion callback or
110+
satisfied set_success_func within settle_time_s.
111+
104112
"""
105113

106114
use_completion_callback: bool = True
107115
set_success_func: Callable[[T, T], bool] | None = None
108116
set_timeout_s: float | None = None
109117
settle_time_s: float = 0.0
110118
use_global_moving_flag: bool = False
119+
timeout_is_error: bool = True
111120

112121

113122
class RunControl(StandardReadable):
@@ -272,7 +281,19 @@ async def set_and_settle(setpoint: T) -> None:
272281
)
273282
await asyncio.sleep(self._write_config.settle_time_s)
274283

275-
await set_and_settle(value)
284+
if self._write_config.timeout_is_error:
285+
await set_and_settle(value)
286+
else:
287+
try:
288+
await set_and_settle(value)
289+
except aio_timeout_error as e:
290+
logger.info(
291+
"block set %s value=%s failed with %s, but continuing anyway because "
292+
"continue_on_failed_write is set.",
293+
self.name,
294+
value,
295+
e,
296+
)
276297
logger.info("block set complete %s value=%s", self.name, value)
277298

278299

@@ -362,6 +383,7 @@ def __init__(
362383
use_global_moving_flag:
363384
This is unnecessary for a single motor block, as a completion callback will always be
364385
used instead to detect when a single move has finished.
386+
365387
"""
366388
self.run_control = RunControl(f"{prefix}CS:SB:{block_name}:RC:")
367389

tests/devices/test_block.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# pyright: reportMissingParameterType=false
22
import asyncio
33
import sys
4+
from contextlib import nullcontext
45
from unittest.mock import ANY, MagicMock, call, patch
56

67
import bluesky.plan_stubs as bps
@@ -73,7 +74,7 @@ def test_block_naming(rw_rbv_block):
7374
def test_mot_block_naming(mot_block):
7475
assert mot_block.name == "mot_block"
7576
assert mot_block.user_readback.name == "mot_block"
76-
assert mot_block.user_setpoint.name == ("mot_block-user_setpoint")
77+
assert mot_block.user_setpoint.name == "mot_block-user_setpoint"
7778

7879

7980
def test_block_signal_monitors_correct_pv(rw_rbv_block):
@@ -353,3 +354,19 @@ async def test_block_mot_set(mot_block):
353354
set_mock_value(mot_block.velocity, 10)
354355
await mot_block.set(20)
355356
get_mock_put(mot_block.user_setpoint).assert_called_once_with(20, wait=True)
357+
358+
359+
@pytest.mark.parametrize("timeout_is_error", [True, False])
360+
async def test_block_failing_write(timeout_is_error):
361+
block = await _block_with_write_config(BlockWriteConfig(timeout_is_error=timeout_is_error))
362+
363+
get_mock_put(block.setpoint).side_effect = aio_timeout_error
364+
365+
with pytest.raises(aio_timeout_error) if timeout_is_error else nullcontext():
366+
await block.set(1)
367+
368+
369+
async def test_block_failing_write_with_default_write_config(writable_block):
370+
get_mock_put(writable_block.setpoint).side_effect = aio_timeout_error
371+
with pytest.raises(aio_timeout_error):
372+
await writable_block.set(1)

0 commit comments

Comments
 (0)