added gcodes classes as a concept

This commit is contained in:
Peter Boin 2017-07-05 02:06:38 +10:00
parent 2d125028b1
commit dcac4fcec5
6 changed files with 877 additions and 13 deletions

View File

@ -1,5 +1,6 @@
import re
from .words import iter_words, WORD_MAP
from .gcodes import words_to_gcodes
class Block(object):
"""GCode block (effectively any gcode file line that defines any <word><value>)"""
@ -20,6 +21,7 @@ class Block(object):
self.text = text
self.words = list(iter_words(self.text))
#self.gcodes = list(words_to_gcodes(self.words))
def __getattr__(self, k):
if k in WORD_MAP:

824
pygcode/gcodes.py Normal file
View File

@ -0,0 +1,824 @@
from .words import Word
class GCode(object):
# Defining Word
word_key = None # Word instance to use in lookup
word_matches = None # function (secondary)
# Parameters associated to this gcode
param_words = set()
def __init__(self):
self.params = None # TODO
# ======================= Motion =======================
# (X Y Z A B C U V W apply to all motions)
# CODE PARAMETERS DESCRIPTION
# G0 Rapid Move
# G1 Linear Move
# G2, G3 I J K or R, P Arc Move
# G4 P Dwell
# G5 I J P Q Cubic Spline
# G5.1 I J Quadratic Spline
# G5.2 P L NURBS
# G38.2 - G38.5 Straight Probe
# G33 K Spindle Synchronized Motion
# G33.1 K Rigid Tapping
# G80 Cancel Canned Cycle
class GCodeMotion(GCode):
param_words = set('XYZABCUVW')
class GCodeRapidMove(GCodeMotion):
"""G0: Rapid Move"""
word_key = Word('G', 0)
class GCodeLinearMove(GCodeMotion):
"""G1: Linear Move"""
word_key = Word('G', 1)
class GCodeArcMove(GCodeMotion):
"""Arc Move"""
param_words = GCodeMotion.param_words | set('IJKRP')
class GCodeArcMoveCW(GCodeArcMove):
"""G2: Arc Move (clockwise)"""
word_key = Word('G', 2)
class GCodeArcMoveCCW(GCodeArcMove):
"""G3: Arc Move (counter-clockwise)"""
word_key = Word('G', 3)
class GCodeDwell(GCodeMotion):
"""G4: Dwell"""
param_words = GCodeMotion.param_words | set('P')
word_key = Word('G', 4)
class GCodeCublcSpline(GCodeMotion):
"""G5: Cubic Spline"""
param_words = GCodeMotion.param_words | set('IJPQ')
word_key = Word('G', 5)
class GCodeQuadraticSpline(GCodeMotion):
"""G5.1: Quadratic Spline"""
param_words = GCodeMotion.param_words | set('IJ')
word_key = Word('G', 5.1)
class GCodeNURBS(GCodeMotion):
"""G5.2: Non-uniform rational basis spline (NURBS)"""
param_words = GCodeMotion.param_words | set('PL')
word_key = Word('G', 5.2)
class GCodeNURBSEnd(GCodeNURBS):
"""G5.3: end NURBS mode"""
word_key = Word('G', 5.3)
class GCodeStraightProbe(GCodeMotion):
"""G38.2-G38.5: Straight Probe"""
@classmethod
def word_matches(cls, w):
return (w.letter == 'G') and (38.2 <= w.value <= 38.5)
class GCodeSpindleSyncMotion(GCodeMotion):
"""G33: Spindle Synchronized Motion"""
param_words = GCodeMotion.param_words | set('K')
word_key = Word('G', 33)
class GCodeRigidTapping(GCodeMotion):
"""G33.1: Rigid Tapping"""
param_words = GCodeMotion.param_words | set('K')
word_key = Word('G', 33.1)
class GCodeCancelCannedCycle(GCodeMotion):
"""G80: Cancel Canned Cycle"""
word_key = Word('G', 80)
# ======================= Canned Cycles =======================
# (X Y Z or U V W apply to canned cycles, depending on active plane)
# CODE PARAMETERS DESCRIPTION
# G81 R L (P) Drilling Cycle
# G82 R L (P) Drilling Cycle, Dwell
# G83 R L Q Drilling Cycle, Peck
# G73 R L Q Drilling Cycle, Chip Breaking
# G85 R L (P) Boring Cycle, Feed Out
# G89 R L (P) Boring Cycle, Dwell, Feed Out
# G76 P Z I J R K Q H L E Threading Cycle
class GCodeCannedCycle(GCode):
param_words = set('XYZUVW')
class GCodeDrillingCycle(GCodeCannedCycle):
"""G81: Drilling Cycle"""
param_words = GCodeCannedCycle.param_words | set('RLP')
word_key = Word('G', 81)
class GCodeDrillingCycleDwell(GCodeCannedCycle):
"""G82: Drilling Cycle, Dwell"""
param_words = GCodeCannedCycle.param_words | set('RLP')
word_key = Word('G', 82)
class GCodeDrillingCyclePeck(GCodeCannedCycle):
"""G83: Drilling Cycle, Peck"""
param_words = GCodeCannedCycle.param_words | set('RLQ')
word_key = Word('G', 83)
class GCodeDrillingCycleChipBreaking(GCodeCannedCycle):
"""G73: Drilling Cycle, ChipBreaking"""
param_words = GCodeCannedCycle.param_words | set('RLQ')
word_key = Word('G', 73)
class GCodeBoringCycleFeedOut(GCodeCannedCycle):
"""G85: Boring Cycle, Feed Out"""
param_words = GCodeCannedCycle.param_words | set('RLP')
word_key = Word('G', 85)
class GCodeBoringCycleDwellFeedOut(GCodeCannedCycle):
"""G89: Boring Cycle, Dwell, Feed Out"""
param_words = GCodeCannedCycle.param_words | set('RLP')
word_key = Word('G', 89)
class GCodeThreadingCycle(GCodeCannedCycle):
"""G76: Threading Cycle"""
param_words = GCodeCannedCycle.param_words | set('PZIJRKQHLE')
word_key = Word('G', 76)
# ======================= Distance Mode =======================
# CODE PARAMETERS DESCRIPTION
# G90, G91 Distance Mode
# G90.1, G91.1 Arc Distance Mode
# G7 Lathe Diameter Mode
# G8 Lathe Radius Mode
class GCodeDistanceMode(GCode):
pass
class GCodeAbsoluteDistanceMode(GCodeDistanceMode):
"""G90: Absolute Distance Mode"""
word_key = Word('G', 90)
class GCodeIncrementalDistanceMode(GCodeDistanceMode):
"""G91: Incremental Distance Mode"""
word_key = Word('G', 91)
class GCodeAbsoluteArcDistanceMode(GCodeDistanceMode):
"""G90.1: Absolute Distance Mode for Arc IJK Parameters"""
word_key = Word('G', 90.1)
class GCodeIncrementalArcDistanceMode(GCodeDistanceMode):
"""G91.1: Incremental Distance Mode for Arc IJK Parameters"""
word_key = Word('G', 91.1)
class GCodeLatheDiameterMode(GCodeDistanceMode):
"""G7: Lathe Diameter Mode"""
word_key = Word('G', 7)
class GCodeLatheRadiusMode(GCodeDistanceMode):
"""G8: Lathe Radius Mode"""
word_key = Word('G', 8)
# ======================= Feed Rate Mode =======================
# CODE PARAMETERS DESCRIPTION
# G93, G94, G95 Feed Rate Mode
class GCodeFeedRateMode(GCode):
pass
class GCodeInverseTimeMode(GCodeFeedRateMode):
"""G93: Inverse Time Mode"""
word_key = Word('G', 93)
class GCodeUnitsPerMinuteMode(GCodeFeedRateMode):
"""G94: Units Per MinuteMode"""
word_key = Word('G', 94)
class GCodeUnitsPerRevolution(GCodeFeedRateMode):
"""G95: Units Per Revolution"""
word_key = Word('G', 95)
# ======================= Spindle Control =======================
# CODE PARAMETERS DESCRIPTION
# M3, M4, M5 S Spindle Control
# M19 Orient Spindle
# G96, G97 S D Spindle Control Mode
class GCodeSpindle(GCode):
pass
class GCodeStartSpindleCW(GCodeSpindle):
"""M3: Start Spindle Clockwise"""
param_words = set('S')
word_key = Word('M', 3)
class GCodeStartSpindleCCW(GCodeSpindle):
"""M4: Start Spindle Counter-Clockwise"""
param_words = set('S')
word_key = Word('M', 4)
class GCodeStopSpindle(GCodeSpindle):
"""M5: Stop Spindle"""
param_words = set('S')
word_key = Word('M', 5)
class GCodeOrientSpindle(GCodeSpindle):
"""M19: Orient Spindle"""
word_key = Word('M', 19)
class GCodeSpindleConstantSurfaceSpeedMode(GCodeSpindle):
"""G96: Spindle Constant Surface Speed"""
param_words = set('DS')
word_key = Word('G', 96)
class GCodeSpindleRPMMode(GCodeSpindle):
"""G97: Spindle RPM Speed"""
param_words = set('D')
word_key = Word('G', 97)
# ======================= Coolant =======================
# CODE PARAMETERS DESCRIPTION
# M7, M8, M9 Coolant Control
class GCodeCoolant(GCode):
pass
class GCodeCoolantMistOn(GCodeCoolant):
"""M7: turn mist coolant on"""
word_key = Word('M', 7)
class GCodeCoolantFloodOn(GCodeCoolant):
"""M8: turn flood coolant on"""
word_key = Word('M', 8)
class GCodeCoolantOff(GCodeCoolant):
"""M9: turn all coolant off"""
word_key = Word('M', 9)
# ======================= Tool Length =======================
# CODE PARAMETERS DESCRIPTION
# G43 H Tool Length Offset
# G43.1 Dynamic Tool Length Offset
# G43.2 H Apply additional Tool Length Offset
# G49 Cancel Tool Length Compensation
class GCodeToolLength(GCode):
pass
class GCodeToolLengthOffset(GCodeToolLength):
"""G43: Tool Length Offset"""
param_words = set('H')
word_key = Word('G', 43)
class GCodeDynamicToolLengthOffset(GCodeToolLength):
"""G43.1: Dynamic Tool Length Offset"""
word_key = Word('G', 43.1)
class GCodeAddToolLengthOffset(GCodeToolLength):
"""G43.2: Appkly Additional Tool Length Offset"""
param_words = set('H')
word_key = Word('G', 43.2)
class GCodeCancelToolLengthOffset(GCodeToolLength):
"""G49: Cancel Tool Length Compensation"""
word_key = Word('G', 49)
# ======================= Stopping (Program Control) =======================
# CODE PARAMETERS DESCRIPTION
# M0, M1 Program Pause
# M2, M30 Program End
# M60 Pallet Change Pause
class GCodeProgramControl(GCode):
pass
class GCodePauseProgram(GCodeProgramControl):
"""M0: Program Pause"""
word_key = Word('M', 0)
class GCodePauseProgramOptional(GCodeProgramControl):
"""M1: Program Pause (optional)"""
word_key = Word('M', 1)
class GCodeEndProgram(GCodeProgramControl):
"""M2: Program End"""
word_key = Word('M', 2)
class GCodeEndProgramPalletShuttle(GCodeProgramControl):
"""M30: exchange pallet shuttles and end the program"""
word_key = Word('M', 30)
class GCodePalletChangePause(GCodeProgramControl):
"""M60: Pallet Change Pause"""
word_key = Word('M', 60)
# ======================= Units =======================
# CODE PARAMETERS DESCRIPTION
# G20, G21 Units
class GCodeUnit(GCode):
pass
class GCodeUseInches(GCodeUnit):
"""G20: use inches for length units"""
word_key = Word('G', 20)
class GCodeUseMillimeters(GCodeUnit):
"""G21: use millimeters for length units"""
word_key = Word('G', 21)
# ======================= Plane Selection =======================
# (affects G2, G3, G81-G89, G40-G42)
# CODE PARAMETERS DESCRIPTION
# G17 - G19.1 Plane Select
class GCodePlaneSelect(GCode):
pass
class GCodeSelectZYPlane(GCodePlaneSelect):
"""G17: select XY plane (default)"""
word_key = Word('G', 17)
class GCodeSelectZXPlane(GCodePlaneSelect):
"""G18: select ZX plane"""
word_key = Word('G', 18)
class GCodeSelectYZPlane(GCodePlaneSelect):
"""G19: select YZ plane"""
word_key = Word('G', 19)
class GCodeSelectUVPlane(GCodePlaneSelect):
"""G17.1: select UV plane"""
word_key = Word('G', 17.1)
class GCodeSelectWUPlane(GCodePlaneSelect):
"""G18.1: select WU plane"""
word_key = Word('G', 18.1)
class GCodeSelectVWPlane(GCodePlaneSelect):
"""G19.1: select VW plane"""
word_key = Word('G', 19.1)
# ======================= Cutter Radius Compensation =======================
# CODE PARAMETERS DESCRIPTION
# G40 Compensation Off
# G41,G42 D Cutter Compensation
# G41.1, G42.1 D L Dynamic Cutter Compensation
class GCodeCutterRadiusComp(GCode):
pass
class GCodeCutterRadiusCompOff(GCodeCutterRadiusComp):
"""G40: Cutter Radius Compensation Off"""
word_key = Word('G', 40)
class GCodeCutterCompLeft(GCodeCutterRadiusComp):
"""G41: Cutter Radius Compensation (left)"""
param_words = set('D')
word_key = Word('G', 41)
class GCodeCutterCompRight(GCodeCutterRadiusComp):
"""G42: Cutter Radius Compensation (right)"""
param_words = set('D')
word_key = Word('G', 42)
class GCodeDynamicCutterCompLeft(GCodeCutterRadiusComp):
"""G41.1: Dynamic Cutter Radius Compensation (left)"""
param_words = set('DL')
word_key = Word('G', 41.1)
class GCodeDynamicCutterCompRight(GCodeCutterRadiusComp):
"""G42.1: Dynamic Cutter Radius Compensation (right)"""
param_words = set('DL')
word_key = Word('G', 42.1)
# ======================= Path Control Mode =======================
# CODE PARAMETERS DESCRIPTION
# G61 G61.1 Exact Path Mode
# G64 P Q Path Blending
class GCodePathControlMode(GCode):
pass
class GCodeExactPathMode(GCodePathControlMode):
"""G61: Exact path mode"""
word_key = Word('G', 61)
class GCodeExactStopMode(GCodePathControlMode):
"""G61.1: Exact stop mode"""
word_key = Word('G', 61.1)
class GCodePathBlendingMode(GCodePathControlMode):
"""G64: Path Blending"""
param_words = set('PQ')
word_key = Word('G', 64)
# ======================= Return Mode in Canned Cycles =======================
# CODE PARAMETERS DESCRIPTION
# G98 Canned Cycle Return Level
class GCodeCannedReturnMode(GCode):
pass
class GCodeCannedCycleReturnLevel(GCodeCannedReturnMode):
"""G98: Canned Cycle Return Level"""
word_key = Word('G', 98)
# ======================= Other Modal Codes =======================
# CODE PARAMETERS DESCRIPTION
# F Set Feed Rate
# S Set Spindle Speed
# T Select Tool
# M48, M49 Speed and Feed Override Control
# M50 P0 (off) or P1 (on) Feed Override Control
# M51 P0 (off) or P1 (on) Spindle Speed Override Control
# M52 P0 (off) or P1 (on) Adaptive Feed Control
# M53 P0 (off) or P1 (on) Feed Stop Control
# G54-G59.3 Select Coordinate System
class GCodeOtherModal(GCode):
pass
class GCodeFeedRate(GCodeOtherModal):
"""F: Set Feed Rate"""
@classmethod
def word_matches(cls, w):
return w.letter == 'F'
class GCodeSpindleSpeed(GCodeOtherModal):
"""S: Set Spindle Speed"""
@classmethod
def word_matches(cls, w):
return w.letter == 'S'
class GCodeSelectTool(GCodeOtherModal):
"""T: Select Tool"""
@classmethod
def word_matches(cls, w):
return w.letter == 'T'
class GCodeSpeedAndFeedOverrideOn(GCodeOtherModal):
"""M48: Speed and Feed Override Control On"""
word_key = Word('M', 48)
class GCodeSpeedAndFeedOverrideOff(GCodeOtherModal):
"""M49: Speed and Feed Override Control Off"""
word_key = Word('M', 49)
class GCodeFeedOverride(GCodeOtherModal):
"""M50: Feed Override Control"""
param_words = set('P')
word_key = Word('M', 50)
class GCodeSpindleSpeedOverride(GCodeOtherModal):
"""M51: Spindle Speed Override Control"""
param_words = set('P')
word_key = Word('M', 51)
class GCodeAdaptiveFeed(GCodeOtherModal):
"""M52: Adaptive Feed Control"""
param_words = set('P')
word_key = Word('M', 52)
class GCodeFeedStop(GCodeOtherModal):
"""M53: Feed Stop Control"""
param_words = set('P')
word_key = Word('M', 53)
class GCodeSelectCoordinateSystem(GCodeOtherModal):
"""
G54 - select coordinate system 1
G55 - select coordinate system 2
G56 - select coordinate system 3
G57 - select coordinate system 4
G58 - select coordinate system 5
G59 - select coordinate system 6
G59.1 - select coordinate system 7
G59.2 - select coordinate system 8
G59.3 - select coordinate system 9
"""
@classmethod
def word_matches(cls, w):
return (w.letter == 'G') and (w.value in [54, 55, 56, 57, 58, 59, 59.1, 59.2, 59.3])
# ======================= Flow-control Codes =======================
# CODE PARAMETERS DESCRIPTION
# o sub Subroutines, sub/endsub call [unsupported]
# o while Looping, while/endwhile do/while [unsupported]
# o if Conditional, if/else/endif [unsupported]
# o repeat Repeat a loop of code [unsupported]
# [] Indirection [unsupported]
# o call Call named file [unsupported]
# M70 Save modal state [unsupported]
# M71 Invalidate stored state [unsupported]
# M72 Restore modal state [unsupported]
# M73 Save and Auto-restore modal state [unsupported]
# ======================= Input/Output Codes =======================
# CODE PARAMETERS DESCRIPTION
# M62 - M65 P Digital Output Control
# M66 P E L Q Wait on Input
# M67 T Analog Output, Synchronized
# M68 T Analog Output, Immediate
class GCodeIO(GCode):
pass
class GCodeDigitalOutput(GCodeIO):
"""Digital Output Control"""
param_words = set('P')
class GCodeDigitalOutputOnSyncd(GCodeDigitalOutput):
"""M62: turn on digital output synchronized with motion"""
word_key = Word('M', 62)
class GCodeDigitalOutputOffSyncd(GCodeDigitalOutput):
"""M63: turn off digital output synchronized with motion"""
word_key = Word('M', 63)
class GCodeDigitalOutputOn(GCodeDigitalOutput):
"""M64: turn on digital output immediately"""
word_key = Word('M', 64)
class GCodeDigitalOutputOff(GCodeDigitalOutput):
"""M65: turn off digital output immediately"""
word_key = Word('M', 65)
class GCodeWaitOnInput(GCodeIO):
"""M66: Wait on Input"""
param_words = set('PELQ')
word_key = Word('M', 66)
class GCodeAnalogOutput(GCodeIO):
"""Analog Output"""
param_words = set('T')
class GCodeAnalogOutputSyncd(GCodeAnalogOutput):
"""M67: Analog Output, Synchronized"""
word_key = Word('M', 67)
class GCodeAnalogOutputImmediate(GCodeAnalogOutput):
"""M68: Analog Output, Immediate"""
word_key = Word('M', 68)
# ======================= Non-modal Codes =======================
# CODE PARAMETERS DESCRIPTION
# M6 T Tool Change
# M61 Q Set Current Tool
# G10 L1 P Q R Set Tool Table
# G10 L10 P Set Tool Table
# G10 L11 P Set Tool Table
# G10 L2 P R Set Coordinate System
# G10 L20 P Set Coordinate System
# G28, G28.1 Go/Set Predefined Position
# G30, G30.1 Go/Set Predefined Position
# G53 Move in Machine Coordinates
# G92 Coordinate System Offset
# G92.1, G92.2 Reset G92 Offsets
# G92.3 Restore G92 Offsets
# M101 - M199 P Q User Defined Commands
class GCodeNonModal(GCode):
pass
class GCodeToolChange(GCodeNonModal):
"""M6: Tool Change"""
param_words = set('T')
word_key = Word('M', 6)
class GCodeToolSetCurrent(GCodeNonModal):
"""M61: Set Current Tool"""
param_words = set('Q')
word_key = Word('M', 61)
class GCodeSet(GCodeNonModal):
"""G10: Set stuff"""
param_words = set('LPQR')
word_key = Word('G', 10)
class GCodeGotoPredefinedPosition(GCodeNonModal):
"""G28,G30: Goto Predefined Position (rapid movement)"""
@classmethod
def word_matches(cls, w):
return (w.letter == 'G') and (w.value in [28, 30])
class GCodeSetPredefinedPosition(GCodeNonModal):
"""G28.1,G30.1: Set Predefined Position""" # redundancy in language there, but I'll let it slide
@classmethod
def word_matches(cls, w):
return (w.letter == 'G') and (w.value in [28.1, 30.1])
class GCodeMoveInMachineCoords(GCodeNonModal):
"""G53: Move in Machine Coordinates"""
word_key = Word('G', 53)
class GCodeCoordSystemOffset(GCodeNonModal):
"""G92: Coordinate System Offset"""
word_key = Word('G', 92)
class GCodeResetCoordSystemOffset(GCodeNonModal):
"""G92.1,G92.2: Reset Coordinate System Offset"""
@classmethod
def word_matches(cls, w):
return (w.letter == 'G') and (w.value in [92.1, 92.2])
class GCodeRestoreCoordSystemOffset(GCodeNonModal):
"""G92.3: Restore Coordinate System Offset"""
word_key = Word('G', 92.3)
class GCodeUserDefined(GCodeNonModal):
"""M101-M199: User Defined Commands"""
# To create user g-codes, inherit from this class
param_words = set('PQ')
#@classmethod
#def word_matches(cls, w):
# return (w.letter == 'M') and (101 <= w.value <= 199)
# ======================= Utilities =======================
def _subclasses_level(root_class, recursion_level=0):
"""
Hierarcical list of all classes inheriting from the given root class (recursive)
:param root_class: class used as trunk of hierarchy (included inoutput)
:param recursion_level: should always be 0 (unless recursively called)
:param
"""
yield (root_class, recursion_level)
for cls in root_class.__subclasses__():
for (sub, level) in _subclasses_level(cls, recursion_level+1):
yield (sub, level)
def _subclasses(root_class):
"""Flat list of all classes inheriting from the given root class (recursive)"""
for (cls, level) in _subclasses_level(root_class):
yield cls
def _gcode_class_infostr(base_class=GCode):
"""
List all ineheriting classes for the given gcode class
:param base_class: root of hierarcy
:return: str listing all gcode classes
"""
info_str = ""
for (cls, level) in _subclasses_level(base_class):
info_str += "{indent}- {name}: {description}\n".format(
indent=(level * " "),
name=cls.__name__,
description=cls.__doc__ or "",
)
return info_str
# ======================= GCode Word Mapping =======================
_gcode_maps_created = False # only set when the below values are populated
_gcode_word_map = {} # of the form: {Word('G', 0): GCodeRapidMove, ... }
_gcode_function_list = [] # of the form: [(lambda w: w.letter == 'F', GCodeFeedRate), ... ]
def _build_maps():
"""
Populate _gcode_word_map and _gcode_function_list
"""
# Ensure lists are clear
global _gcode_word_map
global _gcode_function_list
_gcode_word_map = {}
_gcode_function_list = []
for cls in _subclasses(GCode):
if cls.word_key is not None:
# Map Word instance to g-code class
if cls.word_key in _gcode_word_map:
raise RuntimeError("Multiple GCode classes map to '%s'" % str(cls.word_key))
_gcode_word_map[cls.word_key] = cls
elif cls.word_matches is not None:
# Add to list of functions
_gcode_function_list.append((cls.word_matches, cls))
global _gcode_maps_created
_gcode_maps_created = True
def words_to_gcodes(words):
"""
Group words into g-codes (includes both G & M codes)
:param words: list of Word instances
:return: list containing [<GCode>, <GCode>, ..., list(<words not used in a gcode>)]
"""
if _gcode_maps_created is False:
_build_maps()
# First determine which words are GCodes
# TODO: next up...
unassigned = []
#sdrow = list(reversed(words))
#for (i, word) in reversed(enumerate(words)):

View File

@ -46,8 +46,8 @@ WORD_MAP = {
},
# G-Codes
'G': {
'class': str,
'value_regex': re.compile(r'^\d+(\.\d+)?'),
'class': float,
'value_regex': re.compile(r'^\d+(\.\d)?'),
'description': "Address for preparatory commands",
},
# Tool Offsets
@ -80,8 +80,8 @@ WORD_MAP = {
},
# Miscellaneous Function
'M': {
'class': str,
'value_regex': re.compile(r'^\d+(\.\d+)?'),
'class': float,
'value_regex': re.compile(r'^\d+(\.\d)?'),
'description': "Miscellaneous function",
},
# Line Number
@ -262,6 +262,21 @@ class Word(object):
value=self.value_str,
)
def __repr__(self):
return "<{class_name}: {string}>".format(
class_name=self.__class__.__name__,
string=str(self),
)
def __eq__(self, other):
return (self.letter == other.letter) and (self.value == other.value)
def __ne__(self, other):
return not self.__eq__(other)
def __hash__(self):
return hash((self.letter, self.value))
# Value Properties
@property
def value_str(self):

View File

@ -16,5 +16,5 @@ class FileParseTest(unittest.TestCase):
file = parse(self.FILENAME)
self.assertEqual(len(file.lines), 26)
# FIXME: just verifying content visually
for line in file.lines:
print(' '.join(["%s%s" % (w.letter, w.value_str) for w in line.block.words]))
#for line in file.lines:
# print(' '.join(["%s%s" % (w.letter, w.value_str) for w in line.block.words]))

23
tests/test_gcodes.py Normal file
View File

@ -0,0 +1,23 @@
import sys
import os
import inspect
import unittest
# Units Under Test
_this_path = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
sys.path.insert(0, os.path.join(_this_path, '..'))
from pygcode import gcodes
class TestGCodeWordMapping(unittest.TestCase):
def test_word_map_integrity(self):
gcodes._build_maps()
for (word_maches, fn_class) in gcodes._gcode_function_list:
for (word, key_class) in gcodes._gcode_word_map.items():
# Verify that no mapped word will yield a True result
# from any of the 'word_maches' functions
self.assertFalse(
word_maches(word),
"conflict with %s and %s" % (fn_class, key_class)
)

View File

@ -7,7 +7,7 @@ import unittest
# Units Under Test
_this_path = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
sys.path.insert(0, os.path.join(_this_path, '..'))
import pygcode.words as gcode_words
from pygcode import words
#words.iter_words
@ -20,21 +20,21 @@ class WordTests(unittest.TestCase):
class WordIterTests(unittest.TestCase):
def test_iter1(self):
block_str = 'G01 Z-0.5 F100'
w = list(gcode_words.iter_words(block_str))
w = list(words.iter_words(block_str))
# word length
self.assertEqual(len(w), 3)
# word values
self.assertEqual([w[0].letter, w[0].value], ['G', '01'])
self.assertEqual([w[1].letter, w[1].value], ['Z', -0.5])
self.assertEqual([w[2].letter, w[2].value], ['F', 100])
self.assertEqual(w[0], words.Word('G', 1))
self.assertEqual(w[1], words.Word('Z', -0.5))
self.assertEqual(w[2], words.Word('F', 100))
def test_iter2(self):
block_str = 'G02 X10.75 Y47.44 I-0.11 J-1.26 F70'
w = list(gcode_words.iter_words(block_str))
w = list(words.iter_words(block_str))
# word length
self.assertEqual(len(w), 6)
# word values
self.assertEqual([w[0].letter, w[0].value], ['G', '02'])
self.assertEqual([w[0].letter, w[0].value], ['G', 2])
self.assertEqual([w[1].letter, w[1].value], ['X', 10.75])
self.assertEqual([w[2].letter, w[2].value], ['Y', 47.44])
self.assertEqual([w[3].letter, w[3].value], ['I', -0.11])