{"id":21262791,"url":"https://github.com/michaelfranzl/gcode-machine","last_synced_at":"2025-07-11T04:31:06.819Z","repository":{"id":147443590,"uuid":"57199099","full_name":"michaelfranzl/gcode-machine","owner":"michaelfranzl","description":"A simple CNC state machine implemented in Python that can be used for simulation and processing of G-code","archived":false,"fork":false,"pushed_at":"2023-12-28T07:37:03.000Z","size":96,"stargazers_count":20,"open_issues_count":0,"forks_count":9,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-10-12T14:56:45.263Z","etag":null,"topics":["cnc-state-machine","gcode","gcode-machine","simulation"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/michaelfranzl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-04-27T08:57:06.000Z","updated_at":"2024-09-30T04:44:25.000Z","dependencies_parsed_at":"2023-12-17T09:28:59.838Z","dependency_job_id":"e4393c2e-d758-4443-b55c-bad7f2581224","html_url":"https://github.com/michaelfranzl/gcode-machine","commit_stats":null,"previous_names":["michaelfranzl/gcode-machine"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelfranzl%2Fgcode-machine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelfranzl%2Fgcode-machine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelfranzl%2Fgcode-machine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelfranzl%2Fgcode-machine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/michaelfranzl","download_url":"https://codeload.github.com/michaelfranzl/gcode-machine/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225674898,"owners_count":17506272,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cnc-state-machine","gcode","gcode-machine","simulation"],"created_at":"2024-11-21T04:59:23.878Z","updated_at":"2024-11-21T04:59:24.475Z","avatar_url":"https://github.com/michaelfranzl.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GcodeMachine\n\n![Unit tests on master branch](https://github.com/michaelfranzl/gcode_machine/actions/workflows/test.yml/badge.svg?branch=master)\n\nCNC controller state machine for parsing and pre-processing of G-code, implemented as a Python 3 class.\n\nI split it off my project [grbl-streamer](https://github.com/michaelfranzl/grbl-streamer) to make it generally usable.\n\nThis machine is completely unit tested and was also successfully tested by realtime processing of G-code for my wood-milling CNC machine.\n\n\n## Features\n\nAfter passing initial conditions (machine position, coordinate system offsets, current coordinate\nsystem) to the constructor, you can send G-code lines to the machine. Sent G-code lines change the state\nof the machine:\n\n* machine position, absolute to the machine (`position_m`)\n* working position, relative to current coordinate system (`position_w`)\n* coordinate system (`current_cs`)\n* feed rate (`current_feed`)\n* travel distances (the list `dist_xyz`, the scalar `dist`)\n* spindle speed (`current_spindle_speed`)\n* motion mode (`current_motion_mode`)\n* distance mode (`current_distance_mode`)\n* plane mode (`current_plane_mode`)\n* comment (`comment`)\n\nTo change the current coordinate system, use the accessor `current_cs=`\n(as this also recalculates the working position `pos_w`).\n\nOptionally, the physical machine position can at any time be updated from the state of a real CNC controller;\nfor this, the accessor `position_m=` should be used (as this recalculates the working position `pos_w`).\n\nIn addition, by calling corresponding methods, the machine also can *pre-process* the set G-code line:\n\n* variable substitution (`#1`, `#2`, etc. using the `vars` dict attribute)\n* spindle scaling (`scale_spindle()` using the `spindle_factor` attribute)\n* feed override (`override_feed()` using the `request_feed` attribute)\n* tidying (`tidy()`) (comments, change comment format from parentheses to semicolon, spaces, command allow-list)\n\nThe methods `split_lines` and `fractionize` are pure pre-processing methods that do not modify the\nstate of the machine, but return a list of G-code. In a future release, these methods may be moved\nto class methods, or into a separate class:\n\n* `fractionize()` splits linear (G1) and arc (G2, G3) motions into tiny linear G1 motions\n* `split_lines()` splits several space-separated commands into a list of separate commands\n\nCallers can assign a custom callback function to the attribute `callback`, which will be called when\ncertain processing events happen. Currently, the only evens are:\n\n* `on_feed_change`: When the G-Code line changes the feed speed. The first argument is the feed speed as a floating point number.\n* `on_var_undefined`: When the processor encounters a variable that was not defined before. The\n    first argument is the name of the undefined variable.\n\nLast but not least, the attribute `logger` has a logger created by `logging.getLogger('gcode_machine')`\nthat can be used by the application.\n\n\n## Installation\n\n```sh\npip install gcode-machine\n```\n\n## Requirements\n\n* The Python version specified in the file `.python-version`\n\n\n## Development\n\nDependencies are managed using `pipenv`:\n\n```sh\npip install pipenv --user\npipenv install --dev\n```\n\nTo run the tests:\n\n```sh\npipenv run make test\n```\n\n### Building\n\n```sh\npipenv run make dist\n```\n\n\n## Usage\n\n```python\nfrom gcode_machine import GcodeMachine\n\n# initial conditions\nimpos = (0, 0, 0) # initial machine position, default zero\nics = \"G54\" # initial coordinate system , default G54\ncs_offsets = {\"G54\": (0, 0, 0), \"G55\": (10, 20, 30)} # coordinate system offsets\n\n# make a new machine\ngcm = GcodeMachine(impos, ics, cs_offsets)\ninput = [\"G0 Z-10\", \"G1 X10 Y10\"]\noutput = []\n\nfor line in input:\n    gcm.set_line(line)       # feed the line into the machine\n\n    gcm.strip()              # clean up whitespace\n    gcm.tidy()               # filter commands by a whitelist\n    gcm.find_vars()          # parse variable usages\n    gcm.substitute_vars()    # substitute variables\n    gcm.parse_state()        # parse the line and update the machine state\n    gcm.override_feed()      # substitute F values in the current line\n\n    # When done processing:\n    output.append(gcm.line)  # read the processed line back from the machine\n    gcm.done()               # update the machine position\n```\n\nExplanation of this example:\n\nFor each iteration of the loop, feed the command line\ninto the machine with the method `set_line()`. Then, call individual processing\nmethods as needed for your application; this gives a lot of flexibility to the application.\n\nWhen done with one line, call `done()` -- this will update the virtual tool position.\n\n\n## Processing examples\n\nPlease also see the unit tests for the full feature set.\n\n\n### Linear fractionization of arcs\n\n```python\ngcm.position_m = (0,0,0)\ngcm.set_line(\"G2 X10 Y10 I5 J5\")\ngcm.parse_state()\nprint('\\n'.join(gcm.fractionize()))\n```\n\nResult:\n\n```gcode\n;_gcm.arc_begin[G2 X10 Y10 I5 J5]\n;_gcm.color_begin[0.35,0.50,0.40]\nG1X-0.33Y0.353Z0\nX-0.634Y0.727\nX-0.913Y1.122\n...\nX8.037Y11.386\nX8.466Y11.164\nX8.878Y10.913\nX9.273Y10.634\nX9.647Y10.33\nX10Y10\n;_gcm.color_end\n;_gcm.arc_end\n```\n\n### Comment transform\n\n```python\ngcm.set_line(\"G0 X0 (bob) Y0 (alice)\")\ngcm.transform_comments()\nprint(gcm.line)\n```\n\nResult:\n\n```gcode\nG0 X0  Y0 ;alice\n```\n\n\n### Tidying and allow-listing\n\n```python\ngcm.set_line(\"T2\")\ngcm.tidy()\nprint(gcm.line)\n```\n\nResult:\n\n```gcode\n;T02 ;_gcm.unsupported\n```\n\n\n### Splitting commands\n\n```python\ngcm.set_line(\"G55 M3 T2\")\ngcm.split_lines()\n```\n\nResult:\n\n```gcode\n['G55 ', 'M3 ', 'T2']\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichaelfranzl%2Fgcode-machine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmichaelfranzl%2Fgcode-machine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichaelfranzl%2Fgcode-machine/lists"}