Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(step-generation): fix blowout location from multi-dispense disposal #17012

Closed
2 changes: 1 addition & 1 deletion .github/actions/.gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1 @@
.github/actions/odd-resource-analysis/dist/* binary
odd-resource-analysis/dist/* binary
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ async function getMixpanelResourceMonitorDataFor({
where,
}) {
const params = new URLSearchParams({
project_id: projectId,
project_id: Number(projectId),
from_date: fromDate,
to_date: toDate,
event: '["resourceMonitorReport"]',
Expand Down
9 changes: 6 additions & 3 deletions .github/actions/odd-resource-analysis/dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,11 @@ function processMixpanelData(data) {
const systemMemory = []

data.forEach(entry => {
const { systemUptimeHrs, systemAvailMemMb, processesDetails } =
entry.properties
const {
systemUptimeHrs,
systemAvailMemMb,
processesDetails,
} = entry.properties
const uptime = parseFloat(systemUptimeHrs)

// Validate uptime before adding any measurements
Expand Down Expand Up @@ -582,7 +585,7 @@ async function getMixpanelResourceMonitorDataFor({
where,
}) {
const params = new URLSearchParams({
project_id: projectId,
project_id: Number(projectId),
from_date: fromDate,
to_date: toDate,
event: '["resourceMonitorReport"]',
Expand Down
10 changes: 10 additions & 0 deletions api/src/opentrons/hardware_control/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,16 @@ def engaged_axes(self) -> Dict[Axis, bool]:
async def disengage_axes(self, which: List[Axis]) -> None:
await self._backend.disengage_axes([ot2_axis_to_string(ax) for ax in which])

def axis_is_present(self, axis: Axis) -> bool:
is_ot2 = axis in Axis.ot2_axes()
if not is_ot2:
return False
if axis in Axis.pipette_axes():
mount = Axis.to_ot2_mount(axis)
if self.attached_pipettes.get(mount) is None:
return False
return True

@ExecutionManagerProvider.wait_for_running
async def _fast_home(self, axes: Sequence[str], margin: float) -> Dict[str, float]:
converted_axes = "".join(axes)
Expand Down
7 changes: 6 additions & 1 deletion api/src/opentrons/hardware_control/ot3api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1674,7 +1674,12 @@ async def disengage_axes(self, which: List[Axis]) -> None:
await self._backend.disengage_axes(which)

async def engage_axes(self, which: List[Axis]) -> None:
await self._backend.engage_axes(which)
await self._backend.engage_axes(
[axis for axis in which if self._backend.axis_is_present(axis)]
)

def axis_is_present(self, axis: Axis) -> bool:
return self._backend.axis_is_present(axis)

async def get_limit_switches(self) -> Dict[Axis, bool]:
res = await self._backend.get_limit_switches()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Dict, Optional
from typing_extensions import Protocol

from ..types import SubSystem, SubSystemState
from ..types import SubSystem, SubSystemState, Axis


class HardwareManager(Protocol):
Expand Down Expand Up @@ -45,3 +45,7 @@ def attached_subsystems(self) -> Dict[SubSystem, SubSystemState]:
async def get_serial_number(self) -> Optional[str]:
"""Get the robot serial number, if provisioned. If not provisioned, will be None."""
...

def axis_is_present(self, axis: Axis) -> bool:
"""Get whether a motor axis is present on the machine."""
...
153 changes: 152 additions & 1 deletion api/src/opentrons/protocol_api/_liquid_properties.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
from dataclasses import dataclass
from numpy import interp
from typing import Optional, Dict, Sequence, Tuple
from typing import Optional, Dict, Sequence, Tuple, List

from opentrons_shared_data.liquid_classes.liquid_class_definition import (
AspirateProperties as SharedDataAspirateProperties,
SingleDispenseProperties as SharedDataSingleDispenseProperties,
MultiDispenseProperties as SharedDataMultiDispenseProperties,
DelayProperties as SharedDataDelayProperties,
DelayParams as SharedDataDelayParams,
TouchTipProperties as SharedDataTouchTipProperties,
LiquidClassTouchTipParams as SharedDataTouchTipParams,
MixProperties as SharedDataMixProperties,
MixParams as SharedDataMixParams,
BlowoutProperties as SharedDataBlowoutProperties,
BlowoutParams as SharedDataBlowoutParams,
ByTipTypeSetting as SharedByTipTypeSetting,
Submerge as SharedDataSubmerge,
RetractAspirate as SharedDataRetractAspirate,
Expand Down Expand Up @@ -37,6 +41,10 @@ def as_dict(self) -> Dict[float, float]:
"""Get a dictionary representation of all set volumes and values along with the default."""
return self._properties_by_volume

def as_list_of_tuples(self) -> List[Tuple[float, float]]:
"""Get as list of tuples."""
return list(self._properties_by_volume.items())

def get_for_volume(self, volume: float) -> float:
"""Get a value by volume for this property. Volumes not defined will be interpolated between set volumes."""
validated_volume = validation.ensure_positive_float(volume)
Expand Down Expand Up @@ -101,6 +109,14 @@ def duration(self, new_duration: float) -> None:
validated_duration = validation.ensure_positive_float(new_duration)
self._duration = validated_duration

def as_shared_data_model(self) -> SharedDataDelayProperties:
return SharedDataDelayProperties(
enable=self._enabled,
params=SharedDataDelayParams(duration=self.duration)
if self.duration is not None
else None,
)


@dataclass
class TouchTipProperties:
Expand Down Expand Up @@ -152,6 +168,27 @@ def speed(self, new_speed: float) -> None:
validated_speed = validation.ensure_positive_float(new_speed)
self._speed = validated_speed

def _get_shared_data_params(self) -> Optional[SharedDataTouchTipParams]:
"""Get the touch tip params in schema v1 shape."""
if (
self._z_offset is not None
and self._mm_to_edge is not None
and self._speed is not None
):
return SharedDataTouchTipParams(
zOffset=self._z_offset,
mmToEdge=self._mm_to_edge,
speed=self._speed,
)
else:
return None

def as_shared_data_model(self) -> SharedDataTouchTipProperties:
return SharedDataTouchTipProperties(
enable=self._enabled,
params=self._get_shared_data_params(),
)


@dataclass
class MixProperties:
Expand Down Expand Up @@ -189,6 +226,22 @@ def volume(self, new_volume: float) -> None:
validated_volume = validation.ensure_positive_float(new_volume)
self._volume = validated_volume

def _get_shared_data_params(self) -> Optional[SharedDataMixParams]:
"""Get the mix params in schema v1 shape."""
if self._repetitions is not None and self._volume is not None:
return SharedDataMixParams(
repetitions=self._repetitions,
volume=self._volume,
)
else:
return None

def as_shared_data_model(self) -> SharedDataMixProperties:
return SharedDataMixProperties(
enable=self._enabled,
params=self._get_shared_data_params(),
)


@dataclass
class BlowoutProperties:
Expand Down Expand Up @@ -227,6 +280,22 @@ def flow_rate(self, new_flow_rate: float) -> None:
validated_flow_rate = validation.ensure_positive_float(new_flow_rate)
self._flow_rate = validated_flow_rate

def _get_shared_data_params(self) -> Optional[SharedDataBlowoutParams]:
"""Get the mix params in schema v1 shape."""
if self._location is not None and self._flow_rate is not None:
return SharedDataBlowoutParams(
location=self._location,
flowRate=self._flow_rate,
)
else:
return None

def as_shared_data_model(self) -> SharedDataBlowoutProperties:
return SharedDataBlowoutProperties(
enable=self._enabled,
params=self._get_shared_data_params(),
)


@dataclass
class SubmergeRetractCommon:
Expand Down Expand Up @@ -271,6 +340,14 @@ def delay(self) -> DelayProperties:
class Submerge(SubmergeRetractCommon):
...

def as_shared_data_model(self) -> SharedDataSubmerge:
return SharedDataSubmerge(
positionReference=self._position_reference,
offset=self._offset,
speed=self._speed,
delay=self._delay.as_shared_data_model(),
)


@dataclass
class RetractAspirate(SubmergeRetractCommon):
Expand All @@ -286,6 +363,16 @@ def air_gap_by_volume(self) -> LiquidHandlingPropertyByVolume:
def touch_tip(self) -> TouchTipProperties:
return self._touch_tip

def as_shared_data_model(self) -> SharedDataRetractAspirate:
return SharedDataRetractAspirate(
positionReference=self._position_reference,
offset=self._offset,
speed=self._speed,
airGapByVolume=self._air_gap_by_volume.as_list_of_tuples(),
touchTip=self._touch_tip.as_shared_data_model(),
delay=self._delay.as_shared_data_model(),
)


@dataclass
class RetractDispense(SubmergeRetractCommon):
Expand All @@ -306,6 +393,17 @@ def touch_tip(self) -> TouchTipProperties:
def blowout(self) -> BlowoutProperties:
return self._blowout

def as_shared_data_model(self) -> SharedDataRetractDispense:
return SharedDataRetractDispense(
positionReference=self._position_reference,
offset=self._offset,
speed=self._speed,
airGapByVolume=self._air_gap_by_volume.as_list_of_tuples(),
blowout=self._blowout.as_shared_data_model(),
touchTip=self._touch_tip.as_shared_data_model(),
delay=self._delay.as_shared_data_model(),
)


@dataclass
class BaseLiquidHandlingProperties:
Expand All @@ -314,6 +412,7 @@ class BaseLiquidHandlingProperties:
_position_reference: PositionReference
_offset: Coordinate
_flow_rate_by_volume: LiquidHandlingPropertyByVolume
_correction_by_volume: LiquidHandlingPropertyByVolume
_delay: DelayProperties

@property
Expand Down Expand Up @@ -341,6 +440,10 @@ def offset(self, new_offset: Sequence[float]) -> None:
def flow_rate_by_volume(self) -> LiquidHandlingPropertyByVolume:
return self._flow_rate_by_volume

@property
def correction_by_volume(self) -> LiquidHandlingPropertyByVolume:
return self._correction_by_volume

@property
def delay(self) -> DelayProperties:
return self._delay
Expand Down Expand Up @@ -370,6 +473,19 @@ def retract(self) -> RetractAspirate:
def mix(self) -> MixProperties:
return self._mix

def as_shared_data_model(self) -> SharedDataAspirateProperties:
return SharedDataAspirateProperties(
submerge=self._submerge.as_shared_data_model(),
retract=self._retract.as_shared_data_model(),
positionReference=self._position_reference,
offset=self._offset,
flowRateByVolume=self._flow_rate_by_volume.as_list_of_tuples(),
preWet=self._pre_wet,
mix=self._mix.as_shared_data_model(),
delay=self._delay.as_shared_data_model(),
correctionByVolume=self._correction_by_volume.as_list_of_tuples(),
)


@dataclass
class SingleDispenseProperties(BaseLiquidHandlingProperties):
Expand All @@ -390,6 +506,19 @@ def retract(self) -> RetractDispense:
def mix(self) -> MixProperties:
return self._mix

def as_shared_data_model(self) -> SharedDataSingleDispenseProperties:
return SharedDataSingleDispenseProperties(
submerge=self._submerge.as_shared_data_model(),
retract=self._retract.as_shared_data_model(),
positionReference=self._position_reference,
offset=self._offset,
flowRateByVolume=self._flow_rate_by_volume.as_list_of_tuples(),
mix=self._mix.as_shared_data_model(),
pushOutByVolume=self._push_out_by_volume.as_list_of_tuples(),
delay=self._delay.as_shared_data_model(),
correctionByVolume=self._correction_by_volume.as_list_of_tuples(),
)


@dataclass
class MultiDispenseProperties(BaseLiquidHandlingProperties):
Expand All @@ -410,6 +539,19 @@ def conditioning_by_volume(self) -> LiquidHandlingPropertyByVolume:
def disposal_by_volume(self) -> LiquidHandlingPropertyByVolume:
return self._disposal_by_volume

def as_shared_data_model(self) -> SharedDataMultiDispenseProperties:
return SharedDataMultiDispenseProperties(
submerge=self._submerge.as_shared_data_model(),
retract=self._retract.as_shared_data_model(),
positionReference=self._position_reference,
offset=self._offset,
flowRateByVolume=self._flow_rate_by_volume.as_list_of_tuples(),
conditioningByVolume=self._conditioning_by_volume.as_list_of_tuples(),
disposalByVolume=self._disposal_by_volume.as_list_of_tuples(),
delay=self._delay.as_shared_data_model(),
correctionByVolume=self._correction_by_volume.as_list_of_tuples(),
)


@dataclass
class TransferProperties:
Expand Down Expand Up @@ -543,6 +685,9 @@ def build_aspirate_properties(
_flow_rate_by_volume=LiquidHandlingPropertyByVolume(
aspirate_properties.flowRateByVolume
),
_correction_by_volume=LiquidHandlingPropertyByVolume(
aspirate_properties.correctionByVolume
),
_pre_wet=aspirate_properties.preWet,
_mix=_build_mix_properties(aspirate_properties.mix),
_delay=_build_delay_properties(aspirate_properties.delay),
Expand All @@ -560,6 +705,9 @@ def build_single_dispense_properties(
_flow_rate_by_volume=LiquidHandlingPropertyByVolume(
single_dispense_properties.flowRateByVolume
),
_correction_by_volume=LiquidHandlingPropertyByVolume(
single_dispense_properties.correctionByVolume
),
_mix=_build_mix_properties(single_dispense_properties.mix),
_push_out_by_volume=LiquidHandlingPropertyByVolume(
single_dispense_properties.pushOutByVolume
Expand All @@ -581,6 +729,9 @@ def build_multi_dispense_properties(
_flow_rate_by_volume=LiquidHandlingPropertyByVolume(
multi_dispense_properties.flowRateByVolume
),
_correction_by_volume=LiquidHandlingPropertyByVolume(
multi_dispense_properties.correctionByVolume
),
_conditioning_by_volume=LiquidHandlingPropertyByVolume(
multi_dispense_properties.conditioningByVolume
),
Expand Down
Loading
Loading