From 3674f9d6aad3ef9643301e25dac2d34ff9d5ad50 Mon Sep 17 00:00:00 2001 From: Peter Boin Date: Fri, 7 Jul 2017 14:38:08 +1000 Subject: [PATCH] moved machinestate contents back to machine.py --- pygcode/gcodes.py | 19 ++++---- pygcode/machine.py | 101 +++++++++++++++++++++++++++++++++++++++- pygcode/machinestate.py | 97 -------------------------------------- 3 files changed, 109 insertions(+), 108 deletions(-) delete mode 100644 pygcode/machinestate.py diff --git a/pygcode/gcodes.py b/pygcode/gcodes.py index 499500e..cfe49e4 100644 --- a/pygcode/gcodes.py +++ b/pygcode/gcodes.py @@ -1,4 +1,5 @@ from collections import defaultdict +from copy import copy from .words import Word @@ -140,8 +141,9 @@ class GCode(object): # Parameters associated to this gcode param_letters = set() - # Modal Group + # Modal stuff modal_group = None + modal_param_letters = set() # by default: no parameters are retained in modal state # Execution Order exec_order = 999 # if not otherwise specified, run last @@ -208,15 +210,12 @@ class GCode(object): def description(self): return self.__doc__ - @property - def mode(self): - """ - Mode word for modal GCodes - :return: Word to save machine's state, or None if GCode is not modal - """ - if self.modal_group is None: - return None - return self.word + def modal_copy(self): + """Copy of GCode instance containing only parameters listed in modal_param_letters""" + return self.__class__(self.word, *[ + w for (l, w) in self.params.items() + if l in self.modal_param_letters + ]) # ======================= Motion ======================= diff --git a/pygcode/machine.py b/pygcode/machine.py index 646aa0d..53afa0a 100644 --- a/pygcode/machine.py +++ b/pygcode/machine.py @@ -4,7 +4,98 @@ from collections import defaultdict from .gcodes import MODAL_GROUP_MAP, GCode from .line import Line -from .machinestate import MachineState + +class State(object): + """State of a Machine""" + # State is very forgiving: + # Anything possible in a machine's state may be changed & fetched. + # For example: x, y, z, a, b, c may all be set & requested. + # However, the machine for which this state is stored probably doesn't + # have all possible 6 axes. + # It is also possible to set an axis to an impossibly large distance. + # It is the responsibility of the Machine using this class to be + # discerning in these respects. + def __init__(self): + self.axes = defaultdict(lambda: 0.0) # aka: "machine coordinates" + + self.work_offset = defaultdict(lambda: 0.0) + # TODO: how to manage work offsets? (probs not like the above) + # read up on: + # - G92: coordinate system offset + # - G54-G59: select coordinate system (offsets from machine coordinates set by G10 L2) + + # TODO: Move this class into MachineState + + +class MachineState(object): + """Machine's state, and mode""" + # Default Mode + default_mode = ''' + G17 (plane_selection: X/Y plane) + G90 (distance: absolute position. ie: not "turtle" mode) + G91.1 (arc_ijk_distance: IJK sets arc center vertex relative to current position) + G94 (feed_rate_mode: feed-rate defined in units/min) + G21 (units: mm) + G40 (cutter_diameter_comp: no compensation) + G49 (tool_length_offset: no offset) + G61 (control_mode: exact path mode) + G97 (spindle_speed_mode: RPM Mode) + M0 (stopping: program paused) + M5 (spindle: off) + M9 (coolant: off) + F100 (feed_rate: 100 mm/min) + S1000 (tool: 1000 rpm, when it's switched on) + ''' + + # Mode is defined by gcodes set by processed blocks: + # see modal_group in gcode.py module for details + def __init__(self): + self.modal_groups = defaultdict(lambda: None) + + # Initialize + self.set_mode(*Line(self.default_mode).block.gcodes) + + def set_mode(self, *gcode_list): + for g in sorted(gcode_list): # sorted by execution order + if g.modal_group is not None: + self.modal_groups[g.modal_group] = g.modal_copy() + + def __getattr__(self, key): + if key in MODAL_GROUP_MAP: + return self.modal_groups[MODAL_GROUP_MAP[key]] + + raise AttributeError("'{cls}' object has no attribute '{key}'".format( + cls=self.__class__.__name__, + key=key + )) + + def __setattr__(self, key, value): + if key in MODAL_GROUP_MAP: + # Set/Clear modal group gcode + if value is None: + # clear mode group + self.modal_groups[MODAL_GROUP_MAP[key]] = None + else: + # set mode group explicitly + # (recommended to use self.set_mode(value) instead) + assert isinstance(value, GCode), "invalid value type: %r" % value + assert value.modal_group == MODAL_GROUP_MAP[key], \ + "cannot set '%s' mode as %r, wrong group" % (key, value) + self.modal_groups[MODAL_GROUP_MAP[key]] = value.modal_copy() + else: + self.__dict__[key] = value + + def __str__(self): + gcode_list = [] + for modal_group in sorted(MODAL_GROUP_MAP.values()): + if self.modal_groups[modal_group]: + gcode_list.append(self.modal_groups[modal_group]) + return ' '.join(str(g) for g in gcode_list) + + def __repr__(self): + return "<{class_name}: {gcodes}>".format( + class_name=self.__class__.__name__, gcodes=str(self) + ) class Machine(object): @@ -18,7 +109,15 @@ class Machine(object): :param modal_params: list of Word instances to be applied to current movement mode """ modal_params = kwargs.get('modal_params', []) + + #process_gcodes = + for gcode in sorted(gcode_list): self.state.set_mode(gcode) # if gcode is not modal, it's ignored + gcode.process(self.state) # TODO: gcode instance to change machine's state + # Questions to drive design: + # - how much time did the command take? + # - what was the tool's distance / displacement + # - did the tool travel outside machine boundaries? diff --git a/pygcode/machinestate.py b/pygcode/machinestate.py deleted file mode 100644 index 1e550bf..0000000 --- a/pygcode/machinestate.py +++ /dev/null @@ -1,97 +0,0 @@ -from .gcodes import GCode -from .line import Line - - -class State(object): - """State of a Machine""" - # State is very forgiving: - # Anything possible in a machine's state may be changed & fetched. - # For example: x, y, z, a, b, c may all be set & requested. - # However, the machine for which this state is stored probably doesn't - # have all possible 6 axes. - # It is also possible to set an axis to an impossibly large distance. - # It is the responsibility of the Machine using this class to be - # discerning in these respects. - def __init__(self): - self.axes = defaultdict(lambda: 0.0) # aka: "machine coordinates" - - self.work_offset = defaultdict(lambda: 0.0) - # TODO: how to manage work offsets? (probs not like the above) - # read up on: - # - G92: coordinate system offset - # - G54-G59: select coordinate system (offsets from machine coordinates set by G10 L2) - - # TODO: Move this class into MachineState - - -class MachineState(object): - """Machine's state, and mode""" - # Mode is defined by gcodes set by processed blocks: - # see modal_group in gcode.py module for details - def __init__(self): - self.modal_groups = defaultdict(lambda: None) - # populate with all groups - for modal_group in MODAL_GROUP_MAP.values(): - self.modal_groups[modal_group] = None - - # Default mode: - self.set_mode(*Line(''' - G17 (plane_selection: X/Y plane) - G90 (distance: absolute position. ie: not "turtle" mode) - G91.1 (arc_ijk_distance: IJK sets arc center vertex relative to current position) - G94 (feed_rate_mode: feed-rate defined in units/min) - G21 (units: mm) - G40 (cutter_diameter_comp: no compensation) - G49 (tool_length_offset: no offset) - G61 (control_mode: exact path mode) - G97 (spindle_speed_mode: RPM Mode) - M0 (stopping: program paused) - M5 (spindle: off) - M9 (coolant: off) - F100 (feed_rate: 100 mm/min) - S1000 (tool: 1000 rpm, when it's switched on) - ''').block.gcodes) # note: although this is not a single line - # '\n' is just treated like any other whitespace, - # so it behaves like a single line. - - def set_mode(self, *gcode_list): - for g in sorted(gcode_list): # sorted by execution order - if g.modal_group is not None: - self.modal_groups[g.modal_group] = g - - def __getattr__(self, key): - if key in MODAL_GROUP_MAP: - return self.modal_groups[MODAL_GROUP_MAP[key]] - - raise AttributeError("'{cls}' object has no attribute '{key}'".format( - cls=self.__class__.__name__, - key=key - )) - - def __setattr__(self, key, value): - if key in MODAL_GROUP_MAP: - # Set/Clear modal group gcode - if value is None: - # clear mode group - self.modal_groups[MODAL_GROUP_MAP[key]] = None - else: - # set mode group explicitly - # (recommended to use self.set_mode(value) instead) - assert isinstance(value, GCode), "invalid value type: %r" % value - assert value.modal_group == MODAL_GROUP_MAP[key], \ - "cannot set '%s' mode as %r, wrong group" % (key, value) - self.modal_groups[MODAL_GROUP_MAP[key]] = value - else: - self.__dict__[key] = value - - def __str__(self): - gcode_list = [] - for modal_group in sorted(MODAL_GROUP_MAP.values()): - if self.modal_groups[modal_group]: - gcode_list.append(self.modal_groups[modal_group]) - return ' '.join(str(g) for g in gcode_list) - - def __repr__(self): - return "<{class_name}: {gcodes}>".format( - class_name=self.__class__.__name__, gcodes=str(self) - )