Package: gcode-machine
GcodeMachineCNC controller state machine for parsing and pre-processing of G-code, implemented as a Python 3 class.I split it off my projectgrbl-streamerto make it generally usable.This machine is completely unit tested and was also successfully tested by realtime processing of G-code for my wood-milling CNC machine.FeaturesAfter passing initial conditions (machine position, coordinate system offsets, current coordinate
system) to the constructor, you can send G-code lines to the machine. Sent G-code lines change the state
of the machine:machine position, absolute to the machine (position_m)working position, relative to current coordinate system (position_w)coordinate system (current_cs)feed rate (current_feed)travel distances (the listdist_xyz, the scalardist)spindle speed (current_spindle_speed)motion mode (current_motion_mode)distance mode (current_distance_mode)plane mode (current_plane_mode)comment (comment)To change the current coordinate system, use the accessorcurrent_cs=(as this also recalculates the working positionpos_w).Optionally, the physical machine position can at any time be updated from the state of a real CNC controller;
for this, the accessorposition_m=should be used (as this recalculates the working positionpos_w).In addition, by calling corresponding methods, the machine also canpre-processthe set G-code line:variable substitution (#1,#2, etc. using thevarsdict attribute)spindle scaling (scale_spindle()using thespindle_factorattribute)feed override (override_feed()using therequest_feedattribute)tidying (tidy()) (comments, change comment format from parentheses to semicolon, spaces, command allow-list)The methodssplit_linesandfractionizeare pure pre-processing methods that do not modify the
state of the machine, but return a list of G-code. In a future release, these methods may be moved
to class methods, or into a separate class:fractionize()splits linear (G1) and arc (G2, G3) motions into tiny linear G1 motionssplit_lines()splits several space-separated commands into a list of separate commandsCallers can assign a custom callback function to the attributecallback, which will be called when
certain processing events happen. Currently, the only evens are:on_feed_change: When the G-Code line changes the feed speed. The first argument is the feed speed as a floating point number.on_var_undefined: When the processor encounters a variable that was not defined before. The
first argument is the name of the undefined variable.Last but not least, the attributeloggerhas a logger created bylogging.getLogger('gcode_machine')that can be used by the application.Installationpipinstallgcode-machineRequirementsThe Python version specified in the file.python-versionDevelopmentDependencies are managed usingpipenv:pipinstallpipenv--user
pipenvinstall--devTo run the tests:pipenvrunmaketestBuildingpipenvrunmakedistUsagefromgcode_machineimportGcodeMachine# initial conditionsimpos=(0,0,0)# initial machine position, default zeroics="G54"# initial coordinate system , default G54cs_offsets={"G54":(0,0,0),"G55":(10,20,30)}# coordinate system offsets# make a new machinegcm=GcodeMachine(impos,ics,cs_offsets)input=["G0 Z-10","G1 X10 Y10"]output=[]forlineininput:gcm.set_line(line)# feed the line into the machinegcm.strip()# clean up whitespacegcm.tidy()# filter commands by a whitelistgcm.find_vars()# parse variable usagesgcm.substitute_vars()# substitute variablesgcm.parse_state()# parse the line and update the machine stategcm.override_feed()# substitute F values in the current line# When done processing:output.append(gcm.line)# read the processed line back from the machinegcm.done()# update the machine positionExplanation of this example:For each iteration of the loop, feed the command line
into the machine with the methodset_line(). Then, call individual processing
methods as needed for your application; this gives a lot of flexibility to the application.When done with one line, calldone()-- this will update the virtual tool position.Processing examplesPlease also see the unit tests for the full feature set.Linear fractionization of arcsgcm.position_m=(0,0,0)gcm.set_line("G2 X10 Y10 I5 J5")gcm.parse_state()print('\n'.join(gcm.fractionize()))Result:;_gcm.arc_begin[G2 X10 Y10 I5 J5];_gcm.color_begin[0.35,0.50,0.40]G1X-0.33Y0.353Z0X-0.634Y0.727X-0.913Y1.122...X8.037Y11.386X8.466Y11.164X8.878Y10.913X9.273Y10.634X9.647Y10.33X10Y10;_gcm.color_end;_gcm.arc_endComment transformgcm.set_line("G0 X0 (bob) Y0 (alice)")gcm.transform_comments()print(gcm.line)Result:G0X0Y0;aliceTidying and allow-listinggcm.set_line("T2")gcm.tidy()print(gcm.line)Result:;T02 ;_gcm.unsupportedSplitting commandsgcm.set_line("G55 M3 T2")gcm.split_lines()Result:['G55 ', 'M3 ', 'T2']