/* * Copyright (C) 2013-2014 Synthetos LLC. All Rights reserved. * http://www.synthetos.com */ package tgfx; import java.util.concurrent.BlockingQueue; import java.util.concurrent.atomic.AtomicInteger; import org.apache.log4j.Logger; import tgfx.tinyg.TinygDriver; import tgfx.ui.gcode.GcodeTabController; /** * * @author ril3y */ public class SerialWriter implements Runnable { private static Logger logger = Logger.getLogger(SerialWriter.class); private BlockingQueue<String> queue; private boolean RUN = true; private boolean cleared = false; private String tmpCmd; private int BUFFER_SIZE = 180; public AtomicInteger buffer_available = new AtomicInteger(BUFFER_SIZE); private SerialDriver ser = SerialDriver.getInstance(); private static final Object mutex = new Object(); private static boolean throttled = false; private int pbaChamberedRounds = 0; // public Condition clearToSend = lock.newCondition(); public SerialWriter(BlockingQueue q) { this.queue = q; //Setup Logging for SerialWriter if(Main.LOGLEVEL.equals("INFO")){ logger.setLevel(org.apache.log4j.Level.INFO); }else if( Main.LOGLEVEL.equals("ERROR")){ logger.setLevel(org.apache.log4j.Level.ERROR); }else{ logger.setLevel(org.apache.log4j.Level.OFF); } } public void resetBuffer() { //Called onDisconnectActions buffer_available.set(BUFFER_SIZE); notifyAck(); } public void clearQueueBuffer() { queue.clear(); this.cleared = true; // We set this to tell the mutex with waiting for an ack to send a line that it should not send a line.. we were asked to be cleared. try { //This is done in resetBuffer is this needed? buffer_available.set(BUFFER_SIZE); this.setThrottled(false); this.notifyAck(); } catch (Exception ex) { logger.error(ex); } } public boolean isRUN() { return RUN; } public void setRun(boolean RUN) { this.RUN = RUN; } public synchronized int getBufferValue() { return buffer_available.get(); } public synchronized void setBuffer(int val) { buffer_available.set(val); logger.debug("Got a BUFFER Response.. reset it to: " + val); } public synchronized void addBytesReturnedToBuffer(int lenBytesReturned) { buffer_available.set(getBufferValue() + lenBytesReturned); logger.debug("Returned " + lenBytesReturned + " to buffer. Buffer is now at " + buffer_available + "\n"); } public void addCommandToBuffer(String cmd) { this.queue.add(cmd); } public boolean setThrottled(boolean t) { synchronized (mutex) { if (t == throttled) { logger.debug("Throttled already set"); return false; } logger.debug("Setting Throttled " + t); throttled = t; } return true; } public void notifyAck() { //This is called by the response parser when an ack packet is recvd. This //Will wake up the mutex that is sleeping in the write method of the serialWriter //(this) class. synchronized (mutex) { logger.debug("Notifying the SerialWriter we have recvd an ACK"); mutex.notify(); } } private void sendUiMessage(String str) { //Used to send messages to the console on the GUI String gcodeComment = ""; int startComment = str.indexOf("("); int endComment = str.indexOf(")"); for (int i = startComment; i <= endComment; i++) { gcodeComment += str.charAt(i); } Main.postConsoleMessage(" Gcode Comment << " + gcodeComment); } // private int getChamberCount(int currentPlanningBuffer){ // //This function takes the current planning buffer and determines how many // //"rounds" or lines of gcode should be "chambered" or loaded before re read the pba // //and recalculating. // if(24 - currentPlanningBuffer > 3){ // // } // } public void write(String str) { try { synchronized (mutex) { // if (str.length() > getBufferValue()) { // setThrottled(true); // } else { // this.setBuffer(getBufferValue() - str.length()); // } int _currentPlanningBuffer = TinygDriver.getInstance().qr.getPba(); if(_currentPlanningBuffer < 28){ //if we have less that 28 moves in the planning buffer send a line } while (throttled) { if (str.length() > getBufferValue()) { logger.debug("Throttling: Line Length: " + str.length() + " is smaller than buffer length: " + buffer_available); setThrottled(true); } else { setThrottled(false); buffer_available.set(getBufferValue() - str.length()); break; } logger.debug("We are Throttled in the write method for SerialWriter"); //We wait here until the an ack comes in to the response parser // frees up some buffer space. Then we unlock the mutex and write the next line. mutex.wait(); if(cleared){ //clear out the line we were waiting to send.. we were asked to clear our buffer //includeing this line that is waiting to be sent. cleared = false; //Reset this flag now... return; } logger.debug("We are free from Throttled!"); } } if (str.contains("(")) { //Gcode Comment Push it back to the UI sendUiMessage(str); } ser.write(str); if(!Main.LOGLEVEL.equals("OFF")){ Main.print("+" + str); } } catch (InterruptedException ex) { logger.error("Error in SerialDriver Write"); } } @Override public void run() { Main.print("[+]Serial Writer Thread Running..."); while (RUN) { try { tmpCmd = queue.take(); //Grab the line if(tmpCmd.equals("**FILEDONE**")){ //Our end of file sending token has been detected. //We will not enable jogging by setting isSendingFile to false GcodeTabController.setIsFileSending(false); }else if(tmpCmd.startsWith("**COMMENT**")){ //Display current gcode comment GcodeTabController.setGcodeTextTemp("Comment: " + tmpCmd); continue; } this.write(tmpCmd); } catch (Exception ex) { Main.print("[!]Exception in SerialWriter Thread"); } } Main.print("[+]SerialWriter thread exiting..."); } }