Skip to content
Snippets Groups Projects
LaosCutter.java 28.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • Thomas Oster's avatar
    Thomas Oster committed
    /**
     * This file is part of VisiCut.
     * Copyright (C) 2011 Thomas Oster <thomas.oster@rwth-aachen.de>
     * RWTH Aachen University - 52062 Aachen, Germany
     * 
     *     VisiCut 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.
     * 
     *    VisiCut 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 VisiCut.  If not, see <http://www.gnu.org/licenses/>.
     **/
    package com.t_oster.liblasercut.drivers;
    
    import com.t_oster.liblasercut.BlackWhiteRaster;
    import com.t_oster.liblasercut.IllegalJobException;
    import com.t_oster.liblasercut.LaserCutter;
    import com.t_oster.liblasercut.LaserJob;
    import com.t_oster.liblasercut.LaserProperty;
    
    import com.t_oster.liblasercut.ProgressListener;
    
    Thomas Oster's avatar
    Thomas Oster committed
    import com.t_oster.liblasercut.Raster3dPart;
    import com.t_oster.liblasercut.RasterPart;
    import com.t_oster.liblasercut.VectorCommand;
    import com.t_oster.liblasercut.VectorPart;
    import com.t_oster.liblasercut.platform.Point;
    import com.t_oster.liblasercut.platform.Util;
    
    Thomas Oster's avatar
    Thomas Oster committed
    import java.net.InetSocketAddress;
    import java.net.Socket;
    
    Thomas Oster's avatar
    Thomas Oster committed
    import java.util.Arrays;
    
    Thomas Oster's avatar
    Thomas Oster committed
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Locale;
    
    import org.apache.commons.net.tftp.TFTP;
    import org.apache.commons.net.tftp.TFTPClient;
    
    Thomas Oster's avatar
    Thomas Oster committed
    
    /**
     * This class implements a driver for the LAOS Lasercutter board.
     * Currently it supports the simple code and the G-Code, which may be used in
     * the future.
     * 
     * @author Thomas Oster <thomas.oster@rwth-aachen.de>
     */
    public class LaosCutter extends LaserCutter
    {
    
      private static final String SETTING_HOSTNAME = "Hostname / IP";
      private static final String SETTING_PORT = "Port";
      private static final String SETTING_BEDWIDTH = "Laserbed width";
      private static final String SETTING_BEDHEIGHT = "Laserbed height";
      private static final String SETTING_FLIPX = "X axis goes right to left (yes/no)";
    
      private static final String SETTING_FLIPY = "Y axis goes bottom to top (yes/no)";
    
    Thomas Oster's avatar
    Thomas Oster committed
      private static final String SETTING_MMPERSTEP = "mm per Step (for SimpleMode)";
    
      private static final String SETTING_TFTP = "Use TFTP instead of TCP";
    
      private static final String SETTING_RASTER_WHITESPACE = "Additional space per Raster line";
    
      private static final String SETTING_NATIVERASTER = "Use native (LAOS) rastermode";
      private static final String SETTING_UNIDIR = "Engrave unidirectional";
      
      private boolean unidir = false;
      
      public void setEngraveUnidirectional(boolean uni)
      {
        this.unidir = uni;
      }
      
      public boolean isEngraveUnidirectional()
      {
        return this.unidir;
      }
      
    
      private boolean useLaosRastermode = true;
    
      /**
       * Get the value of useLaosRastermode
       *
       * @return the value of useLaosRastermode
       */
      public boolean isUseLaosRastermode()
      {
        return useLaosRastermode;
      }
    
      /**
       * Set the value of useLaosRastermode
       *
       * @param useLaosRastermode new value of useLaosRastermode
       */
      public void setUseLaosRastermode(boolean useLaosRastermode)
      {
        this.useLaosRastermode = useLaosRastermode;
      }
    
      
    
      private double addSpacePerRasterLine = 5;
    
      /**
       * Get the value of addSpacePerRasterLine
       *
       * @return the value of addSpacePerRasterLine
       */
      public double getAddSpacePerRasterLine()
      {
        return addSpacePerRasterLine;
      }
    
      /**
       * Set the value of addSpacePerRasterLine
       * This is a space (in mm) for the laserhead to gain
       * speed before the first 'black' pixel in every line
       *
       * @param addSpacePerRasterLine new value of addSpacePerRasterLine
       */
      public void setAddSpacePerRasterLine(double addSpacePerRasterLine)
      {
        this.addSpacePerRasterLine = addSpacePerRasterLine;
      }
    
      
    
      @Override
      public String getModelName()
      {
        return "LAOS";
      }
      protected boolean useTftp = true;
    
      /**
       * Get the value of useTftp
       *
       * @return the value of useTftp
       */
      public boolean isUseTftp()
      {
        return useTftp;
      }
    
      /**
       * Set the value of useTftp
       *
       * @param useTftp new value of useTftp
       */
      public void setUseTftp(boolean useTftp)
      {
        this.useTftp = useTftp;
      }
    
    Thomas Oster's avatar
    Thomas Oster committed
      protected boolean flipXaxis = false;
    
      /**
       * Get the value of flipXaxis
       *
       * @return the value of flipXaxis
       */
      public boolean isFlipXaxis()
      {
        return flipXaxis;
      }
    
      /**
       * Set the value of flipXaxis
       *
       * @param flipXaxis new value of flipXaxis
       */
      public void setFlipXaxis(boolean flipXaxis)
      {
        this.flipXaxis = flipXaxis;
      }
    
      protected boolean flipYaxis = true;
    
    
      /**
       * Get the value of flipYaxis
       *
       * @return the value of flipYaxis
       */
      public boolean isFlipYaxis()
      {
        return flipYaxis;
      }
    
      /**
       * Set the value of flipYaxis
       *
       * @param flipYaxis new value of flipYaxis
       */
      public void setFlipYaxis(boolean flipYaxis)
      {
        this.flipYaxis = flipYaxis;
      }
      
    
      protected String hostname = "192.168.123.111";
    
    Thomas Oster's avatar
    Thomas Oster committed
    
      /**
       * Get the value of hostname
       *
       * @return the value of hostname
       */
      public String getHostname()
      {
        return hostname;
      }
    
      /**
       * Set the value of hostname
       *
       * @param hostname new value of hostname
       */
      public void setHostname(String hostname)
      {
        this.hostname = hostname;
      }
    
    Thomas Oster's avatar
    Thomas Oster committed
      protected int port = 69;
    
    Thomas Oster's avatar
    Thomas Oster committed
    
      /**
       * Get the value of port
       *
       * @return the value of port
       */
      public int getPort()
      {
        return port;
      }
    
      /**
       * Set the value of port
       *
       * @param port new value of port
       */
      public void setPort(int port)
      {
        this.port = port;
      }
    
    Thomas Oster's avatar
    Thomas Oster committed
      protected double mmPerStep = 0.001;
    
    Thomas Oster's avatar
    Thomas Oster committed
    
      /**
       * Get the value of mmPerStep
       *
       * @return the value of mmPerStep
       */
      public double getMmPerStep()
      {
        return mmPerStep;
      }
    
      /**
       * Set the value of mmPerStep
       *
       * @param mmPerStep new value of mmPerStep
       */
      public void setMmPerStep(double mmPerStep)
      {
        this.mmPerStep = mmPerStep;
      }
    
      private int px2steps(double px, double dpi)
      {
    
        return (int) (Util.px2mm(px, dpi) / this.mmPerStep);
    
    Thomas Oster's avatar
    Thomas Oster committed
      }
    
      private byte[] generateVectorGCode(VectorPart vp, int resolution) throws UnsupportedEncodingException
      {
        ByteArrayOutputStream result = new ByteArrayOutputStream();
        PrintStream out = new PrintStream(result, true, "US-ASCII");
        int power = 100;
        int speed = 50;
        int frequency = 500;
        double focus = 0;
    
    Thomas Oster's avatar
    Thomas Oster committed
        //reset saved values, so the first ones get verbosed
        this.currentPower = -1;
        this.currentSpeed = -1;
        this.currentFrequency = -1;
    
    Thomas Oster's avatar
    Thomas Oster committed
        for (VectorCommand cmd : vp.getCommandList())
        {
          switch (cmd.getType())
          {
            case MOVETO:
              move(out, cmd.getX(), cmd.getY(), resolution);
              break;
            case LINETO:
              line(out, cmd.getX(), cmd.getY(), power, speed, frequency, resolution);
              break;
            case SETPOWER:
              power = cmd.getPower();
              break;
            case SETFOCUS:
    
              out.printf(Locale.US, "2 %d\n", (int) Util.mm2px(cmd.getFocus(), resolution));
    
    Thomas Oster's avatar
    Thomas Oster committed
              break;
            case SETSPEED:
              speed = cmd.getSpeed();
              break;
            case SETFREQUENCY:
              frequency = cmd.getFrequency();
              break;
          }
        }
        return result.toByteArray();
      }
    
      private void move(PrintStream out, int x, int y, int resolution)
      {
    
        out.printf("0 %d %d\n", px2steps(isFlipXaxis() ? Util.mm2px(bedWidth, resolution) - x : x, resolution), px2steps(isFlipYaxis() ? Util.mm2px(bedHeight, resolution) - y : y, resolution));
    
    Thomas Oster's avatar
    Thomas Oster committed
      }
      private int currentPower = -1;
      private int currentSpeed = -1;
      private int currentFrequency = -1;
    
    Thomas Oster's avatar
    Thomas Oster committed
      private void line(PrintStream out, int x, int y, int power, int speed, int frequency, int resolution)
      {
    
          out.printf("7 101 %d\n", power * 100);
          currentPower = power;
    
        if (currentSpeed != speed)
        {
          out.printf("7 100 %d\n", speed * 100);
          currentSpeed = speed;
        }
        if (currentFrequency != frequency)
        {
          out.printf("7 102 %d\n", frequency);
          currentFrequency = frequency;
    
        out.printf("1 %d %d\n", px2steps(isFlipXaxis() ? Util.mm2px(bedWidth, resolution) - x : x, resolution), px2steps(isFlipYaxis() ? Util.mm2px(bedHeight, resolution) - y : y, resolution));
    
    Thomas Oster's avatar
    Thomas Oster committed
      }
    
      private byte[] generatePseudoRaster3dGCode(Raster3dPart rp, int resolution) throws UnsupportedEncodingException
      {
        ByteArrayOutputStream result = new ByteArrayOutputStream();
        PrintStream out = new PrintStream(result, true, "US-ASCII");
        boolean dirRight = true;
        for (int raster = 0; raster < rp.getRasterCount(); raster++)
        {
          Point rasterStart = rp.getRasterStart(raster);
          LaserProperty prop = rp.getLaserProperty(raster);
          //Set focus
    
          out.printf(Locale.US, "2 %d\n", (int) Util.mm2px(prop.getFocus(), resolution));
    
    Thomas Oster's avatar
    Thomas Oster committed
          for (int line = 0; line < rp.getRasterHeight(raster); line++)
          {
            Point lineStart = rasterStart.clone();
            lineStart.y += line;
            List<Byte> bytes = rp.getRasterLine(raster, line);
            //remove heading zeroes
            while (bytes.size() > 0 && bytes.get(0) == 0)
            {
              bytes.remove(0);
              lineStart.x += 1;
            }
            //remove trailing zeroes
    
            while (bytes.size() > 0 && bytes.get(bytes.size() - 1) == 0)
    
              bytes.remove(bytes.size() - 1);
    
    Thomas Oster's avatar
    Thomas Oster committed
            }
            if (bytes.size() > 0)
            {
              if (dirRight)
              {
                //move to the first nonempyt point of the line
                move(out, lineStart.x, lineStart.y, resolution);
                byte old = bytes.get(0);
                for (int pix = 0; pix < bytes.size(); pix++)
                {
                  if (bytes.get(pix) != old)
                  {
                    if (old == 0)
                    {
    
                      move(out, lineStart.x + pix, lineStart.y, resolution);
    
                      line(out, lineStart.x + pix - 1, lineStart.y, prop.getPower() * (0xFF & old) / 255, prop.getSpeed(), prop.getFrequency(), resolution);
                      move(out, lineStart.x + pix, lineStart.y, resolution);
    
    Thomas Oster's avatar
    Thomas Oster committed
                    }
                    old = bytes.get(pix);
                  }
                }
                //last point is also not "white"
    
                line(out, lineStart.x + bytes.size() - 1, lineStart.y, prop.getPower() * (0xFF & bytes.get(bytes.size() - 1)) / 255, prop.getSpeed(), prop.getFrequency(), resolution);
    
    Thomas Oster's avatar
    Thomas Oster committed
              }
              else
              {
                //move to the last nonempty point of the line
    
                move(out, lineStart.x + bytes.size() - 1, lineStart.y, resolution);
                byte old = bytes.get(bytes.size() - 1);
                for (int pix = bytes.size() - 1; pix >= 0; pix--)
    
    Thomas Oster's avatar
    Thomas Oster committed
                {
                  if (bytes.get(pix) != old || pix == 0)
                  {
                    if (old == 0)
                    {
    
                      move(out, lineStart.x + pix, lineStart.y, resolution);
    
                      line(out, lineStart.x + pix + 1, lineStart.y, prop.getPower() * (0xFF & old) / 255, prop.getSpeed(), prop.getFrequency(), resolution);
                      move(out, lineStart.x + pix, lineStart.y, resolution);
    
    Thomas Oster's avatar
    Thomas Oster committed
                    }
                    old = bytes.get(pix);
                  }
                }
                //last point is also not "white"
    
                line(out, lineStart.x, lineStart.y, prop.getPower() * (0xFF & bytes.get(0)) / 255, prop.getSpeed(), prop.getFrequency(), resolution);
    
            if (!this.isEngraveUnidirectional())
            {
              dirRight = !dirRight;
            }
    
    Thomas Oster's avatar
    Thomas Oster committed
          }
        }
        return result.toByteArray();
      }
    
    Thomas Oster's avatar
    Thomas Oster committed
      private byte[] generatePseudoRasterGCode(RasterPart rp, int resolution) throws UnsupportedEncodingException
      {
        ByteArrayOutputStream result = new ByteArrayOutputStream();
        PrintStream out = new PrintStream(result, true, "US-ASCII");
        boolean dirRight = true;
        for (int raster = 0; raster < rp.getRasterCount(); raster++)
        {
          Point rasterStart = rp.getRasterStart(raster);
          LaserProperty prop = rp.getLaserProperty(raster);
          //Set focus
    
          out.printf(Locale.US, "2 %d\n", (int) Util.mm2px(prop.getFocus(), resolution));
    
    Thomas Oster's avatar
    Thomas Oster committed
          for (int line = 0; line < rp.getRasterHeight(raster); line++)
          {
            Point lineStart = rasterStart.clone();
            lineStart.y += line;
            //Convert BlackWhite line into line of 0 and 255 bytes
            BlackWhiteRaster bwr = rp.getImages()[raster];
            List<Byte> bytes = new LinkedList<Byte>();
            boolean lookForStart = true;
    
            for (int x = 0; x < bwr.getWidth(); x++)
    
    Thomas Oster's avatar
    Thomas Oster committed
            {
              if (lookForStart)
              {
                if (bwr.isBlack(x, line))
                {
                  lookForStart = false;
                  bytes.add((byte) 255);
                }
                else
                {
    
                  lineStart.x += 1;
    
    Thomas Oster's avatar
    Thomas Oster committed
                }
              }
              else
              {
                bytes.add(bwr.isBlack(x, line) ? (byte) 255 : (byte) 0);
              }
            }
            //remove trailing zeroes
    
            while (bytes.size() > 0 && bytes.get(bytes.size() - 1) == 0)
    
              bytes.remove(bytes.size() - 1);
    
    Thomas Oster's avatar
    Thomas Oster committed
            }
            if (bytes.size() > 0)
            {
              if (dirRight)
              {
    
                //add some space to the left
                move(out, Math.max(0, (int) (lineStart.x-Util.mm2px(this.addSpacePerRasterLine, resolution))), lineStart.y, resolution);
    
    Thomas Oster's avatar
    Thomas Oster committed
                //move to the first nonempyt point of the line
                move(out, lineStart.x, lineStart.y, resolution);
                byte old = bytes.get(0);
                for (int pix = 0; pix < bytes.size(); pix++)
                {
                  if (bytes.get(pix) != old)
                  {
                    if (old == 0)
                    {
    
                      move(out, lineStart.x + pix, lineStart.y, resolution);
    
                      line(out, lineStart.x + pix - 1, lineStart.y, prop.getPower() * (0xFF & old) / 255, prop.getSpeed(), prop.getFrequency(), resolution);
                      move(out, lineStart.x + pix, lineStart.y, resolution);
    
    Thomas Oster's avatar
    Thomas Oster committed
                    }
                    old = bytes.get(pix);
                  }
                }
                //last point is also not "white"
    
                line(out, lineStart.x + bytes.size() - 1, lineStart.y, prop.getPower() * (0xFF & bytes.get(bytes.size() - 1)) / 255, prop.getSpeed(), prop.getFrequency(), resolution);
    
                //add some space to the right
                move(out, Math.min((int) Util.mm2px(bedWidth, resolution), (int) (lineStart.x + bytes.size() - 1 + Util.mm2px(this.addSpacePerRasterLine, resolution))), lineStart.y, resolution);
    
                //add some space to the right
                move(out, Math.min((int) Util.mm2px(bedWidth, resolution), (int) (lineStart.x + bytes.size() - 1 + Util.mm2px(this.addSpacePerRasterLine, resolution))), lineStart.y, resolution);
    
    Thomas Oster's avatar
    Thomas Oster committed
                //move to the last nonempty point of the line
    
                move(out, lineStart.x + bytes.size() - 1, lineStart.y, resolution);
                byte old = bytes.get(bytes.size() - 1);
                for (int pix = bytes.size() - 1; pix >= 0; pix--)
    
    Thomas Oster's avatar
    Thomas Oster committed
                {
                  if (bytes.get(pix) != old || pix == 0)
                  {
                    if (old == 0)
                    {
    
                      move(out, lineStart.x + pix, lineStart.y, resolution);
    
                      line(out, lineStart.x + pix + 1, lineStart.y, prop.getPower() * (0xFF & old) / 255, prop.getSpeed(), prop.getFrequency(), resolution);
                      move(out, lineStart.x + pix, lineStart.y, resolution);
    
    Thomas Oster's avatar
    Thomas Oster committed
                    }
                    old = bytes.get(pix);
                  }
                }
                //last point is also not "white"
    
                line(out, lineStart.x, lineStart.y, prop.getPower() * (0xFF & bytes.get(0)) / 255, prop.getSpeed(), prop.getFrequency(), resolution);
    
                //add some space to the left
                move(out, Math.max(0, (int) (lineStart.x-Util.mm2px(this.addSpacePerRasterLine, resolution))), lineStart.y, resolution);
    
            if (!this.isEngraveUnidirectional())
            {
              dirRight = !dirRight;
            }
    
    Thomas Oster's avatar
    Thomas Oster committed
          }
        }
        return result.toByteArray();
      }
    
    
      /**
       * This Method takes a raster-line represented by a list of bytes, 
    
    Thomas Oster's avatar
    Thomas Oster committed
       * where: byte0 ist the left-most byte, in one byte, the MSB is the
    
       * left-most bit, 0 representing laser off, 1 representing laser on.
       * The Output List of longs, where each value is the unsigned dword
       * of 4 bytes of the input each, where the first dword is the leftmost
       * dword and the LSB is the leftmost bit. If outputLeftToRight is false,
       * the first dword is the rightmost dword and the LSB of each dword is the
       * the Output is padded with zeroes on the right side, if leftToRight is true,
       * on the left-side otherwise
       * rightmost bit
       * @param line
       * @param outputLeftToRight
       * @return 
       */
    
    Thomas Oster's avatar
    Thomas Oster committed
      public List<Long> byteLineToDwords(List<Byte> line, boolean outputLeftToRight)
    
      {
        List<Long> result = new ArrayList<Long>();
        int s = line.size();
    
    Thomas Oster's avatar
    Thomas Oster committed
        for (int i=0;i<s;i++)
        {
          line.set(i, (byte) (Integer.reverse(0xFF&line.get(i))>>>24));
        }
    
    Thomas Oster's avatar
    Thomas Oster committed
            (((long) (i+3 < s ? 0xFF&line.get(i+3) : 0))<<24) 
            + (((long) (i+2 < s ? 0xFF&line.get(i+2) : 0))<<16)
            + (((long) (i+1 < s ? 0xFF&line.get(i+1) : 0))<<8)
            + ((long) (0xFF&line.get(i)))
    
            );
        }
        if (!outputLeftToRight)
        {
          Collections.reverse(result);
          for(int i=0;i<result.size();i++)
    
    Thomas Oster's avatar
    Thomas Oster committed
          {   
            result.set(i, Long.reverse(result.get(i)) >>> 32);
    
          }
        }
        return result;
      }
      
      private byte[] generateLaosRasterCode(RasterPart rp, int resolution) throws UnsupportedEncodingException, IOException
    
    Thomas Oster's avatar
    Thomas Oster committed
      {
        ByteArrayOutputStream result = new ByteArrayOutputStream();
        PrintStream out = new PrintStream(result, true, "US-ASCII");
    
        boolean dirRight = true;
        for (int raster = 0; raster < rp.getRasterCount(); raster++)
    
          Point rasterStart = rp.getRasterStart(raster);
          LaserProperty prop = rp.getLaserProperty(raster);
          //Set focus
          out.printf(Locale.US, "2 %d\n", (int) Util.mm2px(prop.getFocus(), resolution));
          for (int line = 0; line < rp.getRasterHeight(raster); line++)
          {
            Point lineStart = rasterStart.clone();
            lineStart.y += line;
            List<Byte> bytes = rp.getRasterLine(raster, line);
            //remove heading zeroes
            while (bytes.size() > 0 && bytes.get(0) == 0)
            {
              lineStart.x += 8;
              bytes.remove(0);
            }
            //remove trailing zeroes
            while (bytes.size() > 0 && bytes.get(bytes.size()-1) == 0)
            {
              bytes.remove(bytes.size()-1);
            }
            if (bytes.size() > 0)
            {
              //add space on the left side
              int space = (int) Util.mm2px(this.getAddSpacePerRasterLine(), resolution);
              while (space > 0 && lineStart.x >= 8)
              {
                bytes.add(0, (byte) 0);
                space -= 8;
                lineStart.x -=8;
              }
              //add space on the right side
              space = (int) Util.mm2px(this.getAddSpacePerRasterLine(), resolution);
              int max = (int) Util.mm2px(this.getBedWidth(), resolution);
              while (space > 0 && lineStart.x+(8*bytes.size()) < max-8)
              {
                bytes.add((byte) 0);
                space -= 8;
              }    
              if (dirRight)
              {
                //move to the first point of the line
                move(out, lineStart.x, lineStart.y, resolution);         
                List<Long> dwords = this.byteLineToDwords(bytes, true);
                out.printf("9 %s %s ", "1", ""+(dwords.size()*32));
                for(Long d:dwords)
                {
                  out.print(" "+d);
                }
                out.printf("\n");
                line(out, lineStart.x + (dwords.size()*32), lineStart.y, prop.getPower(), prop.getSpeed(), prop.getFrequency(), resolution);
              }
              else
              {
                //move to the first point of the line
                List<Long> dwords = this.byteLineToDwords(bytes, false);
                move(out, lineStart.x+(dwords.size()*32), lineStart.y, resolution);         
                out.printf("9 %s %s ", "1", ""+(dwords.size()*32));
                for(Long d:dwords)
                {
                  out.printf(" "+d);
                }
                out.printf("\n");
                line(out, lineStart.x, lineStart.y, prop.getPower(), prop.getSpeed(), prop.getFrequency(), resolution);  
              }
            }
            if (!this.isEngraveUnidirectional())
            {
              dirRight = !dirRight;
            }
          }
    
    Thomas Oster's avatar
    Thomas Oster committed
        }
        return result.toByteArray();
      }
    
      
      private byte[] generateInitializationCode() throws UnsupportedEncodingException
      {
        ByteArrayOutputStream result = new ByteArrayOutputStream();
        PrintStream out = new PrintStream(result, true, "US-ASCII");
        return result.toByteArray();
      }
    
    Thomas Oster's avatar
    Thomas Oster committed
      private byte[] generateShutdownCode() throws UnsupportedEncodingException
      {
        ByteArrayOutputStream result = new ByteArrayOutputStream();
        PrintStream out = new PrintStream(result, true, "US-ASCII");
        //back to origin and shutdown
    
        //out.printf("0 0 0\n");
        //Set focus to 0
        out.printf(Locale.US, "2 %d\n", 0);
    
    Thomas Oster's avatar
    Thomas Oster committed
        return result.toByteArray();
      }
    
    Thomas Oster's avatar
    Thomas Oster committed
      @Override
    
      public void sendJob(LaserJob job, ProgressListener pl) throws IllegalJobException, Exception
    
    Thomas Oster's avatar
    Thomas Oster committed
        this.currentFrequency = -1;
        this.currentPower = -1;
        this.currentSpeed = -1;
    
        BufferedOutputStream out;
        ByteArrayOutputStream buffer = null;
    
        pl.taskChanged(this, "checking job");
        checkJob(job);
    
          pl.taskChanged(this, "connecting");
    
          Socket connection = new Socket();
          connection.connect(new InetSocketAddress(hostname, port), 3000);
          out = new BufferedOutputStream(connection.getOutputStream());
    
        }
        else
        {
          buffer = new ByteArrayOutputStream();
          out = new BufferedOutputStream(buffer);
    
          pl.taskChanged(this, "buffering");
    
    Thomas Oster's avatar
    Thomas Oster committed
        out.write(this.generateInitializationCode());
    
    Thomas Oster's avatar
    Thomas Oster committed
        if (job.contains3dRaster())
        {
          out.write(this.generatePseudoRaster3dGCode(job.getRaster3dPart(), job.getResolution()));
        }
    
    Thomas Oster's avatar
    Thomas Oster committed
        if (job.containsRaster())
        {
    
          if (this.isUseLaosRastermode())
          {
            out.write(this.generateLaosRasterCode(job.getRasterPart(), job.getResolution()));
          }
          else
          {
            out.write(this.generatePseudoRasterGCode(job.getRasterPart(), job.getResolution()));
          }
    
    Thomas Oster's avatar
    Thomas Oster committed
        if (job.containsVector())
        {
          out.write(this.generateVectorGCode(job.getVectorPart(), job.getResolution()));
        }
    
    Thomas Oster's avatar
    Thomas Oster committed
        out.write(this.generateShutdownCode());
        out.close();
    
        if (this.isUseTftp())
        {
    
          pl.taskChanged(this, "connecting");
    
          TFTPClient tftp = new TFTPClient();
    
    Thomas Oster's avatar
    Thomas Oster committed
          tftp.setDefaultTimeout(5000);
    
          //open a local UDP socket
          tftp.open();
    
          ByteArrayInputStream bain = new ByteArrayInputStream(buffer.toByteArray());
    
          tftp.sendFile(job.getName().replace(" ", "") +".lgc", TFTP.BINARY_MODE, bain, this.getHostname(), this.getPort());
    
    Thomas Oster's avatar
    Thomas Oster committed
          tftp.close();
    
          bain.close();
    
    Thomas Oster's avatar
    Thomas Oster committed
          pl.taskChanged(this, "sent.");
    
    Thomas Oster's avatar
    Thomas Oster committed
      }
      private List<Integer> resolutions;
    
      @Override
      public List<Integer> getResolutions()
      {
        if (resolutions == null)
        {
          //TODO: Calculate possible resolutions
          //according to mm/step
          resolutions = Arrays.asList(new Integer[]
            {
    
    Thomas Oster's avatar
    Thomas Oster committed
            });
        }
        return resolutions;
      }
    
    Thomas Oster's avatar
    Thomas Oster committed
    
      /**
       * Get the value of bedWidth
       *
       * @return the value of bedWidth
       */
    
    Thomas Oster's avatar
    Thomas Oster committed
      public double getBedWidth()
      {
        return bedWidth;
      }
    
      /**
       * Set the value of bedWidth
       *
       * @param bedWidth new value of bedWidth
       */
      public void setBedWidth(double bedWidth)
      {
        this.bedWidth = bedWidth;
      }
    
    Thomas Oster's avatar
    Thomas Oster committed
    
      /**
       * Get the value of bedHeight
       *
       * @return the value of bedHeight
       */
    
    Thomas Oster's avatar
    Thomas Oster committed
      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 List<String> settingAttributes;
    
      @Override
      public List<String> getSettingAttributes()
      {
        if (settingAttributes == null)
        {
          settingAttributes = new LinkedList<String>();
          settingAttributes.add(SETTING_HOSTNAME);
          settingAttributes.add(SETTING_PORT);
    
          //settingAttributes.add(SETTING_NATIVERASTER);
    
          settingAttributes.add(SETTING_UNIDIR);
    
    Thomas Oster's avatar
    Thomas Oster committed
          settingAttributes.add(SETTING_BEDWIDTH);
          settingAttributes.add(SETTING_BEDHEIGHT);
    
          //settingAttributes.add(SETTING_FLIPX);
          //settingAttributes.add(SETTING_FLIPY);
          //settingAttributes.add(SETTING_MMPERSTEP);
    
          settingAttributes.add(SETTING_TFTP);
    
          settingAttributes.add(SETTING_RASTER_WHITESPACE);
    
    Thomas Oster's avatar
    Thomas Oster committed
        }
        return settingAttributes;
      }
    
      @Override
      public String getSettingValue(String attribute)
      {
    
        if (SETTING_RASTER_WHITESPACE.equals(attribute))
        {
          return "" + this.getAddSpacePerRasterLine();
        }
    
        else if (SETTING_NATIVERASTER.equals(attribute))
        {
          return this.isUseLaosRastermode() ? "yes" : "no";
        }
        else if (SETTING_UNIDIR.equals(attribute))
        {
          return this.isEngraveUnidirectional() ? "yes" : "no";
        }
    
        else if (SETTING_HOSTNAME.equals(attribute))
    
    Thomas Oster's avatar
    Thomas Oster committed
        {
          return this.getHostname();
        }
        else if (SETTING_FLIPX.equals(attribute))
        {
          return this.isFlipXaxis() ? "yes" : "no";
        }
    
        else if (SETTING_FLIPY.equals(attribute))
        {
          return this.isFlipYaxis() ? "yes" : "no";
        }
    
    Thomas Oster's avatar
    Thomas Oster committed
        else if (SETTING_PORT.equals(attribute))
        {
          return "" + this.getPort();
        }
        else if (SETTING_BEDWIDTH.equals(attribute))
        {
          return "" + this.getBedWidth();
        }
        else if (SETTING_BEDHEIGHT.equals(attribute))
        {
          return "" + this.getBedHeight();
        }
        else if (SETTING_MMPERSTEP.equals(attribute))
        {
          return "" + this.getMmPerStep();
        }
    
        else if (SETTING_TFTP.equals(attribute))
        {
          return this.isUseTftp() ? "yes" : "no";
        }
    
    Thomas Oster's avatar
    Thomas Oster committed
        return null;
      }
    
      @Override
      public void setSettingValue(String attribute, String value)
      {
    
        if (SETTING_RASTER_WHITESPACE.equals(attribute))
        {
          this.setAddSpacePerRasterLine(Double.parseDouble(value));
        }
    
        else if (SETTING_NATIVERASTER.equals(attribute))
        {
          this.setUseLaosRastermode("yes".equals(value));
        }
        else if (SETTING_UNIDIR.endsWith(attribute))
        {
          this.setEngraveUnidirectional("yes".equals(value));
        }
    
        else if (SETTING_HOSTNAME.equals(attribute))
    
    Thomas Oster's avatar
    Thomas Oster committed
        {
          this.setHostname(value);
        }
        else if (SETTING_PORT.equals(attribute))
        {
          this.setPort(Integer.parseInt(value));
        }
        else if (SETTING_FLIPX.equals(attribute))
        {
          this.setFlipXaxis("yes".equals(value));
        }
    
        else if (SETTING_FLIPY.equals(attribute))
        {
          this.setFlipYaxis("yes".equals(value));
        }
    
    Thomas Oster's avatar
    Thomas Oster committed
        else if (SETTING_BEDWIDTH.equals(attribute))
        {
          this.setBedWidth(Double.parseDouble(value));
        }
        else if (SETTING_BEDHEIGHT.equals(attribute))
        {
          this.setBedHeight(Double.parseDouble(value));
        }
        else if (SETTING_MMPERSTEP.equals(attribute))
        {
          this.setMmPerStep(Double.parseDouble(value));
        }
    
        else if (SETTING_TFTP.contains(attribute))
        {
          this.setUseTftp("yes".equals(value));
        }
    
    Thomas Oster's avatar
    Thomas Oster committed
      }
    
      @Override
      public int estimateJobDuration(LaserJob job)
      {
        return 10000;
      }
    
      @Override
      public LaserCutter clone()
      {
        LaosCutter clone = new LaosCutter();
        clone.hostname = hostname;
        clone.port = port;
        clone.bedHeight = bedHeight;
        clone.bedWidth = bedWidth;
        clone.flipXaxis = flipXaxis;
    
        clone.flipYaxis = flipYaxis;
    
    Thomas Oster's avatar
    Thomas Oster committed
        clone.mmPerStep = mmPerStep;
    
        clone.useTftp = useTftp;
    
        clone.addSpacePerRasterLine = addSpacePerRasterLine;
    
        clone.unidir = unidir;
        clone.useLaosRastermode = useLaosRastermode;
    
    Thomas Oster's avatar
    Thomas Oster committed
        return clone;
      }
    }