Skip to content

Commit

Permalink
Merge branch 'pick_up_tip_testing' into chore_release-7.2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Carlos-fernandez committed Feb 28, 2024
2 parents 795bda5 + 56e8c82 commit fb7ddc5
Show file tree
Hide file tree
Showing 25 changed files with 8,282 additions and 43 deletions.
4 changes: 0 additions & 4 deletions api/src/opentrons/config/defaults_ot3.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
DEFAULT_MODULE_OFFSET = [0.0, 0.0, 0.0]

DEFAULT_LIQUID_PROBE_SETTINGS: Final[LiquidProbeSettings] = LiquidProbeSettings(
starting_mount_height=100,
max_z_distance=40,
min_z_distance=5,
mount_speed=10,
Expand Down Expand Up @@ -277,9 +276,6 @@ def _build_default_liquid_probe(
from_conf: Any, default: LiquidProbeSettings
) -> LiquidProbeSettings:
return LiquidProbeSettings(
starting_mount_height=from_conf.get(
"starting_mount_height", default.starting_mount_height
),
max_z_distance=from_conf.get("max_z_distance", default.max_z_distance),
min_z_distance=from_conf.get("min_z_distance", default.min_z_distance),
mount_speed=from_conf.get("mount_speed", default.mount_speed),
Expand Down
1 change: 0 additions & 1 deletion api/src/opentrons/config/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ class ZSenseSettings:

@dataclass
class LiquidProbeSettings:
starting_mount_height: float
max_z_distance: float
min_z_distance: float
mount_speed: float
Expand Down
6 changes: 6 additions & 0 deletions api/src/opentrons/hardware_control/backends/ot3controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,12 @@ async def get_limit_switches(self) -> OT3AxisMap[bool]:
def _tip_motor_nodes(axis_current_keys: KeysView[Axis]) -> List[NodeId]:
return [axis_to_node(Axis.Q)] if Axis.Q in axis_current_keys else []

async def get_tip_present_state(self, mount: OT3Mount) -> TipStateType:
res = await get_tip_ejector_state(
self._messenger, sensor_node_for_mount(OT3Mount(mount.value)) # type: ignore
)
return res

async def set_default_currents(self) -> None:
"""Set both run and hold currents from robot config to each node."""
assert self._current_settings, "Invalid current settings"
Expand Down
29 changes: 26 additions & 3 deletions api/src/opentrons/hardware_control/ot3api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,7 @@ async def move_axes( # noqa: C901
await self.refresh_positions()

for axis in position.keys():
print(axis)
if not self._backend.axis_is_present(axis):
raise InvalidActuator(
message=f"{axis} is not present", detail={"axis": str(axis)}
Expand Down Expand Up @@ -2068,17 +2069,19 @@ async def verify_tip_presence(

async def _force_pick_up_tip(
self, mount: OT3Mount, pipette_spec: TipActionSpec
) -> None:
) -> Dict[Axis, float]:
for press in pipette_spec.tip_action_moves:
async with self._backend.motor_current(run_currents=press.currents):
target_down = target_position_from_relative(
mount, top_types.Point(z=press.distance), self._current_position
)
await self._move(target_down, speed=press.speed, expect_stalls=True)
press_dist = await self.encoder_current_position_ot3(mount)
if press.distance < 0:
# we expect a stall has happened during a downward movement into the tiprack, so
# we want to update the motor estimation
await self._update_position_estimation([Axis.by_mount(mount)])
return press_dist

async def _tip_motor_action(
self, mount: OT3Mount, pipette_spec: List[TipActionMoveSpec]
Expand All @@ -2102,14 +2105,33 @@ async def _tip_motor_action(
)
await self.home_gear_motors()

async def _motor_pickup_move(
self, mount: OT3Mount, motor_current: Mapping[Axis, float],
move_distance: float, speed: float) -> None:
async with self._backend.restore_current():
await self._backend.set_active_current(
{axis: current for axis, current in motor_current.items()}
)
# perform pick up tip
await self._backend.tip_action(
[Axis.of_main_tool_actuator(mount)],
move_distance,
speed,
"clamp",
)

async def get_tip_presence(self, mount: Union[top_types.Mount, OT3Mount]):
state = await self._backend.get_tip_present_state(mount)
return state

async def pick_up_tip(
self,
mount: Union[top_types.Mount, OT3Mount],
tip_length: float,
presses: Optional[int] = None,
increment: Optional[float] = None,
prep_after: bool = True,
) -> None:
) -> Dict[Axis, float]:
"""Pick up tip from current location."""
realmount = OT3Mount.from_mount(mount)
instrument = self._pipette_handler.get_pipette(realmount)
Expand Down Expand Up @@ -2139,7 +2161,7 @@ def add_tip_to_instr() -> None:
presses,
increment,
)
await self._force_pick_up_tip(realmount, spec)
press_dist = await self._force_pick_up_tip(realmount, spec)

# neighboring tips tend to get stuck in the space between
# the volume chamber and the drop-tip sleeve on p1000.
Expand All @@ -2158,6 +2180,7 @@ def add_tip_to_instr() -> None:

if prep_after:
await self.prepare_for_aspirate(realmount)
return press_dist

def set_current_tiprack_diameter(
self, mount: Union[top_types.Mount, OT3Mount], tiprack_diameter: float
Expand Down
70 changes: 46 additions & 24 deletions hardware-testing/hardware_testing/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from pathlib import Path
from subprocess import check_output
from time import time
from typing import Tuple, Union, List, Any
from typing import Tuple, Dict

from opentrons.config import infer_config_base_dir, IS_ROBOT

Expand All @@ -19,14 +19,13 @@ def _build_git_description_string() -> str:
if IS_ROBOT:
raise RuntimeError("unable to run git describe on robot")
raw_description = _git("describe")
raw_hash = _git("rev-parse", "--short", "HEAD")
description_split = raw_description.split("-")
description = "_".join(description_split)
desc_with_hash = description + "-" + raw_hash
# separate everything but git hash with an underscore
description = "_".join(description_split[:-1]) + "-" + description_split[-1]
mods = _git("ls-files", "-m").replace("\n", " ")
if not mods:
return desc_with_hash
return f"{desc_with_hash}-[{mods}]"
return description
return f"{description}-[{mods}]"


def get_git_description() -> str:
Expand Down Expand Up @@ -70,7 +69,7 @@ def create_test_name_from_file(f: str) -> str:
return os.path.basename(f).replace("_", "-").replace(".py", "")


def create_folder_for_test_data(test_name: Union[str, Path]) -> Path:
def create_folder_for_test_data(test_name: str) -> Path:
"""Create a folder for test data."""
base = _initialize_testing_data_base_dir()
test_path = base / test_name
Expand Down Expand Up @@ -100,34 +99,28 @@ def create_file_name(
return f"{test_name}_{run_id}_{tag}.{extension}"


def _save_data(
test_name: str, run_id: str, file_name: str, data: str, perm: str = "w+"
) -> Path:
def _save_data(test_name: str, file_name: str, data: str, perm: str = "w+") -> Path:
test_path = create_folder_for_test_data(test_name)
run_path = create_folder_for_test_data(test_path / run_id)
data_path = test_path / run_path / file_name
data_path = test_path / file_name
with open(data_path, perm) as f:
f.write(data)
return data_path


def dump_data_to_file(test_name: str, run_id: str, file_name: str, data: str) -> Path:
def dump_data_to_file(test_name: str, file_name: str, data: str) -> Path:
"""Save entire file contents to a file on disk."""
return _save_data(test_name, run_id, file_name, data, perm="w+")
return _save_data(test_name, file_name, data, perm="w+")


def append_data_to_file(test_name: str, run_id: str, file_name: str, data: str) -> Path:
def append_data_to_file(test_name: str, file_name: str, data: str) -> Path:
"""Append new content to an already existing file on disk."""
return _save_data(test_name, run_id, file_name, data, perm="a+")
return _save_data(test_name, file_name, data, perm="a+")


def insert_data_to_file(
test_name: str, run_id: str, file_name: str, data: str, line: int
) -> None:
def insert_data_to_file(test_name: str, file_name: str, data: str, line: int) -> None:
"""Insert new data at a specified line."""
test_path = create_folder_for_test_data(test_name)
run_path = create_folder_for_test_data(test_path / run_id)
data_path = run_path / file_name
data_path = test_path / file_name
# read data from file, insert line, then overwrite previous file
with open(data_path, "r") as f:
contents = f.readlines()
Expand All @@ -136,6 +129,35 @@ def insert_data_to_file(
f.write("".join(contents))


def convert_list_to_csv_line(elements: List[Any]) -> str:
"""Convert list of something into CSV line."""
return ",".join(str(elements))
def load_config_(filename: str) -> Dict:
"""This function loads a given config file"""
dir = os.getcwd()
filename = r"{}\{}".format(dir, filename)
# print(filename)
try:
with open(filename, "r") as file:
data = json.load(file)
except FileNotFoundError:
print("Warning: {0} not found".format(filename))
data = {}
except json.decoder.JSONDecodeError:
print("Error: {0} is corrupt".format(filename))
data = {}
return data


def save_config_(filename: str, data: str) -> Dict:
"""This function saves a given config file with data"""
dir = os.getcwd()
filename = r"{}\{}".format(dir, filename)
# print(filename)
try:
with open(filename, "w") as file:
json.dump(data, file, sort_keys=False, indent=4, separators=(",", ": "))
except FileNotFoundError:
print("Warning: {0} not found".format(filename))
data = {}
except json.decoder.JSONDecodeError:
print("Error: {0} is corrupt".format(filename))
data = {}
return data
32 changes: 26 additions & 6 deletions hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py
Original file line number Diff line number Diff line change
Expand Up @@ -557,22 +557,42 @@ async def update_pick_up_current(
) -> None:
"""Update pick-up-tip current."""
pipette = _get_pipette_from_mount(api, mount)
config_model = pipette.pick_up_configurations.press_fit
config_model.current_by_tip_count = {
k: current for k in config_model.current_by_tip_count.keys()
config_model = pipette.pick_up_configurations
print(f'config: {config_model}')
config_model.press_fit.current_by_tip_count = {
k: current for k in config_model.press_fit.current_by_tip_count.keys()
}
pipette.pick_up_configurations.press_fit = config_model
pipette.pick_up_configurations = config_model

async def update_drop_tip_current(
api: OT3API, mount: OT3Mount, current: float = 1.0
) -> None:
"""Update drop-tip current."""
pipette = _get_pipette_from_mount(api, mount)
config_model = pipette._drop_configurations
config_model.current = current
pipette._drop_configurations = config_model


async def update_pick_up_distance(
api: OT3API, mount: OT3Mount, distance: float = 17.0
) -> None:
"""Update pick-up-tip current."""
pipette = _get_pipette_from_mount(api, mount)
config_model = pipette.pick_up_configurations.press_fit
# print(pipette.pick_up_configurations)
config_model = pipette.pick_up_configurations
print(config_model)
config_model.distance = distance
pipette.pick_up_configurations.press_fit = config_model
pipette.pick_up_configurations = config_model

async def update_pick_up_speed(
api: OT3API, mount: OT3Mount, speed: float = 5.0
) -> None:
"""Update pick-up-tip current."""
pipette = _get_pipette_from_mount(api, mount)
config_model = pipette.pick_up_configurations.press_fit
config_model.speed = speed
pipette.pick_up_configurations = config_model

async def move_plunger_absolute_ot3(
api: OT3API,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
requirements = {"robotType": "Flex", "apiLevel": "2.15"}

ALLOW_TEST_PIPETTE_TO_TRANSFER_DILUENT = False
RETURN_TIP = False
RETURN_TIP = True
DILUENT_WELLS = ["A11", "A12"]

TEST_VOLUME = 200
TEST_PUSH_OUT = 15
TEST_PUSH_OUT = 20
TEST_PIPETTE = "flex_8channel_1000"
TEST_TIPS = "opentrons_flex_96_tiprack_200uL"
TEST_SOURCES = [
Expand Down
Loading

0 comments on commit fb7ddc5

Please sign in to comment.