I had heard rumblings of a logix based CNC system at teched last year and thought it was a pretty neat idea.  I thought about it for a while and put my own demo version of it together this week.

<img>

<img>

It ended up being easier than I expected to do.  It is also way more performant than I expected.  I was concerned that the processor would be too bogged down parsing and interpreting the gcode to execute the motion task, but it ended up not being a problem even with a program that has close points generated from splines and with high feed rates (originally I had a bug that had feedrates that should have been in units/minute being used in units/second so it was running at 6000%)

It can handle reading (#) and writing ($) to variables by number (and supports pointers) as well as basic math (+, -, / *). The values returned extend to all commands.

{{{
#100 reads variable 100
$20 5.0 writes 5 to variable 20
##3 reads the variable 3 then reads the variable pointed at by it.
$#3 #4 reads vairable 4 and writes its value to the variable pointed at by variable 3.
$##3 #4 reads vairable 4 and writes its value to the variable pointed at by the variable pointed at by variable 3.
M#2 reads variable 2 and does M<v2> so if variable 2 = 00, you get a MOO.
}}}

It also handles comparisons (<, =, >) and an IF operator (?).  
The ? operator reads the value to the right of it (which can be calculated by a variable or a constant).  If the value is >0 the rest of the line to the right is executed.  Otherwise it is skipped.

It handles G00 (rapid), G01 (linear), G02 (cw circle) and G03 (ccw circle).  Although I didn't spend enough time to get G02 and G03 working exactly right, it handles most cases.

It handles tool offsets. As selected by a T word from 0-1024.  G43 can be used to change the tool offsets from the program.

{{{
N001 T1 ; Select tool offset 1.
...
N100 G43 P10 X0 Y0 Z10; Select tool 10 and set its values to an offset of 0, 0, 10.
}}}

It handles work offsets G54 - G59.  G10 can be used to configuree offsets on the fly and/or change to an arbitrary offset (0-1024) using a P-value.  You can change work offset 0 (G53 - Machine coords) but it reverts to 0's on reset.

{{{
N001 G10 P30 ; Switch to work offset 30 without changing it
...
N050 G10 X5.0 Y2.3 Z0.1 ; Set the value of the current offset to X=5.0, Y=2.3, and Z=0.1
... 
N100 G10 P100 X10 Y11 Z12 ; Switch to offset 100 and change its values to X=10, Y=11, and Z=12
...
}}}

It handles parenthesis comments and end of line comments (everything after a ; is ignored)

It also handles block skip (/), op-stop.

It supports subroutines and subprograms with M97 (jump within program), M98 (jump to seperate routine), M99 (return).  It does not support L to re-run automatically but that can be handled through the ? operator and M96 (relative non-returnable jumps - discussed shortly).
It supports 1024 nested subroutine calls shared between within-program "subroutines" and calling external subroutines, of which it supports up to 10 dedicated seperate subroutines (configurable at the expense of memory).
Loading (sub)programs is handled by a python script that requests the controller clear the (sub)program and then re-fills it with data from a computer one line at a time.
As far as calling subroutines goes M97, 98, and 99 all push a new program pointer onto the stack that can be returned from.  These are the traditional CNC jump commands.  M96 is different.  It jumps a relative distance and does not add a layer to the stack.  It can be used as follows combined with M97 or M98 to run a subroutine multiple times.

{{{
N001 $001 10 ; Set variable 1 to 10.
N002 X0 Y0 Z0 ; Home
N003 M98 P01 ; Run subroutine 1
N004 $001 #001 - 1; Decrement counter
N005 ? #001 M96 -3; Jump back to the move to 0 on line 2
}}}

The biggest problem it's got is that the processor is effectively out of memory.  There are a few ways to correct that.  
The first would be to change the main program (tagname gcocdeprogram), the subroutines (tagname subprograms) and MDI program (tagname mdiprogram) to use a custom string type that is a little shorter.  I had considered this but the default length of 82 is actually pretty right-on or a line of gcode.
The next option would be to change the length of the arrays that hold the programs.
You could also probably get rid of MDI or reduce its size substantially.
A more advanced technique would be to load the gcode program in blocks in realtime and have the upload program be constantly running and handshaking with the controller as it runs.  At a minimum, hooks would have to be added for the controller to request specific lines of programs for jumping to work properly.

TODO:
	-complete- M97 could potentially work incorrectly if a non-M block is the first thing on a line - check that the first character is an N before doing a stod
	-not a problem- Block skip won't work if it's after the N-block.
	-complete- Editor page for work offsets and tool offsets
	-complete- MDI mode
	-complete- Manual mode
	-complete- M96
