Commit 82f91d13 authored by Fabrice Salvaire's avatar Fabrice Salvaire

implemented machine

parent deb49300
......@@ -72,7 +72,28 @@ import colors
####################################################################################################
class Program:
class MachineMixin:
"""Mixin to define the target machine for the AST node"""
##############################################
def __init__(self, machine=None):
self._machine = machine
##############################################
@property
def machine(self):
return self._machine
@machine.setter
def machine(self, value):
self._machine = value
####################################################################################################
class Program(MachineMixin):
"""Class to implement a G-code program
......@@ -93,14 +114,15 @@ class Program:
##############################################
def __init__(self):
def __init__(self, machine=None):
super().__init__(machine)
self._lines = []
##############################################
def clone(self):
program = self.__class__()
program = self.__class__(machine=self._machine)
for line in self:
program += line.clone()
......@@ -151,6 +173,8 @@ class Program:
class CloneMixin:
"""Mixin to provide a method to clone value"""
@staticmethod
def _clone_value(value):
if hasattr(value, 'clone'):
......@@ -160,7 +184,9 @@ class CloneMixin:
####################################################################################################
class LineItem(CloneMixin):
class LineItem(MachineMixin, CloneMixin):
"""Base class for line item"""
##############################################
......@@ -191,7 +217,7 @@ class LineItem(CloneMixin):
####################################################################################################
class Line:
class Line(MachineMixin):
"""Class to implement a G-code line
......@@ -254,7 +280,9 @@ class Line:
##############################################
def __init__(self, deleted=False, line_number=None, comment=None):
def __init__(self, deleted=False, line_number=None, comment=None, machine=None):
super().__init__(machine)
self.deleted = deleted
self.line_number = line_number
......@@ -266,7 +294,7 @@ class Line:
def clone(self):
line = self.__class__(self._deleted, self._line_number, self._comment)
line = self.__class__(self._deleted, self._line_number, self._comment, self._machine)
for item in self:
line += item.clone()
......@@ -428,13 +456,14 @@ class Comment(LineItem):
##############################################
def __init__(self, text):
def __init__(self, text, machine=None):
super().__init__(machine)
self.set(text)
##############################################
def clone(self):
return self.__class__(self._text)
return self.__class__(self._text, self._machine)
##############################################
......@@ -493,14 +522,15 @@ class Word(LineItem):
##############################################
def __init__(self, letter, value):
def __init__(self, letter, value, machine=None):
super().__init__(machine)
self.letter = letter
self.value = value
##############################################
def clone(self):
return self.__class__(self._letter, self._clone_value(self._value))
return self.__class__(self._letter, self._clone_value(self._value), self._machine)
##############################################
......@@ -538,15 +568,58 @@ class Word(LineItem):
else:
return Line.ANSI_X(self._letter) + Line.ANSI_VALUE(str(self._value))
##############################################
def _check_machine(self):
if self._machine is None:
raise NameError('Machine is not defined')
##############################################
@property
def is_valid_gcode(self):
self._check_machine()
return str(self) in self._machine.config.gcodes
##############################################
@property
def meaning(self):
if self.is_valid_gcode:
return self._machine.config.gcodes[str(self)].meaning
else:
return self._machine.config.letters[self.letter].meaning
##############################################
@property
def modal_group(self):
if self.is_valid_gcode:
return self._machine.config.modal_groups[str(self)]
else:
return None
##############################################
@property
def execution_order(self):
if self.is_valid_gcode:
return self._machine.config.execution_order[str(self)]
else:
return None
####################################################################################################
class RealValue(CloneMixin):
class RealValue(MachineMixin, CloneMixin):
"""Base class for real value"""
pass
####################################################################################################
class ParameterMixin:
"""Mixin for parameter"""
##############################################
def __init__(self, parameter):
......@@ -554,11 +627,6 @@ class ParameterMixin:
##############################################
def clone(self):
return self.__class__(self._parameter)
##############################################
@property
def parameter(self):
return self._parameter
......@@ -579,14 +647,15 @@ class ParameterSetting(LineItem, ParameterMixin):
##############################################
def __init__(self, parameter, value):
def __init__(self, parameter, value, machine=None):
super().__init__(machine)
ParameterMixin.__init__(self, parameter)
self.value = value
##############################################
def clone(self):
return self.__class__(self._parameter, self._clone_value(self._value))
return self.__class__(self._parameter, self._clone_value(self._value), self._machine)
##############################################
@property
......@@ -616,11 +685,17 @@ class Parameter(RealValue, ParameterMixin):
##############################################
def __init__(self, parameter):
def __init__(self, parameter, machine=None):
super().__init__(machine)
ParameterMixin.__init__(self, parameter)
##############################################
def clone(self):
return self.__class__(self._parameter, self._machine)
##############################################
def __repr__(self):
return 'Parameter({0._parameter})'.format(self)
......@@ -636,18 +711,21 @@ class Parameter(RealValue, ParameterMixin):
class UnaryOperation(RealValue):
"""Base class for unary operation"""
__function__ = None
__gcode__ = None
##############################################
def __init__(self, arg):
def __init__(self, arg, machine=None):
super().__init__(machine)
self.arg = arg
##############################################
def clone(self):
return self.__class__(self._clone_value(self._arg))
return self.__class__(self._clone_value(self._arg), self._machine)
##############################################
......@@ -732,19 +810,22 @@ class Tangent(UnaryOperation):
class BinaryOperation(RealValue):
"""Base class for binary operation"""
__function__ = None
__gcode__ = None
##############################################
def __init__(self, arg1, arg2):
def __init__(self, arg1, arg2, machine=None):
super().__init__(machine)
self.arg1 = arg1
self.arg2 = arg2
##############################################
def clone(self):
return self.__class__(self._clone_value(self._arg1), self._clone_value(self.arg2))
return self.__class__(self._clone_value(self._arg1), self._clone_value(self.arg2), self._machine)
##############################################
......
......@@ -283,6 +283,9 @@ class Gcodes(YamlMixin, RstMixin):
def __getitem__(self, code):
return self._gcodes[code]
def __contains__(self, code):
return code in self._gcodes
##############################################
def sorted_iter(self):
......@@ -336,6 +339,7 @@ class ExecutionOrder(YamlMixin, RstMixin):
data = self._load_yaml(yaml_path)
self._order = []
self._gcode_map = {}
count = 1
for index, d in data.items():
if index != count:
......@@ -346,6 +350,14 @@ class ExecutionOrder(YamlMixin, RstMixin):
gcodes = [gcodes]
group = ExecutionGroup(index, gcodes, d['meaning'])
self._order.append(group)
for gcode in gcodes:
if '-' in gcode:
start, stop = [int(code[1:]) for code in gcode.split('-')]
letter = gcode[0]
for i in range(start, stop+1):
self._gcode_map['{}{}'.format(letter, i)] = group
else:
self._gcode_map[gcode] = group
##############################################
......@@ -355,8 +367,11 @@ class ExecutionOrder(YamlMixin, RstMixin):
def __iter__(self):
return iter(self._order)
def __getitem__(self, slice_):
return self._order[slice_]
def __getitem__(self, index):
if isinstance(index, int):
return self._order[index]
else:
return self._gcode_map[index]
##############################################
......@@ -404,12 +419,15 @@ class ModalGroups(YamlMixin, RstMixin):
data = self._load_yaml(yaml_path)
self._groups = {}
self._gcode_map = {}
for index, d in data.items():
gcodes = d['gcodes']
if not isinstance(gcodes, list):
gcodes = [gcodes]
group = ExecutionGroup(index, gcodes, d['meaning'])
self._groups[index] = group
for gcode in gcodes:
self._gcode_map[gcode] = group
##############################################
......@@ -420,7 +438,10 @@ class ModalGroups(YamlMixin, RstMixin):
return iter(self._groups.values())
def __getitem__(self, index):
return self._groups[index]
if isinstance(index, int):
return self._groups[index]
else:
return self._gcode_map[index]
##############################################
......
......@@ -18,19 +18,70 @@
#
####################################################################################################
"""
"""Module to implement a basic G-code machine.
"""
####################################################################################################
__all__ = [
'GcodeMachine',
]
####################################################################################################
# class Config:
from pathlib import Path as Path
from .Config import Config
from .Parser import GcodeParser, GcodeParserError
####################################################################################################
class GcodeMachine:
PARSER_CLS = GcodeParser
##############################################
# def __init__(self,
# ):
def __init__(self):
self._config = None
self.load_config()
self._parser = None
self.setup_parser()
##############################################
def load_config(self):
data_path = Path(__file__).parent.joinpath('data')
self._config = Config(
execution_order=data_path.joinpath('rs274-execution-order.yaml'),
gcodes=data_path.joinpath('rs274-gcodes.yaml'),
letters=data_path.joinpath('rs274-word-starting-letter.yaml'),
modal_groups=data_path.joinpath('rs274-modal-groups.yaml'),
parameters=data_path.joinpath('rs274-default-parameter-file.yaml'),
)
##############################################
def setup_parser(self):
self._parser = self.PARSER_CLS(machine=self)
##############################################
@property
def config(self):
return self._config
@property
def parser(self):
return self._parser
##############################################
def reset():
pass
##############################################
......@@ -186,7 +186,7 @@ class GcodeGrammarMixin:
def p_ordinary_comment(self, p):
'ordinary_comment : INLINE_COMMENT'
p[0] = Ast.Comment(p[1])
p[0] = Ast.Comment(p[1], self._machine)
# def p_message(self, p):
# 'message : left_parenthesis + {white_space} + letter_m + {white_space} + letter_s +
......@@ -197,7 +197,7 @@ class GcodeGrammarMixin:
def p_mid_line_word(self, p):
'mid_line_word : mid_line_letter real_value'
p[0] = Ast.Word(p[1], p[2])
p[0] = Ast.Word(p[1], p[2], self._machine)
def p_mid_line_letter(self, p):
# LETTER
......@@ -226,11 +226,11 @@ class GcodeGrammarMixin:
def p_parameter_setting(self, p):
'parameter_setting : PARAMETER_SIGN parameter_index EQUAL_SIGN real_value'
p[0] = Ast.ParameterSetting(p[2], p[4])
p[0] = Ast.ParameterSetting(p[2], p[4], self._machine)
def p_parameter_value(self, p):
'parameter_value : PARAMETER_SIGN parameter_index'
p[0] = Ast.Parameter(p[2])
p[0] = Ast.Parameter(p[2], self._machine)
def p_parameter_index(self, p):
'parameter_index : real_value'
......@@ -340,12 +340,20 @@ class GcodeParserMixin:
##############################################
def __init__(self):
def __init__(self, machine=None):
self._machine = machine
self._build()
self._reset()
##############################################
@property
def machine(self):
return self._machine
##############################################
def _reset(self):
self._line = None
......@@ -374,7 +382,7 @@ class GcodeParserMixin:
line = line.strip()
self._line = Ast.Line()
self._line = Ast.Line(machine=self._machine)
ast = self._parser.parse(
line,
lexer=self._lexer._lexer,
......@@ -398,7 +406,7 @@ class GcodeParserMixin:
if not isinstance(lines, (list, tuple)):
lines = lines.split('\n')
program = Ast.Program()
program = Ast.Program(machine=self._machine)
for line in lines:
try:
program += self.parse(line)
......
......@@ -25,17 +25,6 @@ See :ref:`rs-274-reference-page` for more details about the RS-274 specification
####################################################################################################
from pathlib import Path as _Path
from .Config import Config as _Config
from .Parser import GcodeParser, GcodeParserError
_data_path = _Path(__file__).parent.joinpath('data')
config = _Config(
execution_order=_data_path.joinpath('rs274-execution-order.yaml'),
gcodes=_data_path.joinpath('rs274-gcodes.yaml'),
letters=_data_path.joinpath('rs274-word-starting-letter.yaml'),
modal_groups=_data_path.joinpath('rs274-modal-groups.yaml'),
parameters=_data_path.joinpath('rs274-default-parameter-file.yaml'),
)
# from .Config import Config as _Config
# from .Parser import GcodeParser, GcodeParserError
from .Machine import GcodeMachine
......@@ -34,10 +34,18 @@
from pathlib import Path
from PythonicGcodeMachine.Gcode.Rs274 import GcodeParser, config
from PythonicGcodeMachine.Gcode.Rs274.Machine import GcodeMachine
####################################################################################################
#r# We build a RS-274 G-code Machine
machine = GcodeMachine()
####################################################################################################
#r# We load a G-code program
program_filename = 'mill-example-1.ngc'
programs_directory = Path(__file__).parents[1].joinpath('programs')
......@@ -47,8 +55,13 @@ with open(program_path, 'r') as fh:
if lines[0].startswith(';'):
lines = lines[1:]
parser = GcodeParser()
program = parser.parse_lines(lines)
####################################################################################################
#r# We parse the program
program = machine.parser.parse_lines(lines)
#r# We dump the annotated program
meaning_format = ' {:5}: {}'
for line in program:
......@@ -56,11 +69,11 @@ for line in program:
# print(line.ansi_str()) # Fixme: pyterate
print(str(line))
for word in line.iter_on_word():
if word.letter in 'GM':
meaning = config.gcodes[str(word)].meaning
print(meaning_format.format(str(word), meaning))
if word.is_valid_gcode:
margin = ' '*9
print(meaning_format.format(str(word), word.meaning))
print(margin + 'Modal group: {}'.format(word.modal_group.meaning))
print(margin + 'Execution order: {}'.format(word.execution_order.index))
else:
letter = word.letter
meaning = config.letters[letter].meaning
print(meaning_format.format(letter, meaning))
print(meaning_format.format(word.letter, word.meaning))
#o#
......@@ -32,8 +32,8 @@
####################################################################################################
from PythonicGcodeMachine.Gcode.Rs274 import *
from PythonicGcodeMachine.Gcode.Rs274.Ast import *
from PythonicGcodeMachine.Gcode.Rs274.Parser import GcodeParser
####################################################################################################
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment