Skip to content
Snippets Groups Projects
GenericGcodeDriver.java 31 KiB
Newer Older
/**
 * This file is part of LibLaserCut.
 * Copyright (C) 2011 - 2014 Thomas Oster <mail@thomas-oster.de>
 *
 * LibLaserCut is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * LibLaserCut is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with LibLaserCut. If not, see <http://www.gnu.org/licenses/>.
 *
 **/
package com.t_oster.liblasercut.drivers;

import com.t_oster.liblasercut.*;
import com.t_oster.liblasercut.platform.Util;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.lang.Exception;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import purejavacomm.*;
import java.util.*;
import net.sf.corn.httpclient.HttpClient;
import net.sf.corn.httpclient.HttpResponse;

/**
 * This class implements a driver for a generic GRBL GCode Lasercutter.
 * It should contain all possible options and is inteded to be the superclass
 * for e.g. the SmoothieBoard and the Lasersaur driver.
 *
 * @author Thomas Oster <thomas.oster@rwth-aachen.de>
 */
public class GenericGcodeDriver extends LaserCutter {

  protected static final String SETTING_HOST = "IP/Hostname";
  protected static final String SETTING_COMPORT = "COM Port";
  protected static final String SETTING_BAUDRATE = "Baud Rate (Serial)";
  protected static final String SETTING_BEDWIDTH = "Laserbed width";
  protected static final String SETTING_BEDHEIGHT = "Laserbed height";
  protected static final String SETTING_FLIP_X = "Flip X Axis";
  protected static final String SETTING_FLIP_Y = "Flip Y Axis";
  protected static final String SETTING_HTTP_UPLOAD_URL = "HTTP Upload URL";
  protected static final String SETTING_AUTOPLAY = "Start Job after HTTP Upload";
  protected static final String SETTING_LINEEND = "Lineend (CR,LF,CRLF)";
  protected static final String SETTING_MAX_SPEED = "Max speed (in mm/min)";
  protected static final String SETTING_TRAVEL_SPEED = "Travel (non laser moves) speed (in mm/min)";
  protected static final String SETTING_PRE_JOB_GCODE = "Pre-Job GCode (comma separated)";
  protected static final String SETTING_POST_JOB_GCODE = "Post-Job GCode (comma separated)";
  protected static final String SETTING_RESOLUTIONS = "Supported DPI (comma separated)";
  protected static final String SETTING_IDENTIFICATION_STRING = "Board Identification String (startsWith)";
  protected static final String SETTING_WAIT_FOR_OK = "Wait for OK after each line (interactive mode)";
  protected static final String SETTING_INIT_DELAY = "Seconds to wait for board reset (Serial)";
quillford's avatar
quillford committed
  protected static final String SETTING_SERIAL_TIMEOUT = "Milliseconds to wait for response";
  protected static final String SETTING_BLANK_LASER_DURING_RAPIDS = "Force laser off during G0 moves";
  protected static final String SETTING_FILE_EXPORT_PATH = "Path to save exported gcode";
  protected static final String SETTING_USE_BIDIRECTIONAL_RASTERING = "Use bidirectional rastering";
  protected static final String SETTING_SPINDLE_MAX = "S value for 100% laser power";
  protected static final String SETTING_UPLOAD_METHOD = "Upload method";

  protected static Locale FORMAT_LOCALE = Locale.US;

  protected static final String UPLOAD_METHOD_FILE = "File";
  protected static final String UPLOAD_METHOD_HTTP = "HTTP";
  protected static final String UPLOAD_METHOD_IP = "IP";
  protected static final String UPLOAD_METHOD_SERIAL = "Serial";

  protected static final String[] uploadMethodList = {UPLOAD_METHOD_FILE, UPLOAD_METHOD_HTTP, UPLOAD_METHOD_IP, UPLOAD_METHOD_SERIAL};

  private String lineend = "LF";

  public String getLineend()
  {
    return lineend;
  }

  public void setLineend(String lineend)
  {
    this.lineend = lineend;
  }
  protected String LINEEND()
  {
    return getLineend()
      .replace("LF", "\n")
      .replace("CR", "\r")
      .replace("\\r", "\r")
      .replace("\\n", "\n");
  }

  protected int baudRate = 115200;

  public int getBaudRate()
  {
    return baudRate;
  }

  public void setBaudRate(int baudRate)
  {
    this.baudRate = baudRate;
  }

  protected boolean flipXaxis = false;

  public boolean isFlipXaxis()
  {
    return flipXaxis;
  }

  public void setFlipXaxis(boolean flipXaxis)
  {
    this.flipXaxis = flipXaxis;
  }
  protected boolean flipYaxis = false;

  public boolean isFlipYaxis()
  {
    return flipYaxis;
  }

  public void setFlipYaxis(boolean flipYaxis)
  {
    this.flipYaxis = flipYaxis;
  }
  protected String httpUploadUrl = "http://10.10.10.100/upload";

  public String getHttpUploadUrl()
  {
    return httpUploadUrl;
  }

  public void setHttpUploadUrl(String httpUploadUrl)
  {
    this.httpUploadUrl = httpUploadUrl;
  }
  private boolean autoPlay = true;

  public boolean isAutoPlay()
  {
    return autoPlay;
  }

  public void setAutoPlay(boolean autoPlay)
  {
    this.autoPlay = autoPlay;
  }

  protected String supportedResolutions = "100,500,1000";

  public String getSupportedResolutions()
  {
    return supportedResolutions;
  }

  public void setSupportedResolutions(String supportedResolutions)
  {
    this.resolutions = null;
    this.supportedResolutions = supportedResolutions;
  }
  protected boolean waitForOKafterEachLine = true;

  public boolean isWaitForOKafterEachLine()
  {
    return waitForOKafterEachLine;
  }

  public void setWaitForOKafterEachLine(boolean waitForOKafterEachLine)
  {
    this.waitForOKafterEachLine = waitForOKafterEachLine;
  }

  public String getIdentificationLine()
  {
    return identificationLine;
  }

  public void setIdentificationLine(String identificationLine)
  {
    this.identificationLine = identificationLine;
  }
  protected String preJobGcode = "G21,G90";

  public String getPreJobGcode()
  {
    return preJobGcode;
  }

  public void setPreJobGcode(String preJobGcode)
  {
    this.preJobGcode = preJobGcode;
  }
  protected String postJobGcode = "G0 X0 Y0";

  public String getPostJobGcode()
  {
    return postJobGcode;
  }

  public void setPostJobGcode(String postJobGcode)
  {
    this.postJobGcode = postJobGcode;
  }
quillford's avatar
quillford committed
  protected int serialTimeout= 15000;

  public int getSerialTimeout()
  {
    return serialTimeout;
  }

  public void setSerialTimeout(int serialTimeout)
  {
    this.serialTimeout = serialTimeout;
  }
  private String exportPath = "";

  public void setExportPath(String path)
  {
    this.exportPath = path;
  }

  public String getExportPath()
  {
    return exportPath;
  }

  protected String uploadMethod = "";

  public void setUploadMethod(Object method)
  {
    this.uploadMethod = String.valueOf(method);
  }

  public OptionSelector getUploadMethod()
  {
    if (uploadMethod == null || uploadMethod.length() == 0)
    {
      // Determine using original connect() logic
      if (getHost() != null && getHost().length() > 0)
      {
        uploadMethod = UPLOAD_METHOD_IP;
      }
      else if (getComport() != null && !getComport().equals(""))
      {
        uploadMethod = UPLOAD_METHOD_SERIAL;
      }
      else if (getHttpUploadUrl() != null && getHttpUploadUrl().length() > 0)
      {
        uploadMethod = UPLOAD_METHOD_HTTP;
      }
      else if (getExportPath() != null && getExportPath().length() > 0)
      {
        uploadMethod = UPLOAD_METHOD_FILE;
      }
    }

    OptionSelector o = new OptionSelector(uploadMethodList, uploadMethod);
    return o;
  }

  /**
   * What is expected to be received after serial/telnet connection
   * Used e.g. for auto-detecting the serial port.
   */
  protected String identificationLine = "Grbl";
  @Override
  public String getModelName() {
Michael Adams's avatar
Michael Adams committed
    return "Generic GCode Driver";
  /**
   * Time to wait before firsts reads of serial port.
   * See autoreset feature on arduinos.
   */
  protected int initDelay = 5;

  public int getInitDelay()
  {
    return initDelay;
  }

  public void setInitDelay(int initDelay)
  {
    this.initDelay = initDelay;
  }
  protected String host = "10.10.10.222";

  public String getHost()
  {
    return host;
  }

  public void setHost(String host)
  {
    this.host = host;
  }
  protected String comport = "auto";

  public String getComport()
  {
    return comport;
  }

  public void setComport(String comport)
  {
    this.comport = comport;
  }
  protected double max_speed = 20*60;

  public double getMax_speed()
  {
    return max_speed;
  }

  public void setMax_speed(double max_speed)
  {
    this.max_speed = max_speed;
  }
  protected double travel_speed = 60*60;
quillford's avatar
quillford committed

  public double getTravel_speed()
  {
    return travel_speed;
  }

  public void setTravel_speed(double travel_speed)
  {
    this.travel_speed = travel_speed;
  }
  protected boolean blankLaserDuringRapids = false;
  public boolean getBlankLaserDuringRapids()
  {
    return blankLaserDuringRapids;
  }
  public void setBlankLaserDuringRapids(boolean blankLaserDuringRapids)
  {
    this.blankLaserDuringRapids = blankLaserDuringRapids;
  }
  /**
   * When rastering, whether to always cut from left to right, or to cut in both
   * directions? (i.e. use the return stroke to raster as well)
   */
  protected boolean useBidirectionalRastering = false;
  public boolean getUseBidirectionalRastering()
  {
    return useBidirectionalRastering;
  }
  public void setUseBidirectionalRastering(boolean useBidirectionalRastering)
  {
    this.useBidirectionalRastering = useBidirectionalRastering;
  }
   /*
   * Value to use for feedrate when laser is 100% on.
   * Varies between firmwares... 1, 100, 255, 10000, etc.
   */
  protected double spindleMax = 1.0;
  public double getSpindleMax()
  {
    return spindleMax;
  }
  public void setSpindleMax(double spindleMax)
  {
    this.spindleMax = spindleMax;
  }
  @Override
  /**
   * We do not support Frequency atm, so we return power,speed and focus
   */
  public LaserProperty getLaserPropertyForVectorPart() {
    return new FloatPowerSpeedFocusProperty();
  @Override
  public LaserProperty getLaserPropertyForRaster3dPart()
  {
    return new FloatPowerSpeedFocusProperty();
  }

  @Override
  public LaserProperty getLaserPropertyForRasterPart()
  {
    return new FloatPowerSpeedFocusProperty();
  }

  protected void writeVectorGCode(VectorPart vp, double resolution) throws UnsupportedEncodingException, IOException {
    for (VectorCommand cmd : vp.getCommandList()) {
      switch (cmd.getType()) {
        case MOVETO:
          int x = cmd.getX();
          int y = cmd.getY();
          move(out, x, y, resolution);
          break;
        case LINETO:
          x = cmd.getX();
          y = cmd.getY();
          line(out, x, y, resolution);
          break;
        case SETPROPERTY:
          FloatPowerSpeedFocusProperty p = (FloatPowerSpeedFocusProperty) cmd.getProperty();
          setPower(p.getPower());
          setSpeed(p.getSpeed());
          setFocus(out, p.getFocus(), resolution);
          break;
      }
    }
  }
  private double currentPower = -1;
  private double currentSpeed = -1;
  private double nextPower = -1;
  private double nextSpeed = -1;
  private double currentFocus = 0;

  protected void setSpeed(double speedInPercent) {
    nextSpeed = speedInPercent;
  }

  protected void setPower(double powerInPercent) {
    nextPower = powerInPercent/100.0*spindleMax;
  protected void setFocus(PrintStream out, double focus, double resolution) throws IOException {
    if (currentFocus != focus)
    {
      sendLine("G0 Z%f", Util.px2mm(focus, resolution));
      currentFocus = focus;
    }
  }

  protected void move(PrintStream out, double x, double y, double resolution) throws IOException {
    x = isFlipXaxis() ? getBedWidth() - Util.px2mm(x, resolution) : Util.px2mm(x, resolution);
    y = isFlipYaxis() ? getBedHeight() - Util.px2mm(y, resolution) : Util.px2mm(y, resolution);
quillford's avatar
quillford committed
    currentSpeed = getTravel_speed();
    if (blankLaserDuringRapids)
    {
      currentPower = 0.0;
      sendLine("G0 X%f Y%f F%d S0", x, y, (int) (travel_speed));
    }
    else
    {
      sendLine("G0 X%f Y%f F%d", x, y, (int) (travel_speed));
    }
  protected void line(PrintStream out, double x, double y, double resolution) throws IOException {
    x = isFlipXaxis() ? getBedWidth() - Util.px2mm(x, resolution) : Util.px2mm(x, resolution);
    y = isFlipYaxis() ? getBedHeight() - Util.px2mm(y, resolution) : Util.px2mm(y, resolution);
    String append = "";
    if (nextPower != currentPower)
    {
      append += String.format(FORMAT_LOCALE, " S%f", nextPower);
      currentPower = nextPower;
    }
    if (nextSpeed != currentSpeed)
    {
      append += String.format(FORMAT_LOCALE, " F%d", (int) (max_speed*nextSpeed/100.0));
    sendLine("G1 X%f Y%f"+append, x, y);
  }

  private void writeInitializationCode() throws IOException {
    if (preJobGcode != null)
    {
      for (String line : preJobGcode.split(","))
      {
        sendLine(line);
      }
    }
  }

  private void writeShutdownCode() throws IOException {
    if (postJobGcode != null)
    {
      for (String line : postJobGcode.split(","))
      {
        sendLine(line);
      }
    }
  }

Michael Adams's avatar
Michael Adams committed
  protected BufferedReader in;
  protected PrintStream out;
  private Socket socket;
  private CommPort port;
  private CommPortIdentifier portIdentifier;
  protected void sendLine(String text, Object... parameters) throws IOException
  {
    out.format(FORMAT_LOCALE, text+LINEEND(), parameters);
    System.out.format(FORMAT_LOCALE, "> "+text+LINEEND(), parameters);
    if (isWaitForOKafterEachLine())
    {
      String line = waitForLine();
        throw new IOException("Lasercutter did not respond 'ok', but '"+line+"'instead.");
  protected void http_upload(URI url, String data, String filename) throws IOException
  {
    HttpClient client = new HttpClient(url);
    client.putAdditionalRequestProperty("X-Filename", filename);
    HttpResponse response = client.sendData(HttpClient.HTTP_METHOD.POST, data);
    if (response == null || response.hasError())
    {
      throw new IOException("Error during POST Request");
    }
    System.out.println("Response: "+response.toString());//TODO: Remove
  }
  protected void http_play(String filename) throws IOException, URISyntaxException
  {
    URI url = new URI(getHttpUploadUrl().replace("upload", "command"));
    String command = "play /sd/"+filename+"\n";
    HttpClient client = new HttpClient(url);
    HttpResponse response = client.sendData(HttpClient.HTTP_METHOD.POST, command);
    if (response == null || response.hasError())
    {
      throw new IOException("Error during POST Request");
    }
    System.out.println("Response: "+response.toString());//TODO: Remove
  }
  protected String waitForLine() throws IOException
  {
    String line = "";
    while ("".equals(line))
    {//skip empty lines
      line = in.readLine();
    }
    System.out.println("< "+line);//TODO: remove
    return line;
  }
  /**
   * Waits for the Identification line and returns null if it's allright
   * Otherwise it returns the wrong line
Michael Adams's avatar
Michael Adams committed
   * @param pl Progress listener to update during connect process
Michael Adams's avatar
Michael Adams committed
  protected String waitForIdentificationLine(ProgressListener pl) throws IOException
  {
    if (getIdentificationLine() != null && getIdentificationLine().length() > 0)
    {
      String line = "";
      for (int trials = 3; trials > 0; trials--)
        line = waitForLine();
        if (line.startsWith(getIdentificationLine()))
        {
          return null;
        }
  protected String connect_serial(CommPortIdentifier i, ProgressListener pl) throws PortInUseException, IOException, UnsupportedCommOperationException
  {
    pl.taskChanged(this, "opening '"+i.getName()+"'");
    if (i.getPortType() == CommPortIdentifier.PORT_SERIAL)
    {
      try
      {
        port = i.open("VisiCut", 1000);
quillford's avatar
quillford committed
          port.enableReceiveTimeout(getSerialTimeout());
        }
        catch (UnsupportedCommOperationException e)
        {
          System.err.println("Serial timeout not supported. Driver may hang if device does not respond properly.");
        }
        if (this.getBaudRate() > 0 && port instanceof SerialPort)
        {
          SerialPort sp = (SerialPort) port;
          sp.setSerialPortParams(getBaudRate(), 8, 1, 0);
        out = new PrintStream(port.getOutputStream(), true, "US-ASCII");
        in = new BufferedReader(new InputStreamReader(port.getInputStream()));
        // Wait 5 seconds since GRBL is long to wake up..
        for (int rest = getInitDelay(); rest > 0; rest--) {
          pl.taskChanged(this, String.format(FORMAT_LOCALE, "Waiting %ds", rest));
          try
          {
            Thread.sleep(1000);
          }
          catch(InterruptedException ex)
          {
            Thread.currentThread().interrupt();
          }
        }
Michael Adams's avatar
Michael Adams committed
        if (waitForIdentificationLine(pl) != null)
          in.close();
          out.close();
          port.close();
          return "Does not seem to be a "+getModelName()+" on "+i.getName();
        pl.taskChanged(this, "Connected");
        return null;
      }
      catch (PortInUseException e)
      {
        try { disconnect(""); } catch (Exception ex) { System.out.println(ex.getMessage()); }
        return "Port in use: "+i.getName();
        try { disconnect(""); } catch (Exception ex) { System.out.println(ex.getMessage()); }
        return "IO Error from "+i.getName()+": "+e.getMessage();
      }
      catch (PureJavaIllegalStateException e)
      {
        try { disconnect(""); } catch (Exception ex) { System.out.println(ex.getMessage()); }
        return "Could not open "+i.getName()+": "+e.getMessage();
  /**
   * Used to buffer the file before uploading via http
   */
  private ByteArrayOutputStream outputBuffer;
  private String jobName;
  protected void connect(ProgressListener pl) throws IOException, PortInUseException, NoSuchPortException, UnsupportedCommOperationException
    outputBuffer = null;
    if (UPLOAD_METHOD_IP.equals(uploadMethod))
      if (getHost() == null || getHost().equals(""))
      {
        throw new IOException("IP/Hostname must be set to upload via IP method");
      }
      socket = new Socket();
      socket.connect(new InetSocketAddress(getHost(), 23), 1000);
      in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
      out = new PrintStream(socket.getOutputStream(), true, "US-ASCII");
Michael Adams's avatar
Michael Adams committed
      String line = waitForIdentificationLine(pl);
      if (line != null)
      {
        in.close();
        out.close();
        throw new IOException("Wrong identification Line: "+line+"\n instead of "+getIdentificationLine());
      }
    else if (UPLOAD_METHOD_SERIAL.equals(uploadMethod))
    {
      String error = "No serial port found";
      if (portIdentifier == null && !getComport().equals("auto") && !getComport().equals(""))
        try {
          portIdentifier = CommPortIdentifier.getPortIdentifier(getComport());
        }
        catch (NoSuchPortException e) {
          throw new IOException("No such port: "+getComport());
        }
      if (portIdentifier != null)
      {//use port identifier we had last time
        error = connect_serial(portIdentifier, pl);
      }
      else
      {
        Enumeration e = CommPortIdentifier.getPortIdentifiers();
          CommPortIdentifier i = (CommPortIdentifier) e.nextElement();
          if (i.getPortType() == CommPortIdentifier.PORT_SERIAL)
          {
            error = connect_serial(i, pl);
            if (error == null)
            {
              break;
            }
          }
        }
      }
      if (error != null)
      {
        throw new IOException(error);
      }
    }
    else if (UPLOAD_METHOD_HTTP.equals(uploadMethod))
      if (getHttpUploadUrl() == null || getHttpUploadUrl().equals(""))
      {
        throw new IOException("HTTP Upload URL must be set to upload via HTTP method");
      }
      outputBuffer = new ByteArrayOutputStream();
      out = new PrintStream(outputBuffer);
      setWaitForOKafterEachLine(false);
      in = null;
    }
    else if (UPLOAD_METHOD_FILE.equals(uploadMethod))
      if (getExportPath() == null || getExportPath().equals(""))
      {
        throw new IOException("Export Path must be set to upload via File method.");
      }
      File file = new File(getExportPath(), this.jobName);
      out = new PrintStream(new FileOutputStream(file));
      setWaitForOKafterEachLine(false);
      in = null;
    }
      throw new IOException("Upload Method must be set");
  protected void disconnect(String jobname) throws IOException, URISyntaxException
    if (outputBuffer != null)
      out.close();
      http_upload(new URI(getHttpUploadUrl()), outputBuffer.toString("UTF-8"), jobname);
      if (this.isAutoPlay())
      {
        http_play(jobname);
      }
      out.close();
      if (this.socket != null)
      {
        socket.close();
        socket = null;
      }
      else if (this.port != null)
      {
        this.port.close();
        this.port = null;
  @Override
  public void sendJob(LaserJob job, ProgressListener pl, List<String> warnings) throws IllegalJobException, Exception {
    pl.progressChanged(this, 0);
    this.currentPower = -1;
    this.currentSpeed = -1;

    pl.taskChanged(this, "checking job");
    checkJob(job);
    this.jobName = job.getName()+".gcode";
    job.applyStartPoint();
    pl.taskChanged(this, "connecting...");
    connect(pl);
    pl.taskChanged(this, "sending");
    try {
      writeInitializationCode();
      pl.progressChanged(this, 20);
      int i = 0;
      int max = job.getParts().size();
      for (JobPart p : job.getParts())
        if (p instanceof Raster3dPart || p instanceof RasterPart)
          p = convertRasterizableToVectorPart((RasterizableJobPart) p, p.getDPI(), getUseBidirectionalRastering());
        }
        if (p instanceof VectorPart)
        {
          //TODO: in direct mode use progress listener to indicate progress
          //of individual job
          writeVectorGCode((VectorPart) p, p.getDPI());
        }
        i++;
        pl.progressChanged(this, 20 + (int) (i*(double) 60/max));
      writeShutdownCode();
      disconnect(job.getName()+".gcode");
    }
    catch (IOException e) {
      pl.taskChanged(this, "disconnecting");
      disconnect(this.jobName);
    }
    pl.taskChanged(this, "sent.");
    pl.progressChanged(this, 100);
  }
public void saveJob(java.io.PrintStream fileOutputStream, LaserJob job) throws IllegalJobException, Exception {
  this.currentPower = -1;
  this.currentSpeed = -1;
	this.out = fileOutputStream;
	boolean wasSetWaitingForOk = isWaitForOKafterEachLine();
	writeInitializationCode();
	for (JobPart p : job.getParts())
	{
		if (p instanceof Raster3dPart || p instanceof RasterPart)
			p = convertRasterizableToVectorPart((RasterizableJobPart) p, p.getDPI(), getUseBidirectionalRastering());
		{
			writeVectorGCode((VectorPart) p, p.getDPI());
		}
	}
	writeShutdownCode();
	setWaitForOKafterEachLine(wasSetWaitingForOk);
  private List<Double> resolutions;

  @Override
  public List<Double> getResolutions() {
    if (resolutions == null) {
      resolutions = new LinkedList<Double>();
      for (String s : getSupportedResolutions().split(","))
      {
        resolutions.add(Double.parseDouble(s));
      }
    }
    return resolutions;
  }
  protected double bedWidth = 250;

  /**
   * Get the value of bedWidth
   *
   * @return the value of bedWidth
   */
  @Override
  public double getBedWidth() {
    return bedWidth;
  }

  /**
   * Set the value of bedWidth
   *
   * @param bedWidth new value of bedWidth
   */
  public void setBedWidth(double bedWidth) {
    this.bedWidth = bedWidth;
  }
  protected double bedHeight = 280;

  /**
   * Get the value of bedHeight
   *
   * @return the value of bedHeight
   */
  @Override
  public double getBedHeight() {
    return bedHeight;
  }

  /**
   * Set the value of bedHeight
   *
   * @param bedHeight new value of bedHeight
   */
  public void setBedHeight(double bedHeight) {
    this.bedHeight = bedHeight;
  }
  private static String[] settingAttributes = new String[]{
    SETTING_BEDWIDTH,
    SETTING_BEDHEIGHT,
    SETTING_COMPORT,
    SETTING_FLIP_X,
    SETTING_FLIP_Y,
    SETTING_HTTP_UPLOAD_URL,
    SETTING_AUTOPLAY,
    SETTING_IDENTIFICATION_STRING,
quillford's avatar
quillford committed
    SETTING_TRAVEL_SPEED,
    SETTING_PRE_JOB_GCODE,
    SETTING_POST_JOB_GCODE,
    SETTING_RESOLUTIONS,
quillford's avatar
quillford committed
    SETTING_WAIT_FOR_OK,
    SETTING_SERIAL_TIMEOUT,
    SETTING_FILE_EXPORT_PATH,
    SETTING_USE_BIDIRECTIONAL_RASTERING
  };

  @Override
  public String[] getPropertyKeys() {
    return settingAttributes;
  }

  @Override
  public Object getProperty(String attribute) {
    if (SETTING_HOST.equals(attribute)) {
      return this.getHost();
    } else if (SETTING_BAUDRATE.equals(attribute)) {
      return this.getBaudRate();
    } else if (SETTING_BEDWIDTH.equals(attribute)) {
      return this.getBedWidth();
    } else if (SETTING_BEDHEIGHT.equals(attribute)) {
      return this.getBedHeight();
    } else if (SETTING_COMPORT.equals(attribute)) {
      return this.getComport();
    } else if (SETTING_FLIP_X.equals(attribute)) {
      return this.isFlipXaxis();
    } else if (SETTING_FLIP_Y.equals(attribute)) {
      return this.isFlipYaxis();
    } else if (SETTING_HOST.equals(attribute)) {
      return this.getHost();
    } else if (SETTING_HTTP_UPLOAD_URL.equals(attribute)) {
      return this.getHttpUploadUrl();
    } else if (SETTING_AUTOPLAY.equals(attribute)) {
      return this.isAutoPlay();
    } else if (SETTING_IDENTIFICATION_STRING.equals(attribute)) {
      return this.getIdentificationLine();
    } else if (SETTING_INIT_DELAY.equals(attribute)) {
      return this.getInitDelay();
    } else if (SETTING_LINEEND.equals(attribute)) {
      return this.getLineend();
    } else if (SETTING_MAX_SPEED.equals(attribute)) {
      return this.getMax_speed();
quillford's avatar
quillford committed
    } else if (SETTING_TRAVEL_SPEED.equals(attribute)) {
      return this.getTravel_speed();
    } else if (SETTING_PRE_JOB_GCODE.equals(attribute)) {
      return this.getPreJobGcode();
    } else if (SETTING_POST_JOB_GCODE.equals(attribute)) {
      return this.getPostJobGcode();
    } else if (SETTING_RESOLUTIONS.equals(attribute)) {
      return this.getSupportedResolutions();
    } else if (SETTING_WAIT_FOR_OK.equals(attribute)) {
      return this.isWaitForOKafterEachLine();
quillford's avatar
quillford committed
    } else if (SETTING_SERIAL_TIMEOUT.equals(attribute)) {
      return this.getSerialTimeout();
    } else if (SETTING_BLANK_LASER_DURING_RAPIDS.equals(attribute)) {
      return this.getBlankLaserDuringRapids();
    } else if (SETTING_FILE_EXPORT_PATH.equals(attribute)) {
      return this.getExportPath();
    } else if (SETTING_USE_BIDIRECTIONAL_RASTERING.equals(attribute)) {
      return this.getUseBidirectionalRastering();
    } else if (SETTING_SPINDLE_MAX.equals(attribute)) {
      return this.getSpindleMax();
    } else if (SETTING_UPLOAD_METHOD.equals(attribute)) {
      return this.getUploadMethod();
    return null;
  }

  @Override
  public void setProperty(String attribute, Object value) {
    if (SETTING_HOST.equals(attribute)) {
      this.setHost((String) value);
    } else if (SETTING_BAUDRATE.equals(attribute)) {
      this.setBaudRate((Integer) value);
    } else if (SETTING_BEDWIDTH.equals(attribute)) {
      this.setBedWidth((Double) value);
    } else if (SETTING_BEDHEIGHT.equals(attribute)) {
      this.setBedHeight((Double) value);
    } else if (SETTING_COMPORT.equals(attribute)) {
      this.setComport((String) value);
    } else if (SETTING_FLIP_X.equals(attribute)) {
      this.setFlipXaxis((Boolean) value);
    } else if (SETTING_FLIP_Y.equals(attribute)) {
      this.setFlipYaxis((Boolean) value);
    } else if (SETTING_HOST.equals(attribute)) {
      this.setHost((String) value);
    } else if (SETTING_HTTP_UPLOAD_URL.equals(attribute)) {
      this.setHttpUploadUrl((String) value);
    } else if (SETTING_AUTOPLAY.equals(attribute)) {
      this.setAutoPlay((Boolean) value);
    } else if (SETTING_IDENTIFICATION_STRING.equals(attribute)) {
      this.setIdentificationLine((String) value);
    } else if (SETTING_INIT_DELAY.equals(attribute)) {
      this.setInitDelay((Integer) value);
    } else if (SETTING_LINEEND.equals(attribute)) {
      this.setLineend((String) value);
    } else if (SETTING_MAX_SPEED.equals(attribute)) {
      this.setMax_speed((Double) value);
quillford's avatar
quillford committed
    } else if (SETTING_TRAVEL_SPEED.equals(attribute)) {
      this.setTravel_speed((Double) value);
    } else if (SETTING_PRE_JOB_GCODE.equals(attribute)) {
      this.setPreJobGcode((String) value);
    } else if (SETTING_POST_JOB_GCODE.equals(attribute)) {
      this.setPostJobGcode((String) value);
    } else if (SETTING_RESOLUTIONS.equals(attribute)) {
      this.setSupportedResolutions((String) value);
    } else if (SETTING_WAIT_FOR_OK.equals(attribute)) {
      this.setWaitForOKafterEachLine((Boolean) value);
quillford's avatar
quillford committed
    } else if (SETTING_SERIAL_TIMEOUT.equals(attribute)) {
      this.setSerialTimeout((Integer) value);
    } else if (SETTING_BLANK_LASER_DURING_RAPIDS.equals(attribute)) {
      this.setBlankLaserDuringRapids((Boolean) value);
    } else if (SETTING_FILE_EXPORT_PATH.equals(attribute)) {
      this.setExportPath((String) value);
    } else if (SETTING_USE_BIDIRECTIONAL_RASTERING.equals(attribute)) {
      this.setUseBidirectionalRastering((Boolean) value);
    } else if (SETTING_SPINDLE_MAX.equals(attribute)) {
      this.setSpindleMax((Double) value);
    } else if (SETTING_UPLOAD_METHOD.equals(attribute)) {
      this.setUploadMethod(value);
  /**
   * Adjust defaults after deserializing driver from an old version of XML file
   */
  @Override
  protected void setKeysMissingFromDeserialization()
  {
    // added field spindleMax, needs to be set to 1.0 by default
    // but xstream initializes it to 0.0 when it is missing from XML
    if (this.spindleMax <= 0.0) this.spindleMax = 1.0;
  }
  @Override
  public GenericGcodeDriver clone() {
    GenericGcodeDriver clone = new GenericGcodeDriver();
    clone.copyProperties(this);
    return clone;
  }