From 376b0f8b3ffa81a5ee4881ebfc34ef8a998953f8 Mon Sep 17 00:00:00 2001 From: Federico Fissore Date: Wed, 29 May 2013 16:14:17 +0200 Subject: [PATCH] Serial.dispose() throws IOException SerialException extends IOException SerialMonitor is now a subclass of a generic AbstractMonitor; introducing NetworkMonitor UploaderFactory becomes PerPortObjectFactory and can build AbstractMonitors favouring IOException over SerialException collecting constants in Constants made MessageSiphon stoppable --- app/src/processing/app/AbstractMonitor.java | 183 ++++++++++++++++ app/src/processing/app/Base.java | 12 +- app/src/processing/app/Constants.java | 9 + app/src/processing/app/Editor.java | 22 +- app/src/processing/app/NetworkMonitor.java | 73 +++++++ .../processing/app/PerPortObjectFactory.java | 28 +++ app/src/processing/app/Serial.java | 19 +- app/src/processing/app/SerialException.java | 4 +- app/src/processing/app/SerialMonitor.java | 202 +++--------------- app/src/processing/app/Sketch.java | 6 +- .../processing/app/debug/BasicUploader.java | 8 +- .../processing/app/debug/HttpUploader.java | 9 +- .../processing/app/debug/MessageSiphon.java | 35 +-- app/src/processing/app/debug/Uploader.java | 2 +- .../processing/app/debug/UploaderFactory.java | 18 -- .../app/debug/UploaderFactoryTest.java | 7 +- 16 files changed, 396 insertions(+), 241 deletions(-) create mode 100644 app/src/processing/app/AbstractMonitor.java create mode 100644 app/src/processing/app/Constants.java create mode 100644 app/src/processing/app/NetworkMonitor.java create mode 100644 app/src/processing/app/PerPortObjectFactory.java delete mode 100644 app/src/processing/app/debug/UploaderFactory.java diff --git a/app/src/processing/app/AbstractMonitor.java b/app/src/processing/app/AbstractMonitor.java new file mode 100644 index 000000000..def9fe57e --- /dev/null +++ b/app/src/processing/app/AbstractMonitor.java @@ -0,0 +1,183 @@ +package processing.app; + +import processing.app.debug.MessageConsumer; +import processing.core.PApplet; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import javax.swing.text.DefaultCaret; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.IOException; + +import static processing.app.I18n._; + +public abstract class AbstractMonitor extends JFrame implements MessageConsumer { + + protected JTextArea textArea; + protected JScrollPane scrollPane; + protected JTextField textField; + protected JButton sendButton; + protected JCheckBox autoscrollBox; + protected JComboBox lineEndings; + protected JComboBox serialRates; + protected int serialRate; + + public AbstractMonitor(String title) { + super(title); + + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent event) { + try { + close(); + } catch (IOException e) { + // ignore + } + } + }); + + // obvious, no? + KeyStroke wc = Editor.WINDOW_CLOSE_KEYSTROKE; + getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(wc, "close"); + getRootPane().getActionMap().put("close", (new AbstractAction() { + public void actionPerformed(ActionEvent event) { + try { + close(); + } catch (IOException e) { + // ignore + } + setVisible(false); + } + })); + + getContentPane().setLayout(new BorderLayout()); + + Font consoleFont = Theme.getFont("console.font"); + Font editorFont = Preferences.getFont("editor.font"); + Font font = new Font(consoleFont.getName(), consoleFont.getStyle(), editorFont.getSize()); + + textArea = new JTextArea(16, 40); + textArea.setEditable(false); + textArea.setFont(font); + + // don't automatically update the caret. that way we can manually decide + // whether or not to do so based on the autoscroll checkbox. + ((DefaultCaret) textArea.getCaret()).setUpdatePolicy(DefaultCaret.NEVER_UPDATE); + + scrollPane = new JScrollPane(textArea); + + getContentPane().add(scrollPane, BorderLayout.CENTER); + + JPanel pane = new JPanel(); + pane.setLayout(new BoxLayout(pane, BoxLayout.X_AXIS)); + pane.setBorder(new EmptyBorder(4, 4, 4, 4)); + + textField = new JTextField(40); + sendButton = new JButton(_("Send")); + + pane.add(textField); + pane.add(Box.createRigidArea(new Dimension(4, 0))); + pane.add(sendButton); + + getContentPane().add(pane, BorderLayout.NORTH); + + pane = new JPanel(); + pane.setLayout(new BoxLayout(pane, BoxLayout.X_AXIS)); + pane.setBorder(new EmptyBorder(4, 4, 4, 4)); + + autoscrollBox = new JCheckBox(_("Autoscroll"), true); + + lineEndings = new JComboBox(new String[]{_("No line ending"), _("Newline"), _("Carriage return"), _("Both NL & CR")}); + lineEndings.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + Preferences.setInteger("serial.line_ending", lineEndings.getSelectedIndex()); + } + }); + if (Preferences.get("serial.line_ending") != null) { + lineEndings.setSelectedIndex(Preferences.getInteger("serial.line_ending")); + } + lineEndings.setMaximumSize(lineEndings.getMinimumSize()); + + String[] serialRateStrings = { + "300", "1200", "2400", "4800", "9600", + "19200", "57600", "115200" + }; + + serialRates = new JComboBox(); + for (int i = 0; i < serialRateStrings.length; i++) + serialRates.addItem(serialRateStrings[i] + " " + _("baud")); + + serialRate = Preferences.getInteger("serial.debug_rate"); + serialRates.setSelectedItem(serialRate + " " + _("baud")); + serialRates.setMaximumSize(serialRates.getMinimumSize()); + + pane.add(autoscrollBox); + pane.add(Box.createHorizontalGlue()); + pane.add(lineEndings); + pane.add(Box.createRigidArea(new Dimension(8, 0))); + pane.add(serialRates); + + getContentPane().add(pane, BorderLayout.SOUTH); + + pack(); + + Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); + if (Preferences.get("last.screen.height") != null) { + // if screen size has changed, the window coordinates no longer + // make sense, so don't use them unless they're identical + int screenW = Preferences.getInteger("last.screen.width"); + int screenH = Preferences.getInteger("last.screen.height"); + if ((screen.width == screenW) && (screen.height == screenH)) { + String locationStr = Preferences.get("last.serial.location"); + if (locationStr != null) { + int[] location = PApplet.parseInt(PApplet.split(locationStr, ',')); + setPlacement(location); + } + } + } + } + + public void onSerialRateChange(ActionListener listener) { + serialRates.addActionListener(listener); + } + + public void onSendCommand(ActionListener listener) { + textField.addActionListener(listener); + sendButton.addActionListener(listener); + } + + protected void setPlacement(int[] location) { + setBounds(location[0], location[1], location[2], location[3]); + } + + protected int[] getPlacement() { + int[] location = new int[4]; + + // Get the dimensions of the Frame + Rectangle bounds = getBounds(); + location[0] = bounds.x; + location[1] = bounds.y; + location[2] = bounds.width; + location[3] = bounds.height; + + return location; + } + + public void message(final String s) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + textArea.append(s); + if (autoscrollBox.isSelected()) { + textArea.setCaretPosition(textArea.getDocument().getLength()); + } + } + }); + } + + public abstract void open() throws IOException; + + public abstract void close() throws IOException; +} diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index 59fb09069..1a103562f 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -936,7 +936,11 @@ public class Base { // This will store the sketch count as zero editors.remove(editor); - Editor.serialMonitor.closeSerialPort(); + try { + Editor.serialMonitor.close(); + } catch (IOException e) { + //ignore + } storeSketches(); // Save out the current prefs state @@ -974,7 +978,11 @@ public class Base { // If quit is canceled, this will be replaced anyway // by a later handleQuit() that is not canceled. storeSketches(); - Editor.serialMonitor.closeSerialPort(); + try { + Editor.serialMonitor.close(); + } catch (IOException e) { + // ignore + } if (handleQuitEach()) { // make sure running sketches close before quitting diff --git a/app/src/processing/app/Constants.java b/app/src/processing/app/Constants.java new file mode 100644 index 000000000..b4159cd0c --- /dev/null +++ b/app/src/processing/app/Constants.java @@ -0,0 +1,9 @@ +package processing.app; + +import java.util.regex.Pattern; + +public class Constants { + + public static final Pattern IPV4_ADDRESS = Pattern.compile("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"); + +} diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index b2e61cad2..84ed3afd7 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -99,7 +99,7 @@ public class Editor extends JFrame implements RunnerListener { static List boardsMenus; static JMenu serialMenu; - static SerialMonitor serialMonitor; + static AbstractMonitor serialMonitor; EditorHeader header; EditorStatus status; @@ -204,7 +204,7 @@ public class Editor extends JFrame implements RunnerListener { //sketchbook = new Sketchbook(this); if (serialMonitor == null) { - serialMonitor = new SerialMonitor(Preferences.get("serial.port")); + serialMonitor = new PerPortObjectFactory().newMonitor(Preferences.get("serial.port"), base); serialMonitor.setIconImage(getIconImage()); } @@ -964,9 +964,13 @@ public class Editor extends JFrame implements RunnerListener { Preferences.set("serial.port.file", name.substring(5)); else Preferences.set("serial.port.file", name); - serialMonitor.closeSerialPort(); + try { + serialMonitor.close(); + } catch (IOException e) { + // ignore + } serialMonitor.setVisible(false); - serialMonitor = new SerialMonitor(Preferences.get("serial.port")); + serialMonitor = new PerPortObjectFactory().newMonitor(Preferences.get("serial.port"), base); onBoardOrPortChange(); @@ -2401,7 +2405,7 @@ public class Editor extends JFrame implements RunnerListener { public void run() { try { - serialMonitor.closeSerialPort(); + serialMonitor.close(); serialMonitor.setVisible(false); uploading = true; @@ -2437,7 +2441,7 @@ public class Editor extends JFrame implements RunnerListener { public void run() { try { - serialMonitor.closeSerialPort(); + serialMonitor.close(); serialMonitor.setVisible(false); uploading = true; @@ -2507,9 +2511,11 @@ public class Editor extends JFrame implements RunnerListener { if (uploading) return; try { - serialMonitor.openSerialPort(); + serialMonitor.open(); serialMonitor.setVisible(true); - } catch (SerialException e) { + } catch (ConnectException e) { + statusError(_("Unable to connect: is the sketch using the bridge?")); + } catch (IOException e) { statusError(e); } } diff --git a/app/src/processing/app/NetworkMonitor.java b/app/src/processing/app/NetworkMonitor.java new file mode 100644 index 000000000..408b884a1 --- /dev/null +++ b/app/src/processing/app/NetworkMonitor.java @@ -0,0 +1,73 @@ +package processing.app; + +import processing.app.debug.MessageSiphon; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.regex.Matcher; + +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(); + this.ipAddress = matcher.group(); + + onSendCommand(new ActionListener() { + public void actionPerformed(ActionEvent event) { + try { + socket.getOutputStream().write(textField.getText().getBytes()); + socket.getOutputStream().write('\n'); + socket.getOutputStream().flush(); + } catch (IOException e) { + e.printStackTrace(); + } + textField.setText(""); + } + }); + } + + @Override + public void open() throws IOException { + try { + socket = new Socket(); + socket.connect(new InetSocketAddress(ipAddress, 6571), 5000); + consumer = new MessageSiphon(socket.getInputStream(), this); + return; + } catch (IOException e) { + socket = null; + throw e; + } + } + + private void sleep(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + // ignore + } + } + + @Override + public void close() throws IOException { + if (socket != null) { + consumer.stop(); + socket.close(); + textArea.setText(""); + } + } +} diff --git a/app/src/processing/app/PerPortObjectFactory.java b/app/src/processing/app/PerPortObjectFactory.java new file mode 100644 index 000000000..4670085b0 --- /dev/null +++ b/app/src/processing/app/PerPortObjectFactory.java @@ -0,0 +1,28 @@ +package processing.app; + +import processing.app.debug.BasicUploader; +import processing.app.debug.HttpUploader; +import processing.app.debug.TargetBoard; +import processing.app.debug.Uploader; + +import java.util.regex.Pattern; + +public class PerPortObjectFactory { + + 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(); + } + + public AbstractMonitor newMonitor(String port, Base base) { + if (Constants.IPV4_ADDRESS.matcher(port).find()) { + return new NetworkMonitor(port, base); + } + + return new SerialMonitor(port); + } + +} diff --git a/app/src/processing/app/Serial.java b/app/src/processing/app/Serial.java index 08a436f5c..d5d1a2d70 100644 --- a/app/src/processing/app/Serial.java +++ b/app/src/processing/app/Serial.java @@ -219,24 +219,15 @@ public class Serial implements SerialPortEventListener { //public void key(java.awt.event.KeyEvent e) { } - public void dispose() { - try { - // do io streams need to be closed first? - if (input != null) input.close(); - if (output != null) output.close(); + public void dispose() throws IOException { + // do io streams need to be closed first? + if (input != null) input.close(); + if (output != null) output.close(); - } catch (Exception e) { - e.printStackTrace(); - } input = null; output = null; - try { - if (port != null) port.close(); // close the port - - } catch (Exception e) { - e.printStackTrace(); - } + if (port != null) port.close(); // close the port port = null; } diff --git a/app/src/processing/app/SerialException.java b/app/src/processing/app/SerialException.java index 525c24078..d47e60189 100644 --- a/app/src/processing/app/SerialException.java +++ b/app/src/processing/app/SerialException.java @@ -20,7 +20,9 @@ package processing.app; -public class SerialException extends Exception { +import java.io.IOException; + +public class SerialException extends IOException { public SerialException() { super(); } diff --git a/app/src/processing/app/SerialMonitor.java b/app/src/processing/app/SerialMonitor.java index 90edfbcee..9e95c91c8 100644 --- a/app/src/processing/app/SerialMonitor.java +++ b/app/src/processing/app/SerialMonitor.java @@ -18,200 +18,74 @@ package processing.app; -import processing.app.debug.MessageConsumer; -import processing.core.*; -import static processing.app.I18n._; +import processing.core.PApplet; -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; -import javax.swing.border.*; -import javax.swing.event.*; -import javax.swing.text.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; -public class SerialMonitor extends JFrame implements MessageConsumer { +public class SerialMonitor extends AbstractMonitor { + + private final String port; private Serial serial; - private String port; - private JTextArea textArea; - private JScrollPane scrollPane; - private JTextField textField; - private JButton sendButton; - private JCheckBox autoscrollBox; - private JComboBox lineEndings; - private JComboBox serialRates; private int serialRate; public SerialMonitor(String port) { super(port); - + this.port = port; - - addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) { - closeSerialPort(); - } - }); - - // obvious, no? - KeyStroke wc = Editor.WINDOW_CLOSE_KEYSTROKE; - getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(wc, "close"); - getRootPane().getActionMap().put("close", new AbstractAction() { - public void actionPerformed(ActionEvent e) { - closeSerialPort(); - setVisible(false); - }}); - - getContentPane().setLayout(new BorderLayout()); - - Font consoleFont = Theme.getFont("console.font"); - Font editorFont = Preferences.getFont("editor.font"); - Font font = new Font(consoleFont.getName(), consoleFont.getStyle(), editorFont.getSize()); - textArea = new JTextArea(16, 40); - textArea.setEditable(false); - textArea.setFont(font); - - // don't automatically update the caret. that way we can manually decide - // whether or not to do so based on the autoscroll checkbox. - ((DefaultCaret)textArea.getCaret()).setUpdatePolicy(DefaultCaret.NEVER_UPDATE); - - scrollPane = new JScrollPane(textArea); - - getContentPane().add(scrollPane, BorderLayout.CENTER); - - JPanel pane = new JPanel(); - pane.setLayout(new BoxLayout(pane, BoxLayout.X_AXIS)); - pane.setBorder(new EmptyBorder(4, 4, 4, 4)); - - textField = new JTextField(40); - textField.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - send(textField.getText()); - textField.setText(""); - }}); - - sendButton = new JButton(_("Send")); - sendButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - send(textField.getText()); - textField.setText(""); - }}); - - pane.add(textField); - pane.add(Box.createRigidArea(new Dimension(4, 0))); - pane.add(sendButton); - - getContentPane().add(pane, BorderLayout.NORTH); - - pane = new JPanel(); - pane.setLayout(new BoxLayout(pane, BoxLayout.X_AXIS)); - pane.setBorder(new EmptyBorder(4, 4, 4, 4)); - - autoscrollBox = new JCheckBox(_("Autoscroll"), true); - - lineEndings = new JComboBox(new String[] { _("No line ending"), _("Newline"), _("Carriage return"), _("Both NL & CR") }); - lineEndings.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent event) { - Preferences.setInteger("serial.line_ending", lineEndings.getSelectedIndex()); - } - }); - if (Preferences.get("serial.line_ending") != null) { - lineEndings.setSelectedIndex(Preferences.getInteger("serial.line_ending")); - } - lineEndings.setMaximumSize(lineEndings.getMinimumSize()); - - String[] serialRateStrings = { - "300","1200","2400","4800","9600", - "19200","57600","115200" - }; - - serialRates = new JComboBox(); - for (int i = 0; i < serialRateStrings.length; i++) - serialRates.addItem(serialRateStrings[i] + " " + _("baud")); - - serialRate = Preferences.getInteger("serial.debug_rate"); - serialRates.setSelectedItem(serialRate + " " + _("baud")); - serialRates.addActionListener(new ActionListener() { + onSerialRateChange(new ActionListener() { public void actionPerformed(ActionEvent event) { String wholeString = (String) serialRates.getSelectedItem(); String rateString = wholeString.substring(0, wholeString.indexOf(' ')); serialRate = Integer.parseInt(rateString); Preferences.set("serial.debug_rate", rateString); - closeSerialPort(); try { + close(); Thread.sleep(100); // Wait for serial port to properly close - openSerialPort(); - } catch (SerialException e) { + open(); + } catch (IOException e) { System.err.println(e); } catch (InterruptedException e) { e.printStackTrace(); } - }}); - - serialRates.setMaximumSize(serialRates.getMinimumSize()); - - pane.add(autoscrollBox); - pane.add(Box.createHorizontalGlue()); - pane.add(lineEndings); - pane.add(Box.createRigidArea(new Dimension(8, 0))); - pane.add(serialRates); - - getContentPane().add(pane, BorderLayout.SOUTH); - - pack(); - - Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); - if (Preferences.get("last.screen.height") != null) { - // if screen size has changed, the window coordinates no longer - // make sense, so don't use them unless they're identical - int screenW = Preferences.getInteger("last.screen.width"); - int screenH = Preferences.getInteger("last.screen.height"); - if ((screen.width == screenW) && (screen.height == screenH)) { - String locationStr = Preferences.get("last.serial.location"); - if (locationStr != null) { - int[] location = PApplet.parseInt(PApplet.split(locationStr, ',')); - setPlacement(location); - } } - } - } - - protected void setPlacement(int[] location) { - setBounds(location[0], location[1], location[2], location[3]); - } + }); - protected int[] getPlacement() { - int[] location = new int[4]; - - // Get the dimensions of the Frame - Rectangle bounds = getBounds(); - location[0] = bounds.x; - location[1] = bounds.y; - location[2] = bounds.width; - location[3] = bounds.height; - - return location; + onSendCommand(new ActionListener() { + public void actionPerformed(ActionEvent e) { + send(textField.getText()); + textField.setText(""); + } + }); } private void send(String s) { if (serial != null) { switch (lineEndings.getSelectedIndex()) { - case 1: s += "\n"; break; - case 2: s += "\r"; break; - case 3: s += "\r\n"; break; + case 1: + s += "\n"; + break; + case 2: + s += "\r"; + break; + case 3: + s += "\r\n"; + break; } serial.write(s); } } - - public void openSerialPort() throws SerialException { + + public void open() throws IOException { if (serial != null) return; - + serial = new Serial(port, serialRate); serial.addListener(this); } - - public void closeSerialPort() { + + public void close() throws IOException { if (serial != null) { int[] location = getPlacement(); String locationStr = PApplet.join(PApplet.str(location), ","); @@ -221,14 +95,4 @@ public class SerialMonitor extends JFrame implements MessageConsumer { serial = null; } } - - public void message(final String s) { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - textArea.append(s); - if (autoscrollBox.isSelected()) { - textArea.setCaretPosition(textArea.getDocument().getLength()); - } - }}); - } } diff --git a/app/src/processing/app/Sketch.java b/app/src/processing/app/Sketch.java index e8e57f1ab..b59a4ff7f 100644 --- a/app/src/processing/app/Sketch.java +++ b/app/src/processing/app/Sketch.java @@ -1580,7 +1580,7 @@ public class Sketch { * Handle export to applet. */ public boolean exportApplet(String appletPath, boolean usingProgrammer) - throws RunnerException, IOException, SerialException { + throws RunnerException, IOException { prepare(); @@ -1658,12 +1658,12 @@ public class Sketch { System.out.println(_("Low memory available, stability problems may occur")); } - protected boolean upload(String buildPath, String suggestedClassName, boolean usingProgrammer) throws RunnerException, SerialException { + protected boolean upload(String buildPath, String suggestedClassName, boolean usingProgrammer) throws RunnerException { TargetPlatform target = Base.getTargetPlatform(); String board = Preferences.get("board"); - Uploader uploader = new UploaderFactory().newUploader(target.getBoards().get(board), Preferences.get("serial.port")); + Uploader uploader = new PerPortObjectFactory().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 index 1909c464a..2a370ff06 100644 --- a/app/src/processing/app/debug/BasicUploader.java +++ b/app/src/processing/app/debug/BasicUploader.java @@ -45,7 +45,7 @@ public class BasicUploader extends Uploader { public boolean uploadUsingPreferences(String buildPath, String className, boolean usingProgrammer) - throws RunnerException, SerialException { + throws RunnerException { // FIXME: Preferences should be reorganized TargetPlatform targetPlatform = Base.getTargetPlatform(); PreferencesMap prefs = Preferences.getMap(); @@ -152,7 +152,11 @@ public class BasicUploader extends Uploader { Thread.sleep(250); } } else { - Serial.touchPort(uploadPort, 9600); + try { + Serial.touchPort(uploadPort, 9600); + } catch (SerialException e) { + throw new RunnerException(e); + } } } } catch (InterruptedException ex) { diff --git a/app/src/processing/app/debug/HttpUploader.java b/app/src/processing/app/debug/HttpUploader.java index c04ad8530..10fd7e46c 100644 --- a/app/src/processing/app/debug/HttpUploader.java +++ b/app/src/processing/app/debug/HttpUploader.java @@ -7,8 +7,8 @@ import org.apache.commons.httpclient.NameValuePair; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import processing.app.Base; +import processing.app.Constants; import processing.app.Preferences; -import processing.app.SerialException; import processing.app.helpers.PreferencesMap; import java.io.*; @@ -19,7 +19,6 @@ import java.util.regex.Pattern; public class HttpUploader extends Uploader { - private static final Pattern IPV4_ADDRESS = Pattern.compile("(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})"); private static final String PROTOCOL = "http://"; /* @@ -34,11 +33,11 @@ public class HttpUploader extends Uploader { public HttpUploader(String port) { this.client = new HttpClient(); - Matcher matcher = IPV4_ADDRESS.matcher(port); + Matcher matcher = Constants.IPV4_ADDRESS.matcher(port); if (!matcher.find()) { throw new IllegalArgumentException(port); } - this.ipAddress = matcher.group(1); + this.ipAddress = matcher.group(); this.baseUrl = PROTOCOL + ipAddress + "/cgi-bin/luci/arduino"; } @@ -51,7 +50,7 @@ public class HttpUploader extends Uploader { } @Override - public boolean uploadUsingPreferences(String buildPath, String className, boolean usingProgrammer) throws RunnerException, SerialException { + public boolean uploadUsingPreferences(String buildPath, String className, boolean usingProgrammer) throws RunnerException { if (usingProgrammer) { System.err.println("Http upload using programmer not supported"); return false; diff --git a/app/src/processing/app/debug/MessageSiphon.java b/app/src/processing/app/debug/MessageSiphon.java index 12b1f993b..26901c3f4 100644 --- a/app/src/processing/app/debug/MessageSiphon.java +++ b/app/src/processing/app/debug/MessageSiphon.java @@ -23,27 +23,32 @@ package processing.app.debug; -import java.io.*; - +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.SocketException; /** * Slurps up messages from compiler. */ public class MessageSiphon implements Runnable { - BufferedReader streamReader; - Thread thread; - MessageConsumer consumer; + private final BufferedReader streamReader; + private final MessageConsumer consumer; + + private Thread thread; + private boolean canRun; public MessageSiphon(InputStream stream, MessageConsumer consumer) { this.streamReader = new BufferedReader(new InputStreamReader(stream)); this.consumer = consumer; + this.canRun = true; thread = new Thread(this); // don't set priority too low, otherwise exceptions won't // bubble up in time (i.e. compile errors have a weird delay) //thread.setPriority(Thread.MIN_PRIORITY); - thread.setPriority(Thread.MAX_PRIORITY-1); + thread.setPriority(Thread.MAX_PRIORITY - 1); thread.start(); } @@ -55,36 +60,35 @@ public class MessageSiphon implements Runnable { // when the program is finally done, null will come through. // String currentLine; - while ((currentLine = streamReader.readLine()) != null) { + while (canRun && (currentLine = streamReader.readLine()) != null) { // \n is added again because readLine() strips it out //EditorConsole.systemOut.println("messaging in"); consumer.message(currentLine + "\n"); //EditorConsole.systemOut.println("messaging out"); } //EditorConsole.systemOut.println("messaging thread done"); - thread = null; - } catch (NullPointerException npe) { // Fairly common exception during shutdown - thread = null; - + } catch (SocketException e) { + // socket has been close while we were wainting for data. nothing to see here, move along } catch (Exception e) { // On Linux and sometimes on Mac OS X, a "bad file descriptor" // message comes up when closing an applet that's run externally. // That message just gets supressed here.. String mess = e.getMessage(); if ((mess != null) && - (mess.indexOf("Bad file descriptor") != -1)) { + (mess.indexOf("Bad file descriptor") != -1)) { //if (e.getMessage().indexOf("Bad file descriptor") == -1) { //System.err.println("MessageSiphon err " + e); //e.printStackTrace(); } else { e.printStackTrace(); } + } finally { thread = null; } } - + // Wait until the MessageSiphon thread is complete. public void join() throws java.lang.InterruptedException { // Grab a temp copy in case another thread nulls the "thread" @@ -93,7 +97,8 @@ public class MessageSiphon implements Runnable { if (t != null) t.join(); } - public Thread getThread() { - return thread; + public void stop() { + this.canRun = false; } + } diff --git a/app/src/processing/app/debug/Uploader.java b/app/src/processing/app/debug/Uploader.java index 9073b2d6c..c73fb24fc 100644 --- a/app/src/processing/app/debug/Uploader.java +++ b/app/src/processing/app/debug/Uploader.java @@ -52,7 +52,7 @@ public abstract class Uploader implements MessageConsumer { boolean verbose; public abstract boolean uploadUsingPreferences(String buildPath, String className, boolean usingProgrammer) - throws RunnerException, SerialException; + throws RunnerException; public abstract boolean burnBootloader() throws RunnerException; diff --git a/app/src/processing/app/debug/UploaderFactory.java b/app/src/processing/app/debug/UploaderFactory.java deleted file mode 100644 index e9aeefcda..000000000 --- a/app/src/processing/app/debug/UploaderFactory.java +++ /dev/null @@ -1,18 +0,0 @@ -package processing.app.debug; - -import java.util.Map; -import java.util.regex.Pattern; - -public class UploaderFactory { - - private static final Pattern IPV4_ADDRESS = Pattern.compile("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"); - - public Uploader newUploader(TargetBoard board, String port) { - if ("true".equals(board.getPreferences().get("upload.via_http")) && IPV4_ADDRESS.matcher(port).find()) { - return new HttpUploader(port); - } - - return new BasicUploader(); - } - -} diff --git a/app/test/processing/app/debug/UploaderFactoryTest.java b/app/test/processing/app/debug/UploaderFactoryTest.java index cc7a6b56d..389151404 100644 --- a/app/test/processing/app/debug/UploaderFactoryTest.java +++ b/app/test/processing/app/debug/UploaderFactoryTest.java @@ -3,6 +3,7 @@ package processing.app.debug; import org.junit.Before; import org.junit.Test; import processing.app.AbstractWithPreferencesTest; +import processing.app.PerPortObjectFactory; import processing.app.helpers.PreferencesMap; import java.io.File; @@ -23,7 +24,7 @@ public class UploaderFactoryTest extends AbstractWithPreferencesTest { @Test public void shouldCreateAnInstanceOfHttpUploader() throws Exception { TargetBoard board = targetPackage.getPlatforms().get("avr").getBoards().get("yun"); - Uploader uploader = new UploaderFactory().newUploader(board, "192.168.0.1 (yun)"); + Uploader uploader = new PerPortObjectFactory().newUploader(board, "192.168.0.1 (yun)"); assertTrue(uploader instanceof HttpUploader); } @@ -31,7 +32,7 @@ public class UploaderFactoryTest extends AbstractWithPreferencesTest { @Test public void shouldCreateAnInstanceOfBasicUploaderWhenHTTPIsUnsupported() throws Exception { TargetBoard board = targetPackage.getPlatforms().get("avr").getBoards().get("uno"); - Uploader uploader = new UploaderFactory().newUploader(board, "192.168.0.1 (myyun)"); + Uploader uploader = new PerPortObjectFactory().newUploader(board, "192.168.0.1 (myyun)"); assertTrue(uploader instanceof BasicUploader); } @@ -39,7 +40,7 @@ public class UploaderFactoryTest extends AbstractWithPreferencesTest { @Test public void shouldCreateAnInstanceOfBasicUploaderWhenPortIsSerial() throws Exception { TargetBoard board = targetPackage.getPlatforms().get("avr").getBoards().get("uno"); - Uploader uploader = new UploaderFactory().newUploader(board, "/dev/ttyACM0 (Arduino Leonardo)"); + Uploader uploader = new PerPortObjectFactory().newUploader(board, "/dev/ttyACM0 (Arduino Leonardo)"); assertTrue(uploader instanceof BasicUploader); }