mirror of
https://git.mirrors.martin98.com/https://github.com/petaflot/pygcode
synced 2025-08-15 12:25:54 +08:00
moved machinestate contents back to machine.py
This commit is contained in:
parent
c62ee74b78
commit
3674f9d6aa
@ -1,4 +1,5 @@
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from copy import copy
|
||||||
|
|
||||||
from .words import Word
|
from .words import Word
|
||||||
|
|
||||||
@ -140,8 +141,9 @@ class GCode(object):
|
|||||||
# Parameters associated to this gcode
|
# Parameters associated to this gcode
|
||||||
param_letters = set()
|
param_letters = set()
|
||||||
|
|
||||||
# Modal Group
|
# Modal stuff
|
||||||
modal_group = None
|
modal_group = None
|
||||||
|
modal_param_letters = set() # by default: no parameters are retained in modal state
|
||||||
|
|
||||||
# Execution Order
|
# Execution Order
|
||||||
exec_order = 999 # if not otherwise specified, run last
|
exec_order = 999 # if not otherwise specified, run last
|
||||||
@ -208,15 +210,12 @@ class GCode(object):
|
|||||||
def description(self):
|
def description(self):
|
||||||
return self.__doc__
|
return self.__doc__
|
||||||
|
|
||||||
@property
|
def modal_copy(self):
|
||||||
def mode(self):
|
"""Copy of GCode instance containing only parameters listed in modal_param_letters"""
|
||||||
"""
|
return self.__class__(self.word, *[
|
||||||
Mode word for modal GCodes
|
w for (l, w) in self.params.items()
|
||||||
:return: Word to save machine's state, or None if GCode is not modal
|
if l in self.modal_param_letters
|
||||||
"""
|
])
|
||||||
if self.modal_group is None:
|
|
||||||
return None
|
|
||||||
return self.word
|
|
||||||
|
|
||||||
|
|
||||||
# ======================= Motion =======================
|
# ======================= Motion =======================
|
||||||
|
@ -4,7 +4,98 @@ from collections import defaultdict
|
|||||||
from .gcodes import MODAL_GROUP_MAP, GCode
|
from .gcodes import MODAL_GROUP_MAP, GCode
|
||||||
from .line import Line
|
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):
|
class Machine(object):
|
||||||
@ -18,7 +109,15 @@ class Machine(object):
|
|||||||
:param modal_params: list of Word instances to be applied to current movement mode
|
:param modal_params: list of Word instances to be applied to current movement mode
|
||||||
"""
|
"""
|
||||||
modal_params = kwargs.get('modal_params', [])
|
modal_params = kwargs.get('modal_params', [])
|
||||||
|
|
||||||
|
#process_gcodes =
|
||||||
|
|
||||||
for gcode in sorted(gcode_list):
|
for gcode in sorted(gcode_list):
|
||||||
self.state.set_mode(gcode) # if gcode is not modal, it's ignored
|
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
|
# 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?
|
||||||
|
@ -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)
|
|
||||||
)
|
|
Loading…
x
Reference in New Issue
Block a user