Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Helper to check against motor limits #41

Open
DominicOram opened this issue Oct 29, 2023 · 3 comments
Open

Helper to check against motor limits #41

DominicOram opened this issue Oct 29, 2023 · 3 comments

Comments

@DominicOram
Copy link
Contributor

DominicOram commented Oct 29, 2023

For Hyperion, and in general across MX DAQ, we want to be able to easily check if a position is within motion limits. Specifically, we use optical edge detection to find a pin tip and then want to check if that's within the smargon limits or else stop the collection.

In ophyd we do something like:

class Smargon(Device):
    x: EpicsMotor = Component(EpicsMotor, "X")

    def get_x_in_limits(self, position: float):
        low = self.x.low_limit_travel.get()
        high = self.x.high_limit_travel.get()
        return low <= position <= high

def plan():
    if smargon.get_x_in_limits(100):
        yield from ...
    else:
        yield from ...

There are issues in doing this in ophyd-async:

@DominicOram
Copy link
Contributor Author

My current work around is:

class XYZLimitsChecker(Device):
    """Checks that a position is within the XYZ limits of the motor.
    To use set the x, y, z vector to the device and read check `within_limits`
    """

    def __init__(self, prefix: str) -> None:
        self.within_limits = False

        axes = ["X", "Y", "Z"]
        self.limit_signals = {
            axis: (
                epics_signal_r(float, f"{prefix}{axis}.LLM"),
                epics_signal_r(float, f"{prefix}{axis}.HLM"),
            )
            for axis in axes
        }

    def children(self) -> Iterator[Tuple[str, Device]]:
        for attr, signals in self.limit_signals.items():
            yield attr + "low_lim", signals[0]
            yield attr + "high_lim", signals[1]
        return super().children()

    async def _check_limits(self, position: np.ndarray):
        check_within_limits = True
        for i, axis_limits in enumerate(self.limit_signals.values()):
            check_within_limits &= (
                (await axis_limits[0].get_value())
                < position[i]
                < (await axis_limits[1].get_value())
            )
        self.within_limits = check_within_limits

    def set(self, position: np.ndarray) -> AsyncStatus:
        return AsyncStatus(wait_for(self._check_limits(position), None))

def my_plan():
    yield from bps.set(limit_checker, [1, 2, 3])
    if limit_checker.within_limits:
        ...

@coretl
Copy link
Collaborator

coretl commented Nov 2, 2023

Discussed offline and decided plan logic that gets limits from specific signals on the motor, then put the plan stub in ophyd-async next to the Motor class

@coretl
Copy link
Collaborator

coretl commented Jun 17, 2024

Something like:

def motor_position_within_limits(motor: Motor, position: float):
    low, high = yield bps.locate(motor.low_limit_travel, motor.high_limit_travel)
    return low.readback <= position <= high.readback

Living in ophyd_async.plan_stubs.
Depends on bluesky/bluesky#1753

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants