mirror of
https://git.mirrors.martin98.com/https://github.com/petaflot/pygcode
synced 2025-04-23 22:30:19 +08:00
commit
7f622ac445
3
.gitignore
vendored
3
.gitignore
vendored
@ -4,3 +4,6 @@
|
|||||||
|
|
||||||
# editor backups
|
# editor backups
|
||||||
*.swp
|
*.swp
|
||||||
|
|
||||||
|
# build
|
||||||
|
build/*
|
||||||
|
265
README.rst
265
README.rst
@ -1,86 +1,233 @@
|
|||||||
|
=======
|
||||||
pygcode
|
pygcode
|
||||||
=======
|
=======
|
||||||
|
|
||||||
GCODE Parser for Python
|
GCODE Parser for Python
|
||||||
|
|
||||||
Currently in development, this is planned to be a pythonic interpreter
|
Currently in development, ``pygcode`` is a low-level GCode interpreter
|
||||||
and encoder for g-code. I'll be learning along the way, but the plan is
|
for python.
|
||||||
to follow the lead of `GRBL <https://github.com/gnea/grbl>`__.
|
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
------------
|
============
|
||||||
|
|
||||||
|
Using `PyPi <https://pypi.python.org/pypi/pydemia>`__:
|
||||||
|
|
||||||
``pip install pygcode``
|
``pip install pygcode``
|
||||||
|
|
||||||
FIXME: well, that's the plan... give me some time to get it going
|
|
||||||
though.
|
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
-----
|
=====
|
||||||
|
|
||||||
Just brainstorming here...
|
Just brainstorming here...
|
||||||
|
|
||||||
|
Writing GCode
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Writing gcode from python object instances to text
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
import pygcode
|
>>> from pygcode import *
|
||||||
import math
|
>>> gcodes = [
|
||||||
import euclid
|
... GCodeRapidMove(Z=5),
|
||||||
|
... GCodeStartSpindleCW(),
|
||||||
|
... GCodeRapidMove(X=10, Y=20),
|
||||||
|
... GCodeFeedRate(200),
|
||||||
|
... GCodeLinearMove(Z=-1.5),
|
||||||
|
... GCodeRapidMove(Z=5),
|
||||||
|
... GCodeStopSpindle(),
|
||||||
|
... ]
|
||||||
|
>>> print('\n'.join(str(g) for g in gcodes))
|
||||||
|
|
||||||
gfile_in = pygcode.parse('part1.gcode') #
|
G00 Z5
|
||||||
gfile_out = pygcode.GCodeFile('part2.gcode')
|
M03
|
||||||
|
G00 X10 Y20
|
||||||
total_travel = 0
|
F200
|
||||||
total_time = 0
|
G01 Z-1.5
|
||||||
|
G00 Z5
|
||||||
machine = pygcode.Machine()
|
M05
|
||||||
|
|
||||||
for line in gfile_in.iterlines():
|
|
||||||
|
|
||||||
block = line.block
|
|
||||||
if block is None:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# validation
|
|
||||||
if isinstance(block, pygcode.GCodeArc):
|
|
||||||
error = block.r2 - block.r1
|
|
||||||
if error > 0.0005:
|
|
||||||
raise pygcode.GCodeValidationError("arc points are not on the same circle")
|
|
||||||
#block.set_precision(0.0005, method=pygcode.GCodeArc.EFFECT_ENDPOINT)
|
|
||||||
block.set_precision(0.0005, method=pygcode.GCodeArc.EFFECT_RADIUS)
|
|
||||||
|
|
||||||
# random metrics
|
|
||||||
travel_vector = block.position - machine.state.position # euclid.Vector3 instance
|
|
||||||
distance = travel_vector.magnitude()
|
|
||||||
travel = block.travel_distance(position=machine.state.position) # eg: distance != travel for G02 & G03
|
|
||||||
|
|
||||||
total_travel += travel
|
|
||||||
#total_time += block.time(feed_rate=machine.state.feed_rate) # doesn't consider the feedrate being changed in this block
|
|
||||||
total_time += block.time(state=machine.state)
|
|
||||||
|
|
||||||
# rotate : entire file 90deg CCW
|
|
||||||
block.rotate(euclid.Quaternion.new_rotate_axis(
|
|
||||||
math.pi / 2, euclid.Vector3(0, 0, 1)
|
|
||||||
))
|
|
||||||
# translate : entire file x += 1, y += 2 mm (after rotation)
|
|
||||||
block.translate(euclid.Vector3(1, 2, 0), unit=pygcode.UNIT_MM)
|
|
||||||
|
|
||||||
|
|
||||||
|
To plot along a lines of vectors, you could write...
|
||||||
|
|
||||||
# TODO: then do something like write it to another file
|
::
|
||||||
gfile_out.write(block)
|
|
||||||
|
>>> from pygcode import *
|
||||||
|
>>> from euclid import Vector3
|
||||||
|
|
||||||
|
>>> vectors = [
|
||||||
|
... Vector3(0, 0, 0),
|
||||||
|
... Vector3(10, 0, 0),
|
||||||
|
... Vector3(10, 20, 0),
|
||||||
|
... Vector3(10, 20, 3),
|
||||||
|
... Vector3(0, 20, 3),
|
||||||
|
... Vector3(0, 0, 3),
|
||||||
|
... Vector3(0, 0, 0)
|
||||||
|
... ]
|
||||||
|
|
||||||
|
>>> to_coords = lambda v: {'X': v.x, 'Y': v.y, 'Z': v.z}
|
||||||
|
>>> for v in vectors:
|
||||||
|
... print("%s" % GCodeLinearMove(**to_coords(v)))
|
||||||
|
|
||||||
|
G01 X0 Y0 Z0
|
||||||
|
G01 X10 Y0 Z0
|
||||||
|
G01 X10 Y20 Z0
|
||||||
|
G01 X10 Y20 Z3
|
||||||
|
G01 X0 Y20 Z3
|
||||||
|
G01 X0 Y0 Z3
|
||||||
|
G01 X0 Y0 Z0
|
||||||
|
|
||||||
|
|
||||||
|
Reading / Interpreting GCode
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
To read gcode from a file, utilise the ``Line`` class.
|
||||||
|
Each ``Line`` instance contains a ``Block`` and an optional ``Comment``.
|
||||||
|
The ``Block`` contains a list of gcodes you're after.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
from pygcode import Line
|
||||||
|
|
||||||
|
with open('part.gcode', 'r') as fh:
|
||||||
|
for line_text in fh.readlines():
|
||||||
|
line = Line(line_text)
|
||||||
|
|
||||||
|
print(line) # will print the line (with cosmetic changes)
|
||||||
|
line.block.gcodes # is your list of gcodes
|
||||||
|
line.block.modal_params # are all parameters not assigned to a gcode, assumed to be motion modal parameters
|
||||||
|
if line.comment:
|
||||||
|
line.comment.text # your comment text
|
||||||
|
|
||||||
|
To elaborate, here are some line examples
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
>>> from pygcode import Line
|
||||||
|
|
||||||
|
>>> line = Line('G01 x1 y2 f100 s1000 ; blah')
|
||||||
|
>>> print(line)
|
||||||
|
G01 X1 Y2 F100 S1000 ; blah
|
||||||
|
>>> print(line.block)
|
||||||
|
G01 X1 Y2 F100 S1000
|
||||||
|
>>> print(line.comment)
|
||||||
|
; blah
|
||||||
|
|
||||||
|
>>> line = Line('G0 x1 y2 (foo) f100 (bar) s1000')
|
||||||
|
>>> print(line)
|
||||||
|
G00 X1 Y2 F100 S1000 (foo. bar)
|
||||||
|
>>> print(line.comment)
|
||||||
|
(foo. bar)
|
||||||
|
|
||||||
|
|
||||||
|
Interpreting what a line of gcode does depends on the machine it's running on,
|
||||||
|
and also that machine's state (or 'mode')
|
||||||
|
|
||||||
|
The simple line of a rapid move to ``x=10, y=10`` may be ``G00 X10 Y10``.
|
||||||
|
However, if the machine in question is in "Incremental Motion" mode ``G91`` then
|
||||||
|
the machine will only end up at ``x=10, y=10`` if it started at ``x=0, y=0``
|
||||||
|
|
||||||
|
So, GCode interpretation is done via a virtual machine:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
>>> from pygcode import Machine, GCodeRapidMove
|
||||||
|
|
||||||
|
>>> m = Machine()
|
||||||
|
>>> m.pos
|
||||||
|
<Position: X0 Y0 Z0>
|
||||||
|
>>> g = GCodeRapidMove(X=10, Y=20)
|
||||||
|
>>> m.process_gcodes(g)
|
||||||
|
>>> m.pos
|
||||||
|
<Position: X10 Y20 Z0>
|
||||||
|
>>> m.process_gcodes(g)
|
||||||
|
>>> m.pos
|
||||||
|
<Position: X10 Y20 Z0> # same position; machine in absolute mode
|
||||||
|
>>> m.mode.distance
|
||||||
|
<GCodeAbsoluteDistanceMode: G90> # see
|
||||||
|
|
||||||
|
>>> m.process_gcodes(GCodeIncrementalDistanceMode())
|
||||||
|
>>> m.process_gcodes(g) # same gcode as above
|
||||||
|
>>> m.pos
|
||||||
|
<Position: X20 Y40 Z0>
|
||||||
|
|
||||||
|
all valid ``m.mode`` attributes can be found with ``from pygcode.gcodes import MODAL_GROUP_MAP; MODAL_GROUP_MAP.keys()``
|
||||||
|
|
||||||
|
Also note that the order codes are interpreted is important.
|
||||||
|
For example, the following code is WRONG
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
from pygcode import Machine, Line
|
||||||
|
m = Machine()
|
||||||
|
line = Line('G0 x10 y10 G91')
|
||||||
|
m.process_gcodes(*line.block.gcodes) # WRONG!
|
||||||
|
|
||||||
|
This will process the movement to ``x=10, y=10``, and **then** it will change the
|
||||||
|
distance mode to *Incremental*... there are 2 ways to do this correctly.
|
||||||
|
|
||||||
|
- ``m.process_gcodes(*sorted(line.block.gcodes))``, or simply
|
||||||
|
- ``m.process_block(line.block)``
|
||||||
|
|
||||||
|
sorting a list of gcodes will sort them in execution order (as specified by
|
||||||
|
`LinuxCNC's order of execution <http://linuxcnc.org/docs/html/gcode/overview.html#_g_code_order_of_execution>`__).
|
||||||
|
``process_block`` does this automatically.
|
||||||
|
|
||||||
|
If you need to process & change one type of gcode (usually a movement),
|
||||||
|
you must split a list of gcodes into those executed before, and after the one
|
||||||
|
in question.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
from pygcode import GCodeRapidMove, GCodeLinearMove
|
||||||
|
from pygcode import Machine, Line, split_gcodes
|
||||||
|
m = Machine()
|
||||||
|
line = Line('M0 G0 x10 y10 G91')
|
||||||
|
(befores, (g,), afters) = split_gcodes(line.block.gcodes, (GCodeRapidMove, GCodeLinearMove))
|
||||||
|
m.process_gcodes(*sorted(befores))
|
||||||
|
if g.X is not None:
|
||||||
|
g.X += 100 # shift linear movements (rapid or otherwise)
|
||||||
|
m.process_gcodes(g)
|
||||||
|
m.process_gcodes(*sorted(afters))
|
||||||
|
|
||||||
|
|
||||||
|
For a more practical use of machines & interpreting gcode, have a look at
|
||||||
|
`pygcode-normalize.py <https://github.com/fragmuffin/pygcode/blob/master/scripts/pygcode-normalize.py>`__
|
||||||
|
|
||||||
|
At the time of writing this, that script converts arcs to linear codes, and
|
||||||
|
expands drilling cycles to basic movements (so my
|
||||||
|
`GRBL <https://github.com/gnea/grbl>`__ machine can understand them)
|
||||||
|
|
||||||
|
|
||||||
|
Development
|
||||||
|
===========
|
||||||
|
|
||||||
|
This library came from my own needs to interpret and convert erroneous
|
||||||
|
arcs to linear segments, and to expand canned drilling cycles, but also
|
||||||
|
as a means to *learn* GCode.
|
||||||
|
|
||||||
|
As such there is no direct plan for further development, however I'm
|
||||||
|
interested in what you'd like to use it for, and cater for that.
|
||||||
|
|
||||||
|
Generally, in terms of what to support, I'm following the lead of:
|
||||||
|
|
||||||
|
- `GRBL <https://github.com/gnea/grbl>`__ and
|
||||||
|
- `LinuxCNC <http://linuxcnc.org/>`__
|
||||||
|
|
||||||
|
More support will come with increased interest.
|
||||||
|
So that is... if you don't like what it does, or how it's documented, make some
|
||||||
|
noise in the `issue section <https://github.com/fragmuffin/pygcode/issues>`__.
|
||||||
|
if you get in early, you may get some free labour out of me ;)
|
||||||
|
|
||||||
gfile_in.close()
|
|
||||||
gfile_out.close()
|
|
||||||
|
|
||||||
Supported G-Codes
|
Supported G-Codes
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
GCode support is planned to follow that of
|
All GCodes supported by `LinuxCNC <http://linuxcnc.org>`__ can be written, and
|
||||||
`GRBL <https://github.com/gnea/grbl>`__ which follows
|
parsed by ``pygcode``.
|
||||||
`LinuxCNC <http://linuxcnc.org>`__ (list of gcodes documented
|
|
||||||
`here <http://linuxcnc.org/docs/html/gcode.html>`__).
|
|
||||||
|
|
||||||
But anything pre v1.0 will be a sub-set, focusing on the issues I'm
|
Few GCodes are accurately interpreted by a virtual CNC ``Machine`` instance.
|
||||||
having... I'm selfish that way.
|
Supported movements are currently;
|
||||||
|
|
||||||
TBD: list of gcodes (also as a TODO list)
|
- linear movements
|
||||||
|
- arc movements
|
||||||
|
- canned drilling cycles
|
||||||
|
102
deployment.md
Normal file
102
deployment.md
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
# Notes on deployment
|
||||||
|
|
||||||
|
How I deployed this package (mainly just notes for myself)
|
||||||
|
|
||||||
|
Method based on the articles:
|
||||||
|
|
||||||
|
* http://peterdowns.com/posts/first-time-with-pypi.html and
|
||||||
|
* https://hynek.me/articles/sharing-your-labor-of-love-pypi-quick-and-dirty/
|
||||||
|
|
||||||
|
|
||||||
|
## Pre-requisites
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install -U "pip>=1.4" "setuptools>=0.9" "wheel>=0.21" twine
|
||||||
|
```
|
||||||
|
|
||||||
|
## PyPi rc
|
||||||
|
|
||||||
|
`cat ~/.pypirc`
|
||||||
|
|
||||||
|
```
|
||||||
|
[distutils]
|
||||||
|
index-servers =
|
||||||
|
prod
|
||||||
|
test
|
||||||
|
|
||||||
|
[prod]
|
||||||
|
repository = https://upload.pypi.org/legacy/
|
||||||
|
username=FraggaMuffin
|
||||||
|
password=secret
|
||||||
|
|
||||||
|
[test]
|
||||||
|
repository=https://test.pypi.org/legacy/
|
||||||
|
username=FraggaMuffin
|
||||||
|
password=secret
|
||||||
|
```
|
||||||
|
|
||||||
|
`chmod 600 ~/.pypirc`
|
||||||
|
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
```
|
||||||
|
rm -rf build/
|
||||||
|
python setup.py sdist bdist_wheel
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Build (sdist)
|
||||||
|
|
||||||
|
**Python 2.x**
|
||||||
|
|
||||||
|
```
|
||||||
|
rmvirtualenv 27-test
|
||||||
|
mkvirtualenv 27-test
|
||||||
|
|
||||||
|
$WORKON_HOME/27-test/bin/pip install dist/pygcode-0.1.0.tar.gz
|
||||||
|
|
||||||
|
$WORKON_HOME/27-test/bin/python
|
||||||
|
|
||||||
|
>>> import pygcode
|
||||||
|
>>> pygcode.Line('g1 x2 y3 m3 s1000 f100').block.gcodes # or whatever
|
||||||
|
```
|
||||||
|
|
||||||
|
**Python 3.x**
|
||||||
|
|
||||||
|
```
|
||||||
|
rmvirtualenv 35-test
|
||||||
|
mkvirtualenv -p $(which python3) 35-test
|
||||||
|
|
||||||
|
$WORKON_HOME/35-test/bin/pip install dist/pygcode-0.1.0.tar.gz
|
||||||
|
|
||||||
|
$WORKON_HOME/35-test/bin/python
|
||||||
|
|
||||||
|
>>> import pygcode
|
||||||
|
>>> pygcode.Line('g1 x2 y3 m3 s1000 f100').block.gcodes # or whatever
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Build (wheel)
|
||||||
|
|
||||||
|
similar to above, but the `pip` call references `pygcode-0.1.0-py2.py3-none-any.whl` instead
|
||||||
|
|
||||||
|
make sure to `rmvirtualenv` to ensure `pygcode` is uninstalled from virtual environment
|
||||||
|
|
||||||
|
|
||||||
|
## Upload to PyPi Test server
|
||||||
|
|
||||||
|
`twine upload -r test dist/pygcode-0.1.0*`
|
||||||
|
|
||||||
|
Then another round of testing, where `pip` call is:
|
||||||
|
|
||||||
|
`$WORKON_HOME/<envname>/bin/pip install -i https://testpypi.python.org/pypi pygcode`
|
||||||
|
|
||||||
|
|
||||||
|
## Upload to PyPy server
|
||||||
|
|
||||||
|
all good!? sweet :+1: time to upload to 'production'
|
||||||
|
|
||||||
|
`twine upload -r prod dist/pygcode-0.1.0*`
|
||||||
|
|
||||||
|
and final tests with simply:
|
||||||
|
|
||||||
|
`$WORKON_HOME/<envname>/bin/pip install pygcode`
|
BIN
dist/pygcode-0.1.0-py2.py3-none-any.whl
vendored
Normal file
BIN
dist/pygcode-0.1.0-py2.py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
dist/pygcode-0.1.0.tar.gz
vendored
Normal file
BIN
dist/pygcode-0.1.0.tar.gz
vendored
Normal file
Binary file not shown.
@ -2,5 +2,5 @@
|
|||||||
universal = 1
|
universal = 1
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
description-file = README.md
|
description-file = README.rst
|
||||||
license_file = LICENSE
|
license_file = LICENSE
|
||||||
|
45
setup.py
45
setup.py
@ -5,30 +5,30 @@ import re
|
|||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
|
||||||
|
# Setup template thanks to: Hynek Schlawack
|
||||||
|
# https://hynek.me/articles/sharing-your-labor-of-love-pypi-quick-and-dirty/
|
||||||
###################################################################
|
###################################################################
|
||||||
|
|
||||||
NAME = "attrs"
|
NAME = "pygcode"
|
||||||
PACKAGES = find_packages(where="src")
|
PACKAGES = find_packages(where="src")
|
||||||
META_PATH = os.path.join("src", "attr", "__init__.py")
|
META_PATH = os.path.join("src", NAME, "__init__.py")
|
||||||
KEYWORDS = ["class", "attribute", "boilerplate"]
|
KEYWORDS = ['gcode', 'cnc', 'parser', 'interpreter']
|
||||||
CLASSIFIERS = [
|
CLASSIFIERS = [
|
||||||
"Development Status :: 5 - Production/Stable",
|
"Development Status :: 2 - Pre-Alpha", # see src/pygcode/__init__.py
|
||||||
"Intended Audience :: Developers",
|
"Intended Audience :: Developers",
|
||||||
|
"Intended Audience :: Manufacturing",
|
||||||
|
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||||
"Natural Language :: English",
|
"Natural Language :: English",
|
||||||
"License :: OSI Approved :: MIT License",
|
|
||||||
"Operating System :: OS Independent",
|
"Operating System :: OS Independent",
|
||||||
"Programming Language :: Python",
|
"Programming Language :: Python",
|
||||||
"Programming Language :: Python :: 2",
|
"Programming Language :: Python :: 2",
|
||||||
"Programming Language :: Python :: 2.7",
|
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
"Programming Language :: Python :: 3.3",
|
"Topic :: Scientific/Engineering",
|
||||||
"Programming Language :: Python :: 3.4",
|
]
|
||||||
"Programming Language :: Python :: 3.5",
|
INSTALL_REQUIRES = [
|
||||||
"Programming Language :: Python :: Implementation :: CPython",
|
'euclid3', # 2D and 3D vector, matrix, quaternion and geometry module.
|
||||||
"Programming Language :: Python :: Implementation :: PyPy",
|
'six', # Python 2 and 3 compatibility utilities
|
||||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
||||||
]
|
]
|
||||||
INSTALL_REQUIRES = []
|
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ def find_meta(meta):
|
|||||||
Extract __*meta*__ from META_FILE.
|
Extract __*meta*__ from META_FILE.
|
||||||
"""
|
"""
|
||||||
meta_match = re.search(
|
meta_match = re.search(
|
||||||
r"^(?P<name>__{meta}__)\s*=\s*['\"](?P<value>[^'\"]*)['\"]".format(meta=meta),
|
r"^(?P<name>__{meta}__)\s*=\s*['\"](?P<value>[^'\"]*)['\"](\s*#.*)?$".format(meta=meta),
|
||||||
META_FILE, re.M
|
META_FILE, re.M
|
||||||
)
|
)
|
||||||
if meta_match:
|
if meta_match:
|
||||||
@ -65,7 +65,7 @@ if __name__ == "__main__":
|
|||||||
name=NAME,
|
name=NAME,
|
||||||
description=find_meta("description"),
|
description=find_meta("description"),
|
||||||
license=find_meta("license"),
|
license=find_meta("license"),
|
||||||
url=find_meta("uri"),
|
url=find_meta("url"),
|
||||||
version=find_meta("version"),
|
version=find_meta("version"),
|
||||||
author=find_meta("author"),
|
author=find_meta("author"),
|
||||||
author_email=find_meta("email"),
|
author_email=find_meta("email"),
|
||||||
@ -79,18 +79,3 @@ if __name__ == "__main__":
|
|||||||
classifiers=CLASSIFIERS,
|
classifiers=CLASSIFIERS,
|
||||||
install_requires=INSTALL_REQUIRES,
|
install_requires=INSTALL_REQUIRES,
|
||||||
)
|
)
|
||||||
|
|
||||||
#VERSION = '0.1.dev' # *.dev = release candidate
|
|
||||||
#
|
|
||||||
#setup(
|
|
||||||
# name = 'pygcode',
|
|
||||||
# packages = ['pygcode'],
|
|
||||||
# version = VERSION,
|
|
||||||
# description = 'basic g-code parser, interpreter, and writer library',
|
|
||||||
# author = 'Peter Boin',
|
|
||||||
# author_email = 'peter.boin@gmail.com',
|
|
||||||
# url = 'https://github.com/fragmuffin/pygcode',
|
|
||||||
# download_url = 'https://github.com/fragmuffin/pygcode/archive/%s.tar.gz' % VERSION,
|
|
||||||
# keywords = ['gcode', 'cnc', 'parser', 'interpreter'],
|
|
||||||
# classifiers = [],
|
|
||||||
#)
|
|
||||||
|
254
src/pygcode.egg-info/PKG-INFO
Normal file
254
src/pygcode.egg-info/PKG-INFO
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
Metadata-Version: 1.1
|
||||||
|
Name: pygcode
|
||||||
|
Version: 0.1.0
|
||||||
|
Summary: Basic g-code parser, interpreter, and encoder library.
|
||||||
|
Home-page: https://github.com/fragmuffin/pygcode
|
||||||
|
Author: Peter Boin
|
||||||
|
Author-email: peter.boin@gmail.com
|
||||||
|
License: GPLv3
|
||||||
|
Description: =======
|
||||||
|
pygcode
|
||||||
|
=======
|
||||||
|
|
||||||
|
GCODE Parser for Python
|
||||||
|
|
||||||
|
Currently in development, ``pygcode`` is a low-level GCode interpreter
|
||||||
|
for python.
|
||||||
|
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
Using `PyPi <https://pypi.python.org/pypi/pydemia>`__:
|
||||||
|
|
||||||
|
``pip install pygcode``
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
Just brainstorming here...
|
||||||
|
|
||||||
|
Writing GCode
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Writing gcode from python object instances to text
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
>>> from pygcode import *
|
||||||
|
>>> gcodes = [
|
||||||
|
... GCodeRapidMove(Z=5),
|
||||||
|
... GCodeStartSpindleCW(),
|
||||||
|
... GCodeRapidMove(X=10, Y=20),
|
||||||
|
... GCodeFeedRate(200),
|
||||||
|
... GCodeLinearMove(Z=-1.5),
|
||||||
|
... GCodeRapidMove(Z=5),
|
||||||
|
... GCodeStopSpindle(),
|
||||||
|
... ]
|
||||||
|
>>> print('\n'.join(str(g) for g in gcodes))
|
||||||
|
|
||||||
|
G00 Z5
|
||||||
|
M03
|
||||||
|
G00 X10 Y20
|
||||||
|
F200
|
||||||
|
G01 Z-1.5
|
||||||
|
G00 Z5
|
||||||
|
M05
|
||||||
|
|
||||||
|
|
||||||
|
To plot along a lines of vectors, you could write...
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
>>> from pygcode import *
|
||||||
|
>>> from euclid import Vector3
|
||||||
|
|
||||||
|
>>> vectors = [
|
||||||
|
... Vector3(0, 0, 0),
|
||||||
|
... Vector3(10, 0, 0),
|
||||||
|
... Vector3(10, 20, 0),
|
||||||
|
... Vector3(10, 20, 3),
|
||||||
|
... Vector3(0, 20, 3),
|
||||||
|
... Vector3(0, 0, 3),
|
||||||
|
... Vector3(0, 0, 0)
|
||||||
|
... ]
|
||||||
|
|
||||||
|
>>> to_coords = lambda v: {'X': v.x, 'Y': v.y, 'Z': v.z}
|
||||||
|
>>> for v in vectors:
|
||||||
|
... print("%s" % GCodeLinearMove(**to_coords(v)))
|
||||||
|
|
||||||
|
G01 X0 Y0 Z0
|
||||||
|
G01 X10 Y0 Z0
|
||||||
|
G01 X10 Y20 Z0
|
||||||
|
G01 X10 Y20 Z3
|
||||||
|
G01 X0 Y20 Z3
|
||||||
|
G01 X0 Y0 Z3
|
||||||
|
G01 X0 Y0 Z0
|
||||||
|
|
||||||
|
|
||||||
|
Reading / Interpreting GCode
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
To read gcode from a file, utilise the ``Line`` class.
|
||||||
|
Each ``Line`` instance contains a ``Block`` and an optional ``Comment``.
|
||||||
|
The ``Block`` contains a list of gcodes you're after.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
from pygcode import Line
|
||||||
|
|
||||||
|
with open('part.gcode', 'r') as fh:
|
||||||
|
for line_text in fh.readlines():
|
||||||
|
line = Line(line_text)
|
||||||
|
|
||||||
|
print(line) # will print the line (with cosmetic changes)
|
||||||
|
line.block.gcodes # is your list of gcodes
|
||||||
|
line.block.modal_params # are all parameters not assigned to a gcode, assumed to be motion modal parameters
|
||||||
|
if line.comment:
|
||||||
|
line.comment.text # your comment text
|
||||||
|
|
||||||
|
To elaborate, here are some line examples
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
>>> from pygcode import Line
|
||||||
|
|
||||||
|
>>> line = Line('G01 x1 y2 f100 s1000 ; blah')
|
||||||
|
>>> print(line)
|
||||||
|
G01 X1 Y2 F100 S1000 ; blah
|
||||||
|
>>> print(line.block)
|
||||||
|
G01 X1 Y2 F100 S1000
|
||||||
|
>>> print(line.comment)
|
||||||
|
; blah
|
||||||
|
|
||||||
|
>>> line = Line('G0 x1 y2 (foo) f100 (bar) s1000')
|
||||||
|
>>> print(line)
|
||||||
|
G00 X1 Y2 F100 S1000 (foo. bar)
|
||||||
|
>>> print(line.comment)
|
||||||
|
(foo. bar)
|
||||||
|
|
||||||
|
|
||||||
|
Interpreting what a line of gcode does depends on the machine it's running on,
|
||||||
|
and also that machine's state (or 'mode')
|
||||||
|
|
||||||
|
The simple line of a rapid move to ``x=10, y=10`` may be ``G00 X10 Y10``.
|
||||||
|
However, if the machine in question is in "Incremental Motion" mode ``G91`` then
|
||||||
|
the machine will only end up at ``x=10, y=10`` if it started at ``x=0, y=0``
|
||||||
|
|
||||||
|
So, GCode interpretation is done via a virtual machine:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
>>> from pygcode import Machine, GCodeRapidMove
|
||||||
|
|
||||||
|
>>> m = Machine()
|
||||||
|
>>> m.pos
|
||||||
|
<Position: X0 Y0 Z0>
|
||||||
|
>>> g = GCodeRapidMove(X=10, Y=20)
|
||||||
|
>>> m.process_gcodes(g)
|
||||||
|
>>> m.pos
|
||||||
|
<Position: X10 Y20 Z0>
|
||||||
|
>>> m.process_gcodes(g)
|
||||||
|
>>> m.pos
|
||||||
|
<Position: X10 Y20 Z0> # same position; machine in absolute mode
|
||||||
|
>>> m.mode.distance
|
||||||
|
<GCodeAbsoluteDistanceMode: G90> # see
|
||||||
|
|
||||||
|
>>> m.process_gcodes(GCodeIncrementalDistanceMode())
|
||||||
|
>>> m.process_gcodes(g) # same gcode as above
|
||||||
|
>>> m.pos
|
||||||
|
<Position: X20 Y40 Z0>
|
||||||
|
|
||||||
|
all valid ``m.mode`` attributes can be found with ``from pygcode.gcodes import MODAL_GROUP_MAP; MODAL_GROUP_MAP.keys()``
|
||||||
|
|
||||||
|
Also note that the order codes are interpreted is important.
|
||||||
|
For example, the following code is WRONG
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
from pygcode import Machine, Line
|
||||||
|
m = Machine()
|
||||||
|
line = Line('G0 x10 y10 G91')
|
||||||
|
m.process_gcodes(*line.block.gcodes) # WRONG!
|
||||||
|
|
||||||
|
This will process the movement to ``x=10, y=10``, and **then** it will change the
|
||||||
|
distance mode to *Incremental*... there are 2 ways to do this correctly.
|
||||||
|
|
||||||
|
- ``m.process_gcodes(*sorted(line.block.gcodes))``, or simply
|
||||||
|
- ``m.process_block(line.block)``
|
||||||
|
|
||||||
|
sorting a list of gcodes will sort them in execution order (as specified by
|
||||||
|
`LinuxCNC's order of execution <http://linuxcnc.org/docs/html/gcode/overview.html#_g_code_order_of_execution>`__).
|
||||||
|
``process_block`` does this automatically.
|
||||||
|
|
||||||
|
If you need to process & change one type of gcode (usually a movement),
|
||||||
|
you must split a list of gcodes into those executed before, and after the one
|
||||||
|
in question.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
from pygcode import GCodeRapidMove, GCodeLinearMove
|
||||||
|
from pygcode import Machine, Line, split_gcodes
|
||||||
|
m = Machine()
|
||||||
|
line = Line('M0 G0 x10 y10 G91')
|
||||||
|
(befores, (g,), afters) = split_gcodes(line.block.gcodes, (GCodeRapidMove, GCodeLinearMove))
|
||||||
|
m.process_gcodes(*sorted(befores))
|
||||||
|
if g.X is not None:
|
||||||
|
g.X += 100 # shift linear movements (rapid or otherwise)
|
||||||
|
m.process_gcodes(g)
|
||||||
|
m.process_gcodes(*sorted(afters))
|
||||||
|
|
||||||
|
|
||||||
|
For a more practical use of machines & interpreting gcode, have a look at
|
||||||
|
`pygcode-normalize.py <https://github.com/fragmuffin/pygcode/blob/master/scripts/pygcode-normalize.py>`__
|
||||||
|
|
||||||
|
At the time of writing this, that script converts arcs to linear codes, and
|
||||||
|
expands drilling cycles to basic movements (so my
|
||||||
|
`GRBL <https://github.com/gnea/grbl>`__ machine can understand them)
|
||||||
|
|
||||||
|
|
||||||
|
Development
|
||||||
|
===========
|
||||||
|
|
||||||
|
This library came from my own needs to interpret and convert erroneous
|
||||||
|
arcs to linear segments, and to expand canned drilling cycles, but also
|
||||||
|
as a means to *learn* GCode.
|
||||||
|
|
||||||
|
As such there is no direct plan for further development, however I'm
|
||||||
|
interested in what you'd like to use it for, and cater for that.
|
||||||
|
|
||||||
|
Generally, in terms of what to support, I'm following the lead of:
|
||||||
|
|
||||||
|
- `GRBL <https://github.com/gnea/grbl>`__ and
|
||||||
|
- `LinuxCNC <http://linuxcnc.org/>`__
|
||||||
|
|
||||||
|
More support will come with increased interest.
|
||||||
|
So that is... if you don't like what it does, or how it's documented, make some
|
||||||
|
noise in the `issue section <https://github.com/fragmuffin/pygcode/issues>`__.
|
||||||
|
if you get in early, you may get some free labour out of me ;)
|
||||||
|
|
||||||
|
|
||||||
|
Supported G-Codes
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
All GCodes supported by `LinuxCNC <http://linuxcnc.org>`__ can be written, and
|
||||||
|
parsed by ``pygcode``.
|
||||||
|
|
||||||
|
Few GCodes are accurately interpreted by a virtual CNC ``Machine`` instance.
|
||||||
|
Supported movements are currently;
|
||||||
|
|
||||||
|
- linear movements
|
||||||
|
- arc movements
|
||||||
|
- canned drilling cycles
|
||||||
|
|
||||||
|
Keywords: gcode,cnc,parser,interpreter
|
||||||
|
Platform: UNKNOWN
|
||||||
|
Classifier: Development Status :: 2 - Pre-Alpha
|
||||||
|
Classifier: Intended Audience :: Developers
|
||||||
|
Classifier: Intended Audience :: Manufacturing
|
||||||
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
||||||
|
Classifier: Natural Language :: English
|
||||||
|
Classifier: Operating System :: OS Independent
|
||||||
|
Classifier: Programming Language :: Python
|
||||||
|
Classifier: Programming Language :: Python :: 2
|
||||||
|
Classifier: Programming Language :: Python :: 3
|
||||||
|
Classifier: Topic :: Scientific/Engineering
|
19
src/pygcode.egg-info/SOURCES.txt
Normal file
19
src/pygcode.egg-info/SOURCES.txt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
README.rst
|
||||||
|
setup.cfg
|
||||||
|
setup.py
|
||||||
|
src/pygcode/__init__.py
|
||||||
|
src/pygcode/block.py
|
||||||
|
src/pygcode/comment.py
|
||||||
|
src/pygcode/exceptions.py
|
||||||
|
src/pygcode/gcodes.py
|
||||||
|
src/pygcode/line.py
|
||||||
|
src/pygcode/machine.py
|
||||||
|
src/pygcode/transform.py
|
||||||
|
src/pygcode/utils.py
|
||||||
|
src/pygcode/words.py
|
||||||
|
src/pygcode.egg-info/PKG-INFO
|
||||||
|
src/pygcode.egg-info/SOURCES.txt
|
||||||
|
src/pygcode.egg-info/dependency_links.txt
|
||||||
|
src/pygcode.egg-info/not-zip-safe
|
||||||
|
src/pygcode.egg-info/requires.txt
|
||||||
|
src/pygcode.egg-info/top_level.txt
|
1
src/pygcode.egg-info/dependency_links.txt
Normal file
1
src/pygcode.egg-info/dependency_links.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
1
src/pygcode.egg-info/not-zip-safe
Normal file
1
src/pygcode.egg-info/not-zip-safe
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
2
src/pygcode.egg-info/requires.txt
Normal file
2
src/pygcode.egg-info/requires.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
euclid3
|
||||||
|
six
|
1
src/pygcode.egg-info/top_level.txt
Normal file
1
src/pygcode.egg-info/top_level.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
pygcode
|
@ -1,3 +1,27 @@
|
|||||||
|
# =========================== Package Information ===========================
|
||||||
|
# Version Planning:
|
||||||
|
# 0.1.x - Development Status :: 2 - Pre-Alpha
|
||||||
|
# 0.2.x - Development Status :: 3 - Alpha
|
||||||
|
# 0.3.x - Development Status :: 4 - Beta
|
||||||
|
# 1.x - Development Status :: 5 - Production/Stable
|
||||||
|
# <any above>.y - developments on that version (pre-release)
|
||||||
|
# <any above>*.dev* - development release (intended purely to test deployment)
|
||||||
|
__version__ = "0.1.0"
|
||||||
|
|
||||||
|
__title__ = "pygcode"
|
||||||
|
__description__ = "Basic g-code parser, interpreter, and encoder library."
|
||||||
|
__url__ = "https://github.com/fragmuffin/pygcode"
|
||||||
|
|
||||||
|
__author__ = "Peter Boin"
|
||||||
|
__email__ = "peter.boin@gmail.com"
|
||||||
|
|
||||||
|
__license__ = "GPLv3"
|
||||||
|
|
||||||
|
# not text-parsable
|
||||||
|
__copyright__ = "Copyright (c) 2017 {0}".format(__author__)
|
||||||
|
|
||||||
|
|
||||||
|
# =========================== Imports ===========================
|
||||||
__all__ = [
|
__all__ = [
|
||||||
# Machine
|
# Machine
|
||||||
'Machine', 'Position', 'CoordinateSystem', 'State', 'Mode',
|
'Machine', 'Position', 'CoordinateSystem', 'State', 'Mode',
|
||||||
@ -12,7 +36,7 @@ __all__ = [
|
|||||||
|
|
||||||
# GCodes
|
# GCodes
|
||||||
'words2gcodes', 'text2gcodes', 'split_gcodes',
|
'words2gcodes', 'text2gcodes', 'split_gcodes',
|
||||||
# $ python -c "from pygcode.gcodes import GCode, _subclasses as sc; print(',\\n '.join(sorted('\\'%s\\'' % g.__name__ for g in sc(GCode))))"python -c "from pygcode.gcodes import GCode, _subclasses as sc; print(',\\n '.join(sorted('\\'%s\\'' % g.__name__ for g in sc(GCode))))"
|
# $ python -c "from pygcode.gcodes import GCode, _subclasses as sc; print(',\\n '.join(sorted('\\'%s\\'' % g.__name__ for g in sc(GCode))))"
|
||||||
'GCode',
|
'GCode',
|
||||||
'GCodeAbsoluteArcDistanceMode',
|
'GCodeAbsoluteArcDistanceMode',
|
||||||
'GCodeAbsoluteDistanceMode',
|
'GCodeAbsoluteDistanceMode',
|
||||||
@ -30,6 +54,7 @@ __all__ = [
|
|||||||
'GCodeCancelToolLengthOffset',
|
'GCodeCancelToolLengthOffset',
|
||||||
'GCodeCannedCycle',
|
'GCodeCannedCycle',
|
||||||
'GCodeCannedCycleReturnLevel',
|
'GCodeCannedCycleReturnLevel',
|
||||||
|
'GCodeCannedCycleReturnToR',
|
||||||
'GCodeCannedReturnMode',
|
'GCodeCannedReturnMode',
|
||||||
'GCodeCoolant',
|
'GCodeCoolant',
|
||||||
'GCodeCoolantFloodOn',
|
'GCodeCoolantFloodOn',
|
||||||
@ -174,7 +199,8 @@ from .gcodes import (
|
|||||||
# G83 - GCodeDrillingCyclePeck: G83: Drilling Cycle, Peck
|
# G83 - GCodeDrillingCyclePeck: G83: Drilling Cycle, Peck
|
||||||
# G76 - GCodeThreadingCycle: G76: Threading Cycle
|
# G76 - GCodeThreadingCycle: G76: Threading Cycle
|
||||||
# - GCodeCannedReturnMode:
|
# - GCodeCannedReturnMode:
|
||||||
# G98 - GCodeCannedCycleReturnLevel: G98: Canned Cycle Return Level
|
# G98 - GCodeCannedCycleReturnLevel: G98: Canned Cycle Return to the level set prior to cycle start
|
||||||
|
# G99 - GCodeCannedCycleReturnToR: G99: Canned Cycle Return to the level set by R
|
||||||
# - GCodeCoolant:
|
# - GCodeCoolant:
|
||||||
# M08 - GCodeCoolantFloodOn: M8: turn flood coolant on
|
# M08 - GCodeCoolantFloodOn: M8: turn flood coolant on
|
||||||
# M07 - GCodeCoolantMistOn: M7: turn mist coolant on
|
# M07 - GCodeCoolantMistOn: M7: turn mist coolant on
|
||||||
@ -305,6 +331,7 @@ from .gcodes import (
|
|||||||
GCodeCancelToolLengthOffset,
|
GCodeCancelToolLengthOffset,
|
||||||
GCodeCannedCycle,
|
GCodeCannedCycle,
|
||||||
GCodeCannedCycleReturnLevel,
|
GCodeCannedCycleReturnLevel,
|
||||||
|
GCodeCannedCycleReturnToR,
|
||||||
GCodeCannedReturnMode,
|
GCodeCannedReturnMode,
|
||||||
GCodeCoolant,
|
GCodeCoolant,
|
||||||
GCodeCoolantFloodOn,
|
GCodeCoolantFloodOn,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import sys
|
import sys
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from copy import copy
|
from copy import copy
|
||||||
|
import six
|
||||||
|
|
||||||
from .utils import Vector3, Quaternion, quat2coord_system
|
from .utils import Vector3, Quaternion, quat2coord_system
|
||||||
from .words import Word, text2words
|
from .words import Word, text2words
|
||||||
@ -141,6 +142,8 @@ class GCode(object):
|
|||||||
word_key = None # Word instance to use in lookup
|
word_key = None # Word instance to use in lookup
|
||||||
word_matches = None # function (secondary)
|
word_matches = None # function (secondary)
|
||||||
default_word = None
|
default_word = None
|
||||||
|
word_letter = 'G'
|
||||||
|
word_value_configurable = False # if set, word value can be the first parameter
|
||||||
|
|
||||||
# Parameters associated to this gcode
|
# Parameters associated to this gcode
|
||||||
param_letters = set()
|
param_letters = set()
|
||||||
@ -161,6 +164,8 @@ class GCode(object):
|
|||||||
param_words = words[1:]
|
param_words = words[1:]
|
||||||
if gcode_word_list:
|
if gcode_word_list:
|
||||||
gcode_word = gcode_word_list[0]
|
gcode_word = gcode_word_list[0]
|
||||||
|
if self.word_value_configurable and isinstance(gcode_word, six.integer_types + (float,)):
|
||||||
|
gcode_word = Word(self.word_letter, gcode_word) # cast to Word()
|
||||||
else:
|
else:
|
||||||
gcode_word = self._default_word()
|
gcode_word = self._default_word()
|
||||||
assert isinstance(gcode_word, Word), "invalid gcode word %r" % gcode_word
|
assert isinstance(gcode_word, Word), "invalid gcode word %r" % gcode_word
|
||||||
@ -610,6 +615,7 @@ class GCodeUnitsPerRevolution(GCodeFeedRateMode):
|
|||||||
# G96, G97 S D Spindle Control Mode
|
# G96, G97 S D Spindle Control Mode
|
||||||
|
|
||||||
class GCodeSpindle(GCode):
|
class GCodeSpindle(GCode):
|
||||||
|
word_letter = 'M'
|
||||||
exec_order = 90
|
exec_order = 90
|
||||||
|
|
||||||
|
|
||||||
@ -642,6 +648,7 @@ class GCodeOrientSpindle(GCodeSpindle):
|
|||||||
|
|
||||||
|
|
||||||
class GCodeSpindleSpeedMode(GCodeSpindle):
|
class GCodeSpindleSpeedMode(GCodeSpindle):
|
||||||
|
word_letter = 'G'
|
||||||
modal_group = MODAL_GROUP_MAP['spindle_speed_mode']
|
modal_group = MODAL_GROUP_MAP['spindle_speed_mode']
|
||||||
|
|
||||||
|
|
||||||
@ -663,6 +670,7 @@ class GCodeSpindleRPMMode(GCodeSpindleSpeedMode):
|
|||||||
# M7, M8, M9 Coolant Control
|
# M7, M8, M9 Coolant Control
|
||||||
|
|
||||||
class GCodeCoolant(GCode):
|
class GCodeCoolant(GCode):
|
||||||
|
word_letter = 'M'
|
||||||
modal_group = MODAL_GROUP_MAP['coolant']
|
modal_group = MODAL_GROUP_MAP['coolant']
|
||||||
exec_order = 110
|
exec_order = 110
|
||||||
|
|
||||||
@ -723,6 +731,7 @@ class GCodeCancelToolLengthOffset(GCodeToolLength):
|
|||||||
# M60 Pallet Change Pause
|
# M60 Pallet Change Pause
|
||||||
|
|
||||||
class GCodeProgramControl(GCode):
|
class GCodeProgramControl(GCode):
|
||||||
|
word_letter = 'M'
|
||||||
modal_group = MODAL_GROUP_MAP['stopping']
|
modal_group = MODAL_GROUP_MAP['stopping']
|
||||||
exec_order = 250
|
exec_order = 250
|
||||||
|
|
||||||
@ -950,6 +959,8 @@ class GCodeOtherModal(GCode):
|
|||||||
|
|
||||||
class GCodeFeedRate(GCodeOtherModal):
|
class GCodeFeedRate(GCodeOtherModal):
|
||||||
"""F: Set Feed Rate"""
|
"""F: Set Feed Rate"""
|
||||||
|
word_letter = 'F'
|
||||||
|
word_value_configurable = True
|
||||||
@classmethod
|
@classmethod
|
||||||
def word_matches(cls, w):
|
def word_matches(cls, w):
|
||||||
return w.letter == 'F'
|
return w.letter == 'F'
|
||||||
@ -960,6 +971,8 @@ class GCodeFeedRate(GCodeOtherModal):
|
|||||||
|
|
||||||
class GCodeSpindleSpeed(GCodeOtherModal):
|
class GCodeSpindleSpeed(GCodeOtherModal):
|
||||||
"""S: Set Spindle Speed"""
|
"""S: Set Spindle Speed"""
|
||||||
|
word_letter = 'S'
|
||||||
|
word_value_configurable = True
|
||||||
@classmethod
|
@classmethod
|
||||||
def word_matches(cls, w):
|
def word_matches(cls, w):
|
||||||
return w.letter == 'S'
|
return w.letter == 'S'
|
||||||
@ -971,6 +984,8 @@ class GCodeSpindleSpeed(GCodeOtherModal):
|
|||||||
|
|
||||||
class GCodeSelectTool(GCodeOtherModal):
|
class GCodeSelectTool(GCodeOtherModal):
|
||||||
"""T: Select Tool"""
|
"""T: Select Tool"""
|
||||||
|
word_letter = 'T'
|
||||||
|
word_value_configurable = True
|
||||||
@classmethod
|
@classmethod
|
||||||
def word_matches(cls, w):
|
def word_matches(cls, w):
|
||||||
return w.letter == 'T'
|
return w.letter == 'T'
|
||||||
@ -982,6 +997,7 @@ class GCodeSelectTool(GCodeOtherModal):
|
|||||||
|
|
||||||
class GCodeSpeedAndFeedOverrideOn(GCodeOtherModal):
|
class GCodeSpeedAndFeedOverrideOn(GCodeOtherModal):
|
||||||
"""M48: Speed and Feed Override Control On"""
|
"""M48: Speed and Feed Override Control On"""
|
||||||
|
word_letter = 'M'
|
||||||
word_key = Word('M', 48)
|
word_key = Word('M', 48)
|
||||||
modal_group = MODAL_GROUP_MAP['override_switches']
|
modal_group = MODAL_GROUP_MAP['override_switches']
|
||||||
exec_order = 120
|
exec_order = 120
|
||||||
@ -989,6 +1005,7 @@ class GCodeSpeedAndFeedOverrideOn(GCodeOtherModal):
|
|||||||
|
|
||||||
class GCodeSpeedAndFeedOverrideOff(GCodeOtherModal):
|
class GCodeSpeedAndFeedOverrideOff(GCodeOtherModal):
|
||||||
"""M49: Speed and Feed Override Control Off"""
|
"""M49: Speed and Feed Override Control Off"""
|
||||||
|
word_letter = 'M'
|
||||||
word_key = Word('M', 49)
|
word_key = Word('M', 49)
|
||||||
modal_group = MODAL_GROUP_MAP['override_switches']
|
modal_group = MODAL_GROUP_MAP['override_switches']
|
||||||
exec_order = 120
|
exec_order = 120
|
||||||
@ -996,6 +1013,7 @@ class GCodeSpeedAndFeedOverrideOff(GCodeOtherModal):
|
|||||||
|
|
||||||
class GCodeFeedOverride(GCodeOtherModal):
|
class GCodeFeedOverride(GCodeOtherModal):
|
||||||
"""M50: Feed Override Control"""
|
"""M50: Feed Override Control"""
|
||||||
|
word_letter = 'M'
|
||||||
param_letters = set('P')
|
param_letters = set('P')
|
||||||
word_key = Word('M', 50)
|
word_key = Word('M', 50)
|
||||||
exec_order = 120
|
exec_order = 120
|
||||||
@ -1003,6 +1021,7 @@ class GCodeFeedOverride(GCodeOtherModal):
|
|||||||
|
|
||||||
class GCodeSpindleSpeedOverride(GCodeOtherModal):
|
class GCodeSpindleSpeedOverride(GCodeOtherModal):
|
||||||
"""M51: Spindle Speed Override Control"""
|
"""M51: Spindle Speed Override Control"""
|
||||||
|
word_letter = 'M'
|
||||||
param_letters = set('P')
|
param_letters = set('P')
|
||||||
word_key = Word('M', 51)
|
word_key = Word('M', 51)
|
||||||
exec_order = 120
|
exec_order = 120
|
||||||
@ -1010,6 +1029,7 @@ class GCodeSpindleSpeedOverride(GCodeOtherModal):
|
|||||||
|
|
||||||
class GCodeAdaptiveFeed(GCodeOtherModal):
|
class GCodeAdaptiveFeed(GCodeOtherModal):
|
||||||
"""M52: Adaptive Feed Control"""
|
"""M52: Adaptive Feed Control"""
|
||||||
|
word_letter = 'M'
|
||||||
param_letters = set('P')
|
param_letters = set('P')
|
||||||
word_key = Word('M', 52)
|
word_key = Word('M', 52)
|
||||||
exec_order = 120
|
exec_order = 120
|
||||||
@ -1017,6 +1037,7 @@ class GCodeAdaptiveFeed(GCodeOtherModal):
|
|||||||
|
|
||||||
class GCodeFeedStop(GCodeOtherModal):
|
class GCodeFeedStop(GCodeOtherModal):
|
||||||
"""M53: Feed Stop Control"""
|
"""M53: Feed Stop Control"""
|
||||||
|
word_letter = 'M'
|
||||||
param_letters = set('P')
|
param_letters = set('P')
|
||||||
word_key = Word('M', 53)
|
word_key = Word('M', 53)
|
||||||
exec_order = 120
|
exec_order = 120
|
||||||
@ -1105,6 +1126,7 @@ class GCodeSelectCoordinateSystem9(GCodeSelectCoordinateSystem):
|
|||||||
# M68 T Analog Output, Immediate
|
# M68 T Analog Output, Immediate
|
||||||
|
|
||||||
class GCodeIO(GCode):
|
class GCodeIO(GCode):
|
||||||
|
word_letter = 'M'
|
||||||
exec_order = 70
|
exec_order = 70
|
||||||
|
|
||||||
|
|
||||||
@ -1179,6 +1201,7 @@ class GCodeToolChange(GCodeNonModal):
|
|||||||
"""M6: Tool Change"""
|
"""M6: Tool Change"""
|
||||||
param_letters = set('T')
|
param_letters = set('T')
|
||||||
word_key = Word('M', 6)
|
word_key = Word('M', 6)
|
||||||
|
word_letter = 'M'
|
||||||
exec_order = 80
|
exec_order = 80
|
||||||
|
|
||||||
|
|
||||||
@ -1186,6 +1209,7 @@ class GCodeToolSetCurrent(GCodeNonModal):
|
|||||||
"""M61: Set Current Tool"""
|
"""M61: Set Current Tool"""
|
||||||
param_letters = set('Q')
|
param_letters = set('Q')
|
||||||
word_key = Word('M', 61)
|
word_key = Word('M', 61)
|
||||||
|
word_letter = 'M'
|
||||||
exec_order = 80
|
exec_order = 80
|
||||||
|
|
||||||
|
|
||||||
@ -1245,6 +1269,7 @@ class GCodeRestoreCoordSystemOffset(GCodeNonModal):
|
|||||||
|
|
||||||
class GCodeUserDefined(GCodeNonModal):
|
class GCodeUserDefined(GCodeNonModal):
|
||||||
"""M101-M199: User Defined Commands"""
|
"""M101-M199: User Defined Commands"""
|
||||||
|
word_letter = 'M'
|
||||||
# To create user g-codes, inherit from this class
|
# To create user g-codes, inherit from this class
|
||||||
param_letters = set('PQ')
|
param_letters = set('PQ')
|
||||||
#@classmethod
|
#@classmethod
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
import sys
|
import sys
|
||||||
from copy import copy, deepcopy
|
from copy import copy, deepcopy
|
||||||
|
|
||||||
if sys.version_info < (3, 0):
|
from euclid3 import Vector3, Quaternion
|
||||||
from euclid import Vector3, Quaternion
|
|
||||||
else:
|
|
||||||
from euclid3 import Vector3, Quaternion
|
|
||||||
|
|
||||||
|
|
||||||
# ==================== Geometric Utilities ====================
|
# ==================== Geometric Utilities ====================
|
||||||
|
Loading…
x
Reference in New Issue
Block a user