Source code for gcode_reader.interface

from .emulate import machines as _machines  # noqa: F401 — registers all machines
from .emulate.machine import Machine  # noqa: F401 — also registers "default"
from .emulate.operations import AdditiveOperation, Operation
from .emulate.registry import get as _registry_get, list_machines, load_entry_points
from .emulate.additive_part import AdditivePart, Bead
from . import analysis
import numpy as np
import pandas as pd

load_entry_points()

FLAVOR_ALIASES = {
    "default": "default",
    "marlin": "default",
    "reprap": "default",
    "cead": "cead",
    "flexbot": "cead",
    "hendrick": "hendrick",
    "juggerbot": "juggerbot",
    "ingersoll": "ingersoll",
    "cincinnati": "cincinnati",
    "baam": "cincinnati",
}


def _get_machine_type(machine: str) -> type:
    return _registry_get(machine)


[docs] def list_supported_machines(): """List all supported machine types and their aliases. Returns: dict: A dictionary mapping machine names/aliases to their class types. Use the keys to identify machines for gcode_to_operation(). Examples: >>> import gcode_reader as gr >>> machines = gr.list_supported_machines() >>> print(list(machines.keys())) ['baam', 'cead', 'cincinnati', 'default', 'desktop', ...] """ return list_machines()
def _validate_flavor(flavor: str) -> str: """Validate and resolve a G-code flavor to a machine type. Args: flavor (str): The G-code flavor name. Returns: str: The normalized machine type key. Raises: ValueError: If the flavor is not recognized. """ key = flavor.lower().replace(" ", "").replace("_", "") if key not in FLAVOR_ALIASES: raise ValueError( f"Flavor ({flavor}) not recognized. Available flavors: {list(FLAVOR_ALIASES.keys())}" ) return key
[docs] def gcode_to_operation(filepath: str, machine: str = "default"): """Parses a G-code file into a structured Operation object. This function is the primary entry point for loading manufacturing instructions into the emulation environment. It reads a G-code file and interprets its commands in the context of a specific machine model. The result is an `Operation` object (or a specialized subclass like `AdditiveOperation`) that contains a sequence of commands and associated process data. Args: filepath (str): The absolute or relative path to the G-code file. machine (str, optional): A string identifying the machine model to be used for parsing. This is not case-sensitive and ignores spaces. See the `MACHINE_TYPE_MAP` dictionary for a full list of supported aliases (e.g., "Cincinnati BAAM", "CEAD", "Default"). Defaults to "default", a generic machine. Returns: Operation: An `Operation` instance containing the commands parsed from the G-code file and associated process data. The specific type (e.g., `AdditiveOperation`, `SubtractiveOperation`) depends on the selected machine model. Raises: ValueError: If the `machine` is not a recognized key or alias in the `MACHINE_TYPE_MAP`. Examples: >>> # Assume a G-code file named 'part1.gcode' exists. >>> import gcode_reader as gr >>> >>> # Load the file using the default machine profile. >>> operation = gr.gcode_file_to_operation( ... 'part1.gcode' ... ) >>> >>> # The returned object is now ready for further processing. >>> print(f"Loaded operation: {operation.name}") >>> print(f"All locations in G-code: {operation.get_all_locations()}") """ machine = _get_machine_type(machine) return machine().gcode_file_to_operation(filepath)
[docs] def export_event_series_from_gcode( filepath: str, machine: str = "default", spatial_scale_factor: float = 1e-3, destination: str | None = None, ): """Processes a G-code file and exports a process model event series. This function provides a direct pipeline from a G-code file to a saved Additive Event Series (`.aes`) file. The resulting event series is saved to a CSV-based file and is also returned as a pandas DataFrame for immediate use. Args: filepath (str): The absolute or relative path to the G-code file. machine (str, optional): A string identifying the machine model to be used for parsing and emulation. Defaults to "default". spatial_scale_factor (float, optional): A multiplicative factor to convert spatial units from the G-code (typically millimeters) to the desired output units for analysis (typically meters). Defaults to 1e-3 (mm to m). destination (str | None, optional): The full path for the output file. If `None`, a path is automatically generated by appending `_event_series.aes` to the input filename. Defaults to None. Returns: pandas.DataFrame: A DataFrame containing the time-stamped event series, with columns representing machine states over time. Raises: ValueError: If the `machine` is not a recognized key or alias in the `MACHINE_TYPE_MAP`. FileNotFoundError: If the `filepath` does not exist. Examples: >>> import gcode_reader as gr >>> >>> # Generate and save the event series file ('part1_event_series.aes') >>> event_series_df = gr.export_event_series_from_gcode( ... 'part1.gcode', ... machine='cincinnati' ... ) >>> >>> # The returned DataFrame can be used immediately. >>> print(event_series_df) """ machine: Machine = _get_machine_type(machine)() # default init operation = machine.gcode_file_to_operation( filepath, operation_type=AdditiveOperation ) return machine.export_operation_event_series( operation, spatial_scale_factor, destination )
[docs] def gcode_file_to_dataframe_simple(filepath: str, flavor: str = "default"): """Reads a G-code file directly into a pandas DataFrame (parsing only, no emulation). This function provides fast parsing without machine emulation. The resulting DataFrame includes raw G-code parsing results with columns for coordinates, distances, elapsed time estimates, contextual tags, and more. For full machine simulation results, use `gcode_file_to_dataframe()` instead. Args: filepath (str): Path (absolute or relative) to a G-code file. flavor (str, optional): The G-code syntax flavor to use. Supports: 'default', 'marlin', 'reprap', 'cead', 'flexbot', 'hendrick', 'juggerbot', 'ingersoll', 'cincinnati', 'baam'. Defaults to "default". Returns: pandas.DataFrame: A DataFrame with parsed G-code data. Columns include: - Coordinates: x, y, z (machine position) - Deltas: dx, dy, dz (distance changes) - Time: time (estimated seconds) - Tags: contextual information (move type, extrusion, etc.) - Raw words: individual G-code words (G, M, X, Y, Z, E, F, etc.) Raises: FileNotFoundError: If the filepath does not exist. ValueError: If the flavor is not recognized. Examples: >>> import gcode_reader as gr >>> >>> # For quick parsing without machine emulation >>> df = gr.gcode_file_to_dataframe_simple('part.gcode') >>> print(df[['x', 'y', 'z', 'time']].head()) >>> >>> # For full emulation results, use gcode_file_to_dataframe() instead >>> df_emulated = gr.gcode_file_to_dataframe('part.gcode', machine='cead') """ from .emulate._dataframe import ( gcode_file_to_dataframe as _gcode_file_to_dataframe, ) # Map flavor to machine type for consistency _validate_flavor(flavor) return _gcode_file_to_dataframe(filepath, flavor)
[docs] def gcode_to_additive_part( filepath: str, bead: Bead, machine: str = "default", layer_normal: tuple[float, float, float] | None = None, ) -> AdditivePart: """Parses a G-code file into a structured AdditivePart object. This function is a convenience pipeline from a G-code file to an ``AdditivePart``, combining machine emulation and part construction in a single call. Args: filepath (str): The absolute or relative path to the G-code file. bead (Bead): The uniform cross-sectional bead geometry to apply to all extruded filaments (width, height, density, shape). machine (str, optional): A string identifying the machine model to be used for parsing and emulation. Not case-sensitive and ignores spaces. See ``list_supported_machines()`` for available options. Defaults to "default", a generic machine. layer_normal (tuple[float, float, float] | None, optional): The plane normal direction used to identify layers. If ``None``, the default layer detection logic is applied. Defaults to None. Returns: AdditivePart: A structured part containing layers, segments, and process data derived from the G-code file. Raises: ValueError: If ``machine`` is not a recognized key or alias. FileNotFoundError: If ``filepath`` does not exist. Examples: >>> import gcode_reader as gr >>> >>> bead = gr.emulate.additive_part.Bead(width=8.0, height=2.0, density=1200.0) >>> part = gr.gcode_to_additive_part('part.gcode', bead, machine='cincinnati') >>> print(f"Layers: {part.n_layers}") """ machine_instance = _get_machine_type(machine)() operation = machine_instance.gcode_file_to_operation( filepath, operation_type=AdditiveOperation ) return AdditivePart.from_operation(operation, bead, layer_normal)
[docs] def operation_to_dataframe(operation: Operation) -> pd.DataFrame: """Converts a processed Operation to a pandas DataFrame with emulation data. This function provides direct access to the emulation results as a DataFrame, allowing for analysis of machine kinematics, motion profiles, and timing after full machine simulation. The resulting DataFrame includes all process data from the operation including coordinates, velocities, feed rates, elapsed time, and motion profile information. Args: operation (Operation): A processed Operation object (result of `gcode_to_operation()` or equivalent). Must have been processed by a machine (i.e., `operation.processed_by_machine` must not be None). Returns: pandas.DataFrame: A DataFrame with simple process data. Columns include: - Coordinates: x, y, z (machine position) - Deltas: dx, dy, dz (coordinate changes) - Distance: distance (move distance) - Time: time (elapsed time in seconds) - Tags: tags (contextual information) Raises: ValueError: If the operation has not been processed by a machine. Examples: >>> import gcode_reader as gr >>> >>> # Load and process a G-code file with full machine emulation >>> operation = gr.gcode_to_operation('part.gcode', machine='cead') >>> >>> # Convert to DataFrame for analysis >>> df = gr.operation_to_dataframe(operation) >>> print(df[['x', 'y', 'z', 'time']].head()) >>> >>> # Analyze motion profile >>> print(f"Total elapsed time: {df['time'].iloc[-1]:.2f}s") >>> print(f"Average distance: {df['distance'].mean():.2f}") """ if operation.processed_by_machine is None: raise ValueError( "Operation must be processed by a machine before converting to DataFrame. " "Use gcode_to_operation() with a valid machine parameter." ) # Extract fields efficiently using direct iteration (avoid intermediate lists) process_data_list = operation.process_data n_commands = len(process_data_list) # Pre-allocate arrays for efficiency elapsed_times = np.empty(n_commands, dtype=np.float64) locations = np.empty((n_commands, 3), dtype=np.float64) distances = np.empty(n_commands, dtype=np.float64) # Single pass through data - faster than multiple list comprehensions for i in range(n_commands): data = process_data_list[i] elapsed_times[i] = data.elapsed_time distances[i] = data.distance if data.location is not None: locations[i, 0] = data.location[0] locations[i, 1] = data.location[1] locations[i, 2] = data.location[2] else: locations[i] = np.nan # Extract x, y, z coordinates directly from pre-allocated array x_coords = locations[:, 0] y_coords = locations[:, 1] z_coords = locations[:, 2] # Compute deltas (differences from previous position) dx = np.diff(x_coords, prepend=np.nan) dy = np.diff(y_coords, prepend=np.nan) dz = np.diff(z_coords, prepend=np.nan) df = pd.DataFrame( { "x": x_coords, "y": y_coords, "z": z_coords, "dx": dx, "dy": dy, "dz": dz, "distance": distances, "time": elapsed_times, "tags": np.full(n_commands, "", dtype=object), } ) return df
[docs] def gcode_file_to_dataframe( filepath: str, machine: str = "default", flavor: str | None = None, fast: bool = False, ) -> pd.DataFrame: """Reads a G-code file into a pandas DataFrame with optional machine emulation. This is the primary way to get G-code data as a DataFrame. It combines parsing, optional machine emulation, and DataFrame conversion into a single call. For fast parsing without machine emulation (10-50x faster), set `fast=True`. For full machine simulation with kinematics, leave `fast=False`. Args: filepath (str): Path (absolute or relative) to a G-code file. machine (str, optional): A string identifying the machine model to use for emulation. This is not case-sensitive and ignores spaces. See `list_supported_machines()` for all available options. Ignored if `fast=True`. Defaults to "default". flavor (str, optional): G-code syntax flavor (for compatibility). If provided, overrides the machine parameter. Supports 'default', 'marlin', 'reprap', 'cead', 'flexbot', 'hendrick', 'juggerbot', 'ingersoll', 'cincinnati', 'baam'. fast (bool, optional): If True, use fast parsing without machine emulation (50-100x faster). If False, use full machine emulation with kinematics. Defaults to False. Returns: pandas.DataFrame: A DataFrame with process data. Columns include: - Coordinates: x, y, z (machine position) - Deltas: dx, dy, dz (coordinate changes) - Distance: distance (move distance) - Time: time (elapsed time in seconds) - Tags: tags (contextual information) Raises: ValueError: If the `machine` or `flavor` is not recognized. FileNotFoundError: If the filepath does not exist. Examples: >>> import gcode_reader as gr >>> >>> # Fast parsing without emulation (10-50ms) >>> df = gr.gcode_file_to_dataframe('part.gcode', fast=True) >>> >>> # Full machine emulation with kinematics (much slower) >>> df = gr.gcode_file_to_dataframe('part.gcode', machine='cead', fast=False) >>> print(df[['x', 'y', 'z', 'time']].head()) """ # Fast path: skip expensive machine emulation if fast: return gcode_file_to_dataframe_simple(filepath, flavor or "default") # Slow path: full machine emulation # If flavor is provided, use it to determine machine type if flavor is not None: _validate_flavor(flavor) machine = FLAVOR_ALIASES[flavor.lower().replace(" ", "").replace("_", "")] operation = gcode_to_operation(filepath, machine) return operation_to_dataframe(operation)