another idea from my call with @VilhelmKM this morning: supporting all different arm configurations. Specify them in the initializer for the EVO:
evo = EVOBackend( # typical
arms=[LiHa(), MCA(), RoMa()] # in order
)
evo = EVOBackend(
arms=[RoMa(), LiHa()] # possible
)
evo = EVOBackend(
arms=[LiHa(), LiHa(), RoMa()]
)
evo = EVOBackend(
arms=[MCA(), RoMa()]
)
This may be more of a design related question, but would it make more sense to initialize the evo backend using the evo arm class name rather than an instantiation of each arm? i.e
evo = EVOBackend(
arms=["liha", "mca", "roma"]
)
# or
evo = EVOBackend(
arms=[LiHa, MCA, RoMa] # Class names
)
vs
evo = EVOBackend(
arms=[LiHa(), ...]
)
since EVOArm() takes in backend and module as arguments.
It might be more of a semantics question, but just wanted to know if there was a particular reason for the above format or if theres some flexibility.
first of all: i am undecided. do you have a preference?
thoughts:
implementation-wise it is possible to use the Arm() (instance) approach by having a set_evo method (kind of like LiquidHandlerBackend.set_deck). this part is a little ugly imo
if we want to reference arms in for example pick_up_resource(..., use_arm=[liha_1]) this is nice if we already have an instance that we also pass to the initializer. this is not possible when passing strings or class names
The current implementation of Evo in PLR creates the arm instances inside of EVOBackend.setup(). Since the instantiation of each arm requires you to pass the backend as an argument, it would sort of seem like an anti-pattern (at least to me anyways) to initialize the arms before the backend (or would at least require some changes to setup() ).
assuming that the backend must call setup() before using any of the arms, then i think instantiating the arms the way it currently is written is fine, so long as the arm order is configured upon creating the backend.
Take the following for example
evo = EVOBackend(arm_order=[PnP, LiHa, RoMa])
# Example 1
roma = evo.roma # Will not work because setup has not been called yet
# Example 2
evo.setup()
roma = evo.roma # Works, since self.roma = RoMa(self, EVOBackend.ROMA) is called in setup
For this reason, I think it would be simpler to implement if the arm order used class names instead of instances, but I’m not married to the idea. Plus i think it’d still be possible to use the same pick_up_resource(..., use_arm=[roma]) idea you’ve mentioned, so long as EVOBackend.setup() has been called first. Feel free to correct me though.
we could initialize the arms before we call setup and not initialize them yet? this would fit better with the PLR pattern.
i take your broader point as: you don’t have to immediately create reference instances of arms before referencing them later on, you can get them from the evo backend object. (In terms of API i would suggest evo.arms[i] because one system might have multiple arms of the same type)
evo = EVOBackend(arm_order=[PnP, LiHa, RoMa, LiHa]) # two liha arms
# Example 1
roma = evo.roma # works
# Example 2
evo.setup()
roma = evo.roma # also works
I think I get what you mean. But is there any reason why you’d want to initialize the arms before the evo backend is initialized?
Precisely.
I was thinking the same.
I think that would work. It would require that the arm instances be created during the instantiation of EVOBackend rather than during the setup() call, since currently, there are no references to evo.roma until setup is called.
If there is a general design/diagram showing how PLR intends broader setup across varying devices, I can definitely follow that for sake of uniformity.
implementation-wise it is possible to use the Arm() (instance) approach by having a set_evo method (kind of like LiquidHandlerBackend.set_deck). this part is a little ugly imo
your suggest alternative:
roma = evo.roma
iteration:
evo.arms[0]
conclusion: there isn’t really a reason I think, especially if it makes the implementation worse (ugly set_evo). that answers your question
however, your question was made in response to my point: “we could initialize the arm”. I should have clarified I meant initialize in terms of python object, not initialize hardware. initializing hardware is always done in the setup method in PLR. But to facilitate nice apis (example 1 and 2 above should both work), we should be setting those attributes on evo.init (python), not evo.setup (machine initi/setup, which is the current implementation)