PyLabRobot / pylabrobot

interactive & hardware agnostic SDK for lab automation
https://docs.pylabrobot.org
MIT License
178 stars 63 forks source link

Tip state tracking allows for contradictory states #309

Open maraxen opened 1 day ago

maraxen commented 1 day ago

Hello,

When using the robot today, I encountered an error after sending the command to pick up the tips, but before it had successfully picked them up.

Attempts to pick up tips resulted in this error:

HasTipError                               Traceback (most recent call last)
Cell In[13], line 3
      1 for tips, source_wells, target_wells in zip(fprimer_tips.traverse(8, direction="down")
      2 , primer_f_plate.traverse(8, direction='down'), pcr_plate.traverse(8, direction="down")):
----> 3   await lh.pick_up_tips(tips)
      4   await lh.aspirate(
      5       resources=source_wells,
      6       vols=[2.5] * 8
      7   )
      8   await lh.dispense(
      9       resources=target_wells,
     10       vols=[2.5] * 8
     11   )

File ~/pylabrobot/pylabrobot/machines/machine.py:23, in need_setup_finished.<locals>.wrapper(self, *args, **kwargs)
     21 if not self.setup_finished:
     22   raise RuntimeError("The setup has not finished. See `setup`.")
---> 23 return await func(self, *args, **kwargs)

File ~/pylabrobot/pylabrobot/liquid_handling/liquid_handler.py:419, in LiquidHandler.pick_up_tips(self, tip_spots, use_channels, offsets, **backend_kwargs)
    417 for channel, op in zip(use_channels, pickups):
    418   if self.head[channel].has_tip:
--> 419     raise HasTipError("Channel has tip")
    420   if does_tip_tracking() and not op.resource.tracker.is_disabled:
    421     op.resource.tracker.remove_tip()

HasTipError: Channel has tip

However, attempting to return tips results in this error.

---------------------------------------------------------------------------
NoTipError                                Traceback (most recent call last)
Cell In[14], line 1
----> 1 await lh.return_tips()

File ~/pylabrobot/pylabrobot/liquid_handling/liquid_handler.py:628, in LiquidHandler.return_tips(self, use_channels, **backend_kwargs)
    625 if len(tip_spots) == 0:
    626   raise RuntimeError("No tips have been picked up.")
--> 628 return await self.drop_tips(tip_spots=tip_spots, use_channels=channels, **backend_kwargs)

File ~/pylabrobot/pylabrobot/machines/machine.py:23, in need_setup_finished.<locals>.wrapper(self, *args, **kwargs)
     21 if not self.setup_finished:
     22   raise RuntimeError("The setup has not finished. See `setup`.")
---> 23 return await func(self, *args, **kwargs)

File ~/pylabrobot/pylabrobot/liquid_handling/liquid_handler.py:524, in LiquidHandler.drop_tips(self, tip_spots, use_channels, offsets, allow_nonzero_volume, **backend_kwargs)
    522 tips = []
    523 for channel in use_channels:
--> 524   tip = self.head[channel].get_tip()
    525   if tip.tracker.get_used_volume() > 0 and not allow_nonzero_volume:
    526     raise RuntimeError(f"Cannot drop tip with volume {tip.tracker.get_used_volume()}")

File ~/pylabrobot/pylabrobot/resources/tip_tracker.py:66, in TipTracker.get_tip(self)
     59 """Get the tip. Note that does includes pending operations.
     60 
     61 Raises:
     62   NoTipError: If the tip spot does not have a tip.
     63 """
     65 if self._tip is None:
---> 66   raise NoTipError(f"{self.thing} does not have a tip.")
     67 return self._tip

By running the command lh.head[channel_index].remove_tip(tip, origin) or lh.head[channel_index].add_tip(tip, origin) I can manually reset the state to reflect reality, but given that HasTipError and NoTipError are contradictory, it feels like this behavior should be handled differently. I think if there is an error during a tip pickup there are a few possible outcomes:

  1. The command completes, tips are picked up, and the error only comes after in which case you want the state to reflect that there are tips.
  2. The error occurs prior to the tips being picked up
  3. Partial success

I am not sure of the best, safest approach to handle this overall. My initial thoughts are that if there is uncertainty over the state of the tip, that should actually be a separate state in tip tracking that leads to its own error on any actions involving the head in question. Ideally, the message displayed with this error should always contain all the information necessary to resolve the problem and reflect the reality of the tip state. But I would love to hear any additional thoughts on the best approach to resolve this issue.