BowlerKernel
GCodeInterpreter.java
Go to the documentation of this file.
1 package com.neuronrobotics.replicator.driver.interpreter;
2 
3 import java.io.BufferedReader;
4 import java.io.InputStream;
5 import java.io.InputStreamReader;
6 import java.util.ArrayList;
7 import java.util.Arrays;
8 import java.util.Comparator;
9 import java.util.List;
10 import java.util.concurrent.locks.ReentrantLock;
11 
12 import com.neuronrobotics.sdk.common.Log;
13 
14 // TODO: Auto-generated Javadoc
30 public class GCodeInterpreter {
31 
32 
37  ArrayList<Integer> mcodes = new ArrayList<Integer>();
42  ArrayList<Integer> gcodes = new ArrayList<Integer>(); // Keep this is an
43  // order that works
44  // properly. Code
45  // implementations
46  // are allowed to
47  // modify the line.
48 
50  private CodeHandler errorHandler=null;
56  List<CodeHandler> gHandlers[];
57 
62  List<Integer> gClearOnSet[];
63 
68  List<Integer> gOneShot;
69 
74  List<CodeHandler> mHandlers[];
75 
81  Comparator<Integer> gCodeOrdering;
86  char motion_axes[] = { 'X', 'Y', 'Z' };
87 
92  GCodeLineData lastLine;
97  GCodeLineData nextLine;
98 
104  Thread interpretingThread;
105 
107  ReentrantLock executingLock;
108 
110  private int lineNumber=0;
116  @SuppressWarnings("unchecked")
117  public GCodeInterpreter() {
118  gHandlers = (List<CodeHandler>[]) new ArrayList<?>[310];
119  mHandlers = (List<CodeHandler>[]) new ArrayList<?>[310];
120  gClearOnSet = (List<Integer>[]) new List<?>[310]; // Maybe not the best
121  // storage
122  // mechanism. Good
123  // in C...
124  nextLine = new GCodeLineData();
125  lastLine = new GCodeLineData();
127  executingLock = new ReentrantLock();
128  }
129 
130 
137  public void processSingleGCODELine(String line) throws Exception{
138  String delims;
139  String[] tokens;
140 
141  delims = "[ ]+";
142  tokens = line.split(delims);
143  nextLine.storeWord('G', 0);
144  nextLine.storeWord('M', 0);
145  nextLine.storeWord('P', lineNumber);
146  System.out.println("GCODE: "+line);
147 
148  for(int i=0;i<tokens.length;i++){
149  tokens[i] = tokens[i].trim();
150  if(!tokens[i].isEmpty()){
151  double val = Double.parseDouble(tokens[i].substring(1));
152  char code = tokens[i].charAt(0);
153  if(code == 'M'){
154  mcodes.add((int) val);
155  }
156  if(code == 'G'){
157  int theCode = (int) val;
158  if (gClearOnSet[theCode] != null)
159  gcodes.removeAll(gClearOnSet[theCode]);
160  gcodes.add(theCode);
161  }
162  Log.debug("Code Token: "+tokens[i]+" "+code+" "+val);
163  nextLine.storeWord(code, val);
164  }
165  }
166  //System.out.println(nextLine);
167  executeLine(line);
168  }
169 
176  private void parseLine(InputStream r) throws Exception {
177  BufferedReader br = new BufferedReader(new InputStreamReader(r));
178  String line;
179  boolean inCommentSection = false;
180  lineNumber=0;
181  while ((line = br.readLine()) != null) {
182  lineNumber++;// lines in the file
183  String delims;
184  String[] tokens;
185  if(line.indexOf(';')>-1){
186  //this line contains a comment
187  if(line.indexOf(';') ==0){
188  // this is just a comment
189  line = null;
190  }else{
191  delims = "[;]+";
192  tokens = line.split(delims);
193  //strip the comment and place the rest of the line
194  // in the to-be-parsed variable
195  line = tokens[0];
196  }
197  }else{
198  // no comment on this line
199  }
200  //Check for the block comment case
201  if(line != null){
202  if(line.indexOf('(')>-1){
203  // block comment section
204  inCommentSection = true;
205  line = null;
206  }
207 
208  }
209  if(line != null){
210  if(line.indexOf(')')>-1){
211  // end block comment section
212  inCommentSection = false;
213  line = null;
214  }
215  }
216  if(inCommentSection)
217  line = null;
218 
219  // Check for the empty line case
220  if(line != null){
221  if (line.trim().isEmpty()){
222  //empty line detect
223  line = null;
224  }
225  }
226  // OK, now we have a valid line
227  if(line !=null){
228 
229 
230  processSingleGCODELine( line);
231  }
232 
233  }
234  br.close();
235  }
236 
237 
244  private void executeLine(String rawLine) throws Exception {
245 
246  Log.debug("Next Gcode Line " + nextLine);
247  Log.debug("Active Gcodes: " + gcodes);
248  Log.debug("Active Mcodes: " + mcodes);
249  for (int m : mcodes)
250  if (mHandlers[m] != null) {
251  for (CodeHandler handler : mHandlers[m]) {
252  handler.execute(lastLine, nextLine);
253  }
254  } else {
255  // Log.debug("No implementation found for M"+m);
256  if(getErrorHandler() ==null)
257  throw new RuntimeException("No implementation found for M" + m);
258  else{
259  getErrorHandler().execute(lastLine, nextLine);
260  }
261  }
262  for (int g : gcodes)
263  if (gHandlers[g] != null) {
264  for (CodeHandler handler : gHandlers[g]) {
265  handler.execute(lastLine, nextLine);
266  }
267  } else {
268  if(getErrorHandler() ==null)
269  throw new RuntimeException("No implementation found for G" + g);
270  else{
271  getErrorHandler().execute(lastLine, nextLine);
272  }
273  }
274 
275  lastLine = nextLine;
276  nextLine = new GCodeLineData(lastLine);
277  gcodes.removeAll(gOneShot);
278  mcodes.clear();
279  }
280 
288  public void interpretStream(InputStream in) throws Exception {
289  executingLock.lock();
290 
291  interpretingThread = Thread.currentThread();
292 
293  parseLine(in);
294 
295  interpretingThread = null;
296  executingLock.unlock();
297 
298  }
299 
307  public void tryInterpretStream(InputStream in) throws Exception {
308  if (executingLock.tryLock()) {
309  try {
310  interpretStream(in);
311  } finally {
312  executingLock.unlock();
313  }
314  } else {
315  throw(new RuntimeException("Printer not ready"));
316  }
317  }
318 
325  public boolean cancel() {
326  if (interpretingThread != null) {
327  interpretingThread.interrupt();
328  return true;
329  }
330  return false;
331  }
332 
346  public void addGHandler(int code, CodeHandler handler) { // Should really do
347  // boundschecking.
348  // New code
349  // happens
350  // before old.
351  if (gHandlers[code] == null)
352  gHandlers[code] = new ArrayList<CodeHandler>();
353  gHandlers[code].add(handler);
354  }
355 
366  public void setGHandler(int code, CodeHandler handler) { // For overriding
367  // all default
368  // behavior of a
369  // code.
370  handler.setSubHandlers(gHandlers[code]);
371  gHandlers[code] = new ArrayList<CodeHandler>();
372  gHandlers[code].add(handler);
373  }
374 
388  public void addMHandler(int code, CodeHandler handler) { // Should really do
389  // boundschecking.
390  // New code
391  // happens
392  // before old.
393  if (mHandlers[code] == null)
394  mHandlers[code] = new ArrayList<CodeHandler>();
395  mHandlers[code].add(handler);
396  }
397 
408  public void setMHandler(int code, CodeHandler handler) { // For overriding
409  // all default
410  // behavior of a
411  // code.
412  handler.setSubHandlers(mHandlers[code]);
413  mHandlers[code] = new ArrayList<CodeHandler>();
414  mHandlers[code].add(handler);
415  }
416 
444  public void addGSorting(final Comparator<Integer> c) {
445  if (gCodeOrdering == null) {
446  gCodeOrdering = c;
447  return;
448  }
449  // ...perhaps I've had too much haskell to write java-style code easily.
450  // I do believe this is a combinator.
451  final Comparator<Integer> oldComparator = gCodeOrdering; // Make a
452  // constant
453  // reference
454  // to the
455  // old one,
456  // for the
457  // inner
458  // class.
459  gCodeOrdering = new Comparator<Integer>() {
460  public int compare(Integer o1, Integer o2) {
461  int r1 = c.compare(o1, o2);
462  if (r1 == 0)
463  return oldComparator.compare(o1, o2);
464  return r1;
465  }
466 
467  @SuppressWarnings("unused")
468  public boolean equals(Integer o1, Integer o2) {
469  return this.compare(o1, o2) == 0;
470  }
471  };
472  }
473 
482  @SuppressWarnings("unchecked")
483  public void setGSorting(@SuppressWarnings("rawtypes") Comparator c) {
484  gCodeOrdering = c;
485  }
486 
492  public void addDefaultHandlers() {
493  final double curOffset[] = new double[26]; // Yes, we'll let them offset
494  // on any axis they feel
495  // like.
496  // G0 - no handler.
497  addGHandler(0, new CodeHandler() {
498  public void execute(GCodeLineData prev, GCodeLineData next) {
499  Log.debug("Rapid move to " + next.getWord('X') + ", "
500  + next.getWord('Y') + ", " + next.getWord('Z'));
501  }
502  });
503  // G1 - no handler.
504  addGHandler(1, new CodeHandler() {
505  public void execute(GCodeLineData prev, GCodeLineData next) {
506  if (next.getWord('F') == 0.0)
507  Log.error("Zero feedrate; action will never complete.");
508  Log.debug("Feed move to " + next.getWord('X') + ", "
509  + next.getWord('Y') + ", " + next.getWord('Z')
510  + " at feed " + next.getWord('F'));
511  }
512  });
513  // G21 - program in mm - present but nullary.
514  addGHandler(21, new EmptyCodeHandler());
515  // G22 - program in in - convert to mm. Only on xyz for now.
516  addGHandler(22, new CodeHandler() {
517  public void execute(GCodeLineData prev, GCodeLineData next) {
518  char axes[] = { 'X', 'Y', 'Z' };
519  for (char c : axes) {
520  next.storeWord(c, next.getWord(c) * 25.4);
521  }
522  }
523  });
524  // G90 - absolute positioning - default, so empty.
525  addGHandler(90, new EmptyCodeHandler());
526  // G91 - relative positioning - convert to absolute. Only do this for
527  // xyz by default.
528  addGHandler(91, new CodeHandler() {
529  public void execute(GCodeLineData prev, GCodeLineData next) {
530  char axes[] = { 'X', 'Y', 'Z' };
531  for (char c : axes) {
532  next.storeWord(c, next.getWord(c) + prev.getWord(c));
533  }
534  }
535  });
536  // G92 - set position ( reprap usage ) - messy. This probably goes in
537  // the printer class.
538  addGHandler(92, new CodeHandler() {
539  public void execute(GCodeLineData prev, GCodeLineData next) {
540  Log.debug("G92 is not complete");
541  char axes[] = { 'X', 'Y', 'Z' };
542  for (char c : axes) {
543  next.storeWord(c, next.getWord(c) + curOffset[c - 'A']);
544  }
545  // And copy the last line to the current line, because we don't
546  // want to change actual positions.
547  }
548  });
549  // GROUPS:
550  // 21,22 , 90,91, 0-5
551  setGSorting(new Comparator<Integer>() {
552  public int compare(Integer o1, Integer o2) {
553  int a = o1.intValue();
554  int b = o2.intValue();
555  if (a >= 21 && a <= 22)
556  return -1; // Unit conversions default to first.
557  if (a >= 0 && a < 6)
558  return 1; // Movements are last.
559  if (b >= 21 && b <= 22)
560  return 1;
561  if (b >= 0 && b < 6)
562  return -1;
563  if (a >= 90 && a <= 91)
564  return -1; // Relative/absolute are immediately after unit
565  // conversion unless otherwise specified.
566  if (b >= 90 && b <= 91)
567  return 1;
568  return 0;
569  }
570  });
571 
572  @SuppressWarnings("unchecked")
573  List<Integer>[] exclGroups = (List<Integer>[]) new List<?>[] {
574  Arrays.asList(0, 1, 4, 28), // All of these might need to change
575  // to be mutable later.
576  Arrays.asList(20, 21), Arrays.asList(90, 91) };
577  for (List<Integer> group : exclGroups) {
578  for (int code : group) {
579  gClearOnSet[code] = group;
580  }
581  }
582  gClearOnSet[91] = Arrays.asList(90, 91, 92); // Incremental and setting
583  // positions makes no
584  // sense.
585  gOneShot = Arrays.asList(28, 92);
586  }
587 
595  public static void main(String args[]) throws Exception {
596  GCodeInterpreter interp = new GCodeInterpreter();
597  interp.interpretStream(System.in);
598  }
599 
606  return errorHandler;
607  }
608 
615  this.errorHandler = errorHandler;
616  }
617 }
void setSubHandlers(List< CodeHandler > subHandlers)
abstract void execute(GCodeLineData prev, GCodeLineData line)
void setGSorting(@SuppressWarnings("rawtypes") Comparator c)
static void error(String message)
Definition: Log.java:92
static void debug(String message)
Definition: Log.java:128