Java tutorial
/* Copyright 2017 Will Winder This file is part of Universal Gcode Sender (UGS). UGS is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. UGS 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 General Public License for more details. You should have received a copy of the GNU General Public License along with UGS. If not, see <http://www.gnu.org/licenses/>. */ package com.willwinder.ugp.tools; import com.willwinder.ugs.nbp.lib.lookup.CentralLookup; import com.willwinder.ugs.nbp.lib.services.LocalizingService; import static com.willwinder.ugs.nbp.lib.services.LocalizingService.lang; import com.willwinder.ugs.nbp.lib.services.TopComponentLocalizer; import com.willwinder.universalgcodesender.gcode.GcodeParser; import com.willwinder.universalgcodesender.gcode.processors.M30Processor; import com.willwinder.universalgcodesender.gcode.processors.Translator; import com.willwinder.universalgcodesender.gcode.util.GcodeParserException; import com.willwinder.universalgcodesender.gcode.util.GcodeUtils; import com.willwinder.universalgcodesender.i18n.Localization; import com.willwinder.universalgcodesender.model.BackendAPI; import com.willwinder.universalgcodesender.model.Position; import com.willwinder.universalgcodesender.model.UnitUtils; import com.willwinder.universalgcodesender.types.GcodeCommand; import com.willwinder.universalgcodesender.utils.GUIHelpers; import com.willwinder.universalgcodesender.utils.GcodeStreamReader; import com.willwinder.universalgcodesender.utils.Settings; import com.willwinder.universalgcodesender.utils.SwingHelpers; import static com.willwinder.universalgcodesender.utils.SwingHelpers.selectedUnit; import org.netbeans.api.settings.ConvertAsProperties; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.windows.TopComponent; import javax.swing.*; import java.io.BufferedReader; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collection; import net.miginfocom.swing.MigLayout; import org.apache.commons.lang3.StringUtils; import org.openide.modules.OnStart; import org.openide.util.Exceptions; /** * Top component which displays something. */ @ConvertAsProperties(dtd = "-//com.willwinder.ugp.gcodetools//GcodeTiler//EN", autostore = false) @TopComponent.Description(preferredID = "GcodeTilerTopComponent", //iconBase="SET/PATH/TO/ICON/HERE", persistenceType = TopComponent.PERSISTENCE_ALWAYS) @TopComponent.Registration(mode = "output", openAtStartup = false) @ActionID(category = GcodeTilerTopComponent.GcodeTilerCategory, id = GcodeTilerTopComponent.GcodeTilerActionId) @ActionReference(path = LocalizingService.MENU_WINDOW_PLUGIN) @TopComponent.OpenActionRegistration(displayName = "GcodeTiler", preferredID = "GcodeTilerTopComponent") public final class GcodeTilerTopComponent extends TopComponent { public final static String GcodeTilerTitle = Localization.getString("platform.plugin.gcode-tools.tiler.window", lang); public final static String GcodeTilerTooltip = Localization .getString("platform.plugin.gcode-tools.tiler.tooltip", lang); public final static String GcodeTilerActionId = "com.willwinder.ugp.gcodetools.GcodeTilerTopComponent"; public final static String GcodeTilerCategory = LocalizingService.CATEGORY_WINDOW; private final BackendAPI backend; private static String ERROR_GENERATING = "An error occurred generating tile program: "; private static String ERROR_LOADING = "An error occurred loading generated tile program: "; private final JButton generateGcodeButton = new JButton( Localization.getString("platform.plugin.dowel-module.generate")); private final JButton exportGcodeButton = new JButton( Localization.getString("platform.plugin.dowel-module.export")); private final SpinnerNumberModel numCopiesX; private final SpinnerNumberModel numCopiesY; private final SpinnerNumberModel padding; private final JComboBox<String> units; // Temporary state. private static Double xWidth = null; private static Double yWidth = null; private File outputFile = null; @OnStart public static class Localizer extends TopComponentLocalizer { public Localizer() { super(GcodeTilerCategory, GcodeTilerActionId, GcodeTilerTitle); } } public GcodeTilerTopComponent() { setName(GcodeTilerTitle); setToolTipText(GcodeTilerTooltip); backend = CentralLookup.getDefault().lookup(BackendAPI.class); numCopiesX = new SpinnerNumberModel(3, 1, 1000, 1); numCopiesY = new SpinnerNumberModel(3, 1, 1000, 1); padding = new SpinnerNumberModel(5, 0, 1000, 0.1); units = new JComboBox<>(SwingHelpers.getUnitOptions()); // Button callbacks generateGcodeButton.addActionListener(al -> generateGcode()); exportGcodeButton.addActionListener(al -> exportGcode()); // Change listeners numCopiesX.addChangeListener(l -> controlChangeListener()); numCopiesY.addChangeListener(l -> controlChangeListener()); padding.addChangeListener(l -> controlChangeListener()); // Dowel settings setLayout(new MigLayout("fillx, wrap 4")); add(new JLabel(Localization.getString("platform.plugin.dowel-module.x")), "growx"); add(new JSpinner(numCopiesX), "growx"); add(new JLabel(Localization.getString("gcode.setting.units")), "growx"); add(units, "growx"); add(new JLabel(Localization.getString("platform.plugin.dowel-module.y")), "growx"); add(new JSpinner(numCopiesY), "growx"); add(new JLabel(Localization.getString("platform.plugin.gcode-tools.tiler.padding")), "growx"); add(new JSpinner(padding), "growx"); add(generateGcodeButton, "growx, span2"); add(exportGcodeButton, "growx, span2"); } private void controlChangeListener() { // Clear the cached output file if settings are changed. if (this.outputFile != null && this.outputFile.exists()) { this.outputFile.delete(); } } private boolean loadDimensions() { if (backend.getProcessedGcodeFile() == null) { GUIHelpers.displayErrorDialog("Must load gcode file first."); return false; } Settings.FileStats fs = backend.getSettings().getFileStats(); UnitUtils.Units u = selectedUnit(this.units.getSelectedIndex()); Position min = fs.minCoordinate.getPositionIn(u); Position max = fs.maxCoordinate.getPositionIn(u); GcodeTilerTopComponent.xWidth = max.x - min.x; GcodeTilerTopComponent.yWidth = max.y - min.y; return true; } private void generateAndLoadGcode(File file) { try { if (this.outputFile == null) { try (PrintWriter writer = new PrintWriter(new FileWriter(file))) { double padding = SwingHelpers.getDouble(this.padding); double stepX = padding + GcodeTilerTopComponent.xWidth; double stepY = padding + GcodeTilerTopComponent.yWidth; // loop over offsets and call generateOneTile a bunch for (int x = 0; x < SwingHelpers.getInt(this.numCopiesX); x++) { for (int y = 0; y < SwingHelpers.getInt(this.numCopiesY); y++) { this.generateOneTile((x * stepX), (y * stepY), writer); } } } } backend.setGcodeFile(file); } catch (IOException e) { GUIHelpers.displayErrorDialog(ERROR_GENERATING + e.getLocalizedMessage()); } catch (Exception e) { GUIHelpers.displayErrorDialog(ERROR_LOADING + e.getLocalizedMessage()); } } private void generateGcode() { if (!loadDimensions()) return; Path path = null; try { path = Files.createTempFile("dowel_program", ".gcode"); File file = path.toFile(); generateAndLoadGcode(file); } catch (IOException e) { GUIHelpers.displayErrorDialog(ERROR_LOADING + e.getLocalizedMessage()); } } private void exportGcode() { if (!loadDimensions()) return; String sourceDir = backend.getSettings().getLastOpenedFilename(); SwingHelpers.createFile(sourceDir).ifPresent(file -> generateAndLoadGcode(file)); } // Helpers to actually process the gcode private void applyTranslation(String command, GcodeParser parser, PrintWriter output) throws GcodeParserException { if (StringUtils.isEmpty(command)) return; Collection<String> lines = parser.preprocessCommand(command, parser.getCurrentState()); for (String processedLine : lines) { output.println(processedLine); } parser.addCommand(command); } private void generateOneTile(double offsetX, double offsetY, PrintWriter output) { String gcodeFile = backend.getGcodeFile().getAbsolutePath(); UnitUtils.Units u = selectedUnit(this.units.getSelectedIndex()); GcodeParser parser = new GcodeParser(); parser.addCommandProcessor(new Translator(new Position(offsetX, offsetY, 0.0, u))); parser.addCommandProcessor(new M30Processor()); output.println(GcodeUtils.unitCommand(u) + "G90"); output.println("G0X" + offsetX + "Y" + offsetY); try { File file = new File(gcodeFile); try { try (GcodeStreamReader gsr = new GcodeStreamReader(file)) { while (gsr.getNumRowsRemaining() > 0) { GcodeCommand next = gsr.getNextCommand(); applyTranslation(next.getCommandString(), parser, output); } } } catch (GcodeStreamReader.NotGcodeStreamFile e) { try (FileInputStream fstream = new FileInputStream(file); DataInputStream dis = new DataInputStream(fstream); BufferedReader fileStream = new BufferedReader(new InputStreamReader(dis))) { String line; while ((line = fileStream.readLine()) != null) { applyTranslation(line, parser, output); } } } } catch (GcodeParserException | IOException ex) { Exceptions.printStackTrace(ex); } } @Override public void componentOpened() { // Unused for this top component. } @Override public void componentClosed() { // Unused for this top component. } public void writeProperties(java.util.Properties p) { // better to version settings since initial version as advocated at // http://wiki.apidesign.org/wiki/PropertyFiles p.setProperty("version", "1.0"); } public void readProperties(java.util.Properties p) { //String version = p.getProperty("version"); } }