Skip to content

Commit 3185656

Browse files
authored
feat(api): Add Magdeck G-Code Parsing (#8159)
1 parent 73afee9 commit 3185656

File tree

12 files changed

+324
-58
lines changed

12 files changed

+324
-58
lines changed

api/src/opentrons/hardware_control/g_code_parsing/g_code.py

Lines changed: 67 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
from typing import List
55

66
from .errors import UnparsableGCodeError
7-
from opentrons.drivers.smoothie_drivers.driver_3_0 import GCODE as SMOOTHIE_GCODE
7+
from opentrons.drivers.smoothie_drivers.driver_3_0 import GCODE as SMOOTHIE_G_CODE
8+
from opentrons.drivers.mag_deck.driver import GCODE as MAGDECK_G_CODE
89
from opentrons.hardware_control.g_code_parsing.utils import reverse_enum
910
from opentrons.hardware_control.emulation.parser import Parser
1011
from opentrons.hardware_control.g_code_parsing.g_code_functionality_defs.\
1112
g_code_functionality_def_base import Explanation
12-
from .g_code_functionality_defs import smoothie
13+
from .g_code_functionality_defs import smoothie, magdeck
1314

1415

1516
class GCode:
@@ -18,91 +19,115 @@ class GCode:
1819
convert them to human-readable JSON form
1920
"""
2021

21-
G_CODE_EXPLANATION_MAPPING = {
22+
SMOOTHIE_G_CODE_EXPLANATION_MAPPING = {
2223
# Weird Enum thing stopping me from using values from
2324
# enum for MOVE and SET_SPEED see g_code.py get_gcode_function for explanation
2425
'MOVE':
2526
smoothie.MoveGCodeFunctionalityDef,
2627
'SET_SPEED':
2728
smoothie.SetSpeedGCodeFunctionalityDef,
28-
SMOOTHIE_GCODE.WAIT.name:
29+
SMOOTHIE_G_CODE.WAIT.name:
2930
smoothie.WaitGCodeFunctionalityDef,
30-
SMOOTHIE_GCODE.HOME.name:
31+
SMOOTHIE_G_CODE.HOME.name:
3132
smoothie.HomeGCodeFunctionalityDef,
32-
SMOOTHIE_GCODE.SET_CURRENT.name:
33+
SMOOTHIE_G_CODE.SET_CURRENT.name:
3334
smoothie.SetCurrentGCodeFunctionalityDef,
34-
SMOOTHIE_GCODE.DWELL.name:
35+
SMOOTHIE_G_CODE.DWELL.name:
3536
smoothie.DwellGCodeFunctionalityDef,
36-
SMOOTHIE_GCODE.CURRENT_POSITION.name:
37-
smoothie.CurrentPositionCodeFunctionalityDef,
38-
SMOOTHIE_GCODE.LIMIT_SWITCH_STATUS.name:
37+
SMOOTHIE_G_CODE.CURRENT_POSITION.name:
38+
smoothie.CurrentPositionGCodeFunctionalityDef,
39+
SMOOTHIE_G_CODE.LIMIT_SWITCH_STATUS.name:
3940
smoothie.LimitSwitchStatusGCodeFunctionalityDef,
40-
SMOOTHIE_GCODE.PROBE.name:
41+
SMOOTHIE_G_CODE.PROBE.name:
4142
smoothie.ProbeGCodeFunctionalityDef,
42-
SMOOTHIE_GCODE.ABSOLUTE_COORDS.name:
43+
SMOOTHIE_G_CODE.ABSOLUTE_COORDS.name:
4344
smoothie.AbsoluteCoordinateModeGCodeFunctionalityDef,
44-
SMOOTHIE_GCODE.RELATIVE_COORDS.name:
45+
SMOOTHIE_G_CODE.RELATIVE_COORDS.name:
4546
smoothie.RelativeCoordinateModeGCodeFunctionalityDef,
46-
SMOOTHIE_GCODE.RESET_FROM_ERROR.name:
47+
SMOOTHIE_G_CODE.RESET_FROM_ERROR.name:
4748
smoothie.ResetFromErrorGCodeFunctionalityDef,
48-
SMOOTHIE_GCODE.PUSH_SPEED.name:
49+
SMOOTHIE_G_CODE.PUSH_SPEED.name:
4950
smoothie.PushSpeedGCodeFunctionalityDef,
50-
SMOOTHIE_GCODE.POP_SPEED.name:
51+
SMOOTHIE_G_CODE.POP_SPEED.name:
5152
smoothie.PopSpeedGCodeFunctionalityDef,
52-
SMOOTHIE_GCODE.STEPS_PER_MM.name:
53+
SMOOTHIE_G_CODE.STEPS_PER_MM.name:
5354
smoothie.StepsPerMMGCodeFunctionalityDef,
54-
SMOOTHIE_GCODE.SET_MAX_SPEED.name:
55+
SMOOTHIE_G_CODE.SET_MAX_SPEED.name:
5556
smoothie.SetMaxSpeedGCodeFunctionalityDef,
56-
SMOOTHIE_GCODE.ACCELERATION.name:
57+
SMOOTHIE_G_CODE.ACCELERATION.name:
5758
smoothie.AccelerationGCodeFunctionalityDef,
58-
SMOOTHIE_GCODE.DISENGAGE_MOTOR.name:
59+
SMOOTHIE_G_CODE.DISENGAGE_MOTOR.name:
5960
smoothie.DisengageMotorGCodeFunctionalityDef,
60-
SMOOTHIE_GCODE.HOMING_STATUS.name:
61+
SMOOTHIE_G_CODE.HOMING_STATUS.name:
6162
smoothie.HomingStatusGCodeFunctionalityDef,
62-
SMOOTHIE_GCODE.MICROSTEPPING_B_DISABLE.name:
63+
SMOOTHIE_G_CODE.MICROSTEPPING_B_DISABLE.name:
6364
smoothie.MicrosteppingBDisableGCodeFunctionalityDef,
64-
SMOOTHIE_GCODE.MICROSTEPPING_B_ENABLE.name:
65+
SMOOTHIE_G_CODE.MICROSTEPPING_B_ENABLE.name:
6566
smoothie.MicrosteppingBEnableGCodeFunctionalityDef,
66-
SMOOTHIE_GCODE.MICROSTEPPING_C_DISABLE.name:
67+
SMOOTHIE_G_CODE.MICROSTEPPING_C_DISABLE.name:
6768
smoothie.MicrosteppingCDisableGCodeFunctionalityDef,
68-
SMOOTHIE_GCODE.MICROSTEPPING_C_ENABLE.name:
69+
SMOOTHIE_G_CODE.MICROSTEPPING_C_ENABLE.name:
6970
smoothie.MicrosteppingCEnableGCodeFunctionalityDef,
70-
SMOOTHIE_GCODE.PIPETTE_HOME.name:
71+
SMOOTHIE_G_CODE.PIPETTE_HOME.name:
7172
smoothie.SetPipetteHomeGCodeFunctionalityDef,
72-
SMOOTHIE_GCODE.PIPETTE_RETRACT.name:
73+
SMOOTHIE_G_CODE.PIPETTE_RETRACT.name:
7374
smoothie.SetPipetteRetractGCodeFunctionalityDef,
74-
SMOOTHIE_GCODE.PIPETTE_DEBOUNCE.name:
75+
SMOOTHIE_G_CODE.PIPETTE_DEBOUNCE.name:
7576
smoothie.SetPipetteDebounceGCodeFunctionalityDef,
76-
SMOOTHIE_GCODE.PIPETTE_MAX_TRAVEL.name:
77+
SMOOTHIE_G_CODE.PIPETTE_MAX_TRAVEL.name:
7778
smoothie.SetPipetteMaxTravelGCodeFunctionalityDef,
78-
SMOOTHIE_GCODE.READ_INSTRUMENT_MODEL.name:
79+
SMOOTHIE_G_CODE.READ_INSTRUMENT_MODEL.name:
7980
smoothie.ReadInstrumentModelGCodeFunctionalityDef,
80-
SMOOTHIE_GCODE.READ_INSTRUMENT_ID.name:
81+
SMOOTHIE_G_CODE.READ_INSTRUMENT_ID.name:
8182
smoothie.ReadInstrumentIDGCodeFunctionalityDef,
82-
SMOOTHIE_GCODE.WRITE_INSTRUMENT_ID.name:
83+
SMOOTHIE_G_CODE.WRITE_INSTRUMENT_ID.name:
8384
smoothie.WriteInstrumentIDGCodeFunctionalityDef,
84-
SMOOTHIE_GCODE.WRITE_INSTRUMENT_MODEL.name:
85+
SMOOTHIE_G_CODE.WRITE_INSTRUMENT_MODEL.name:
8586
smoothie.WriteInstrumentModelGCodeFunctionalityDef,
8687
}
8788

89+
MAGDECK_G_CODE_EXPLANATION_MAPPING = {
90+
MAGDECK_G_CODE.HOME.name:
91+
magdeck.HomeGCodeFunctionalityDef,
92+
MAGDECK_G_CODE.MOVE.name:
93+
magdeck.MoveGCodeFunctionalityDef,
94+
MAGDECK_G_CODE.GET_CURRENT_POSITION.name:
95+
magdeck.CurrentPositionGCodeFunctionalityDef,
96+
MAGDECK_G_CODE.PROBE_PLATE.name:
97+
magdeck.ProbeGCodeFunctionalityDef,
98+
MAGDECK_G_CODE.GET_PLATE_HEIGHT.name:
99+
magdeck.GetPlateHeightGCodeFunctionalityDef,
100+
MAGDECK_G_CODE.DEVICE_INFO.name:
101+
magdeck.DeviceInfoGCodeFunctionalityDef
102+
}
103+
88104
# Smoothie G-Code Parsing Characters
89105
SET_SPEED_CHARACTER = 'F'
90106
MOVE_CHARACTERS = ['X', 'Y', 'Z', 'A', 'B', 'C']
91107
SET_SPEED_NAME = 'SET_SPEED'
92108
MOVE_NAME = 'MOVE'
93109

94110
SMOOTHIE_IDENT = 'smoothie'
95-
SMOOTHIE_GCODE_LOOKUP = reverse_enum(SMOOTHIE_GCODE)
111+
SMOOTHIE_G_CODE_LOOKUP = reverse_enum(SMOOTHIE_G_CODE)
112+
113+
MAGDECK_IDENT = 'magdeck'
114+
MAGDECK_G_CODE_LOOKUP = reverse_enum(MAGDECK_G_CODE)
96115

97116
DEVICE_GCODE_LOOKUP = {
98-
SMOOTHIE_IDENT: SMOOTHIE_GCODE_LOOKUP
117+
SMOOTHIE_IDENT: SMOOTHIE_G_CODE_LOOKUP,
118+
MAGDECK_IDENT: MAGDECK_G_CODE_LOOKUP,
99119
}
100120

101121
SPECIAL_HANDLING_REQUIRED_G_CODES = [
102-
SMOOTHIE_GCODE.WRITE_INSTRUMENT_ID,
103-
SMOOTHIE_GCODE.WRITE_INSTRUMENT_MODEL,
122+
SMOOTHIE_G_CODE.WRITE_INSTRUMENT_ID,
123+
SMOOTHIE_G_CODE.WRITE_INSTRUMENT_MODEL,
104124
]
105125

126+
EXPLANATION_LOOKUP = {
127+
SMOOTHIE_IDENT: SMOOTHIE_G_CODE_EXPLANATION_MAPPING,
128+
MAGDECK_IDENT: MAGDECK_G_CODE_EXPLANATION_MAPPING,
129+
}
130+
106131
@classmethod
107132
def from_raw_code(cls, raw_code: str, device: str, response: str) -> List[GCode]:
108133
g_code_list = []
@@ -214,11 +239,11 @@ def get_gcode_function(self) -> str:
214239
])
215240

216241
# For the following if/else I was going to grab the enum names
217-
# from SMOOTHIE_GCODE but due to the way that enums work, if I
242+
# from SMOOTHIE_G_CODE but due to the way that enums work, if I
218243
# have 2 enum entries with the same value the second value will
219244
# act as an alias to the first.
220245
# Since the value for SET_SPEED and MOVE are both G0 and MOVE is defined
221-
# first, calling SMOOTHIE_GCODE.SET_SPEED.name returns MOVE.
246+
# first, calling SMOOTHIE_G_CODE.SET_SPEED.name returns MOVE.
222247
# Super annoying but it's how it works.
223248
# Super annoying, so I am just going to hard code the value for now.
224249

@@ -243,7 +268,9 @@ def get_gcode_function(self) -> str:
243268
def get_explanation(self) -> Explanation:
244269
g_code_function = self.get_gcode_function()
245270
try:
246-
explanation_class = self.G_CODE_EXPLANATION_MAPPING[g_code_function]
271+
explanation_class = \
272+
self.EXPLANATION_LOOKUP[self.device_name][g_code_function]
273+
247274
except KeyError:
248275
return Explanation(
249276
self.g_code,
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from .home_g_code_functionality_def import HomeGCodeFunctionalityDef
2+
from .move_g_code_functionality_def import MoveGCodeFunctionalityDef
3+
from .current_position_g_code_functionality_def import \
4+
CurrentPositionGCodeFunctionalityDef
5+
from .probe_g_code_functionality_def import ProbeGCodeFunctionalityDef
6+
from .get_plate_height_g_code_functionality_def import \
7+
GetPlateHeightGCodeFunctionalityDef
8+
from .device_info_g_code_functionality_def import DeviceInfoGCodeFunctionalityDef
9+
10+
__all__ = [
11+
'HomeGCodeFunctionalityDef',
12+
'MoveGCodeFunctionalityDef',
13+
'CurrentPositionGCodeFunctionalityDef',
14+
'ProbeGCodeFunctionalityDef',
15+
'GetPlateHeightGCodeFunctionalityDef',
16+
'DeviceInfoGCodeFunctionalityDef'
17+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import re
2+
from typing import Dict
3+
from opentrons.hardware_control.g_code_parsing.g_code_functionality_defs.\
4+
g_code_functionality_def_base import GCodeFunctionalityDefBase
5+
6+
7+
class CurrentPositionGCodeFunctionalityDef(GCodeFunctionalityDefBase):
8+
RESPONSE_RE = re.compile(r'Z:(\d+.\d+)')
9+
10+
@classmethod
11+
def _generate_command_explanation(cls, g_code_args: Dict[str, str]) -> str:
12+
return 'Reading current position of magnets'
13+
14+
@classmethod
15+
def _generate_response_explanation(cls, response: str) -> str:
16+
match = cls.RESPONSE_RE.match(response)
17+
message = ''
18+
if match is not None:
19+
message = f'Current height of magnets are {match.group(1)}mm'
20+
return message
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import re
2+
from typing import Dict
3+
from opentrons.hardware_control.g_code_parsing.g_code_functionality_defs.\
4+
g_code_functionality_def_base import GCodeFunctionalityDefBase
5+
6+
7+
class DeviceInfoGCodeFunctionalityDef(GCodeFunctionalityDefBase):
8+
RESPONSE_RE = re.compile(r'serial:(.*?)model:(.*?)version:(.*?)$')
9+
10+
@classmethod
11+
def _generate_command_explanation(cls, g_code_args: Dict[str, str]) -> str:
12+
return 'Getting magdeck device info'
13+
14+
@classmethod
15+
def _generate_response_explanation(cls, response: str) -> str:
16+
match = cls.RESPONSE_RE.match(response)
17+
message = ''
18+
if match is not None:
19+
serial_number, model, fw_version = match.groups()
20+
message = f'Magdeck info:' \
21+
f'\n\tSerial Number: {serial_number.strip()}' \
22+
f'\n\tModel: {model.strip()}' \
23+
f'\n\tFirmware Version: {fw_version.strip()}'
24+
25+
return message
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import re
2+
from typing import Dict
3+
from opentrons.hardware_control.g_code_parsing.g_code_functionality_defs.\
4+
g_code_functionality_def_base import GCodeFunctionalityDefBase
5+
6+
7+
class GetPlateHeightGCodeFunctionalityDef(GCodeFunctionalityDefBase):
8+
RESPONSE_RE = re.compile(r'height:(\d+.\d+)')
9+
10+
@classmethod
11+
def _generate_command_explanation(cls, g_code_args: Dict[str, str]) -> str:
12+
return 'Calculating magdeck labware height'
13+
14+
@classmethod
15+
def _generate_response_explanation(cls, response: str) -> str:
16+
match = cls.RESPONSE_RE.match(response)
17+
message = ''
18+
19+
if match is not None:
20+
message = f'Magdeck calculated labware height at {match.group(1)}mm'
21+
22+
return message
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from typing import Dict
2+
from opentrons.hardware_control.g_code_parsing.g_code_functionality_defs.\
3+
g_code_functionality_def_base import GCodeFunctionalityDefBase
4+
5+
6+
class HomeGCodeFunctionalityDef(GCodeFunctionalityDefBase):
7+
@classmethod
8+
def _generate_command_explanation(cls, g_code_args: Dict[str, str]) -> str:
9+
return 'Homing the magdeck'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from typing import Dict
2+
from opentrons.hardware_control.g_code_parsing.g_code_functionality_defs.\
3+
g_code_functionality_def_base import GCodeFunctionalityDefBase
4+
5+
6+
class MoveGCodeFunctionalityDef(GCodeFunctionalityDefBase):
7+
8+
HEIGHT_ARG = 'Z'
9+
10+
@classmethod
11+
def _generate_command_explanation(cls, g_code_args: Dict[str, str]) -> str:
12+
height = g_code_args[cls.HEIGHT_ARG]
13+
return f'Setting magnet height to {height}mm'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from typing import Dict
2+
from opentrons.hardware_control.g_code_parsing.g_code_functionality_defs.\
3+
g_code_functionality_def_base import GCodeFunctionalityDefBase
4+
5+
6+
class ProbeGCodeFunctionalityDef(GCodeFunctionalityDefBase):
7+
8+
@classmethod
9+
def _generate_command_explanation(cls, g_code_args: Dict[str, str]) -> str:
10+
return 'Magdeck probing attached labware to get height in mm'

api/src/opentrons/hardware_control/g_code_parsing/g_code_functionality_defs/smoothie/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from .acceleration_g_code_functionality_def import \
44
AccelerationGCodeFunctionalityDef
55
from .current_position_g_code_functionality_def import \
6-
CurrentPositionCodeFunctionalityDef
6+
CurrentPositionGCodeFunctionalityDef
77
from .disengage_motor_g_code_functionality_def import \
88
DisengageMotorGCodeFunctionalityDef
99
from .dwell_g_code_functionality_def import \
@@ -62,7 +62,7 @@
6262
WriteInstrumentModelGCodeFunctionalityDef
6363

6464
__all__ = [
65-
'CurrentPositionCodeFunctionalityDef',
65+
'CurrentPositionGCodeFunctionalityDef',
6666
'DwellGCodeFunctionalityDef',
6767
'HomeGCodeFunctionalityDef',
6868
'LimitSwitchStatusGCodeFunctionalityDef',

api/src/opentrons/hardware_control/g_code_parsing/g_code_functionality_defs/smoothie/current_position_g_code_functionality_def.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
g_code_functionality_def_base import GCodeFunctionalityDefBase
55

66

7-
class CurrentPositionCodeFunctionalityDef(GCodeFunctionalityDefBase):
7+
class CurrentPositionGCodeFunctionalityDef(GCodeFunctionalityDefBase):
88
RESPONSE_RE = re.compile(r'(?P<axis>[ABCXYZ]):(?P<value>-?\d+.\d+)')
99

1010
@classmethod

0 commit comments

Comments
 (0)