import re
# Matches +1.4e+05
floating_point_regex = r"[-+]?\d*\.?\d+(?:[eE](?:[-+]?\d*\.?\d+))?"
ABS_POSITION = ["x", "y", "z"]
[docs]
def resolve_flavor(flavor: str) -> dict:
"""Helper function to resolve gcode flavor from string or dict
Args:
flavor (str | dict): G-code flavor as a string key or a dictionary
Raises:
ValueError: If the flavor string is not recognized
TypeError: If the flavor is not a string or a dictionary
Returns:
dict: G-code flavor dictionary
"""
if isinstance(flavor, str):
flavor_dict = flavors.get(flavor)
if flavor_dict is None:
raise ValueError(
f"Unknown flavor string: {flavor}. Must be one of {list(flavors.keys())}"
)
return flavor_dict
elif not isinstance(flavor, dict):
raise TypeError("flavor must be a string or a dictionary")
return flavor
[docs]
def available_flavors() -> list[str]:
"""Returns a list of available G-code flavor keys
Returns:
list[str]: List of available G-code flavor keys
"""
return list(flavors.keys())
flavors = {
# Reprap and Marlin
# Marlin: https://marlinfw.org/meta/gcode/
# RepRap: https://reprap.org/wiki/G-code
"default": {
"command_re": re.compile(r"([GMgm])(\d+)"),
"word_re": re.compile(rf"([A-Za-z])({floating_point_regex})"),
"comment_re": re.compile(r";(.*)$"),
"generic_expression_re": r"\[\s*[^\[\]]*\s*\]",
"function_expression_re": [
rf"((COS|TAN|SIN|ACOS|ATAN|ASIN|ABS|ROUND|SQRT)\[({floating_point_regex})\])",
rf"((COS|TAN|SIN|ACOS|ATAN|ASIN|ABS|ROUND|SQRT)({floating_point_regex}))",
],
"parameter": "#<PARAM>",
"parameter_re": r"#\<(.*?)\>",
"parameter_declare_re": rf"#\<(.*?)\>\s?=\s?({floating_point_regex})", # Format: #<key> = value
"data_type_map": {
"default": float,
"g": int,
"m": int,
"n": int,
"comment": str,
"raw": str,
},
"word_map": {
"config": "M",
"location": ["X", "Y", "Z"],
"arc_center": ["I", "J"],
"feed_rate": "F",
"extrusion_rate": "E",
"time_s": "S",
"time_ms": "P",
"other": None,
"line_number": None,
},
"code_map": { # NOTE: code_map reserved for 'G' codes
"linear_move": (0, 1),
"rapid": 0,
"arc_move": (2, 3),
"dwell": 4,
"absolute_position": 90,
"incremental_position": 91,
"work_coordinate": 54,
"machine_coordinate": 53,
},
"special_codes": {}, # NOTE: Special codes reserved for 'M' codes
"units": {
"feed_rate": 60, # Conversion to seconds
"commands": {"G20": "inches", "G21": "mm"},
},
},
"cead": {
"command_re": re.compile(r"([GMgm])(\d+)"),
"word_re": re.compile(
rf"([NXYZABCEFGMgmnxyzabcef]|ELX|elx)=?({floating_point_regex})"
), # Only specific words allowed
"comment_re": re.compile(r";(.*)$"),
"generic_expression_re": r"\(\s*[^\(\)]*\s*\)",
"function_expression_re": r"((DEVICE|PRESETON|IC|COS|TAN|SIN|ACOS|ATAN|ASIN|ABS|ROUND|SQRT)(\(.+\)))",
"parameter": "PARAM",
"parameter_line_indicator": r"DEF ",
"parameter_re": r"([A-Za-z_]+)(?!=)", # Format: PARAM (without an '=' after it)
"parameter_declare_re": rf"([A-Za-z_]+)=({floating_point_regex})", # Format: key=value
"data_type_map": {
"default": float,
"g": int,
"m": int,
"n": int,
"comment": str,
"raw": str,
},
"word_map": {
"config": "M",
"location": ["X", "Y", "Z"],
"rotation": ["A", "B", "C"], # x,y,z axis
"arc_center": ["I", "J"],
"feed_rate": "F",
"extrusion_rate": "E",
"time_s": "S",
"time_ms": "P",
"other": None,
"line_number": "N",
},
"code_map": {
"linear_move": (0, 1),
"arc_move": (2, 3),
"dwell": 4,
"absolute_position": 90,
"incremental_position": 91,
"work_coordinate": 54,
"machine_coordinate": 53,
},
"special_codes": {},
"units": {
"feed_rate": 1, # TODO: Conversion to seconds
"commands": {},
},
},
"juggerbot": {
"command_re": re.compile(r"([GMgm])(\d+)"),
"word_re": re.compile(rf"([A-Za-z])({floating_point_regex})"),
"comment_re": re.compile(r";(.*)$"),
"generic_expression_re": r"\[\s*[^\[\]]*\s*\]",
"function_expression_re": [
rf"((COS|TAN|SIN|ACOS|ATAN|ASIN|ABS|ROUND|SQRT)\[({floating_point_regex})\])",
rf"((COS|TAN|SIN|ACOS|ATAN|ASIN|ABS|ROUND|SQRT)({floating_point_regex}))",
],
"parameter": "#<PARAM>",
"parameter_re": r"#\<(.*?)\>",
"parameter_declare_re": rf"#\<(.*?)\>\s?=\s?({floating_point_regex})", # Format: #<key> = value
"data_type_map": {
"default": float,
"g": int,
"m": int,
"n": int,
"comment": str,
"raw": str,
},
"word_map": {
"config": "M",
"tool": "T",
"location": ["X", "Y", "Z", "B"],
"arc_center": ["I", "J"],
"feed_rate": "F",
"extrusion_rate": "S",
"time_s": "s",
"time_ms": "P",
"other": None,
"line_number": None,
},
"code_map": {
"linear_move": (0, 1),
"arc_move": (2, 3),
"dwell": 4,
"absolute_position": 90,
"incremental_position": 91,
"work_coordinate": 54,
"machine_coordinate": 53,
},
"special_codes": {
"extruder_on": 3,
"extruder_off": 5,
},
"units": {
"feed_rate": 1, # TODO: Conversion to seconds
"commands": {},
},
},
"cincinnati": {
"command_re": re.compile(r"([GMgm])(\d+)"),
"word_re": re.compile(rf"([A-Za-z])({floating_point_regex})"),
"comment_re": re.compile(r"\((.*)\)$"),
"generic_expression_re": r"\[\s*[^\[\]]*\s*\]",
"function_expression_re": [
rf"((COS|TAN|SIN|ACOS|ATAN|ASIN|ABS|ROUND|SQRT)\[({floating_point_regex})\])",
rf"((COS|TAN|SIN|ACOS|ATAN|ASIN|ABS|ROUND|SQRT)({floating_point_regex}))",
],
"parameter": "#<PARAM>",
"parameter_re": r"#\<(.*?)\>",
"parameter_declare_re": rf"#\<(.*?)\>\s?=\s?({floating_point_regex})", # Format: #<key> = value
"data_type_map": {
"default": float,
"g": int,
"m": int,
"n": int,
"comment": str,
"raw": str,
},
"word_map": {
"config": "M",
"location": ["X", "Y", "Z", "W"],
"arc_center": ["I", "J"],
"feed_rate": "F",
"extrusion_rate": "S",
# "time_s": "s", # Cincinnati might not have this
"time_ms": "P",
"other": "L",
"line_number": None,
},
"code_map": {
"linear_move": (0, 1),
"arc_move": (2, 3),
"dwell": 4,
"absolute_position": 90,
"incremental_position": 91,
"work_coordinate": 54,
"machine_coordinate": 53,
},
"special_codes": {
"purge": 69,
"extruder_on": 3,
"extruder_off": 5,
"set_acceleration": 66,
"park": 68,
},
"location_correction_pairs": {
"Z": ("W", "-"),
}, # "Z" is location, "W" is correction, "-" is subtraction --> z=Z-W
"units": {
"feed_rate": 1, # TODO: Conversion to seconds
"commands": {},
},
},
"ingersoll": {
"command_re": re.compile(r"([GMgm])(\d+)"),
"word_re": re.compile(rf"([A-Za-z]|CA|ca)({floating_point_regex})"),
"comment_re": re.compile(r";(.*)$"),
"generic_expression_re": [r"\(\s*[^\(\)]*\s*\)"],
"function_expression_re": [
r"((EXTRUDER|COS|TAN|SIN|ACOS|ATAN|ASIN|ABS|ROUND|SQRT)(\(\d+.?\d*\)|\d+.?\d*))",
r"((EXTRUDERB)\((True|False)\))",
r"((CA\s*)=(\s*\S+))",
],
"parameter": None,
"parameter_re": None,
"parameter_declare_re": rf"(\w+)\s*=\s*({floating_point_regex})", # Format: #<key> = value
"data_type_map": {
"default": float,
"g": int,
"m": int,
"n": int,
"comment": str,
"raw": str,
},
"word_map": {
"config": "M",
"location": ["X", "Y", "Z"],
"arc_center": ["I", "J"],
"feed_rate": "F",
"extrusion_rate": "E",
"time_s": "F",
"line_number": "N",
"other": [],
},
"code_map": {
"linear_move": (0, 1),
"arc_move": (2, 3),
"dwell": 4,
"absolute_position": 90,
"incremental_position": 91,
"work_coordinate": 54,
"machine_coordinate": 53,
},
"special_codes": {},
"units": {
"feed_rate": 1, # TODO: Conversion to seconds
"commands": {},
},
},
"hendrick": {
"command_re": re.compile(r"([GMgm])(\d+)"),
"word_re": re.compile(rf"([A-Za-z])({floating_point_regex})"),
"comment_re": re.compile(r";(.*)$|\((.*)\)"),
"generic_expression_re": None,
"function_expression_re": None,
"parameter": None,
"parameter_re": None,
"parameter_declare_re": None,
"data_type_map": {
"default": float,
"g": int,
"m": int,
"n": int,
"t": int,
"comment": str,
"raw": str,
},
"word_map": { # https://www.machinistguides.com/cnc-code-guide/
"config": "M",
"tool": "T",
"location": ["X", "Y", "Z"],
"rotation": ["A", "B", "C"], # x,y,z axis
"arc_center": ["I", "J", "K"],
"feed_rate": "F",
"spindle": "S",
"height": "h",
"other": "L",
"line_number": None,
},
"code_map": {
"linear_move": (0, 1),
"arc_move": (2, 3),
"dwell": 4,
"absolute_position": 90,
"incremental_position": 91,
"work_coordinate": 54,
"machine_coordinate": 53,
},
"special_codes": {},
"units": {
"feed_rate": 1, # TODO: Conversion to seconds
"commands": {},
},
},
"electroimpact": {
"command_re": re.compile(r"([GMgm])(\d+)"),
"word_re": re.compile(
rf"([NXYZABJFSTKGMnxyzabjfstkgm]|CP|cp)=?({floating_point_regex})"
),
"comment_re": re.compile(r"\((.*)\)$"),
"generic_expression_re": None,
"function_expression_re": None,
"parameter": None,
"parameter_re": None,
"parameter_declare_re": None,
"data_type_map": {
"default": float,
"g": int,
"m": int,
"n": int,
"comment": str,
"raw": str,
},
"word_map": {
"config": "M",
"location": ["X", "Y", "Z"],
"rotation": ["A", "B"], # x,y axis
"feed_rate": "F",
"extrusion_rate": "J", # J = bead area
"extrusion_length": "K", # K = cumulative extrusion length
"line_number": "N",
},
"code_map": {
"linear_move": (0, 1),
},
"special_codes": {
"head_on": 9,
"lane_on": 301, # Enables extruder lanes
"part_offset": 38,
"head_offset": 22,
},
"units": {
"feed_rate": 1, # TODO: Conversion to seconds
"commands": {},
},
},
}