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

Cannot connect devices from both ophyd and ophyd-async while a plan is running #548

Open
callumforrester opened this issue Aug 30, 2024 · 2 comments
Labels
bug Something isn't working

Comments

@callumforrester
Copy link
Contributor

Issue encountered on I24

If a plan is running and I attempt to connect ophyd and ophyd-async devices within it, the connection freezes, code to reproduce below:

from bluesky.run_engine import RunEngine
from ophyd import Device, Component
from ophyd.signal import EpicsSignalRO
from ophyd_async.core import StandardReadable, DeviceCollector
from ophyd_async.epics.signal import epics_signal_r
import bluesky.plans as bp
from softioc import softioc, builder, asyncio_dispatcher
from multiprocessing.pool import Pool
import signal

def make_ioc() -> None:
    builder.SetDeviceName("FAKEIOC")
    builder.aIn("FOO", initial_value=5.0)
    builder.LoadDatabase()
    softioc.iocInit(asyncio_dispatcher.AsyncioDispatcher())


def run_ioc_in_subprocess() -> None:
    subprocess = Pool(
        initializer=lambda: signal.signal(signal.SIGINT, signal.SIG_IGN), processes=1
    )
    subprocess.apply(make_ioc)


class OphydAsyncDevice(StandardReadable):
    def __init__(self, prefix: str, name: str = "") -> None:
        self.foo = epics_signal_r(float, prefix + "FOO")
        super().__init__(name)


class OphydDevice(Device):
    foo = Component(EpicsSignalRO, "FOO")

    def __init__(self, prefix: str = "", *, name: str, **kwargs):
        super().__init__(prefix, name=name, **kwargs)


RE = RunEngine()


def ophyd_device() -> OphydDevice:
    od = OphydDevice(prefix="FAKEIOC:", name="od")
    od.wait_for_connection()
    print("connected ophyd device")
    return od


def ophyd_async_device() -> OphydAsyncDevice:
    with DeviceCollector():
        oad = OphydAsyncDevice(prefix="FAKEIOC:")
    print("connected ophyd-async device")
    return oad


def my_plan():
    od = ophyd_device()
    oad = ophyd_async_device()
    yield from bp.count([od, oad])
    print("counted devices")


if __name__ == "__main__":
    make_ioc()
    # od = ophyd_device()
    # oad = ophyd_async_device()
    RE(my_plan())

If I comment out these lines in the plan

    od = ophyd_device()
    oad = ophyd_async_device()

...and uncomment them in main, outside where the plan is running, everything works.

I suspect this is more interplay issues between pyepics and aioca. See #89 and #291. Also I don't think we should be making blocking calls to connect inside a plan in the first place, but this is what dodal potentially does if you have, say, i24.pmac() inside your plan. Ideally we should defer to the RunEngine, maybe with an ensure_connected() plan?

@coretl
Copy link
Collaborator

coretl commented Sep 2, 2024

Does it fail in the same way with https://github.com/DiamondLightSource/aioca/tree/contexts?

@callumforrester
Copy link
Contributor Author

The example above fails in exactly the same way with that branch, yes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants