com.neuronrobotics.replicator.driver.interpreter.GCodeInterpreter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-bowler Show documentation
Show all versions of java-bowler Show documentation
A command line utility for accesing the bowler framework.
package com.neuronrobotics.replicator.driver.interpreter;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import com.neuronrobotics.sdk.common.Log;
// TODO: Auto-generated Javadoc
/**
* An extensible G-code interpreter. Parses a stream containing G-code commands,
* stores register values, and executes handlers. The default handler set
* converts position axes to mm if neccessary, converts relative positioning to
* absolute, and prints a message for rapid and interpolated linear motion. You
* can register additional handlers using addGHandler and addMHandler, which
* compose with, rather than replacing, the previous implementations.
* {@link GCodeInterpreter#addDefaultHandlers}
*
* To use, bind handlers to at least G00 and G01, and feed in a stream of
* G-code. An example can be found in
*
* @author Jonathan D.K. Gibbons
* @version 1
*/
public class GCodeInterpreter {
/**
* The list of currently active M-codes. This defines what handlers will be
* called for M codes, and must be kept in a useful order.
*/
ArrayList mcodes = new ArrayList();
/**
* The list of currently active G-codes. This defines what handlers will be
* called for G codes, and must be kept in a useful order.
*/
ArrayList gcodes = new ArrayList(); // Keep this is an
// order that works
// properly. Code
// implementations
// are allowed to
// modify the line.
/** The error handler. */
private CodeHandler errorHandler=null;
/**
* The list of handlers for G codes. gHandlers[0] is the list of handlers
* for G0. The handlers must be called from last to first, to preserve
* composition semantics.
*/
List gHandlers[];
/**
* Exclusive codes, implemented as a list of codes to clear before setting a
* given code.
*/
List gClearOnSet[];
/**
* The list of one-shot G codes. These are codes such as cycle times or
* setting offsets, which happen exactly once when they are in the stream.
*/
List gOneShot;
/**
* The list of handlers for M codes. This is stored and handled the same as
* for G codes.
*/
List mHandlers[];
/**
* A comparator to define G code ordering. This gives the order in which G
* codes must be interpreted in a line - units conversion happens before
* conversion to absolute, which happens before motion modes.
*/
Comparator gCodeOrdering;
/**
* What axes are motion - this mostly defines which axes get
* relative/absolute and units conversion.
*/
char motion_axes[] = { 'X', 'Y', 'Z' };
/**
* Stores the previous position and other data for the previous line of G
* code.
*/
GCodeLineData lastLine;
/**
* Stores the currently in-progress position and other data for this line of
* G code.
*/
GCodeLineData nextLine;
/**
* The thread that is currently parsing G-code; for sending interruptions to
* cancel prints.
*/
Thread interpretingThread;
/** The executing lock. */
ReentrantLock executingLock;
/** The line number. */
private int lineNumber=0;
/**
* Default Constructor. This builds an interpreter and adds the default set
* of handlers and configuration to it. @see
* GCodeInterpreter#addDefaultHandlers
*/
@SuppressWarnings("unchecked")
public GCodeInterpreter() {
gHandlers = (List[]) new ArrayList>[310];
mHandlers = (List[]) new ArrayList>[310];
gClearOnSet = (List[]) new List>[310]; // Maybe not the best
// storage
// mechanism. Good
// in C...
nextLine = new GCodeLineData();
lastLine = new GCodeLineData();
addDefaultHandlers();
executingLock = new ReentrantLock();
}
/**
* Process single gcode line.
*
* @param line the line
* @throws Exception the exception
*/
public void processSingleGCODELine(String line) throws Exception{
String delims;
String[] tokens;
delims = "[ ]+";
tokens = line.split(delims);
nextLine.storeWord('G', 0);
nextLine.storeWord('M', 0);
nextLine.storeWord('P', lineNumber);
System.out.println("GCODE: "+line);
for(int i=0;i-1){
//this line contains a comment
if(line.indexOf(';') ==0){
// this is just a comment
line = null;
}else{
delims = "[;]+";
tokens = line.split(delims);
//strip the comment and place the rest of the line
// in the to-be-parsed variable
line = tokens[0];
}
}else{
// no comment on this line
}
//Check for the block comment case
if(line != null){
if(line.indexOf('(')>-1){
// block comment section
inCommentSection = true;
line = null;
}
}
if(line != null){
if(line.indexOf(')')>-1){
// end block comment section
inCommentSection = false;
line = null;
}
}
if(inCommentSection)
line = null;
// Check for the empty line case
if(line != null){
if (line.trim().isEmpty()){
//empty line detect
line = null;
}
}
// OK, now we have a valid line
if(line !=null){
processSingleGCODELine( line);
}
}
br.close();
}
/**
* Execute the action(s) specified by the already built-up line of G-code.
*
* @param rawLine the raw line
* @throws Exception the exception
*/
private void executeLine(String rawLine) throws Exception {
Log.debug("Next Gcode Line " + nextLine);
Log.debug("Active Gcodes: " + gcodes);
Log.debug("Active Mcodes: " + mcodes);
for (int m : mcodes)
if (mHandlers[m] != null) {
for (CodeHandler handler : mHandlers[m]) {
handler.execute(lastLine, nextLine);
}
} else {
// Log.debug("No implementation found for M"+m);
if(getErrorHandler() ==null)
throw new RuntimeException("No implementation found for M" + m);
else{
getErrorHandler().execute(lastLine, nextLine);
}
}
for (int g : gcodes)
if (gHandlers[g] != null) {
for (CodeHandler handler : gHandlers[g]) {
handler.execute(lastLine, nextLine);
}
} else {
if(getErrorHandler() ==null)
throw new RuntimeException("No implementation found for G" + g);
else{
getErrorHandler().execute(lastLine, nextLine);
}
}
lastLine = nextLine;
nextLine = new GCodeLineData(lastLine);
gcodes.removeAll(gOneShot);
mcodes.clear();
}
/**
* Main entry point; take an InputStream of G code and run the sequence of
* actions it describes.
*
* @param in the in
* @throws Exception the exception
*/
public void interpretStream(InputStream in) throws Exception {
executingLock.lock();
interpretingThread = Thread.currentThread();
parseLine(in);
interpretingThread = null;
executingLock.unlock();
}
/**
* Nonblocking version of interpretStream(); fails rather than waiting.
*
* @param in the in
* @throws Exception the exception
*/
public void tryInterpretStream(InputStream in) throws Exception {
if (executingLock.tryLock()) {
try {
interpretStream(in);
} finally {
executingLock.unlock();
}
} else {
throw(new RuntimeException("Printer not ready"));
}
}
/**
* Cancel a run of a G-code stream. This interrupts the thread that is
* currently parsing a G-code stream, canceling its execution.
*
* @return true, if successful
*/
public boolean cancel() {
if (interpretingThread != null) {
interpretingThread.interrupt();
return true;
}
return false;
}
/**
* Add a handler for a G code. The new handler executes before any
* previously installed handler for the code, to permit composition; for
* instance, where the first-installed G01 handler specifies motion and
* flushes the commands to device, a handler installed later could handle
* tool behavior without flushing the device, and provide coordinated
* behavior.
*
* @param code
* the G code to bind this handler to.
* @param handler
* the handler implementation.
*/
public void addGHandler(int code, CodeHandler handler) { // Should really do
// boundschecking.
// New code
// happens
// before old.
if (gHandlers[code] == null)
gHandlers[code] = new ArrayList();
gHandlers[code].add(handler);
}
/**
* Clear any previous handlers for a code, and install a new one. This is
* usually unneccessary, but it allows you to clear a handler set and start
* from scratch.
*
* @param code
* the G code to bind this handler to.
* @param handler
* the handler implementation.
*/
public void setGHandler(int code, CodeHandler handler) { // For overriding
// all default
// behavior of a
// code.
handler.setSubHandlers(gHandlers[code]);
gHandlers[code] = new ArrayList();
gHandlers[code].add(handler);
}
/**
* Add a handler for an M code. The new handler executes before any
* previously installed handler for the code, to permit composition; for
* instance, where the first-installed G01 handler specifies motion and
* flushes the commands to device, a handler installed later could handle
* tool behavior without flushing the device, and provide coordinated
* behavior.
*
* @param code
* the G code to bind this handler to.
* @param handler
* the handler implementation.
*/
public void addMHandler(int code, CodeHandler handler) { // Should really do
// boundschecking.
// New code
// happens
// before old.
if (mHandlers[code] == null)
mHandlers[code] = new ArrayList();
mHandlers[code].add(handler);
}
/**
* Clear any previous handlers for a code, and install a new one. This is
* usually unneccessary, but it allows you to clear a handler set and start
* from scratch.
*
* @param code
* the G code to bind this handler to.
* @param handler
* the handler implementation.
*/
public void setMHandler(int code, CodeHandler handler) { // For overriding
// all default
// behavior of a
// code.
handler.setSubHandlers(mHandlers[code]);
mHandlers[code] = new ArrayList();
mHandlers[code].add(handler);
}
/**
* Add rules for how to sort G codes before executing them. The new and old
* Comparators are combined; whenever the new comparator returns equal, the
* old comparator is consulted, permitting straightforward overridability.
*
* For instance, to add a rule stating that the handlers for code G07 should
* always execute after those for G09:
*
*
* interp.addGSorting(new Comparator<Integer>() {
* public int compare(Integer c1, Integer c2) {
* if (c1 == 7 && c2 == 9)
* return -1;
* if (c1 == 9 && c2 == 7)
* return 1;
* return 0;
* }
* });
*
*
* Note: this does not implement any sort of transitivity, that is
* left to the new comparator, and is required for the contract of a
* comparator.
*
* @param c
* the comparator implementing the new ordering rules.
*/
public void addGSorting(final Comparator c) {
if (gCodeOrdering == null) {
gCodeOrdering = c;
return;
}
// ...perhaps I've had too much haskell to write java-style code easily.
// I do believe this is a combinator.
final Comparator oldComparator = gCodeOrdering; // Make a
// constant
// reference
// to the
// old one,
// for the
// inner
// class.
gCodeOrdering = new Comparator() {
public int compare(Integer o1, Integer o2) {
int r1 = c.compare(o1, o2);
if (r1 == 0)
return oldComparator.compare(o1, o2);
return r1;
}
@SuppressWarnings("unused")
public boolean equals(Integer o1, Integer o2) {
return this.compare(o1, o2) == 0;
}
};
}
/**
* Override the default sort for G codes. Not recommended unless all G codes
* are being written by the user; this can break dependencies between
* handlers.
*
* @param c
* the new comparator.
*/
@SuppressWarnings("unchecked")
public void setGSorting(@SuppressWarnings("rawtypes") Comparator c) {
gCodeOrdering = c;
}
/**
* Add the default set of handlers to this interpreter.
*
*
*/
public void addDefaultHandlers() {
final double curOffset[] = new double[26]; // Yes, we'll let them offset
// on any axis they feel
// like.
// G0 - no handler.
addGHandler(0, new CodeHandler() {
public void execute(GCodeLineData prev, GCodeLineData next) {
Log.debug("Rapid move to " + next.getWord('X') + ", "
+ next.getWord('Y') + ", " + next.getWord('Z'));
}
});
// G1 - no handler.
addGHandler(1, new CodeHandler() {
public void execute(GCodeLineData prev, GCodeLineData next) {
if (next.getWord('F') == 0.0)
Log.error("Zero feedrate; action will never complete.");
Log.debug("Feed move to " + next.getWord('X') + ", "
+ next.getWord('Y') + ", " + next.getWord('Z')
+ " at feed " + next.getWord('F'));
}
});
// G21 - program in mm - present but nullary.
addGHandler(21, new EmptyCodeHandler());
// G22 - program in in - convert to mm. Only on xyz for now.
addGHandler(22, new CodeHandler() {
public void execute(GCodeLineData prev, GCodeLineData next) {
char axes[] = { 'X', 'Y', 'Z' };
for (char c : axes) {
next.storeWord(c, next.getWord(c) * 25.4);
}
}
});
// G90 - absolute positioning - default, so empty.
addGHandler(90, new EmptyCodeHandler());
// G91 - relative positioning - convert to absolute. Only do this for
// xyz by default.
addGHandler(91, new CodeHandler() {
public void execute(GCodeLineData prev, GCodeLineData next) {
char axes[] = { 'X', 'Y', 'Z' };
for (char c : axes) {
next.storeWord(c, next.getWord(c) + prev.getWord(c));
}
}
});
// G92 - set position ( reprap usage ) - messy. This probably goes in
// the printer class.
addGHandler(92, new CodeHandler() {
public void execute(GCodeLineData prev, GCodeLineData next) {
Log.debug("G92 is not complete");
char axes[] = { 'X', 'Y', 'Z' };
for (char c : axes) {
next.storeWord(c, next.getWord(c) + curOffset[c - 'A']);
}
// And copy the last line to the current line, because we don't
// want to change actual positions.
}
});
// GROUPS:
// 21,22 , 90,91, 0-5
setGSorting(new Comparator() {
public int compare(Integer o1, Integer o2) {
int a = o1.intValue();
int b = o2.intValue();
if (a >= 21 && a <= 22)
return -1; // Unit conversions default to first.
if (a >= 0 && a < 6)
return 1; // Movements are last.
if (b >= 21 && b <= 22)
return 1;
if (b >= 0 && b < 6)
return -1;
if (a >= 90 && a <= 91)
return -1; // Relative/absolute are immediately after unit
// conversion unless otherwise specified.
if (b >= 90 && b <= 91)
return 1;
return 0;
}
});
@SuppressWarnings("unchecked")
List[] exclGroups = (List[]) new List>[] {
Arrays.asList(0, 1, 4, 28), // All of these might need to change
// to be mutable later.
Arrays.asList(20, 21), Arrays.asList(90, 91) };
for (List group : exclGroups) {
for (int code : group) {
gClearOnSet[code] = group;
}
}
gClearOnSet[91] = Arrays.asList(90, 91, 92); // Incremental and setting
// positions makes no
// sense.
gOneShot = Arrays.asList(28, 92);
}
/**
* Default executable; runs as a pipe, parsing standard input with the
* default handlers.
*
* @param args the arguments
* @throws Exception the exception
*/
public static void main(String args[]) throws Exception {
GCodeInterpreter interp = new GCodeInterpreter();
interp.interpretStream(System.in);
}
/**
* Gets the error handler.
*
* @return the error handler
*/
public CodeHandler getErrorHandler() {
return errorHandler;
}
/**
* Sets the error handler.
*
* @param errorHandler the new error handler
*/
public void setErrorHandler(CodeHandler errorHandler) {
this.errorHandler = errorHandler;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy