diff --git a/PythonicGcodeMachine/Gcode/Rs274/Ast.py b/PythonicGcodeMachine/Gcode/Rs274/Ast.py
index 6cc4493e35cf1d3e493c1a13ed41b3368b05fb1c..e3df3e62bd0d49ff57123bd151e1f3d912ed235e 100644
--- a/PythonicGcodeMachine/Gcode/Rs274/Ast.py
+++ b/PythonicGcodeMachine/Gcode/Rs274/Ast.py
@@ -382,15 +382,26 @@ class Line(MachineMixin):
yield item
def iter_on_gm_word(self):
- for item in self:
- if isinstance(item, Word) and item.is_gm_gcode:
+ for item in self.iter_on_word():
+ if item.is_gm_gcode:
yield item
def iter_on_x_word(self):
- for item in self:
- if isinstance(item, Word) and not item.is_gm_gcode:
+ for item in self.iter_on_word():
+ if not item.is_gm_gcode:
+ yield item
+
+ def iter_on_letter(self, letters):
+ for item in self.iter_on_word():
+ if item.letter in letters:
yield item
+ def iter_on_g_word(self):
+ return self.iter_on_letter('G')
+
+ def iter_on_m_word(self):
+ return self.iter_on_letter('M')
+
def iter_on_setting(self):
for item in self:
if isinstance(item, ParameterSetting):
@@ -421,6 +432,19 @@ class Line(MachineMixin):
##############################################
+ def check_modal_group(self):
+
+ modal_groups = {}
+ for item in self.iter_on_gm_word():
+ group = item.modal_group
+ modal_groups.setdefault(group, 0)
+ modal_groups[group] += 1
+ errors = [modal_group for modal_group, count in modal_groups.items() if count > 1]
+ if errors:
+ raise ValueError("Modal group errors {}".format(errors))
+
+ ##############################################
+
def __repr__(self):
items = []
@@ -514,6 +538,7 @@ class Word(LineItem):
"""Class to implement word"""
+ # Fixme: config ???
LETTERS = (
'A', 'B', 'C', 'D',
'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', # 'N',
@@ -594,12 +619,20 @@ class Word(LineItem):
if self._machine is None:
raise NameError('Machine is not defined')
+ @property
+ def _machine_config(self):
+ self._check_machine()
+ return self._machine.config
+
##############################################
@property
def is_gm_gcode(self):
- self._check_machine()
- return self._machine.is_gm_word(self)
+ return self._machine_config.letters.is_gm_word(self)
+
+ @property
+ def is_axis_gcode(self):
+ return self._machine_config.letters.is_axis_word(self)
##############################################
diff --git a/PythonicGcodeMachine/Gcode/Rs274/Config.py b/PythonicGcodeMachine/Gcode/Rs274/Config.py
index 84c64a41af51684df9b00f2ee96d8640d245acf1..2663fc9daf243064b2599eb026e58ae281145180 100644
--- a/PythonicGcodeMachine/Gcode/Rs274/Config.py
+++ b/PythonicGcodeMachine/Gcode/Rs274/Config.py
@@ -36,9 +36,9 @@ __all__ = [
'ExecutionOrder',
'Gcode',
'GcodeSet',
- 'Letters',
+ 'LetterSet',
'ModalGroup',
- 'ModalGroups',
+ 'ModalGroupSet',
'Parameter',
'ParameterSet',
]
@@ -156,6 +156,11 @@ class Parameter(MeaningMixin):
def default_value(self):
return self._value
+ ##############################################
+
+ def __repr__(self):
+ return '#{0._index}: {0._meaning}'.format(self)
+
####################################################################################################
class ParameterSet(YamlMixin, RstMixin):
@@ -210,12 +215,36 @@ class Letter(MeaningMixin):
"""G-code letter (table key)"""
return self._letter
+ ##############################################
+
+ def __repr__(self):
+ return '#{0._letter}: {0._meaning}'.format(self)
+
####################################################################################################
-class Letters(YamlMixin, RstMixin):
+class LetterSet(YamlMixin, RstMixin):
"""Class for the table of letters."""
+ GM_LETTERS = 'GM'
+ AXIS_LETTERS = 'XYZABC' # 6-axis
+
+ ##############################################
+
+ def is_gm_letter(self, letter):
+ return letter in self.GM_LETTERS
+
+ def is_axis_letter(self, letter):
+ return letter in self.AXIS_LETTERS
+
+ ##############################################
+
+ def is_gm_word(self, word):
+ return self.is_gm_letter(word.letter)
+
+ def is_axis_word(self, word):
+ return self.is_axis_letter(word.letter)
+
##############################################
def __init__(self, yaml_path):
@@ -251,7 +280,11 @@ class Gcode(MeaningMixin):
##############################################
- def __init__(self, gcode, meaning, modal_group=None, execution_order=None):
+ def __init__(self, gcode, meaning,
+ modal_group=None,
+ execution_order=None,
+ doc=None,
+ ):
MeaningMixin.__init__(self, meaning)
self._gcode = str(gcode)
@@ -259,6 +292,7 @@ class Gcode(MeaningMixin):
# Those are set later due to the initialisation process
self._modal_group = modal_group
self._execution_order = execution_order
+ self._doc = doc
##############################################
@@ -279,6 +313,21 @@ class Gcode(MeaningMixin):
def execution_order_index(self):
return self._execution_order.index
+ @property
+ def doc(self):
+ return self._doc
+
+ ##############################################
+
+ def __str__(self):
+ return self._gcode
+
+ ##############################################
+
+ def convert_doc(self, format):
+ import pypandoc
+ return pypandoc.convert_text(self.doc, 'rst', format=format)
+
####################################################################################################
class GcodeSet(YamlMixin, RstMixin):
@@ -295,6 +344,8 @@ class GcodeSet(YamlMixin, RstMixin):
gcode = Gcode(gcode_txt, d['meaning'])
self._gcodes[gcode_txt] = gcode
+ self._sorted_gcodes = None
+
##############################################
def __len__(self):
@@ -311,11 +362,34 @@ class GcodeSet(YamlMixin, RstMixin):
##############################################
+ def _sort(self):
+
+ if self._sorted_gcodes is None:
+ items = list(self)
+ items.sort(key=lambda item: str(ord(item.gcode[0])*1000) + item.gcode[1:])
+ self._sorted_gcodes = items
+ return self._sorted_gcodes
+
+ ##############################################
+
def sorted_iter(self):
+ return iter(self._sort())
- items = list(self)
- items.sort(key=lambda item: str(ord(item.gcode[0])*1000) + item.gcode[1:])
- return items
+ ##############################################
+
+ def iter_on_slice(self, start, stop):
+
+ start_index = None
+ stop_index = None
+ for i, item in enumerate(self._sort()):
+ if item.gcode == start:
+ start_index = i
+ elif item.gcode == stop:
+ stop_index = i
+ if start_index > stop_index:
+ raise ValueError('{} > {}'.format(start, stop))
+
+ return iter(self._sorted_gcodes[start_index:stop_index+1])
##############################################
@@ -356,6 +430,11 @@ class ExecutionGroup(MeaningMixin):
"""Raw G-Codes list"""
return self._raw_gcodes
+ ##############################################
+
+ def __str__(self):
+ return '#{0._index} Meaning: {0._meaning}'.format(self)
+
####################################################################################################
class ExecutionOrder(YamlMixin, RstMixin):
@@ -445,9 +524,14 @@ class ModalGroup(MeaningMixin):
"""G-Codes list"""
return self._gcodes
+ ##############################################
+
+ def __repr__(self):
+ return '#{0._index}: ({1}) Meaning: {0._meaning}'.format(self, ' '.join([str(gcode) for gcode in self._gcodes]))
+
####################################################################################################
-class ModalGroups(YamlMixin, RstMixin):
+class ModalGroupSet(YamlMixin, RstMixin):
"""Class for the table of modal groups."""
@@ -521,13 +605,54 @@ class Config:
self._gcodes = GcodeSet(gcodes)
self._execution_order = ExecutionOrder(execution_order, self._gcodes)
- self._modal_groups = ModalGroups(modal_groups, self._gcodes)
+ self._modal_groups = ModalGroupSet(modal_groups, self._gcodes)
# self._letters = str(letters)
# self._parameters = str(parameters)
- self._letters = Letters(letters)
+ self._letters = LetterSet(letters)
self._parameters = ParameterSet(parameters)
+ self._load_doc()
+
+ ##############################################
+
+ def _load_doc(self):
+
+ from . import GcodeDoc as gcode_doc
+ for obj in gcode_doc.__dict__.values():
+ if isinstance(obj, type):
+ self._load_gcode_doc_cls(obj)
+
+ ##############################################
+
+ def _set_gcode_doc(self, gcode, cls):
+ rst_doc = cls.__doc__
+ rst_doc = rst_doc.replace('\n' + ' '*4, '\n')
+ self._gcodes[gcode]._doc = rst_doc
+
+ ##############################################
+
+ def _load_gcode_doc_cls(self, cls):
+
+ cls_name = cls.__name__
+ for letter in self._letters.GM_LETTERS:
+ cls_name = cls_name.replace('_' + letter, ' ' + letter)
+ cls_name = cls_name.replace('_to', '-')
+ cls_name = cls_name.replace('_', '.')
+ gcodes = cls_name.split(' ')
+ i = 0
+ while i < len(gcodes):
+ gcode = gcodes[i]
+ if gcode.endswith('-'):
+ start = gcode[:-1]
+ i += 1
+ stop = gcodes[i]
+ for _gcode in self._gcodes.iter_on_slice(start, stop):
+ self._set_gcode_doc(str(_gcode), cls)
+ else:
+ self._set_gcode_doc(gcode, cls)
+ i += 1
+
##############################################
@property
@@ -542,14 +667,14 @@ class Config:
@property
def letters(self):
- """:class:`Letters` instance"""
+ """:class:`LetterSet` instance"""
# if isinstance(self._letters, str):
- # self._letters = Letters(self._letters)
+ # self._letters = LetterSet(self._letters)
return self._letters
@property
def modal_groups(self):
- """:class:`ModalGroups` instance"""
+ """:class:`ModalGroupSet` instance"""
return self._modal_groups
@property
diff --git a/PythonicGcodeMachine/Gcode/Rs274/Machine.py b/PythonicGcodeMachine/Gcode/Rs274/Machine.py
index 1864685cb6050496468cb011df905d26b7bbde0b..8e8f4f50521ae64d3c3acc74bba21de6586180a6 100644
--- a/PythonicGcodeMachine/Gcode/Rs274/Machine.py
+++ b/PythonicGcodeMachine/Gcode/Rs274/Machine.py
@@ -40,8 +40,6 @@ class GcodeMachine:
PARSER_CLS = GcodeParser
- GM_LETTERS = 'GM'
-
##############################################
def __init__(self):
@@ -63,7 +61,7 @@ class GcodeMachine:
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'),
- )
+ )
##############################################
@@ -85,13 +83,3 @@ class GcodeMachine:
def reset():
pass
-
- ##############################################
-
- def is_gm_letter(self, letter):
- return letter in self.GM_LETTERS
-
- ##############################################
-
- def is_gm_word(self, word):
- return self.is_gm_letter(word.letter)
diff --git a/examples/gcode/annotate-gcode.py b/examples/gcode/annotate-gcode.py
index 0fbf7504963406188d4dd5f97394c1e6bb0b4c63..08a5dc1514a34ee60ea35a78dd62d5bcf56c9bc1 100644
--- a/examples/gcode/annotate-gcode.py
+++ b/examples/gcode/annotate-gcode.py
@@ -28,6 +28,8 @@
#r#
#r# * :mod:`PythonicGcodeMachine.Gcode.Rs274`
#r# * :mod:`PythonicGcodeMachine.Gcode.Rs274.Ast`
+#r# * :mod:`PythonicGcodeMachine.Gcode.Rs274.Condig`
+#r# * :mod:`PythonicGcodeMachine.Gcode.Rs274.Machine`
#r# * :mod:`PythonicGcodeMachine.Gcode.Rs274.Parser`
####################################################################################################
@@ -71,6 +73,7 @@ for line in program:
print()
# print(line.ansi_str()) # Fixme: pyterate
print(str(line))
+ line.check_modal_group()
for word in line.iter_on_word():
if word.is_gm_gcode:
margin = ' '*9
diff --git a/examples/gcode/query-gcode.py b/examples/gcode/query-gcode.py
new file mode 100644
index 0000000000000000000000000000000000000000..76d39474d8b58b5a5498371251e1fb2fe979f6bc
--- /dev/null
+++ b/examples/gcode/query-gcode.py
@@ -0,0 +1,74 @@
+#?##################################################################################################
+#?#
+#?# PythonicGcodeMachine - A Python G-code Toolkit
+#?# Copyright (C) 2018 Fabrice Salvaire
+#?#
+#?# This program is free software: you can redistribute it and/or modify
+#?# it under the terms of the GNU General Public License as published by
+#?# the Free Software Foundation, either version 3 of the License, or
+#?# (at your option) any later version.
+#?#
+#?# This program is distributed in the hope that it will be useful,
+#?# but WITHOUT ANY WARRANTY; without even the implied warranty of
+#?# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#?# GNU General Public License for more details.
+#?#
+#?# You should have received a copy of the GNU General Public License
+#?# along with this program. If not, see .
+#?#
+#?##################################################################################################
+
+####################################################################################################
+
+#r# ===============
+#r# Query G-codes
+#r# ===============
+#r#
+#r# For API see
+#r#
+#r# * :mod:`PythonicGcodeMachine.Gcode.Rs274`
+#r# * :mod:`PythonicGcodeMachine.Gcode.Rs274.Config`
+#r# * :mod:`PythonicGcodeMachine.Gcode.Rs274.Machine`
+
+####################################################################################################
+
+from pathlib import Path
+
+from PythonicGcodeMachine.Gcode.Rs274.Machine import GcodeMachine
+
+####################################################################################################
+
+#r# We build a RS-274 G-code Machine
+
+machine = GcodeMachine()
+
+####################################################################################################
+
+#r# We get G-code information
+
+gcode = machine.config.gcodes['G0']
+
+print('Modal group:', gcode.modal_group)
+print('Execution order:', gcode.execution_order)
+#o#
+
+print('\nreStructuredText doc:\n')
+print(gcode.doc)
+#o#
+
+#r# Convert the reStructuredText doc using `pypandoc `_ and
+#r# `Pandoc `_
+
+try:
+ print('\nMarkdown doc:')
+ print(gcode.convert_doc('md'))
+except (ImportError, RuntimeError):
+ pass
+#o#
+
+try:
+ print('\nHTML doc:')
+ print(gcode.convert_doc('html5'))
+except (ImportError, RuntimeError):
+ pass
+#o#