From 3fa950181dd6e9b7f8a922de026c936f30890c50 Mon Sep 17 00:00:00 2001 From: Maria Date: Thu, 18 Jan 2024 16:33:29 -0800 Subject: [PATCH] Removed utils.py file and added _getxyz() to :class: as a instead. Modified all .py files to reflect this change and consolidated module imports --- science_jubilee/labware/Labware.py | 28 +++++- science_jubilee/tools/Camera.py | 14 +-- science_jubilee/tools/Loop.py | 14 +-- science_jubilee/tools/PeristalticPumps.py | 4 +- science_jubilee/tools/Pipette.py | 17 ++-- science_jubilee/tools/PumpDispenser.py | 8 +- science_jubilee/tools/Sonicator.py | 14 ++- science_jubilee/tools/Syringe.py | 109 +++++----------------- science_jubilee/tools/WebCamera.py | 29 +----- science_jubilee/utils.py | 23 ----- 10 files changed, 86 insertions(+), 174 deletions(-) delete mode 100644 science_jubilee/utils.py diff --git a/science_jubilee/labware/Labware.py b/science_jubilee/labware/Labware.py index fd1341d..3117302 100644 --- a/science_jubilee/labware/Labware.py +++ b/science_jubilee/labware/Labware.py @@ -1,9 +1,11 @@ +import os +import json + import numpy as np + from dataclasses import dataclass from itertools import chain from typing import List, Dict, Tuple, Union, Iterable, NamedTuple -import os -import json @dataclass @@ -509,6 +511,28 @@ def withWellOrder(self, order) -> list: self.wells = ordered_wells + @staticmethod + def _getxyz(location: Union[Well, Tuple, 'Location']): + """Helper function to extract the x, y, z coordinates of a location object. + + :param location: The location object to extract the coordinates from. This can either be a + :class:`Well`, a :tuple: of x, y, z coordinates, or a :class:`Location` object + :type location: Union[Well, Tuple, Location] + :raises ValueError: If the location is not a :class:`Well`, a :class:`tuple`, or a :class:`Location` object + :return: The x, y, z coordinates of the location + :rtype: float, float, float + """ + if type(location) == Well: + x, y, z = location.x, location.y, location.z + elif type(location) == Tuple: + x, y, z = location + elif type(location)==Location: + x,y,z= location._point + else: + raise ValueError("Location should be of type Well or Tuple") + + return x,y,z + ## Adapted from Opentrons API opentrons.types## class Point(NamedTuple): """A point in the Jubilee 3D coordinate system. diff --git a/science_jubilee/tools/Camera.py b/science_jubilee/tools/Camera.py index 97506f4..a7fa2d7 100644 --- a/science_jubilee/tools/Camera.py +++ b/science_jubilee/tools/Camera.py @@ -1,14 +1,16 @@ -from .Tool import Tool, ToolStateError, requires_active_tool -from science_jubilee.labware.Labware import Labware, Well -from typing import Tuple, Union import cv2 import matplotlib +import platform +import time + +import numpy as np matplotlib.use("TkAgg") from matplotlib import pyplot as plt -import numpy as np -import time -import platform +from science_jubilee.labware.Labware import Well +from science_jubilee.tools.Tool import Tool, requires_active_tool +from typing import Tuple + if platform.system() == "Linux": import picamera # Note that this can only be installed on raspbery pi. diff --git a/science_jubilee/tools/Loop.py b/science_jubilee/tools/Loop.py index de099ad..76a52e0 100644 --- a/science_jubilee/tools/Loop.py +++ b/science_jubilee/tools/Loop.py @@ -1,11 +1,13 @@ -from science_jubilee.tools.Tool import Tool, ToolStateError, ToolConfigurationError, requires_active_tool -from science_jubilee.labware.Labware import Labware, Well -from typing import Tuple, Union -import warnings -import numpy as np -import os import json +import os import random +import warnings + +import numpy as np + +from science_jubilee.labware.Labware import Well +from science_jubilee.tools.Tool import Tool, requires_active_tool +from typing import Tuple class Loop(Tool): diff --git a/science_jubilee/tools/PeristalticPumps.py b/science_jubilee/tools/PeristalticPumps.py index 001f87d..476c26d 100644 --- a/science_jubilee/tools/PeristalticPumps.py +++ b/science_jubilee/tools/PeristalticPumps.py @@ -2,8 +2,8 @@ import logging import os -from science_jubilee.tools.Tool import Tool, ToolStateError, ToolConfigurationError -from typing import Tuple, Union +from science_jubilee.tools.Tool import Tool +from typing import Union class PeristalticPumps(Tool): """ diff --git a/science_jubilee/tools/Pipette.py b/science_jubilee/tools/Pipette.py index 187f7cd..228f05f 100644 --- a/science_jubilee/tools/Pipette.py +++ b/science_jubilee/tools/Pipette.py @@ -4,7 +4,6 @@ from science_jubilee.labware.Labware import Labware, Well, Location from science_jubilee.tools.Tool import Tool, ToolStateError, ToolConfigurationError, requires_active_tool -from science_jubilee import utils from typing import Tuple, Union @@ -139,7 +138,7 @@ def aspirate(self, vol: float, location : Union[Well, Tuple, Location], s:int = :type s: int, optional :raises ToolStateError: If the pipette does not have a tip attached """ - x, y, z = utils.getxyz(location) + x, y, z = Labware._getxyz(location) if type(location) == Well: self.current_well = location @@ -189,7 +188,7 @@ def dispense(self, vol: float, location :Union[Well, Tuple, Location], s:int = 2 :type s: int, optional :raises ToolStateError: If the pipette does not have a tip attached """ - x, y, z = utils.getxyz(location) + x, y, z = Labware._getxyz(location) if type(location) == Well: self.current_well = location @@ -240,7 +239,7 @@ def transfer(self, vol: float, source_well: Union[Well, Tuple, Location], vol_ = self.vol2move(vol) # get locations - xs, ys, zs = utils.getxyz(source_well) + xs, ys, zs = Labware._getxyz(source_well) if self.is_primed == True: pass @@ -253,7 +252,7 @@ def transfer(self, vol: float, source_well: Union[Well, Tuple, Location], if isinstance(destination_well, list): for well in destination_well: - xd, yd, zd = utils.getxyz(well) + xd, yd, zd = Labware._getxyz(well) self._machine.safe_z_movement() self._machine.move_to(x= xs, y=ys) @@ -454,7 +453,7 @@ def pickup_tip(self, tip_ : Union[Well, Tuple] = None): else: tip = tip_ - x, y, z = utils.getxyz(tip) + x, y, z = Labware._getxyz(tip) self._machine.safe_z_movement() self._machine.move_to(x=x, y=y) self._pickup_tip(z) @@ -475,9 +474,9 @@ def return_tip(self, location: Well = None): :type location: :class:`Well`, optional """ if location is None: - x, y, z = utils.getxyz(self.first_available_tip) + x, y, z = Labware._getxyz(self.first_available_tip) else: - x, y, z = utils.getxyz(location) + x, y, z = Labware._getxyz(location) self._machine.safe_z_movement() self._machine.move_to(x=x, y=y) # z moves up/down to make sure tip actually makes it into rack @@ -512,7 +511,7 @@ def drop_tip(self, location: Union[Well, Tuple]): :param location: The location to drop the tip into :type location: Union[:class:`Well`, tuple] """ - x, y, z = utils.getxyz(location) + x, y, z = Labware._getxyz(location) self._machine.safe_z_movement() if x is not None or y is not None: diff --git a/science_jubilee/tools/PumpDispenser.py b/science_jubilee/tools/PumpDispenser.py index 3392fa1..daa0c98 100644 --- a/science_jubilee/tools/PumpDispenser.py +++ b/science_jubilee/tools/PumpDispenser.py @@ -3,9 +3,9 @@ import os from science_jubilee.labware.Labware import Labware, Well, Location -from science_jubilee.tools.Tool import Tool, ToolStateError, ToolConfigurationError, requires_active_tool +from science_jubilee.tools.Tool import Tool from typing import Tuple, Union -from science_jubilee import utils + class PumpDispenser(Tool): """ @@ -116,7 +116,7 @@ def dispense(self, vol: Union[float, int, list], location:Union[Well, Tuple, Loc # calculate XY location for each dispense head - x, y, z = utils.getxyz(location) + x, y, z = Labware._getxyz(location) if type(location) == Well: if z == location.z: @@ -171,7 +171,7 @@ def prime_lines(self, volume: Union[int, float] = None, location:Union[Well, Tup location = self.waste # calculate XY location for each dispense head - x, y, z = utils.getxyz(location) + x, y, z = Labware._getxyz(location) if type(location) == Well: if z == location.z: diff --git a/science_jubilee/tools/Sonicator.py b/science_jubilee/tools/Sonicator.py index 6c19922..13eb2e0 100644 --- a/science_jubilee/tools/Sonicator.py +++ b/science_jubilee/tools/Sonicator.py @@ -1,17 +1,15 @@ +import adafruit_mcp4725 +import board +import busio +import digitalio import json import logging import serial import time -import digitalio -import board -import busio -import adafruit_mcp4725 - from science_jubilee.labware.Labware import Labware, Well, Location -from science_jubilee.tools.Tool import Tool, ToolStateError, ToolConfigurationError, requires_active_tool -from science_jubilee import utils +from science_jubilee.tools.Tool import Tool, requires_active_tool from typing import Tuple, Union @@ -226,7 +224,7 @@ def sonicate_well(self, location:Union[Well, Tuple, Location], raise ValueError("Error: plunge depth is too deep.") - x, y, z = utils._getxyz(location) + x, y, z = Labware.__getxyz(location) self._machine.safe_z_movement() self._machine.move_to(x=x,y=y) # Position over the well at safe z height. diff --git a/science_jubilee/tools/Syringe.py b/science_jubilee/tools/Syringe.py index 9467bfb..b259a89 100644 --- a/science_jubilee/tools/Syringe.py +++ b/science_jubilee/tools/Syringe.py @@ -1,11 +1,12 @@ -from science_jubilee.tools.Tool import Tool, ToolStateError, ToolConfigurationError, requires_active_tool -from science_jubilee.labware.Labware import Labware, Well -from typing import Tuple, Union +import json +import os import warnings + import numpy as np -import os -import json +from science_jubilee.labware.Labware import Labware, Well, Location +from science_jubilee.tools.Tool import Tool, ToolStateError, ToolConfigurationError, requires_active_tool +from typing import Tuple, Union class Syringe(Tool): """A class representation of a syringe. @@ -26,7 +27,7 @@ def __init__(self, index, name, config): self.load_config(config) def load_config(self, config): - """_summary_ + """Loads the confirguration file for the syringe tool :param config: Name of the config file for your syringe. Expects the file to be in /tools/configs :type config: str @@ -108,36 +109,20 @@ def _dispense(self, vol, s: int = 2000): def aspirate( self, vol: float, - s: int = 2000, - well: Well = None, - from_bottom: float = 5, - from_top: float = None, - location: Tuple[float] = None, + location :Union[Well, Tuple, Location], + s: int = 2000 ): """Aspirate a certain volume from a given well. :param vol: Volume to aspirate, in milliliters :type vol: float + :param location: The location (e.g. a `Well` object) from where to aspirate the liquid from. + :type location: Union[Well, Tuple, Location] :param s: Speed at which to aspirate in mm/min, defaults to 2000 :type s: int, optional - :param well: Well to aspirate from, defaults to None - :type well: Well, optional - :param from_bottom: Distance in z from the bottom of the well to aspirate from in mm, defaults to 5 - :type from_bottom: float, optional - :param from_top: Distance in z from the top of the well to aspirate from in mm, can be used instead of from_bottom, defaults to None - :type from_top: float, optional - :param location: Explicit coordinates to aspirate from, can be specified instead of a Well, defaults to None - :type location: Tuple[float], optional """ - x, y, z = self._get_xyz(well=well, location=location) - if well is not None: - top, bottom = self._get_top_bottom(well=well) - self.current_well = well - - if from_bottom is not None and well is not None: - z = bottom + from_bottom - elif from_top is not None and well is not None: - z = top + from_top # TODO: this should be minus, if I'm understanding right? + x, y, z = Labware._getxyz(location) + self._machine.safe_z_movement() self._machine.move_to(x=x, y=y) self._machine.move_to(z=z) @@ -147,38 +132,21 @@ def aspirate( def dispense( self, vol: float, - s: int = 2000, - well: Well = None, - from_bottom: float = None, - from_top: float = 2, - location: Tuple[float] = None, + location :Union[Well, Tuple, Location], + s: int = 2000 ): """Dispense a certain volume into a given well. :param vol: Volume to dispense, in milliliters :type vol: float + :param location: The location to dispense the liquid into. + :type location: Union[Well, Tuple, Location] :param s: Speed at which to dispense in mm/min, defaults to 2000 :type s: int, optional - :param well: Well to dispense into, defaults to None, defaults to None - :type well: Well, optional - :param from_bottom: Distance in z from the bottom of the well to dispense from, in mm, defaults to None - :type from_bottom: float, optional - :param from_top: Distance in z from the top of the well to dispense from in mm, can be used instead of from_bottom, defaults to 2 - :type from_top: float, optional - :param location: Explicit coordinates to dispense at, can be specified instead of a Well, defaults to None - :type location: Tuple[float], optional - """ - x, y, z = self._get_xyz(well=well, location=location) - if well is not None: - top, bottom = self._get_top_bottom(well=well) - self.current_well = well + """ + x, y, z = Labware._getxyz(location) - if from_bottom is not None and well is not None: - z = bottom + from_bottom - elif from_top is not None and well is not None: - z = top + from_top # TODO: This should be minus, if I understand right? - pass self._machine.safe_z_movement() self._machine.move_to(x=x, y=y) self._machine.move_to(z=z) @@ -237,8 +205,8 @@ def transfer( source_destination_pairs = list(zip(source, destination)) for source_well, destination_well in source_destination_pairs: # TODO: Large volume transfers which exceed tool capacity should be split up into several transfers - xs, ys, zs = self._get_xyz(well=source_well) - xd, yd, zd = self._get_xyz(well=destination_well) + xs, ys, zs =Labware._getxyz(source_well) + xd, yd, zd = Labware._getxyz(destination_well) self._machine.safe_z_movement() self._machine.move_to(x=xs, y=ys) @@ -261,38 +229,3 @@ def transfer( # self.mix(mix_after[0], mix_after[1]) # else: # pass - - - @staticmethod - def _get_xyz(well: Well = None, location: Tuple[float] = None): - """Get the (x,y,z) position of a well. - - :param well: The well to fetch position of, defaults to None - :type well: :class:`Well`, optional - :param location: Directly specify an (x,y,z) location, defaults to None - :type location: Tuple[float], optional - :raises ValueError: Must specify either a well or a location - :return: The well location - :rtype: Tuple[float, float, float] - """ - if well is not None and location is not None: - raise ValueError("Specify only one of Well or x,y,z location") - elif well is not None: - x, y, z = well.x, well.y, well.z - else: - x, y, z = location - return x, y, z - - @staticmethod - def _get_top_bottom(well: Well = None): - """Get the top and bottom heights of a well. - - :param well: The well to fetch position of, defaults to None - :type well: Well, optional - :return: The z-height of the top and bottom of the well - :rtype: Tuple[float, float] - """ - top = well.top - bottom = well.bottom - return top, bottom - diff --git a/science_jubilee/tools/WebCamera.py b/science_jubilee/tools/WebCamera.py index 3f1c380..c357811 100644 --- a/science_jubilee/tools/WebCamera.py +++ b/science_jubilee/tools/WebCamera.py @@ -8,9 +8,9 @@ import matplotlib.pyplot as plt import numpy as np -from labware.Labware import Well, Location -from typing import Tuple, Union +from labware.Labware import Well, Location, Labware from science_jubilee.tools.Tool import Tool, requires_active_tool +from typing import Tuple, Union class Camera(Tool): @@ -74,29 +74,6 @@ def from_config(cls, index, name, config_file: str, kwargs = json.load(f) return cls(index=index, name=name,**kwargs) - @staticmethod - def _getxyz(location: Union[Well, Tuple, Location]): - """Helper function to extract the x, y, z coordinates of a location object. - - :param location: The location object to extract the coordinates from. This can either be a - :class:`Well`, a :tuple: of x, y, z coordinates, or a :class:`Location` object - :type location: Union[Well, Tuple, Location] - :raises ValueError: If the location is not a :class:`Well`, a :class:`tuple`, or a :class:`Location` object - :return: The x, y, z coordinates of the location - :rtype: float, float, float - """ - - if type(location) == Well: - x, y, z = location.x, location.y, location.z - elif type(location) == Tuple: - x, y, z = location - elif type(location)== Location: - x,y,z= location._point - else: - raise ValueError("Location should be of type Well or Tuple") - - return x,y,z - @requires_active_tool def _capture_image(self, timeout = 10): """ Capture image from raspberry pi and write to file @@ -131,7 +108,7 @@ def capture_image(self, location: Union[Well, Tuple], light: bool = False, """ assert 0 <= light_intensity <=1, "Light intensity must be between 0 and 1" - x, y, z = self._getxyz(location) + x, y, z = Labware._getxyz(location) self._machine.safe_z_movement() self._machine.move_to(x=x, y=y, wait=True) diff --git a/science_jubilee/utils.py b/science_jubilee/utils.py deleted file mode 100644 index 783ee73..0000000 --- a/science_jubilee/utils.py +++ /dev/null @@ -1,23 +0,0 @@ -from science_jubilee.labware.Labware import Labware, Well, Location -from typing import Tuple, Union - -def getxyz(location: Union[Well, Tuple, Location]): - """Helper function to extract the x, y, z coordinates of a location object. - - :param location: The location object to extract the coordinates from. This can either be a - :class:`Well`, a :tuple: of x, y, z coordinates, or a :class:`Location` object - :type location: Union[Well, Tuple, Location] - :raises ValueError: If the location is not a :class:`Well`, a :class:`tuple`, or a :class:`Location` object - :return: The x, y, z coordinates of the location - :rtype: float, float, float - """ - if type(location) == Well: - x, y, z = location.x, location.y, location.z - elif type(location) == Tuple: - x, y, z = location - elif type(location)==Location: - x,y,z= location._point - else: - raise ValueError("Location should be of type Well or Tuple") - - return x,y,z \ No newline at end of file