/** * This file is part of LibLaserCut. * Copyright (C) 2011 - 2013 Thomas Oster <thomas.oster@rwth-aachen.de> * RWTH Aachen University - 52062 Aachen, Germany * * 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 java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.*; /** * This class implements a dummy driver that accepts laserjobs and prints debug information about them. * You can use it to test the VisiCut GUI without having a real lasercutter. * * @author Max Gaukler <development@maxgaukler.de>, based on the LAOS driver by Thomas Oster <thomas.oster@rwth-aachen.de> */ public class Dummy extends LaserCutter { private static final String SETTING_BEDWIDTH = "Laserbed width"; private static final String SETTING_BEDHEIGHT = "Laserbed height"; private static final String SETTING_RUNTIME = "Fake estimated run-time in seconds (-1 to disable)"; private static final String SETTING_SVG_OUTDIR = "SVG Debug output directory (set empty to disable)"; /** * SVG output creator, mostly for testing vector-sorting */ class SVGWriter { private double xPrev,xNow,yPrev,yNow; private StringBuilder svg = new StringBuilder(); private boolean vectorPathActive=false; private boolean partActive=false; private int idCounter=0; private int partCounter=0; private LaserCutter cutter; private double dpi; public SVGWriter(LaserCutter cutter) { this.cutter = cutter; } /** * start a new JobPart * @param title some string that will be included in the group ID * @param dpi */ public void startPart(String title, double dpi) { endPart(); partCounter += 1; this.dpi=dpi; this.partActive=true; svg.append("<g style=\"fill:none;stroke:#000000;stroke-width:0.1mm;\" id=\""); svg.append("visicut-part").append(partCounter).append("-"); svg.append(title.replaceAll("[^a-zA-Z0-9]","_")); svg.append("\">\n"); } /** * end a JobPart */ public void endPart() { moveTo(0,0); // end path if (partActive) { partActive=false; svg.append("</g>\n"); } } private void setLocation(int x, int y) { xPrev=xNow; yPrev=yNow; double factor = 25.4/dpi; // convert units to millimeters xNow=x*factor; yNow=y*factor; } /** * move to somewhere with laser off * @param x * @param y */ void moveTo(int x, int y) { setLocation(x,y); if (vectorPathActive) { // end the previous path svg.append("\"/>\n"); vectorPathActive=false; } } /** * move to somewhere with laser on * @param x * @param y */ void lineTo(int x, int y) { setLocation(x,y); if (!partActive) { throw new RuntimeException("lineTo called outside of a part!"); } if (!vectorPathActive) { // start a new path vectorPathActive=true; svg.append("<path id=\"visicut-").append(idCounter).append("\" d=\"M "); idCounter += 1; svg.append(xPrev).append(",").append(yPrev).append(" "); } svg.append(xNow).append(",").append(yNow).append(" "); } /** * generate SVG output string and reset everything (delete all path data) * @return */ private String getSVG() { endPart(); svg.insert(0,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?> \n" + "<!-- Created by VisiCut Debug output -->\n" + "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" " + "xmlns=\"http://www.w3.org/2000/svg\" " + "width=\"" + cutter.getBedWidth() + "mm\" " + "height=\"" + cutter.getBedHeight() + "mm\" " + "viewBox=\"0 0 " + cutter.getBedWidth() + " " + cutter.getBedHeight() + "\" " + "version=\"1.1\" id=\"svg\"> \n"); svg.append("</svg>\n"); String result=svg.toString(); svg = new StringBuilder(); idCounter=0; return result; } /** * store a String into a file * @param path the filename * @param str the content to be stored */ private void storeString(String path, String str) { try { FileWriter f = new FileWriter(path); BufferedWriter b = new BufferedWriter(f); b.write(str); b.close(); } catch (Exception e) { System.out.println("Could not write debug SVG: Exception: " + e); } } /** * store XHTML viewer to file * @param path * @param svgString */ void storeXHTML(String path, String svgString) { BufferedReader br = null; StringBuilder xhtml = new StringBuilder(); try { InputStream stream = new Dummy().getClass().getResourceAsStream("resources/visicut-svg-output-viewer.xhtml"); StringBuilder s = new StringBuilder(); br = new BufferedReader(new InputStreamReader(stream, "UTF-8")); String line=""; while ((line=br.readLine()) != null) { if (line.contains("<!-- REPLACE THIS WITH SVG -->")) { // insert svg, but skip first line with <?xml... line=svgString.substring(svgString.indexOf("\n")); } xhtml.append(line).append("\n"); } storeString(path, xhtml.toString()); } catch (Exception e) { System.out.println("could not store debug XHTML: " + e); } finally { try { br.close(); } catch (IOException ex) { System.out.println("could not close bufferedWriter when storing debug XHTML"); } } } void store(String directory) { if (directory == null || directory.isEmpty()) { System.out.println("Not writing debug SVG - no output directory set (edit lasercutter settings to change)"); } else { String pathSVG=directory + "/visicut-debug.svg"; System.out.println("storing SVG debug output to "+pathSVG); String svgString=getSVG(); storeString(pathSVG,svgString); String pathXHTML=directory + "/visicut-svg-output-viewer.xhtml"; System.out.println("storing SVG debug output (XHTML viewer) to "+pathXHTML); storeXHTML(pathXHTML, svgString); } } } @Override public String getModelName() { return "Dummy"; } @Override public void sendJob(LaserJob job, ProgressListener pl, List<String> warnings) throws IllegalJobException, Exception { pl.progressChanged(this, 0); BufferedOutputStream out; pl.taskChanged(this, "checking job"); checkJob(job); job.applyStartPoint(); pl.taskChanged(this, "sending"); pl.taskChanged(this, "sent."); SVGWriter svg = new SVGWriter(this); // SVG debug output System.out.println("dummy-driver got LaserJob: "); // TODO don't just print the parts and settins, but also the commands // TODO improve SVG-debug output: support bitmaps, add animation for (JobPart p : job.getParts()) { svg.startPart(p.getClass().getSimpleName(), p.getDPI()); if (p instanceof VectorPart) { System.out.println("VectorPart"); for (VectorCommand cmd : ((VectorPart) p).getCommandList()) { if (cmd.getType() == VectorCommand.CmdType.SETPROPERTY) { if (!(cmd.getProperty() instanceof PowerSpeedFocusFrequencyProperty)) { throw new IllegalJobException("This driver expects Power,Speed,Frequency and Focus as settings"); } System.out.println(((PowerSpeedFocusFrequencyProperty) cmd.getProperty()).toString()); } else if (cmd.getType() == VectorCommand.CmdType.LINETO) { System.out.println("LINETO \t" + cmd.getX() + ", \t" + cmd.getY()); svg.lineTo(cmd.getX(),cmd.getY()); } else if (cmd.getType() == VectorCommand.CmdType.MOVETO) { System.out.println("MOVETO \t" + cmd.getX() + ", \t" + cmd.getY()); svg.moveTo(cmd.getX(),cmd.getY()); } } } if (p instanceof RasterPart) { System.out.println("RasterPart"); // TODO add raster output for SVG debug output RasterPart rp = ((RasterPart) p); if (rp.getLaserProperty() != null && !(rp.getLaserProperty() instanceof PowerSpeedFocusProperty)) { throw new IllegalJobException("This driver expects Power,Speed and Focus as settings"); } System.out.println(((PowerSpeedFocusProperty) rp.getLaserProperty()).toString()); } if (p instanceof Raster3dPart) { System.out.println("Raster3dPart"); Raster3dPart rp = (Raster3dPart) p; if (rp.getLaserProperty() != null && !(rp.getLaserProperty() instanceof PowerSpeedFocusProperty)) { throw new IllegalJobException("This driver expects Power,Speed and Focus as settings"); } System.out.println(((PowerSpeedFocusProperty) rp.getLaserProperty()).toString()); } } System.out.println("end of job."); svg.store(svgOutdir); pl.progressChanged(this, 100); } @Override public int estimateJobDuration(LaserJob job) { // instead of really calculating some duration, just print the number configured from the settings // if <0, act as if the driver can not calculate a job duration if (!canEstimateJobDuration()) { throw new RuntimeException("cannot estimate job duration (dummy driver: fake runtime is set to negative value)"); } // return bogus value to test codepaths of GUI return fakeRunTime; } private List<Double> resolutions; protected int fakeRunTime = -1; @Override public boolean canEstimateJobDuration() { return (fakeRunTime >= 0); } @Override public List<Double> getResolutions() { if (resolutions == null) { resolutions = Arrays.asList(new Double[]{ 500d }); } 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; } public String svgOutdir=""; private static String[] settingAttributes = new String[]{ SETTING_BEDWIDTH, SETTING_BEDHEIGHT, SETTING_RUNTIME, SETTING_SVG_OUTDIR }; @Override public String[] getPropertyKeys() { return settingAttributes; } @Override public Object getProperty(String attribute) { if (SETTING_BEDWIDTH.equals(attribute)) { return this.getBedWidth(); } else if (SETTING_BEDHEIGHT.equals(attribute)) { return this.getBedHeight(); } else if (SETTING_RUNTIME.equals(attribute)) { return this.fakeRunTime; } else if (SETTING_SVG_OUTDIR.equals(attribute)) { return this.svgOutdir; } return null; } @Override public void setProperty(String attribute, Object value) { if (SETTING_BEDWIDTH.equals(attribute)) { this.setBedWidth((Double) value); } else if (SETTING_BEDHEIGHT.equals(attribute)) { this.setBedHeight((Double) value); } else if (SETTING_RUNTIME.equals(attribute)) { this.fakeRunTime=Integer.parseInt(value.toString()); } else if (SETTING_SVG_OUTDIR.equals(attribute)) { this.svgOutdir=value.toString(); } } @Override public LaserCutter clone() { Dummy clone = new Dummy(); clone.bedHeight = bedHeight; clone.bedWidth = bedWidth; clone.fakeRunTime = this.fakeRunTime; return clone; } }