You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
BlueAPI doesn't seem to be happy with this kind of thing right now. When doing a blueapi controller plans, the server outputs: ValueError: Value not declarable with JSON Schema, field: name='aperture_scatterguard' type=ApertureScatterguard required=True
FlyScanXrayCentreComposite is a dataclass rather than an ophyd/ophyd-async device, which is why blueapi doesn't like it. It checks whether things being injected as arguments are plans or devices as different behaviour is needed for each.
I guess we have 2 options:
Make blueapi consider dataclasses to be a type of "device"
Make the composite devices actual devices (probably StandardReadables or just plain ophyd-async Devices)
How would you feel about option 2? I'm not hugely against option 1 but it would be nice to keep blueapi's definition of a device somewhat idomatic for future-proofing purposes.
Hmm, I don't think so, if the devices already exist and you pass them in via the constructor, they may get .connect() called on them twice, but it is idempotent and cached to reduce network calls so that's not the end of the world. @coretl or @abbiemery can you see any problems with this approach?
I made a prototype implementation here (select just the final commit, I merged main into both branches but github hasn't figured it out yet).
The best way for handling when to inject a Composite, what types should be allowed to be a composite etc. are open for later discussion, but it's proof of concept that with only minor changes we can get it working.
@tpoliaw you had some opinions earlier about this re: Composite model/dataclass vs. Device class and inject (a device) vs. assemble (a collection of devices)
I don't like inject being used to both get devices from the environment based on name and create new models on the fly. The inject handling is already complex enough that it's not easy to follow what is going on and making it do two things is not appealing.
On the other hand, after talking through the use cases, I can see the benefit of this approach over creating a plan with many, many parameters so we should support it in some way if possible.
It could be possible to have an alternative that can be handled separately.
Ideally something like a type annotation could work, eg composite: Annotated[Assemble, MyComposite] from the example above, although it would be difficult to know where the definition would live if it's needed in both dodal and blueapi. It might be that once blueapi stops depending on dodal, dodal could include a dependency on blueapi (or a module split out of it?) to include that sort of annotation. Maybe we should have gone for the utils suggestion in Dodal #1189
Activity
callumforrester commentedon Jun 18, 2024
FlyScanXrayCentreCompositeis adataclassrather than an ophyd/ophyd-async device, which is why blueapi doesn't like it. It checks whether things being injected as arguments are plans or devices as different behaviour is needed for each.I guess we have 2 options:
StandardReadables or just plain ophyd-asyncDevices)How would you feel about option 2? I'm not hugely against option 1 but it would be nice to keep blueapi's definition of a device somewhat idomatic for future-proofing purposes.
olliesilvester commentedon Jun 18, 2024
I can't see any issues with doing 2. I think @DominicOram also mentioned this as a possibility
DominicOram commentedon Jun 18, 2024
If it's a
Devicedo we have to do the instantiation of the sub devices inside it? I'm not a big fan of that because we'll end up with potentially multiple instances. Alternatively we could get something like https://github.com/DiamondLightSource/hyperion/blob/main/src/hyperion/utils/context.py#L47 in toblueapi?callumforrester commentedon Jun 18, 2024
Hmm, I don't think so, if the devices already exist and you pass them in via the constructor, they may get
.connect()called on them twice, but it is idempotent and cached to reduce network calls so that's not the end of the world. @coretl or @abbiemery can you see any problems with this approach?coretl commentedon Jun 18, 2024
The other issue is naming. If you name the parent it will attempt to name the child.
The other option is to do the connect explicitly in a plan as proposed in DiamondLightSource/dodal#415 (comment)
callumforrester commentedon Jun 19, 2024
From a discussion on Slack we think the idiomatic way to do this in blueapi is:
Unsure if it will do this out of box, if it doesn't, this ticket will become a bug report.
stan-dot commentedon Sep 9, 2024
@olliesilvester have you tried the implementation suggested above?
olliesilvester commentedon Sep 9, 2024
No, I haven't tried this yet
olliesilvester commentedon Oct 10, 2024
I think #647 is a prerequisite to seeing if this would work
stan-dot commentedon Oct 10, 2024
I see, ok
DominicOram commentedon Jul 29, 2025
Is this still what we want to work? This doesn't work out of the box, the test in 2716de2 fails
DiamondJoseph commentedon Aug 14, 2025
I made a prototype implementation here (select just the final commit, I merged main into both branches but github hasn't figured it out yet).
The best way for handling when to inject a Composite, what types should be allowed to be a composite etc. are open for later discussion, but it's proof of concept that with only minor changes we can get it working.
rtuck99 commentedon Oct 23, 2025
See additional PR
DiamondJoseph commentedon Oct 28, 2025
@tpoliaw you had some opinions earlier about this re: Composite model/dataclass vs. Device class and
inject(a device) vs.assemble(a collection of devices)tpoliaw commentedon Oct 30, 2025
I don't like
injectbeing used to both get devices from the environment based on name and create new models on the fly. Theinjecthandling is already complex enough that it's not easy to follow what is going on and making it do two things is not appealing.On the other hand, after talking through the use cases, I can see the benefit of this approach over creating a plan with many, many parameters so we should support it in some way if possible.
It could be possible to have an alternative that can be handled separately.
Ideally something like a type annotation could work, eg
composite: Annotated[Assemble, MyComposite]from the example above, although it would be difficult to know where the definition would live if it's needed in both dodal and blueapi. It might be that once blueapi stops depending on dodal, dodal could include a dependency on blueapi (or a module split out of it?) to include that sort of annotation. Maybe we should have gone for the utils suggestion in Dodal #1189abbiemery commentedon Feb 25, 2026
Closed as done. #1242