From 4b0a976686ec918d333c4c72d73fe6a324415b19 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 3 Jun 2013 00:44:15 +0200 Subject: [PATCH] First refactoring of uploaders --- app/src/cc/arduino/packages/Uploader.java | 137 ++++++++ .../arduino/packages/UploaderFactory.java} | 18 +- .../packages/uploaders}/HttpUploader.java | 7 +- .../packages/uploaders/SerialUploader.java | 317 ++++++++++++++++- app/src/processing/app/Editor.java | 9 +- app/src/processing/app/NetworkMonitor.java | 21 +- app/src/processing/app/Sketch.java | 5 +- .../processing/app/debug/BasicUploader.java | 318 ------------------ app/src/processing/app/debug/Uploader.java | 227 ------------- .../app/debug/UploaderFactoryTest.java | 16 +- 10 files changed, 495 insertions(+), 580 deletions(-) create mode 100644 app/src/cc/arduino/packages/Uploader.java rename app/src/{processing/app/PerPortObjectFactory.java => cc/arduino/packages/UploaderFactory.java} (56%) rename app/src/{processing/app/debug => cc/arduino/packages/uploaders}/HttpUploader.java (97%) delete mode 100644 app/src/processing/app/debug/BasicUploader.java delete mode 100644 app/src/processing/app/debug/Uploader.java diff --git a/app/src/cc/arduino/packages/Uploader.java b/app/src/cc/arduino/packages/Uploader.java new file mode 100644 index 000000000..b03ca3d41 --- /dev/null +++ b/app/src/cc/arduino/packages/Uploader.java @@ -0,0 +1,137 @@ +/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Uploader - abstract uploading baseclass (common to both uisp and avrdude) + Part of the Arduino project - http://www.arduino.cc/ + + Copyright (c) 2004-05 + Hernando Barragan + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + $Id$ +*/ + +package cc.arduino.packages; + +import static processing.app.I18n._; + +import java.util.Collection; + +import processing.app.I18n; +import processing.app.Preferences; +import processing.app.debug.MessageConsumer; +import processing.app.debug.MessageSiphon; +import processing.app.debug.RunnerException; + +public abstract class Uploader implements MessageConsumer { + + private String error = null; + + protected boolean verbose = Preferences.getBoolean("upload.verbose"); + + public abstract boolean uploadUsingPreferences(String buildPath, String className, boolean usingProgrammer) + throws RunnerException; + + public abstract boolean burnBootloader() throws RunnerException; + + public boolean requiresAuthorization() { + return false; + } + + public String getAuthorizationKey() { + return null; + } + + protected boolean executeUploadCommand(Collection command) + throws RunnerException { + return executeUploadCommand(command.toArray(new String[0])); + } + + protected boolean executeUploadCommand(String command[]) + throws RunnerException { + notFoundError = false; + int result = -1; + + try { + if (verbose) { + for (String c : command) + System.out.print(c + " "); + System.out.println(); + } + Process process = Runtime.getRuntime().exec(command); + new MessageSiphon(process.getInputStream(), this); + new MessageSiphon(process.getErrorStream(), this); + + // wait for the process to finish. + result = process.waitFor(); + } catch (Exception e) { + e.printStackTrace(); + } + + if (error != null) { + RunnerException exception = new RunnerException(error); + exception.hideStackTrace(); + throw exception; + } + + return result == 0; + } + + boolean notFoundError; + + public void message(String s) { + // selectively suppress a bunch of avrdude output for AVR109/Caterina that should already be quelled but isn't + if (!verbose && ( + s.indexOf("Connecting to programmer:") != -1 || + s.indexOf("Found programmer: Id = \"CATERIN\"; type = S") != -1 || + s.indexOf("Software Version = 1.0; No Hardware Version given.") != -1 || + s.indexOf("Programmer supports auto addr increment.") != -1 || + s.indexOf("Programmer supports buffered memory access with buffersize=128 bytes.") != -1 || + s.indexOf("Programmer supports the following devices:") != -1 || + s.indexOf("Device code: 0x44") != -1)) + s = ""; + + System.err.print(s); + + // ignore cautions + if (s.contains("Error")) { + notFoundError = true; + return; + } + if (notFoundError) { + error = I18n.format(_("the selected serial port {0} does not exist or your board is not connected"), s); + return; + } + if (s.contains("Device is not responding")) { + error = _("Device is not responding, check the right serial port is selected or RESET the board right before exporting"); + return; + } + if (s.contains("Programmer is not responding") || + s.contains("programmer is not responding") || + s.contains("protocol error") || + s.contains("avrdude: ser_open(): can't open device") || + s.contains("avrdude: ser_drain(): read error") || + s.contains("avrdude: ser_send(): write error") || + s.contains("avrdude: error: buffered memory access not supported.")) { + error = _("Problem uploading to board. See http://www.arduino.cc/en/Guide/Troubleshooting#upload for suggestions."); + return; + } + if (s.contains("Expected signature")) { + error = _("Wrong microcontroller found. Did you select the right board from the Tools > Board menu?"); + return; + } + } +} diff --git a/app/src/processing/app/PerPortObjectFactory.java b/app/src/cc/arduino/packages/UploaderFactory.java similarity index 56% rename from app/src/processing/app/PerPortObjectFactory.java rename to app/src/cc/arduino/packages/UploaderFactory.java index 4670085b0..36a4fbbf3 100644 --- a/app/src/processing/app/PerPortObjectFactory.java +++ b/app/src/cc/arduino/packages/UploaderFactory.java @@ -1,20 +1,22 @@ -package processing.app; +package cc.arduino.packages; -import processing.app.debug.BasicUploader; -import processing.app.debug.HttpUploader; +import processing.app.AbstractMonitor; +import processing.app.Base; +import processing.app.Constants; +import processing.app.NetworkMonitor; +import processing.app.SerialMonitor; import processing.app.debug.TargetBoard; -import processing.app.debug.Uploader; +import cc.arduino.packages.uploaders.HttpUploader; +import cc.arduino.packages.uploaders.SerialUploader; -import java.util.regex.Pattern; - -public class PerPortObjectFactory { +public class UploaderFactory { public Uploader newUploader(TargetBoard board, String port) { if ("true".equals(board.getPreferences().get("upload.via_http")) && Constants.IPV4_ADDRESS.matcher(port).find()) { return new HttpUploader(port); } - return new BasicUploader(); + return new SerialUploader(); } public AbstractMonitor newMonitor(String port, Base base) { diff --git a/app/src/processing/app/debug/HttpUploader.java b/app/src/cc/arduino/packages/uploaders/HttpUploader.java similarity index 97% rename from app/src/processing/app/debug/HttpUploader.java rename to app/src/cc/arduino/packages/uploaders/HttpUploader.java index 10fd7e46c..bb52a8c61 100644 --- a/app/src/processing/app/debug/HttpUploader.java +++ b/app/src/cc/arduino/packages/uploaders/HttpUploader.java @@ -1,4 +1,4 @@ -package processing.app.debug; +package cc.arduino.packages.uploaders; import org.apache.commons.codec.binary.Base64; import org.apache.commons.httpclient.HttpClient; @@ -6,9 +6,14 @@ import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.NameValuePair; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; + +import cc.arduino.packages.Uploader; + import processing.app.Base; import processing.app.Constants; import processing.app.Preferences; +import processing.app.debug.RunnerException; +import processing.app.debug.TargetPlatform; import processing.app.helpers.PreferencesMap; import java.io.*; diff --git a/app/src/cc/arduino/packages/uploaders/SerialUploader.java b/app/src/cc/arduino/packages/uploaders/SerialUploader.java index 7f05860f3..595214501 100644 --- a/app/src/cc/arduino/packages/uploaders/SerialUploader.java +++ b/app/src/cc/arduino/packages/uploaders/SerialUploader.java @@ -1,7 +1,322 @@ +/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + BasicUploader - generic command line uploader implementation + Part of the Arduino project - http://www.arduino.cc/ + + Copyright (c) 2004-05 + Hernando Barragan + Copyright (c) 2012 + Cristian Maglie + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + $Id$ +*/ + package cc.arduino.packages.uploaders; +import static processing.app.I18n._; + +import java.util.ArrayList; +import java.util.List; + import cc.arduino.packages.Uploader; -public class SerialUploader implements Uploader { +import processing.app.Base; +import processing.app.I18n; +import processing.app.Preferences; +import processing.app.Serial; +import processing.app.SerialException; +import processing.app.debug.RunnerException; +import processing.app.debug.TargetPlatform; +import processing.app.helpers.PreferencesMap; +import processing.app.helpers.StringReplacer; +public class SerialUploader extends Uploader { + + public boolean uploadUsingPreferences(String buildPath, String className, + boolean usingProgrammer) + throws RunnerException { + // FIXME: Preferences should be reorganized + TargetPlatform targetPlatform = Base.getTargetPlatform(); + PreferencesMap prefs = Preferences.getMap(); + prefs.putAll(Base.getBoardPreferences()); + prefs.putAll(targetPlatform.getTool(prefs.get("upload.tool"))); + + // if no protocol is specified for this board, assume it lacks a + // bootloader and upload using the selected programmer. + if (usingProgrammer || prefs.get("upload.protocol") == null) { + return uploadUsingProgrammer(buildPath, className); + } + + // need to do a little dance for Leonardo and derivatives: + // open then close the port at the magic baudrate (usually 1200 bps) first + // to signal to the sketch that it should reset into bootloader. after doing + // this wait a moment for the bootloader to enumerate. On Windows, also must + // deal with the fact that the COM port number changes from bootloader to + // sketch. + String t = prefs.get("upload.use_1200bps_touch"); + boolean doTouch = t != null && t.equals("true"); + + t = prefs.get("upload.wait_for_upload_port"); + boolean waitForUploadPort = (t != null) && t.equals("true"); + + if (doTouch) { + String uploadPort = prefs.get("serial.port"); + try { + // Toggle 1200 bps on selected serial port to force board reset. + List before = Serial.list(); + if (before.contains(uploadPort)) { + if (verbose) + System.out + .println(_("Forcing reset using 1200bps open/close on port ") + + uploadPort); + Serial.touchPort(uploadPort, 1200); + } + if (waitForUploadPort) { + // Scanning for available ports seems to open the port or + // otherwise assert DTR, which would cancel the WDT reset if + // it happened within 250 ms. So we wait until the reset should + // have already occured before we start scanning. + if (!Base.isMacOS()) + Thread.sleep(300); + + uploadPort = waitForUploadPort(uploadPort, before); + } else { + Thread.sleep(400); + } + } catch (SerialException e) { + throw new RunnerException(e.getMessage()); + } catch (InterruptedException e) { + throw new RunnerException(e.getMessage()); + } + prefs.put("serial.port", uploadPort); + if (uploadPort.startsWith("/dev/")) + prefs.put("serial.port.file", uploadPort.substring(5)); + else + prefs.put("serial.port.file", uploadPort); + } + + prefs.put("build.path", buildPath); + prefs.put("build.project_name", className); + if (verbose) + prefs.put("upload.verbose", prefs.get("upload.params.verbose")); + else + prefs.put("upload.verbose", prefs.get("upload.params.quiet")); + + boolean uploadResult; + try { +// if (prefs.get("upload.disable_flushing") == null +// || prefs.get("upload.disable_flushing").toLowerCase().equals("false")) { +// flushSerialBuffer(); +// } + + String pattern = prefs.get("upload.pattern"); + String[] cmd = StringReplacer.formatAndSplit(pattern, prefs, true); + uploadResult = executeUploadCommand(cmd); + } catch (Exception e) { + throw new RunnerException(e); + } + + // Remove the magic baud rate (1200bps) to avoid + // future unwanted board resets + try { + if (uploadResult && doTouch) { + String uploadPort = Preferences.get("serial.port"); + if (waitForUploadPort) { + // For Due/Leonardo wait until the bootloader serial port disconnects and the + // sketch serial port reconnects (or timeout after a few seconds if the + // sketch port never comes back). Doing this saves users from accidentally + // opening Serial Monitor on the soon-to-be-orphaned bootloader port. + Thread.sleep(500); + long timeout = System.currentTimeMillis() + 2000; + while (timeout > System.currentTimeMillis()) { + List portList = Serial.list(); + if (portList.contains(uploadPort)) { + try { + Serial.touchPort(uploadPort, 9600); + break; + } catch (SerialException e) { + // Port already in use + } + } + Thread.sleep(250); + } + } else { + try { + Serial.touchPort(uploadPort, 9600); + } catch (SerialException e) { + throw new RunnerException(e); + } + } + } + } catch (InterruptedException ex) { + } + return uploadResult; + } + + private String waitForUploadPort(String uploadPort, List before) + throws InterruptedException, RunnerException { + // Wait for a port to appear on the list + int elapsed = 0; + while (elapsed < 10000) { + List now = Serial.list(); + List diff = new ArrayList(now); + diff.removeAll(before); + if (verbose) { + System.out.print("PORTS {"); + for (String p : before) + System.out.print(p + ", "); + System.out.print("} / {"); + for (String p : now) + System.out.print(p + ", "); + System.out.print("} => {"); + for (String p : diff) + System.out.print(p + ", "); + System.out.println("}"); + } + if (diff.size() > 0) { + String newPort = diff.get(0); + if (verbose) + System.out.println("Found upload port: " + newPort); + return newPort; + } + + // Keep track of port that disappears + before = now; + Thread.sleep(250); + elapsed += 250; + + // On Windows, it can take a long time for the port to disappear and + // come back, so use a longer time out before assuming that the + // selected + // port is the bootloader (not the sketch). + if (((!Base.isWindows() && elapsed >= 500) || elapsed >= 5000) && + now.contains(uploadPort)) { + if (verbose) + System.out.println("Uploading using selected port: " + + uploadPort); + return uploadPort; + } + } + + // Something happened while detecting port + throw new RunnerException( + _("Couldn't find a Board on the selected port. Check that you have the correct port selected. If it is correct, try pressing the board's reset button after initiating the upload.")); + } + + public boolean uploadUsingProgrammer(String buildPath, String className) + throws RunnerException { + + TargetPlatform targetPlatform = Base.getTargetPlatform(); + String programmer = Preferences.get("programmer"); + if (programmer.contains(":")) { + String[] split = programmer.split(":", 2); + targetPlatform = Base.getCurrentTargetPlatformFromPackage(split[0]); + programmer = split[1]; + } + + PreferencesMap prefs = Preferences.getMap(); + prefs.putAll(Base.getBoardPreferences()); + prefs.putAll(targetPlatform.getProgrammer(programmer)); + prefs.putAll(targetPlatform.getTool(prefs.get("program.tool"))); + + prefs.put("build.path", buildPath); + prefs.put("build.project_name", className); + + if (verbose) + prefs.put("program.verbose", prefs.get("program.params.verbose")); + else + prefs.put("program.verbose", prefs.get("program.params.quiet")); + + try { + // if (prefs.get("program.disable_flushing") == null + // || prefs.get("program.disable_flushing").toLowerCase().equals("false")) + // { + // flushSerialBuffer(); + // } + + String pattern = prefs.get("program.pattern"); + String[] cmd = StringReplacer.formatAndSplit(pattern, prefs, true); + return executeUploadCommand(cmd); + } catch (Exception e) { + throw new RunnerException(e); + } + } + + public boolean burnBootloader() throws RunnerException { + TargetPlatform targetPlatform = Base.getTargetPlatform(); + + // Find preferences for the selected programmer + PreferencesMap programmerPrefs; + String programmer = Preferences.get("programmer"); + if (programmer.contains(":")) { + String[] split = programmer.split(":", 2); + TargetPlatform platform = Base + .getCurrentTargetPlatformFromPackage(split[0]); + programmer = split[1]; + programmerPrefs = platform.getProgrammer(programmer); + } else { + programmerPrefs = targetPlatform.getProgrammer(programmer); + } + + // Build configuration for the current programmer + PreferencesMap prefs = Preferences.getMap(); + prefs.putAll(Base.getBoardPreferences()); + prefs.putAll(programmerPrefs); + + // Create configuration for bootloader tool + PreferencesMap toolPrefs = new PreferencesMap(); + String tool = prefs.get("bootloader.tool"); + if (tool.contains(":")) { + String[] split = tool.split(":", 2); + TargetPlatform platform = Base.getCurrentTargetPlatformFromPackage(split[0]); + tool = split[1]; + toolPrefs.putAll(platform.getTool(tool)); + if (toolPrefs.size() == 0) + throw new RunnerException( + I18n.format(_("Could not find tool {0} from package {1}"), tool, + split[0])); + } + toolPrefs.putAll(targetPlatform.getTool(tool)); + if (toolPrefs.size() == 0) + throw new RunnerException(I18n.format(_("Could not find tool {0}"), + tool)); + + // Merge tool with global configuration + prefs.putAll(toolPrefs); + if (verbose) { + prefs.put("erase.verbose", prefs.get("erase.params.verbose")); + prefs.put("bootloader.verbose", prefs.get("bootloader.params.verbose")); + } else { + prefs.put("erase.verbose", prefs.get("erase.params.quiet")); + prefs.put("bootloader.verbose", prefs.get("bootloader.params.quiet")); + } + + try { + String pattern = prefs.get("erase.pattern"); + String[] cmd = StringReplacer.formatAndSplit(pattern, prefs, true); + if (!executeUploadCommand(cmd)) + return false; + + pattern = prefs.get("bootloader.pattern"); + cmd = StringReplacer.formatAndSplit(pattern, prefs, true); + return executeUploadCommand(cmd); + } catch (Exception e) { + throw new RunnerException(e); + } + } } diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index e768a22b1..23a3b1fb3 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -44,6 +44,9 @@ import javax.swing.text.*; import javax.swing.undo.*; import cc.arduino.packages.BoardPort; +import cc.arduino.packages.Uploader; +import cc.arduino.packages.UploaderFactory; +import cc.arduino.packages.uploaders.SerialUploader; /** * Main editor panel for the Processing Development Environment. @@ -204,7 +207,7 @@ public class Editor extends JFrame implements RunnerListener { //sketchbook = new Sketchbook(this); if (serialMonitor == null) { - serialMonitor = new PerPortObjectFactory().newMonitor(Preferences.get("serial.port"), base); + serialMonitor = new UploaderFactory().newMonitor(Preferences.get("serial.port"), base); serialMonitor.setIconImage(getIconImage()); } @@ -970,7 +973,7 @@ public class Editor extends JFrame implements RunnerListener { // ignore } serialMonitor.setVisible(false); - serialMonitor = new PerPortObjectFactory().newMonitor(Preferences.get("serial.port"), base); + serialMonitor = new UploaderFactory().newMonitor(Preferences.get("serial.port"), base); onBoardOrPortChange(); @@ -2507,7 +2510,7 @@ public class Editor extends JFrame implements RunnerListener { SwingUtilities.invokeLater(new Runnable() { public void run() { try { - Uploader uploader = new BasicUploader(); + Uploader uploader = new SerialUploader(); if (uploader.burnBootloader()) { statusNotice(_("Done burning bootloader.")); } else { diff --git a/app/src/processing/app/NetworkMonitor.java b/app/src/processing/app/NetworkMonitor.java index 408b884a1..45ba8cf2c 100644 --- a/app/src/processing/app/NetworkMonitor.java +++ b/app/src/processing/app/NetworkMonitor.java @@ -5,23 +5,21 @@ import processing.app.debug.MessageSiphon; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; +import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; import java.util.regex.Matcher; +@SuppressWarnings("serial") public class NetworkMonitor extends AbstractMonitor { - private static final int MAX_CONNECT_RETRIES = 3; - private final String ipAddress; - private final Base base; private Socket socket; private MessageSiphon consumer; public NetworkMonitor(String port, Base base) { super(port); - this.base = base; Matcher matcher = Constants.IPV4_ADDRESS.matcher(port); matcher.find(); @@ -30,9 +28,10 @@ public class NetworkMonitor extends AbstractMonitor { onSendCommand(new ActionListener() { public void actionPerformed(ActionEvent event) { try { - socket.getOutputStream().write(textField.getText().getBytes()); - socket.getOutputStream().write('\n'); - socket.getOutputStream().flush(); + OutputStream out = socket.getOutputStream(); + out.write(textField.getText().getBytes()); + out.write('\n'); + out.flush(); } catch (IOException e) { e.printStackTrace(); } @@ -54,14 +53,6 @@ public class NetworkMonitor extends AbstractMonitor { } } - private void sleep(long millis) { - try { - Thread.sleep(millis); - } catch (InterruptedException e) { - // ignore - } - } - @Override public void close() throws IOException { if (socket != null) { diff --git a/app/src/processing/app/Sketch.java b/app/src/processing/app/Sketch.java index b59a4ff7f..040a6cb24 100644 --- a/app/src/processing/app/Sketch.java +++ b/app/src/processing/app/Sketch.java @@ -24,6 +24,9 @@ package processing.app; import org.apache.commons.codec.digest.DigestUtils; + +import cc.arduino.packages.Uploader; +import cc.arduino.packages.UploaderFactory; import processing.app.debug.*; import processing.app.debug.Compiler; import processing.app.forms.PasswordAuthorizationDialog; @@ -1663,7 +1666,7 @@ public class Sketch { TargetPlatform target = Base.getTargetPlatform(); String board = Preferences.get("board"); - Uploader uploader = new PerPortObjectFactory().newUploader(target.getBoards().get(board), Preferences.get("serial.port")); + Uploader uploader = new UploaderFactory().newUploader(target.getBoards().get(board), Preferences.get("serial.port")); boolean success = false; do { diff --git a/app/src/processing/app/debug/BasicUploader.java b/app/src/processing/app/debug/BasicUploader.java deleted file mode 100644 index 2a370ff06..000000000 --- a/app/src/processing/app/debug/BasicUploader.java +++ /dev/null @@ -1,318 +0,0 @@ -/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - BasicUploader - generic command line uploader implementation - Part of the Arduino project - http://www.arduino.cc/ - - Copyright (c) 2004-05 - Hernando Barragan - Copyright (c) 2012 - Cristian Maglie - - This program 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 2 of the License, or - (at your option) any later version. - - This program 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 this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - $Id$ -*/ - -package processing.app.debug; - -import java.util.ArrayList; -import java.util.List; - -import processing.app.Base; -import processing.app.I18n; -import processing.app.Preferences; -import processing.app.Serial; -import processing.app.SerialException; -import processing.app.helpers.PreferencesMap; -import processing.app.helpers.StringReplacer; - -import static processing.app.I18n._; - -public class BasicUploader extends Uploader { - - public boolean uploadUsingPreferences(String buildPath, String className, - boolean usingProgrammer) - throws RunnerException { - // FIXME: Preferences should be reorganized - TargetPlatform targetPlatform = Base.getTargetPlatform(); - PreferencesMap prefs = Preferences.getMap(); - prefs.putAll(Base.getBoardPreferences()); - prefs.putAll(targetPlatform.getTool(prefs.get("upload.tool"))); - - // if no protocol is specified for this board, assume it lacks a - // bootloader and upload using the selected programmer. - if (usingProgrammer || prefs.get("upload.protocol") == null) { - return uploadUsingProgrammer(buildPath, className); - } - - // need to do a little dance for Leonardo and derivatives: - // open then close the port at the magic baudrate (usually 1200 bps) first - // to signal to the sketch that it should reset into bootloader. after doing - // this wait a moment for the bootloader to enumerate. On Windows, also must - // deal with the fact that the COM port number changes from bootloader to - // sketch. - String t = prefs.get("upload.use_1200bps_touch"); - boolean doTouch = t != null && t.equals("true"); - - t = prefs.get("upload.wait_for_upload_port"); - boolean waitForUploadPort = (t != null) && t.equals("true"); - - if (doTouch) { - String uploadPort = prefs.get("serial.port"); - try { - // Toggle 1200 bps on selected serial port to force board reset. - List before = Serial.list(); - if (before.contains(uploadPort)) { - if (verbose || Preferences.getBoolean("upload.verbose")) - System.out - .println(_("Forcing reset using 1200bps open/close on port ") + - uploadPort); - Serial.touchPort(uploadPort, 1200); - } - if (waitForUploadPort) { - // Scanning for available ports seems to open the port or - // otherwise assert DTR, which would cancel the WDT reset if - // it happened within 250 ms. So we wait until the reset should - // have already occured before we start scanning. - if (!Base.isMacOS()) - Thread.sleep(300); - - uploadPort = waitForUploadPort(uploadPort, before); - } else { - Thread.sleep(400); - } - } catch (SerialException e) { - throw new RunnerException(e.getMessage()); - } catch (InterruptedException e) { - throw new RunnerException(e.getMessage()); - } - prefs.put("serial.port", uploadPort); - if (uploadPort.startsWith("/dev/")) - prefs.put("serial.port.file", uploadPort.substring(5)); - else - prefs.put("serial.port.file", uploadPort); - } - - prefs.put("build.path", buildPath); - prefs.put("build.project_name", className); - if (verbose) - prefs.put("upload.verbose", prefs.get("upload.params.verbose")); - else - prefs.put("upload.verbose", prefs.get("upload.params.quiet")); - - boolean uploadResult; - try { -// if (prefs.get("upload.disable_flushing") == null -// || prefs.get("upload.disable_flushing").toLowerCase().equals("false")) { -// flushSerialBuffer(); -// } - - String pattern = prefs.get("upload.pattern"); - String[] cmd = StringReplacer.formatAndSplit(pattern, prefs, true); - uploadResult = executeUploadCommand(cmd); - } catch (Exception e) { - throw new RunnerException(e); - } - - // Remove the magic baud rate (1200bps) to avoid - // future unwanted board resets - try { - if (uploadResult && doTouch) { - String uploadPort = Preferences.get("serial.port"); - if (waitForUploadPort) { - // For Due/Leonardo wait until the bootloader serial port disconnects and the - // sketch serial port reconnects (or timeout after a few seconds if the - // sketch port never comes back). Doing this saves users from accidentally - // opening Serial Monitor on the soon-to-be-orphaned bootloader port. - Thread.sleep(500); - long timeout = System.currentTimeMillis() + 2000; - while (timeout > System.currentTimeMillis()) { - List portList = Serial.list(); - if (portList.contains(uploadPort)) { - try { - Serial.touchPort(uploadPort, 9600); - break; - } catch (SerialException e) { - // Port already in use - } - } - Thread.sleep(250); - } - } else { - try { - Serial.touchPort(uploadPort, 9600); - } catch (SerialException e) { - throw new RunnerException(e); - } - } - } - } catch (InterruptedException ex) { - } - return uploadResult; - } - - private String waitForUploadPort(String uploadPort, List before) - throws InterruptedException, RunnerException { - // Wait for a port to appear on the list - int elapsed = 0; - while (elapsed < 10000) { - List now = Serial.list(); - List diff = new ArrayList(now); - diff.removeAll(before); - if (verbose || Preferences.getBoolean("upload.verbose")) { - System.out.print("PORTS {"); - for (String p : before) - System.out.print(p + ", "); - System.out.print("} / {"); - for (String p : now) - System.out.print(p + ", "); - System.out.print("} => {"); - for (String p : diff) - System.out.print(p + ", "); - System.out.println("}"); - } - if (diff.size() > 0) { - String newPort = diff.get(0); - if (verbose || Preferences.getBoolean("upload.verbose")) - System.out.println("Found upload port: " + newPort); - return newPort; - } - - // Keep track of port that disappears - before = now; - Thread.sleep(250); - elapsed += 250; - - // On Windows, it can take a long time for the port to disappear and - // come back, so use a longer time out before assuming that the - // selected - // port is the bootloader (not the sketch). - if (((!Base.isWindows() && elapsed >= 500) || elapsed >= 5000) && - now.contains(uploadPort)) { - if (verbose || Preferences.getBoolean("upload.verbose")) - System.out.println("Uploading using selected port: " + - uploadPort); - return uploadPort; - } - } - - // Something happened while detecting port - throw new RunnerException( - _("Couldn't find a Board on the selected port. Check that you have the correct port selected. If it is correct, try pressing the board's reset button after initiating the upload.")); - } - - public boolean uploadUsingProgrammer(String buildPath, String className) - throws RunnerException { - - TargetPlatform targetPlatform = Base.getTargetPlatform(); - String programmer = Preferences.get("programmer"); - if (programmer.contains(":")) { - String[] split = programmer.split(":", 2); - targetPlatform = Base.getCurrentTargetPlatformFromPackage(split[0]); - programmer = split[1]; - } - - PreferencesMap prefs = Preferences.getMap(); - prefs.putAll(Base.getBoardPreferences()); - prefs.putAll(targetPlatform.getProgrammer(programmer)); - prefs.putAll(targetPlatform.getTool(prefs.get("program.tool"))); - - prefs.put("build.path", buildPath); - prefs.put("build.project_name", className); - - if (verbose) - prefs.put("program.verbose", prefs.get("program.params.verbose")); - else - prefs.put("program.verbose", prefs.get("program.params.quiet")); - - try { - // if (prefs.get("program.disable_flushing") == null - // || prefs.get("program.disable_flushing").toLowerCase().equals("false")) - // { - // flushSerialBuffer(); - // } - - String pattern = prefs.get("program.pattern"); - String[] cmd = StringReplacer.formatAndSplit(pattern, prefs, true); - return executeUploadCommand(cmd); - } catch (Exception e) { - throw new RunnerException(e); - } - } - - public boolean burnBootloader() throws RunnerException { - TargetPlatform targetPlatform = Base.getTargetPlatform(); - - // Find preferences for the selected programmer - PreferencesMap programmerPrefs; - String programmer = Preferences.get("programmer"); - if (programmer.contains(":")) { - String[] split = programmer.split(":", 2); - TargetPlatform platform = Base - .getCurrentTargetPlatformFromPackage(split[0]); - programmer = split[1]; - programmerPrefs = platform.getProgrammer(programmer); - } else { - programmerPrefs = targetPlatform.getProgrammer(programmer); - } - - // Build configuration for the current programmer - PreferencesMap prefs = Preferences.getMap(); - prefs.putAll(Base.getBoardPreferences()); - prefs.putAll(programmerPrefs); - - // Create configuration for bootloader tool - PreferencesMap toolPrefs = new PreferencesMap(); - String tool = prefs.get("bootloader.tool"); - if (tool.contains(":")) { - String[] split = tool.split(":", 2); - TargetPlatform platform = Base.getCurrentTargetPlatformFromPackage(split[0]); - tool = split[1]; - toolPrefs.putAll(platform.getTool(tool)); - if (toolPrefs.size() == 0) - throw new RunnerException( - I18n.format(_("Could not find tool {0} from package {1}"), tool, - split[0])); - } - toolPrefs.putAll(targetPlatform.getTool(tool)); - if (toolPrefs.size() == 0) - throw new RunnerException(I18n.format(_("Could not find tool {0}"), - tool)); - - // Merge tool with global configuration - prefs.putAll(toolPrefs); - if (verbose) { - prefs.put("erase.verbose", prefs.get("erase.params.verbose")); - prefs.put("bootloader.verbose", prefs.get("bootloader.params.verbose")); - } else { - prefs.put("erase.verbose", prefs.get("erase.params.quiet")); - prefs.put("bootloader.verbose", prefs.get("bootloader.params.quiet")); - } - - try { - String pattern = prefs.get("erase.pattern"); - String[] cmd = StringReplacer.formatAndSplit(pattern, prefs, true); - if (!executeUploadCommand(cmd)) - return false; - - pattern = prefs.get("bootloader.pattern"); - cmd = StringReplacer.formatAndSplit(pattern, prefs, true); - return executeUploadCommand(cmd); - } catch (Exception e) { - throw new RunnerException(e); - } - } -} diff --git a/app/src/processing/app/debug/Uploader.java b/app/src/processing/app/debug/Uploader.java deleted file mode 100644 index c73fb24fc..000000000 --- a/app/src/processing/app/debug/Uploader.java +++ /dev/null @@ -1,227 +0,0 @@ -/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Uploader - abstract uploading baseclass (common to both uisp and avrdude) - Part of the Arduino project - http://www.arduino.cc/ - - Copyright (c) 2004-05 - Hernando Barragan - - This program 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 2 of the License, or - (at your option) any later version. - - This program 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 this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - $Id$ -*/ - -package processing.app.debug; - -import static processing.app.I18n._; - -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Collection; - -import processing.app.I18n; -import processing.app.Preferences; -import processing.app.Serial; -import processing.app.SerialException; -import processing.app.SerialNotFoundException; - -public abstract class Uploader implements MessageConsumer { - static final String BUGS_URL = - _("https://developer.berlios.de/bugs/?group_id=3590"); - static final String SUPER_BADNESS = - I18n.format(_("Compiler error, please submit this code to {0}"), BUGS_URL); - - RunnerException exception; - - static InputStream serialInput; - static OutputStream serialOutput; - - boolean verbose; - - public abstract boolean uploadUsingPreferences(String buildPath, String className, boolean usingProgrammer) - throws RunnerException; - - public abstract boolean burnBootloader() throws RunnerException; - - public boolean requiresAuthorization() { - return false; - } - - public String getAuthorizationKey() { - return null; - } - - protected void flushSerialBuffer() throws RunnerException, SerialException { - // Cleanup the serial buffer - try { - Serial serialPort = new Serial(); - while(serialPort.available() > 0) { - serialPort.readBytes(); - try { - Thread.sleep(100); - } catch (InterruptedException e) {} - } - - serialPort.setDTR(false); - serialPort.setRTS(false); - - try { - Thread.sleep(100); - } catch (InterruptedException e) {} - - serialPort.setDTR(true); - serialPort.setRTS(true); - - serialPort.dispose(); - } catch (SerialNotFoundException e) { - throw e; - } catch(Exception e) { - e.printStackTrace(); - throw new RunnerException(e.getMessage()); - } - } - - protected boolean executeUploadCommand(Collection commandDownloader) - throws RunnerException { - String[] commandArray = new String[commandDownloader.size()]; - commandDownloader.toArray(commandArray); - return executeUploadCommand(commandArray); - } - - protected boolean executeUploadCommand(String commandArray[]) - throws RunnerException - { - firstErrorFound = false; // haven't found any errors yet - secondErrorFound = false; - notFoundError = false; - int result=0; // pre-initialized to quiet a bogus warning from jikes - - try { - if (verbose || Preferences.getBoolean("upload.verbose")) { - for(int i = 0; i < commandArray.length; i++) { - System.out.print(commandArray[i] + " "); - } - System.out.println(); - } - Process process = Runtime.getRuntime().exec(commandArray); - new MessageSiphon(process.getInputStream(), this); - new MessageSiphon(process.getErrorStream(), this); - - // wait for the process to finish. if interrupted - // before waitFor returns, continue waiting - // - boolean compiling = true; - while (compiling) { - try { - result = process.waitFor(); - compiling = false; - } catch (InterruptedException intExc) { - } - } - if(exception!=null) { - exception.hideStackTrace(); - throw exception; - } - if(result!=0) - return false; - } catch (Exception e) { - String msg = e.getMessage(); - if ((msg != null) && (msg.indexOf("uisp: not found") != -1) && (msg.indexOf("avrdude: not found") != -1)) { - //System.err.println("uisp is missing"); - //JOptionPane.showMessageDialog(editor.base, - // "Could not find the compiler.\n" + - // "uisp is missing from your PATH,\n" + - // "see readme.txt for help.", - // "Compiler error", - // JOptionPane.ERROR_MESSAGE); - return false; - } else { - e.printStackTrace(); - result = -1; - } - } - //System.out.println("result2 is "+result); - // if the result isn't a known, expected value it means that something - // is fairly wrong, one possibility is that jikes has crashed. - // - if (exception != null) throw exception; - - if ((result != 0) && (result != 1 )) { - exception = new RunnerException(SUPER_BADNESS); - //editor.error(exception); - //PdeBase.openURL(BUGS_URL); - //throw new PdeException(SUPER_BADNESS); - } - - return (result == 0); // ? true : false; - - } - - boolean firstErrorFound; - boolean secondErrorFound; - - // part of the PdeMessageConsumer interface - // - boolean notFoundError; - - public void message(String s) { - // selectively suppress a bunch of avrdude output for AVR109/Caterina that should already be quelled but isn't - if (!Preferences.getBoolean("upload.verbose") && ( - s.indexOf("Connecting to programmer:") != -1 || - s.indexOf("Found programmer: Id = \"CATERIN\"; type = S") != -1 || - s.indexOf("Software Version = 1.0; No Hardware Version given.") != -1 || - s.indexOf("Programmer supports auto addr increment.") != -1 || - s.indexOf("Programmer supports buffered memory access with buffersize=128 bytes.") != -1 || - s.indexOf("Programmer supports the following devices:") != -1 || - s.indexOf("Device code: 0x44") != -1)) - s = ""; - - System.err.print(s); - - // ignore cautions - if (s.indexOf("Error") != -1) { - //exception = new RunnerException(s+" Check the serial port selected or your Board is connected"); - //System.out.println(s); - notFoundError = true; - return; - } - if(notFoundError) { - //System.out.println("throwing something"); - exception = new RunnerException(I18n.format(_("the selected serial port {0} does not exist or your board is not connected"), s)); - return; - } - if (s.indexOf("Device is not responding") != -1 ) { - exception = new RunnerException(_("Device is not responding, check the right serial port is selected or RESET the board right before exporting")); - return; - } - if (s.indexOf("Programmer is not responding") != -1 || - s.indexOf("programmer is not responding") != -1 || - s.indexOf("protocol error") != -1 || - s.indexOf("avrdude: ser_open(): can't open device") != -1 || - s.indexOf("avrdude: ser_drain(): read error") != -1 || - s.indexOf("avrdude: ser_send(): write error") != -1 || - s.indexOf("avrdude: error: buffered memory access not supported.") != -1) { - exception = new RunnerException(_("Problem uploading to board. See http://www.arduino.cc/en/Guide/Troubleshooting#upload for suggestions.")); - return; - } - if (s.indexOf("Expected signature") != -1) { - exception = new RunnerException(_("Wrong microcontroller found. Did you select the right board from the Tools > Board menu?")); - return; - } - } - - -} diff --git a/app/test/processing/app/debug/UploaderFactoryTest.java b/app/test/processing/app/debug/UploaderFactoryTest.java index 92d93b1ca..0f40b8066 100644 --- a/app/test/processing/app/debug/UploaderFactoryTest.java +++ b/app/test/processing/app/debug/UploaderFactoryTest.java @@ -2,8 +2,12 @@ package processing.app.debug; import org.junit.Before; import org.junit.Test; + +import cc.arduino.packages.Uploader; +import cc.arduino.packages.UploaderFactory; +import cc.arduino.packages.uploaders.HttpUploader; +import cc.arduino.packages.uploaders.SerialUploader; import processing.app.AbstractWithPreferencesTest; -import processing.app.PerPortObjectFactory; import java.io.File; @@ -21,7 +25,7 @@ public class UploaderFactoryTest extends AbstractWithPreferencesTest { @Test public void shouldCreateAnInstanceOfHttpUploader() throws Exception { TargetBoard board = targetPackage.getPlatforms().get("avr").getBoards().get("yun"); - Uploader uploader = new PerPortObjectFactory().newUploader(board, "192.168.0.1 (yun)"); + Uploader uploader = new UploaderFactory().newUploader(board, "192.168.0.1 (yun)"); assertTrue(uploader instanceof HttpUploader); } @@ -29,16 +33,16 @@ public class UploaderFactoryTest extends AbstractWithPreferencesTest { @Test public void shouldCreateAnInstanceOfBasicUploaderWhenHTTPIsUnsupported() throws Exception { TargetBoard board = targetPackage.getPlatforms().get("avr").getBoards().get("uno"); - Uploader uploader = new PerPortObjectFactory().newUploader(board, "192.168.0.1 (myyun)"); + Uploader uploader = new UploaderFactory().newUploader(board, "192.168.0.1 (myyun)"); - assertTrue(uploader instanceof BasicUploader); + assertTrue(uploader instanceof SerialUploader); } @Test public void shouldCreateAnInstanceOfBasicUploaderWhenPortIsSerial() throws Exception { TargetBoard board = targetPackage.getPlatforms().get("avr").getBoards().get("uno"); - Uploader uploader = new PerPortObjectFactory().newUploader(board, "/dev/ttyACM0 (Arduino Leonardo)"); + Uploader uploader = new UploaderFactory().newUploader(board, "/dev/ttyACM0 (Arduino Leonardo)"); - assertTrue(uploader instanceof BasicUploader); + assertTrue(uploader instanceof SerialUploader); } }