opentrons 8.4.1a1__py2.py3-none-any.whl → 8.5.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. opentrons/config/defaults_ot3.py +1 -1
  2. opentrons/hardware_control/backends/flex_protocol.py +25 -0
  3. opentrons/hardware_control/backends/ot3controller.py +76 -1
  4. opentrons/hardware_control/backends/ot3simulator.py +27 -0
  5. opentrons/hardware_control/instruments/ot3/pipette_handler.py +1 -0
  6. opentrons/hardware_control/ot3api.py +32 -0
  7. opentrons/legacy_commands/commands.py +16 -4
  8. opentrons/legacy_commands/robot_commands.py +51 -0
  9. opentrons/legacy_commands/types.py +91 -2
  10. opentrons/protocol_api/_liquid.py +60 -15
  11. opentrons/protocol_api/_liquid_properties.py +149 -90
  12. opentrons/protocol_api/_transfer_liquid_validation.py +43 -14
  13. opentrons/protocol_api/core/engine/instrument.py +367 -221
  14. opentrons/protocol_api/core/engine/protocol.py +14 -15
  15. opentrons/protocol_api/core/engine/robot.py +2 -2
  16. opentrons/protocol_api/core/engine/transfer_components_executor.py +275 -163
  17. opentrons/protocol_api/core/engine/well.py +16 -0
  18. opentrons/protocol_api/core/instrument.py +11 -5
  19. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +11 -5
  20. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +2 -2
  21. opentrons/protocol_api/core/legacy/legacy_well_core.py +8 -0
  22. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +11 -5
  23. opentrons/protocol_api/core/protocol.py +3 -3
  24. opentrons/protocol_api/core/well.py +8 -0
  25. opentrons/protocol_api/instrument_context.py +478 -111
  26. opentrons/protocol_api/labware.py +10 -0
  27. opentrons/protocol_api/module_contexts.py +5 -2
  28. opentrons/protocol_api/protocol_context.py +76 -11
  29. opentrons/protocol_api/robot_context.py +48 -6
  30. opentrons/protocol_api/validation.py +15 -8
  31. opentrons/protocol_engine/commands/command_unions.py +10 -10
  32. opentrons/protocol_engine/commands/generate_command_schema.py +1 -1
  33. opentrons/protocol_engine/commands/get_next_tip.py +2 -2
  34. opentrons/protocol_engine/commands/load_labware.py +0 -19
  35. opentrons/protocol_engine/commands/pick_up_tip.py +9 -3
  36. opentrons/protocol_engine/commands/robot/__init__.py +20 -20
  37. opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +34 -24
  38. opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +29 -20
  39. opentrons/protocol_engine/commands/seal_pipette_to_tip.py +1 -1
  40. opentrons/protocol_engine/commands/unsafe/__init__.py +17 -1
  41. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +1 -2
  42. opentrons/protocol_engine/execution/labware_movement.py +9 -2
  43. opentrons/protocol_engine/execution/movement.py +12 -9
  44. opentrons/protocol_engine/execution/queue_worker.py +8 -1
  45. opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +52 -19
  46. opentrons/protocol_engine/resources/labware_validation.py +7 -1
  47. opentrons/protocol_engine/state/_well_math.py +2 -2
  48. opentrons/protocol_engine/state/commands.py +14 -28
  49. opentrons/protocol_engine/state/frustum_helpers.py +11 -7
  50. opentrons/protocol_engine/state/labware.py +12 -0
  51. opentrons/protocol_engine/state/modules.py +1 -1
  52. opentrons/protocol_engine/state/pipettes.py +8 -0
  53. opentrons/protocol_engine/state/tips.py +46 -83
  54. opentrons/protocol_engine/state/update_types.py +8 -23
  55. opentrons/protocol_engine/types/liquid_level_detection.py +68 -8
  56. opentrons/protocol_runner/legacy_command_mapper.py +12 -6
  57. opentrons/protocol_runner/run_orchestrator.py +1 -1
  58. opentrons/protocols/advanced_control/transfers/common.py +54 -11
  59. opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +55 -28
  60. opentrons/protocols/api_support/definitions.py +1 -1
  61. opentrons/types.py +6 -6
  62. {opentrons-8.4.1a1.dist-info → opentrons-8.5.0.dist-info}/METADATA +4 -4
  63. {opentrons-8.4.1a1.dist-info → opentrons-8.5.0.dist-info}/RECORD +67 -66
  64. {opentrons-8.4.1a1.dist-info → opentrons-8.5.0.dist-info}/LICENSE +0 -0
  65. {opentrons-8.4.1a1.dist-info → opentrons-8.5.0.dist-info}/WHEEL +0 -0
  66. {opentrons-8.4.1a1.dist-info → opentrons-8.5.0.dist-info}/entry_points.txt +0 -0
  67. {opentrons-8.4.1a1.dist-info → opentrons-8.5.0.dist-info}/top_level.txt +0 -0
@@ -158,7 +158,7 @@ DEFAULT_HOLD_CURRENT: Final[ByGantryLoad[Dict[OT3AxisKind, float]]] = ByGantryLo
158
158
  OT3AxisKind.X: 0.5,
159
159
  OT3AxisKind.Y: 0.5,
160
160
  OT3AxisKind.Z: 0.5,
161
- OT3AxisKind.P: 0.3,
161
+ OT3AxisKind.P: 0.8,
162
162
  OT3AxisKind.Z_G: 0.2,
163
163
  OT3AxisKind.Q: 0.3,
164
164
  },
@@ -42,6 +42,7 @@ from opentrons.hardware_control.module_control import AttachedModulesControl
42
42
  from ..dev_types import OT3AttachedInstruments
43
43
  from .types import HWStopCondition
44
44
 
45
+
45
46
  Cls = TypeVar("Cls")
46
47
 
47
48
 
@@ -466,3 +467,27 @@ class FlexBackend(Protocol):
466
467
  async def increase_evo_disp_count(self, mount: OT3Mount) -> None:
467
468
  """Tell a pipette to increase it's evo-tip-dispense-count in eeprom."""
468
469
  ...
470
+
471
+ async def read_env_temp_sensor(
472
+ self, mount: OT3Mount, primary: bool
473
+ ) -> Optional[float]:
474
+ """Read and return the current sensor information."""
475
+ ...
476
+
477
+ async def read_env_hum_sensor(
478
+ self, mount: OT3Mount, primary: bool
479
+ ) -> Optional[float]:
480
+ """Read and return the current sensor information."""
481
+ ...
482
+
483
+ async def read_pressure_sensor(
484
+ self, mount: OT3Mount, primary: bool
485
+ ) -> Optional[float]:
486
+ """Read and return the current sensor information."""
487
+ ...
488
+
489
+ async def read_capacitive_sensor(
490
+ self, mount: OT3Mount, primary: bool
491
+ ) -> Optional[float]:
492
+ """Read and return the current sensor information."""
493
+ ...
@@ -215,7 +215,13 @@ from ..types import HepaFanState, HepaUVState, StatusBarState
215
215
  from .types import HWStopCondition
216
216
  from .flex_protocol import FlexBackend
217
217
  from .status_bar_state import StatusBarStateController
218
- from opentrons_hardware.sensors.types import SensorDataType
218
+ from opentrons_hardware.sensors.sensor_types import (
219
+ EnvironmentSensor,
220
+ CapacitiveSensor,
221
+ PressureSensor,
222
+ )
223
+ from opentrons_hardware.sensors.types import SensorDataType, EnvironmentSensorDataType
224
+ from opentrons_hardware.sensors.sensor_driver import SensorDriver
219
225
  from opentrons_hardware.sensors.utils import send_evo_dispense_count_increase
220
226
 
221
227
  log = logging.getLogger(__name__)
@@ -1835,3 +1841,72 @@ class OT3Controller(FlexBackend):
1835
1841
  await send_evo_dispense_count_increase(
1836
1842
  self._messenger, sensor_node_for_pipette(OT3Mount(mount.value))
1837
1843
  )
1844
+
1845
+ async def _read_env_sensor(
1846
+ self, mount: OT3Mount, primary: bool
1847
+ ) -> Optional[EnvironmentSensorDataType]:
1848
+ """Read and return the current sensor information."""
1849
+ sensor = EnvironmentSensor.build(
1850
+ sensor_id=SensorId.S0 if primary else SensorId.S1,
1851
+ node_id=sensor_node_for_mount(mount),
1852
+ )
1853
+ s_driver = SensorDriver()
1854
+ sensor_data = await s_driver.read(
1855
+ can_messenger=self._messenger,
1856
+ sensor=sensor,
1857
+ offset=False,
1858
+ )
1859
+ assert sensor_data is None or isinstance(sensor_data, EnvironmentSensorDataType)
1860
+ return sensor_data
1861
+
1862
+ async def read_env_temp_sensor(
1863
+ self, mount: OT3Mount, primary: bool
1864
+ ) -> Optional[float]:
1865
+ """Read and return the current sensor information."""
1866
+ s_data = await self._read_env_sensor(mount, primary)
1867
+ if s_data is None or s_data.temperature is None:
1868
+ return None
1869
+ return s_data.temperature.to_float()
1870
+
1871
+ async def read_env_hum_sensor(
1872
+ self, mount: OT3Mount, primary: bool
1873
+ ) -> Optional[float]:
1874
+ """Read and return the current sensor information."""
1875
+ s_data = await self._read_env_sensor(mount, primary)
1876
+ if s_data is None or s_data.humidity is None:
1877
+ return None
1878
+ return s_data.humidity.to_float()
1879
+
1880
+ async def read_pressure_sensor(
1881
+ self, mount: OT3Mount, primary: bool
1882
+ ) -> Optional[float]:
1883
+ """Read and return the current sensor information."""
1884
+ sensor = PressureSensor.build(
1885
+ sensor_id=SensorId.S0 if primary else SensorId.S1,
1886
+ node_id=sensor_node_for_mount(mount),
1887
+ )
1888
+ s_driver = SensorDriver()
1889
+ sensor_data = await s_driver.read(
1890
+ can_messenger=self._messenger,
1891
+ sensor=sensor,
1892
+ offset=False,
1893
+ )
1894
+ assert sensor_data is None or isinstance(sensor_data, SensorDataType)
1895
+ return sensor_data.to_float() if sensor_data else None
1896
+
1897
+ async def read_capacitive_sensor(
1898
+ self, mount: OT3Mount, primary: bool
1899
+ ) -> Optional[float]:
1900
+ """Read and return the current sensor information."""
1901
+ sensor = CapacitiveSensor.build(
1902
+ sensor_id=SensorId.S0 if primary else SensorId.S1,
1903
+ node_id=sensor_node_for_mount(mount),
1904
+ )
1905
+ s_driver = SensorDriver()
1906
+ sensor_data = await s_driver.read(
1907
+ can_messenger=self._messenger,
1908
+ sensor=sensor,
1909
+ offset=False,
1910
+ )
1911
+ assert sensor_data is None or isinstance(sensor_data, SensorDataType)
1912
+ return sensor_data.to_float() if sensor_data else None
@@ -67,6 +67,7 @@ from .flex_protocol import (
67
67
  FlexBackend,
68
68
  )
69
69
 
70
+
70
71
  log = logging.getLogger(__name__)
71
72
 
72
73
  AXIS_TO_SUBSYSTEM = {
@@ -874,3 +875,29 @@ class OT3Simulator(FlexBackend):
874
875
 
875
876
  async def increase_evo_disp_count(self, mount: OT3Mount) -> None:
876
877
  pass
878
+
879
+ async def read_env_temp_sensor(
880
+ self, mount: OT3Mount, primary: bool
881
+ ) -> Optional[float]:
882
+ """Read and return the current sensor information."""
883
+
884
+ return 0.0
885
+
886
+ async def read_env_hum_sensor(
887
+ self, mount: OT3Mount, primary: bool
888
+ ) -> Optional[float]:
889
+ """Read and return the current sensor information."""
890
+
891
+ return 0.0
892
+
893
+ async def read_pressure_sensor(
894
+ self, mount: OT3Mount, primary: bool
895
+ ) -> Optional[float]:
896
+ """Read and return the current sensor information."""
897
+ return 0.0
898
+
899
+ async def read_capacitive_sensor(
900
+ self, mount: OT3Mount, primary: bool
901
+ ) -> Optional[float]:
902
+ """Read and return the current sensor information."""
903
+ return 0.0
@@ -665,6 +665,7 @@ class OT3PipetteHandler:
665
665
  return None
666
666
 
667
667
  if is_full_dispense:
668
+ disp_vol = instrument.current_volume
668
669
  if push_out is None:
669
670
  push_out_ul = instrument.push_out_volume
670
671
  else:
@@ -3140,3 +3140,35 @@ class OT3API(
3140
3140
  """Tell a pipette to increase its evo-tip-dispense-count in eeprom."""
3141
3141
  realmount = OT3Mount.from_mount(mount)
3142
3142
  await self._backend.increase_evo_disp_count(realmount)
3143
+
3144
+ async def read_stem_temperature(
3145
+ self, mount: Union[top_types.Mount, OT3Mount], primary: bool = True
3146
+ ) -> float:
3147
+ """Read and return the current stem temperature."""
3148
+ realmount = OT3Mount.from_mount(mount)
3149
+ s_data = await self._backend.read_env_temp_sensor(realmount, primary)
3150
+ return s_data if s_data else 0.0
3151
+
3152
+ async def read_stem_humidity(
3153
+ self, mount: Union[top_types.Mount, OT3Mount], primary: bool = True
3154
+ ) -> float:
3155
+ """Read and return the current primary stem humidity."""
3156
+ realmount = OT3Mount.from_mount(mount)
3157
+ s_data = await self._backend.read_env_hum_sensor(realmount, primary)
3158
+ return s_data if s_data else 0.0
3159
+
3160
+ async def read_stem_pressure(
3161
+ self, mount: Union[top_types.Mount, OT3Mount], primary: bool = True
3162
+ ) -> float:
3163
+ """Read and return the current primary stem pressure."""
3164
+ realmount = OT3Mount.from_mount(mount)
3165
+ s_data = await self._backend.read_pressure_sensor(realmount, primary)
3166
+ return s_data if s_data else 0.0
3167
+
3168
+ async def read_stem_capacitance(
3169
+ self, mount: Union[top_types.Mount, OT3Mount], primary: bool = True
3170
+ ) -> float:
3171
+ """Read and return the current primary stem capacitance."""
3172
+ realmount = OT3Mount.from_mount(mount)
3173
+ s_data = await self._backend.read_capacitive_sensor(realmount, primary)
3174
+ return s_data if s_data else 0.0
@@ -328,12 +328,18 @@ def transfer_with_liquid_class(
328
328
  liquid_class: LiquidClass,
329
329
  volume: float,
330
330
  source: Union[Well, Sequence[Well], Sequence[Sequence[Well]]],
331
- destination: Union[Well, Sequence[Well], Sequence[Sequence[Well]]],
331
+ destination: Union[
332
+ Well, Sequence[Well], Sequence[Sequence[Well]], TrashBin, WasteChute
333
+ ],
332
334
  ) -> command_types.TransferWithLiquidClassCommand:
335
+ if isinstance(destination, (TrashBin, WasteChute)):
336
+ destination_text = stringify_disposal_location(destination)
337
+ else:
338
+ destination_text = stringify_well_list(destination)
333
339
  text = (
334
340
  "Transferring "
335
341
  + f"{volume} uL of {liquid_class.display_name} liquid class from "
336
- + f"{stringify_well_list(source)} to {stringify_well_list(destination)}"
342
+ + f"{stringify_well_list(source)} to {destination_text}"
337
343
  )
338
344
  return {
339
345
  "name": command_types.TRANSFER_WITH_LIQUID_CLASS,
@@ -378,12 +384,18 @@ def consolidate_with_liquid_class(
378
384
  liquid_class: LiquidClass,
379
385
  volume: float,
380
386
  source: Union[Well, Sequence[Well], Sequence[Sequence[Well]]],
381
- destination: Union[Well, Sequence[Well], Sequence[Sequence[Well]]],
387
+ destination: Union[
388
+ Well, Sequence[Well], Sequence[Sequence[Well]], TrashBin, WasteChute
389
+ ],
382
390
  ) -> command_types.ConsolidateWithLiquidClassCommand:
391
+ if isinstance(destination, (TrashBin, WasteChute)):
392
+ destination_text = stringify_disposal_location(destination)
393
+ else:
394
+ destination_text = stringify_well_list(destination)
383
395
  text = (
384
396
  "Consolidating "
385
397
  + f"{volume} uL of {liquid_class.display_name} liquid class from "
386
- + f"{stringify_well_list(source)} to {stringify_well_list(destination)}"
398
+ + f"{stringify_well_list(source)} to {destination_text}"
387
399
  )
388
400
  return {
389
401
  "name": command_types.CONSOLIDATE_WITH_LIQUID_CLASS,
@@ -0,0 +1,51 @@
1
+ from opentrons.types import Location, Mount, AxisMapType
2
+ from .helpers import stringify_location
3
+ from . import types as command_types
4
+ from typing import Optional
5
+
6
+
7
+ def move_to(
8
+ mount: Mount, location: Location, speed: Optional[float]
9
+ ) -> command_types.RobotMoveToCommand:
10
+ location_text = stringify_location(location)
11
+ text = f"Moving to {location_text} at {speed}"
12
+ return {
13
+ "name": command_types.ROBOT_MOVE_TO,
14
+ "payload": {"mount": mount, "location": location, "text": text},
15
+ }
16
+
17
+
18
+ def move_axis_to(
19
+ axis_map: AxisMapType, speed: Optional[float]
20
+ ) -> command_types.RobotMoveAxisToCommand:
21
+ text = f"Moving to the provided absolute axis map {axis_map} at {speed}."
22
+ return {
23
+ "name": command_types.ROBOT_MOVE_AXES_TO,
24
+ "payload": {"absolute_axes": axis_map, "text": text},
25
+ }
26
+
27
+
28
+ def move_axis_relative(
29
+ axis_map: AxisMapType, speed: Optional[float]
30
+ ) -> command_types.RobotMoveAxisRelativeCommand:
31
+ text = f"Moving to the provided relative axis map {axis_map} as {speed}"
32
+ return {
33
+ "name": command_types.ROBOT_MOVE_RELATIVE_TO,
34
+ "payload": {"relative_axes": axis_map, "text": text},
35
+ }
36
+
37
+
38
+ def open_gripper() -> command_types.RobotOpenGripperJawCommand:
39
+ text = "Opening the gripper jaw."
40
+ return {
41
+ "name": command_types.ROBOT_OPEN_GRIPPER_JAW,
42
+ "payload": {"text": text},
43
+ }
44
+
45
+
46
+ def close_gripper(force: Optional[float]) -> command_types.RobotCloseGripperJawCommand:
47
+ text = f"Closing the gripper jaw with force {force}."
48
+ return {
49
+ "name": command_types.ROBOT_CLOSE_GRIPPER_JAW,
50
+ "payload": {"text": text},
51
+ }
@@ -10,7 +10,7 @@ if TYPE_CHECKING:
10
10
  from opentrons.protocol_api.disposal_locations import TrashBin, WasteChute
11
11
  from opentrons.protocol_api._liquid import LiquidClass
12
12
 
13
- from opentrons.types import Location
13
+ from opentrons.types import Location, Mount, AxisMapType
14
14
 
15
15
 
16
16
  # type for subscriptions
@@ -86,6 +86,13 @@ THERMOCYCLER_SET_LID_TEMP: Final = "command.THERMOCYCLER_SET_LID_TEMP"
86
86
  THERMOCYCLER_DEACTIVATE_LID: Final = "command.THERMOCYCLER_DEACTIVATE_LID"
87
87
  THERMOCYCLER_DEACTIVATE_BLOCK: Final = "command.THERMOCYCLER_DEACTIVATE_BLOCK"
88
88
 
89
+ # Robot #
90
+ ROBOT_MOVE_TO: Final = "command.ROBOT_MOVE_TO"
91
+ ROBOT_MOVE_AXES_TO: Final = "command.ROBOT_MOVE_AXES_TO"
92
+ ROBOT_MOVE_RELATIVE_TO: Final = "command.ROBOT_MOVE_RELATIVE_TO"
93
+ ROBOT_OPEN_GRIPPER_JAW: Final = "command.ROBOT_OPEN_GRIPPER_JAW"
94
+ ROBOT_CLOSE_GRIPPER_JAW: Final = "command.ROBOT_CLOSE_GRIPPER_JAW"
95
+
89
96
 
90
97
  class TextOnlyPayload(TypedDict):
91
98
  text: str
@@ -548,7 +555,9 @@ class LiquidClassCommandPayload(TextOnlyPayload, SingleInstrumentPayload):
548
555
  liquid_class: LiquidClass
549
556
  volume: float
550
557
  source: Union[Well, Sequence[Well], Sequence[Sequence[Well]]]
551
- destination: Union[Well, Sequence[Well], Sequence[Sequence[Well]]]
558
+ destination: Union[
559
+ Well, Sequence[Well], Sequence[Sequence[Well]], TrashBin, WasteChute
560
+ ]
552
561
 
553
562
 
554
563
  class TransferWithLiquidClassCommand(TypedDict):
@@ -600,6 +609,49 @@ class PressurizeCommand(TypedDict):
600
609
  payload: PressurizeCommandPayload
601
610
 
602
611
 
612
+ # Robot Commands and Payloads
613
+ class GripperCommandPayload(TextOnlyPayload):
614
+ pass
615
+
616
+
617
+ class RobotMoveToCommandPayload(TextOnlyPayload):
618
+ location: Location
619
+ mount: Mount
620
+
621
+
622
+ class RobotMoveAxisToCommandPayload(TextOnlyPayload):
623
+ absolute_axes: AxisMapType
624
+
625
+
626
+ class RobotMoveAxisRelativeCommandPayload(TextOnlyPayload):
627
+ relative_axes: AxisMapType
628
+
629
+
630
+ class RobotMoveToCommand(TypedDict):
631
+ name: Literal["command.ROBOT_MOVE_TO"]
632
+ payload: RobotMoveToCommandPayload
633
+
634
+
635
+ class RobotMoveAxisToCommand(TypedDict):
636
+ name: Literal["command.ROBOT_MOVE_AXES_TO"]
637
+ payload: RobotMoveAxisToCommandPayload
638
+
639
+
640
+ class RobotMoveAxisRelativeCommand(TypedDict):
641
+ name: Literal["command.ROBOT_MOVE_RELATIVE_TO"]
642
+ payload: RobotMoveAxisRelativeCommandPayload
643
+
644
+
645
+ class RobotOpenGripperJawCommand(TypedDict):
646
+ name: Literal["command.ROBOT_OPEN_GRIPPER_JAW"]
647
+ payload: GripperCommandPayload
648
+
649
+
650
+ class RobotCloseGripperJawCommand(TypedDict):
651
+ name: Literal["command.ROBOT_CLOSE_GRIPPER_JAW"]
652
+ payload: GripperCommandPayload
653
+
654
+
603
655
  Command = Union[
604
656
  DropTipCommand,
605
657
  DropTipInDisposalLocationCommand,
@@ -654,6 +706,12 @@ Command = Union[
654
706
  SealCommand,
655
707
  UnsealCommand,
656
708
  PressurizeCommand,
709
+ # Robot commands
710
+ RobotMoveToCommand,
711
+ RobotMoveAxisToCommand,
712
+ RobotMoveAxisRelativeCommand,
713
+ RobotOpenGripperJawCommand,
714
+ RobotCloseGripperJawCommand,
657
715
  ]
658
716
 
659
717
 
@@ -707,6 +765,11 @@ CommandPayload = Union[
707
765
  SealCommandPayload,
708
766
  UnsealCommandPayload,
709
767
  PressurizeCommandPayload,
768
+ # Robot payloads
769
+ RobotMoveToCommandPayload,
770
+ RobotMoveAxisRelativeCommandPayload,
771
+ RobotMoveAxisToCommandPayload,
772
+ GripperCommandPayload,
710
773
  ]
711
774
 
712
775
 
@@ -947,6 +1010,26 @@ class MoveLabwareMessage(CommandMessageFields, MoveLabwareCommand):
947
1010
  pass
948
1011
 
949
1012
 
1013
+ class RobotMoveToMessage(CommandMessageFields, RobotMoveToCommand):
1014
+ pass
1015
+
1016
+
1017
+ class RobotMoveAxisToMessage(CommandMessageFields, RobotMoveAxisToCommand):
1018
+ pass
1019
+
1020
+
1021
+ class RobotMoveAxisRelativeMessage(CommandMessageFields, RobotMoveAxisRelativeCommand):
1022
+ pass
1023
+
1024
+
1025
+ class RobotOpenGripperJawMessage(CommandMessageFields, RobotOpenGripperJawCommand):
1026
+ pass
1027
+
1028
+
1029
+ class RobotCloseGripperJawMessage(CommandMessageFields, RobotCloseGripperJawCommand):
1030
+ pass
1031
+
1032
+
950
1033
  CommandMessage = Union[
951
1034
  DropTipMessage,
952
1035
  DropTipInDisposalLocationMessage,
@@ -994,4 +1077,10 @@ CommandMessage = Union[
994
1077
  MoveToMessage,
995
1078
  MoveToDisposalLocationMessage,
996
1079
  MoveLabwareMessage,
1080
+ # Robot Messages
1081
+ RobotMoveToMessage,
1082
+ RobotMoveAxisToMessage,
1083
+ RobotMoveAxisRelativeMessage,
1084
+ RobotOpenGripperJawMessage,
1085
+ RobotCloseGripperJawMessage,
997
1086
  ]
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
- from typing import Optional, Dict, Union, TYPE_CHECKING
4
+ from typing import Optional, Dict, Union, TYPE_CHECKING, Tuple
5
5
 
6
6
  from opentrons_shared_data.liquid_classes.liquid_class_definition import (
7
7
  LiquidClassSchemaV1,
@@ -63,6 +63,20 @@ class LiquidClass:
63
63
  _by_pipette_setting=by_pipette_settings,
64
64
  )
65
65
 
66
+ @classmethod
67
+ def create_from(
68
+ cls,
69
+ name: str,
70
+ display_name: str,
71
+ by_pipette_setting: Dict[str, Dict[str, TransferProperties]],
72
+ ) -> "LiquidClass":
73
+ """Create a liquid class from the passed in args."""
74
+ return cls(
75
+ _name=name,
76
+ _display_name=display_name,
77
+ _by_pipette_setting=by_pipette_setting,
78
+ )
79
+
66
80
  @property
67
81
  def name(self) -> str:
68
82
  return self._name
@@ -71,10 +85,54 @@ class LiquidClass:
71
85
  def display_name(self) -> str:
72
86
  return self._display_name
73
87
 
88
+ def update_for(
89
+ self,
90
+ pipette: Union[str, InstrumentContext],
91
+ tip_rack: Union[str, Labware],
92
+ transfer_properties: TransferProperties,
93
+ ) -> None:
94
+ """Update the transfer properties for the given pipette and tip combo.
95
+
96
+ If an entry does not exist, it will be created.
97
+ """
98
+ pipette_name, tiprack_uri = self._get_pipette_and_tiprack_names(
99
+ pipette, tip_rack
100
+ )
101
+ try:
102
+ self._by_pipette_setting[pipette_name].update(
103
+ {tiprack_uri: transfer_properties}
104
+ )
105
+ except KeyError:
106
+ self._by_pipette_setting[pipette_name] = {tiprack_uri: transfer_properties}
107
+
74
108
  def get_for(
75
109
  self, pipette: Union[str, InstrumentContext], tip_rack: Union[str, Labware]
76
110
  ) -> TransferProperties:
77
111
  """Get liquid class transfer properties for the specified pipette and tip."""
112
+ pipette_name, tiprack_uri = self._get_pipette_and_tiprack_names(
113
+ pipette, tip_rack
114
+ )
115
+
116
+ try:
117
+ settings_for_pipette = self._by_pipette_setting[pipette_name]
118
+ except KeyError:
119
+ raise NoLiquidClassPropertyError(
120
+ f"No properties found for {pipette_name} in {self._name} liquid class"
121
+ )
122
+ try:
123
+ transfer_properties = settings_for_pipette[tiprack_uri]
124
+ except KeyError:
125
+ raise NoLiquidClassPropertyError(
126
+ f"No properties found for {tiprack_uri} for {pipette_name} in {self._name} liquid class"
127
+ )
128
+ return transfer_properties
129
+
130
+ @staticmethod
131
+ def _get_pipette_and_tiprack_names(
132
+ pipette: Union[str, InstrumentContext],
133
+ tip_rack: Union[str, Labware],
134
+ ) -> Tuple[str, str]:
135
+ """Return the pipette and tip rack name strings from the given pipette and tip rack."""
78
136
  from . import InstrumentContext, Labware
79
137
 
80
138
  if isinstance(pipette, InstrumentContext):
@@ -96,17 +154,4 @@ class LiquidClass:
96
154
  f"{tip_rack} should either be a tiprack Labware object"
97
155
  f" or a tiprack URI string."
98
156
  )
99
-
100
- try:
101
- settings_for_pipette = self._by_pipette_setting[pipette_name]
102
- except KeyError:
103
- raise NoLiquidClassPropertyError(
104
- f"No properties found for {pipette_name} in {self._name} liquid class"
105
- )
106
- try:
107
- transfer_properties = settings_for_pipette[tiprack_uri]
108
- except KeyError:
109
- raise NoLiquidClassPropertyError(
110
- f"No properties found for {tiprack_uri} for {pipette_name} in {self._name} liquid class"
111
- )
112
- return transfer_properties
157
+ return pipette_name, tiprack_uri