mirror of
https://git.mirrors.martin98.com/https://github.com/petaflot/pygcode
synced 2025-07-28 23:32:04 +08:00
simplified machine class
separated machine state to avoid circular dependency between gcodes.py and machine.py
This commit is contained in:
parent
3d849667f5
commit
c62ee74b78
@ -1,6 +1,6 @@
|
||||
from .line import Line
|
||||
|
||||
from .machine import AbstractMachine
|
||||
#from .machine import AbstractMachine
|
||||
|
||||
class GCodeFile(object):
|
||||
def __init__(self, filename=None):
|
||||
@ -14,10 +14,6 @@ class GCodeFile(object):
|
||||
self.lines.append(line)
|
||||
|
||||
|
||||
class GCodeWriterMachine(AbstractMachine):
|
||||
def machine_init(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def parse(filename):
|
||||
# FIXME: should be an iterator, and also not terrible
|
||||
file = GCodeFile()
|
||||
|
@ -1,70 +1,24 @@
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
from .gcodes import MODAL_GROUP_MAP, GCode
|
||||
from .line import Line
|
||||
|
||||
from .machinestate import MachineState
|
||||
|
||||
|
||||
class MachineState(object):
|
||||
def __init__(self, axes=('x', 'y', 'z')):
|
||||
self.axes = axes
|
||||
class Machine(object):
|
||||
def __init__(self):
|
||||
self.state = MachineState()
|
||||
|
||||
# initialize
|
||||
self.position = {}
|
||||
for axis in self.axes:
|
||||
self.position[axis] = 0
|
||||
def process(self, *gcode_list, **kwargs):
|
||||
"""
|
||||
Process gcodes
|
||||
:param gcode_list: list of GCode instances
|
||||
:param modal_params: list of Word instances to be applied to current movement mode
|
||||
"""
|
||||
modal_params = kwargs.get('modal_params', [])
|
||||
for gcode in sorted(gcode_list):
|
||||
self.state.set_mode(gcode) # if gcode is not modal, it's ignored
|
||||
|
||||
self.time = 0
|
||||
|
||||
|
||||
|
||||
class AbstractMachine(object):
|
||||
"""Basis for a real / virtualized machine to process gcode"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.axes = kwargs.get('axes', ('x', 'y', 'z'))
|
||||
self.max_rate = kwargs.get('max_rate', {
|
||||
'x': 500, # mm/min
|
||||
'y': 500, # mm/min
|
||||
'z': 500, # mm/min
|
||||
})
|
||||
self.max_travel = kwargs.get('max_travel', {
|
||||
'x': 200, # mm
|
||||
'y': 200, # mm
|
||||
'z': 200, # mm
|
||||
})
|
||||
self.max_spindle_speed = kwargs.get('max_spindle_speed', 1000) # rpm
|
||||
self.min_spindle_speed = kwargs.get('max_spindle_speed', 0) # rpm
|
||||
|
||||
# initialize
|
||||
self.state = MachineState(self.axes)
|
||||
|
||||
# machine-specific initialization
|
||||
self.machine_init(*args, **kwargs)
|
||||
|
||||
def machine_init(self, *args, **kwargs):
|
||||
# Executed last in instances' __init__ call.
|
||||
# Parameters are identical to that of __init__
|
||||
pass
|
||||
|
||||
def process_line(self, line):
|
||||
"""Change machine's state based on the given gcode line"""
|
||||
pass # TODO
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
class Axes(object):
|
||||
pass
|
||||
|
||||
class MyMachineState(MachineState):
|
||||
axes_state_class = AxesState
|
||||
pass
|
||||
|
||||
class MyMachine(AbstractMachine):
|
||||
available_axes = set('xyz')
|
||||
state_class = MyMachineState
|
||||
|
||||
|
||||
m = MyMachine(
|
||||
state=MyMachineState(
|
||||
absolute_position=
|
||||
),
|
||||
)
|
||||
|
||||
"""
|
||||
# TODO: gcode instance to change machine's state
|
||||
|
97
pygcode/machinestate.py
Normal file
97
pygcode/machinestate.py
Normal file
@ -0,0 +1,97 @@
|
||||
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)
|
||||
)
|
@ -4,7 +4,6 @@ import six
|
||||
|
||||
from .exceptions import GCodeBlockFormatError
|
||||
|
||||
|
||||
REGEX_FLOAT = re.compile(r'^-?(\d+\.?\d*|\.\d+)') # testcase: ..tests.test_words.WordValueMatchTests.test_float
|
||||
REGEX_INT = re.compile(r'^-?\d+')
|
||||
REGEX_POSITIVEINT = re.compile(r'^\d+')
|
||||
@ -201,7 +200,15 @@ WORD_MAP = {
|
||||
|
||||
|
||||
class Word(object):
|
||||
def __init__(self, letter, value):
|
||||
def __init__(self, *args):
|
||||
assert len(args) in [1, 2], "input arguments either: (letter, value) or (word_str)"
|
||||
if len(args) == 2:
|
||||
(letter, value) = args
|
||||
else:
|
||||
word_str = args[0]
|
||||
letter = word_str[0] # first letter
|
||||
value = word_str[1:] # rest of string
|
||||
|
||||
self.letter = letter.upper()
|
||||
|
||||
self._value_str = None
|
||||
|
Loading…
x
Reference in New Issue
Block a user