1
0
mirror of https://github.com/arduino/Arduino.git synced 2024-11-29 10:24:12 +01:00

Merge pull request #2177 from henningpohl/master

Adding plotting functionality to the editor
This commit is contained in:
Federico Fissore 2015-06-29 16:14:47 +02:00
commit 09b29b3d40
8 changed files with 700 additions and 132 deletions

View File

@ -3,6 +3,7 @@ package processing.app;
import static processing.app.I18n._;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Rectangle;
@ -15,15 +16,10 @@ import java.awt.event.WindowEvent;
import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
@ -37,19 +33,11 @@ import processing.app.legacy.PApplet;
@SuppressWarnings("serial")
public abstract class AbstractMonitor extends JFrame implements ActionListener {
protected final JLabel noLineEndingAlert;
protected TextAreaFIFO textArea;
protected JScrollPane scrollPane;
protected JTextField textField;
protected JButton sendButton;
protected JCheckBox autoscrollBox;
protected JComboBox lineEndings;
protected JComboBox serialRates;
private boolean monitorEnabled;
private boolean closed;
private Timer updateTimer;
private StringBuffer updateBuffer;
private Timer updateTimer;
private BoardPort boardPort;
@ -82,84 +70,10 @@ public abstract class AbstractMonitor extends JFrame implements ActionListener {
}
}));
getContentPane().setLayout(new BorderLayout());
Font consoleFont = Theme.getFont("console.font");
Font editorFont = PreferencesData.getFont("editor.font");
Font font = new Font(consoleFont.getName(), consoleFont.getStyle(), editorFont.getSize());
onCreateWindow(getContentPane());
textArea = new TextAreaFIFO(8000000);
textArea.setRows(16);
textArea.setColumns(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 upperPane = new JPanel();
upperPane.setLayout(new BoxLayout(upperPane, BoxLayout.X_AXIS));
upperPane.setBorder(new EmptyBorder(4, 4, 4, 4));
textField = new JTextField(40);
sendButton = new JButton(_("Send"));
upperPane.add(textField);
upperPane.add(Box.createRigidArea(new Dimension(4, 0)));
upperPane.add(sendButton);
getContentPane().add(upperPane, BorderLayout.NORTH);
final JPanel pane = new JPanel();
pane.setLayout(new BoxLayout(pane, BoxLayout.X_AXIS));
pane.setBorder(new EmptyBorder(4, 4, 4, 4));
autoscrollBox = new JCheckBox(_("Autoscroll"), true);
noLineEndingAlert = new JLabel(I18n.format(_("You've pressed {0} but nothing was sent. Should you select a line ending?"), _("Send")));
noLineEndingAlert.setToolTipText(noLineEndingAlert.getText());
noLineEndingAlert.setForeground(pane.getBackground());
Dimension minimumSize = new Dimension(noLineEndingAlert.getMinimumSize());
minimumSize.setSize(minimumSize.getWidth() / 3, minimumSize.getHeight());
noLineEndingAlert.setMinimumSize(minimumSize);
lineEndings = new JComboBox(new String[]{_("No line ending"), _("Newline"), _("Carriage return"), _("Both NL & CR")});
lineEndings.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
PreferencesData.setInteger("serial.line_ending", lineEndings.getSelectedIndex());
noLineEndingAlert.setForeground(pane.getBackground());
}
});
if (PreferencesData.get("serial.line_ending") != null) {
lineEndings.setSelectedIndex(PreferencesData.getInteger("serial.line_ending"));
}
lineEndings.setMaximumSize(lineEndings.getMinimumSize());
String[] serialRateStrings = {"300", "1200", "2400", "4800", "9600", "19200", "38400", "57600", "74880", "115200", "230400", "250000"};
serialRates = new JComboBox();
for (String rate : serialRateStrings) {
serialRates.addItem(rate + " " + _("baud"));
}
serialRates.setMaximumSize(serialRates.getMinimumSize());
pane.add(autoscrollBox);
pane.add(Box.createHorizontalGlue());
pane.add(noLineEndingAlert);
pane.add(Box.createRigidArea(new Dimension(8, 0)));
pane.add(lineEndings);
pane.add(Box.createRigidArea(new Dimension(8, 0)));
pane.add(serialRates);
this.setMinimumSize(new Dimension(pane.getMinimumSize().width, this.getPreferredSize().height));
getContentPane().add(pane, BorderLayout.SOUTH);
this.setMinimumSize(new Dimension(getContentPane().getMinimumSize().width, this.getPreferredSize().height));
pack();
@ -185,18 +99,15 @@ public abstract class AbstractMonitor extends JFrame implements ActionListener {
monitorEnabled = true;
closed = false;
}
protected abstract void onCreateWindow(Container mainPane);
public void enableWindow(boolean enable) {
textArea.setEnabled(enable);
scrollPane.setEnabled(enable);
textField.setEnabled(enable);
sendButton.setEnabled(enable);
autoscrollBox.setEnabled(enable);
lineEndings.setEnabled(enable);
serialRates.setEnabled(enable);
onEnableWindow(enable);
monitorEnabled = enable;
}
protected abstract void onEnableWindow(boolean enable);
// Puts the window in suspend state, closing the serial port
// to allow other entity (the programmer) to use it
@ -220,15 +131,6 @@ public abstract class AbstractMonitor extends JFrame implements ActionListener {
open();
}
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]);
}
@ -246,16 +148,7 @@ public abstract class AbstractMonitor extends JFrame implements ActionListener {
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 message(final String s);
public boolean requiresAuthorization() {
return false;
@ -295,21 +188,13 @@ public abstract class AbstractMonitor extends JFrame implements ActionListener {
updateBuffer.setLength(0);
return s;
}
public void actionPerformed(ActionEvent e) {
String s = consumeUpdateBuffer();
if (s.isEmpty()) {
return;
}
//System.out.println("gui append " + s.length());
if (autoscrollBox.isSelected()) {
textArea.appendTrim(s);
textArea.setCaretPosition(textArea.getDocument().getLength());
} else {
textArea.appendNoTrim(s);
message(s);
}
}
}

View File

@ -0,0 +1,165 @@
package processing.app;
import static processing.app.I18n._;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import javax.swing.text.DefaultCaret;
import cc.arduino.packages.BoardPort;
import processing.app.debug.TextAreaFIFO;
import processing.app.legacy.PApplet;
@SuppressWarnings("serial")
public abstract class AbstractTextMonitor extends AbstractMonitor {
protected JLabel noLineEndingAlert;
protected TextAreaFIFO textArea;
protected JScrollPane scrollPane;
protected JTextField textField;
protected JButton sendButton;
protected JCheckBox autoscrollBox;
protected JComboBox lineEndings;
protected JComboBox serialRates;
public AbstractTextMonitor(BoardPort boardPort) {
super(boardPort);
}
protected void onCreateWindow(Container mainPane) {
Font consoleFont = Theme.getFont("console.font");
Font editorFont = PreferencesData.getFont("editor.font");
Font font = new Font(consoleFont.getName(), consoleFont.getStyle(), editorFont.getSize());
mainPane.setLayout(new BorderLayout());
textArea = new TextAreaFIFO(8000000);
textArea.setRows(16);
textArea.setColumns(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);
mainPane.add(scrollPane, BorderLayout.CENTER);
JPanel upperPane = new JPanel();
upperPane.setLayout(new BoxLayout(upperPane, BoxLayout.X_AXIS));
upperPane.setBorder(new EmptyBorder(4, 4, 4, 4));
textField = new JTextField(40);
sendButton = new JButton(_("Send"));
upperPane.add(textField);
upperPane.add(Box.createRigidArea(new Dimension(4, 0)));
upperPane.add(sendButton);
mainPane.add(upperPane, BorderLayout.NORTH);
final JPanel pane = new JPanel();
pane.setLayout(new BoxLayout(pane, BoxLayout.X_AXIS));
pane.setBorder(new EmptyBorder(4, 4, 4, 4));
autoscrollBox = new JCheckBox(_("Autoscroll"), true);
noLineEndingAlert = new JLabel(I18n.format(_("You've pressed {0} but nothing was sent. Should you select a line ending?"), _("Send")));
noLineEndingAlert.setToolTipText(noLineEndingAlert.getText());
noLineEndingAlert.setForeground(pane.getBackground());
Dimension minimumSize = new Dimension(noLineEndingAlert.getMinimumSize());
minimumSize.setSize(minimumSize.getWidth() / 3, minimumSize.getHeight());
noLineEndingAlert.setMinimumSize(minimumSize);
lineEndings = new JComboBox(new String[]{_("No line ending"), _("Newline"), _("Carriage return"), _("Both NL & CR")});
lineEndings.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
PreferencesData.setInteger("serial.line_ending", lineEndings.getSelectedIndex());
noLineEndingAlert.setForeground(pane.getBackground());
}
});
if (PreferencesData.get("serial.line_ending") != null) {
lineEndings.setSelectedIndex(PreferencesData.getInteger("serial.line_ending"));
}
lineEndings.setMaximumSize(lineEndings.getMinimumSize());
String[] serialRateStrings = {
"300", "1200", "2400", "4800", "9600",
"19200", "38400", "57600", "115200", "230400", "250000"
};
serialRates = new JComboBox();
for (String rate : serialRateStrings) {
serialRates.addItem(rate + " " + _("baud"));
}
serialRates.setMaximumSize(serialRates.getMinimumSize());
pane.add(autoscrollBox);
pane.add(Box.createHorizontalGlue());
pane.add(noLineEndingAlert);
pane.add(Box.createRigidArea(new Dimension(8, 0)));
pane.add(lineEndings);
pane.add(Box.createRigidArea(new Dimension(8, 0)));
pane.add(serialRates);
mainPane.add(pane, BorderLayout.SOUTH);
}
protected void onEnableWindow(boolean enable)
{
textArea.setEnabled(enable);
scrollPane.setEnabled(enable);
textField.setEnabled(enable);
sendButton.setEnabled(enable);
autoscrollBox.setEnabled(enable);
lineEndings.setEnabled(enable);
serialRates.setEnabled(enable);
}
public void onSendCommand(ActionListener listener) {
textField.addActionListener(listener);
sendButton.addActionListener(listener);
}
public void onSerialRateChange(ActionListener listener) {
serialRates.addActionListener(listener);
}
public void message(final String s) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
textArea.append(s);
if (autoscrollBox.isSelected()) {
textArea.setCaretPosition(textArea.getDocument().getLength());
}
}
});
}
}

View File

@ -138,7 +138,8 @@ public class Editor extends JFrame implements RunnerListener {
private static JMenu portMenu;
static volatile AbstractMonitor serialMonitor;
static AbstractMonitor serialPlotter;
final EditorHeader header;
EditorStatus status;
EditorConsole console;
@ -230,7 +231,7 @@ public class Editor extends JFrame implements RunnerListener {
//PdeKeywords keywords = new PdeKeywords();
//sketchbook = new Sketchbook(this);
buildMenuBar();
// For rev 0120, placing things inside a JPanel
@ -787,6 +788,14 @@ public class Editor extends JFrame implements RunnerListener {
item.addActionListener(e -> handleSerial());
toolsMenu.add(item);
item = newJMenuItemShift(_("Serial Plotter"), 'L');
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handlePlotter();
}
});
toolsMenu.add(item);
addTools(toolsMenu, BaseNoGui.getToolsFolder());
File sketchbookTools = new File(BaseNoGui.getSketchbookFolder(), "tools");
addTools(toolsMenu, sketchbookTools);
@ -1113,6 +1122,7 @@ public class Editor extends JFrame implements RunnerListener {
}
if (selection != null) selection.setState(true);
//System.out.println(item.getLabel());
BaseNoGui.selectSerialPort(name);
if (serialMonitor != null) {
try {
@ -1123,6 +1133,16 @@ public class Editor extends JFrame implements RunnerListener {
}
}
if (serialPlotter != null) {
try {
serialPlotter.close();
serialPlotter.setVisible(false);
} catch (Exception e) {
// ignore
}
}
onBoardOrPortChange();
base.onBoardOrPortChange();
//System.out.println("set to " + get("serial.port"));
@ -2389,6 +2409,9 @@ public class Editor extends JFrame implements RunnerListener {
if (serialMonitor != null) {
serialMonitor.suspend();
}
if (serialPlotter != null) {
serialPlotter.suspend();
}
uploading = true;
@ -2420,6 +2443,7 @@ public class Editor extends JFrame implements RunnerListener {
toolbar.deactivate(EditorToolbar.EXPORT);
resumeOrCloseSerialMonitor();
resumeOrCloseSerialPlotter();
base.onBoardOrPortChange();
}
}
@ -2439,6 +2463,8 @@ public class Editor extends JFrame implements RunnerListener {
}
}
try {
if (serialMonitor != null)
serialMonitor.resume(boardPort);
if (boardPort == null) {
serialMonitor.close();
handleSerial();
@ -2448,7 +2474,26 @@ public class Editor extends JFrame implements RunnerListener {
} catch (Exception e) {
statusError(e);
}
}
}
}
private void resumeOrCloseSerialPlotter() {
// Return the serial plotter window to its initial state
if (serialPlotter != null) {
BoardPort boardPort = BaseNoGui.getDiscoveryManager().find(PreferencesData.get("serial.port"));
try {
if (serialPlotter != null)
serialPlotter.resume(boardPort);
if (boardPort == null) {
serialPlotter.close();
handlePlotter();
} else {
serialPlotter.resume(boardPort);
}
} catch (Exception e) {
statusError(e);
}
}
}
// DAM: in Arduino, this is upload (with verbose output)
@ -2459,6 +2504,9 @@ public class Editor extends JFrame implements RunnerListener {
if (serialMonitor != null) {
serialMonitor.suspend();
}
if (serialPlotter != null) {
serialPlotter.suspend();
}
uploading = true;
@ -2490,12 +2538,23 @@ public class Editor extends JFrame implements RunnerListener {
toolbar.deactivate(EditorToolbar.EXPORT);
resumeOrCloseSerialMonitor();
resumeOrCloseSerialPlotter();
base.onBoardOrPortChange();
}
}
public void handleSerial() {
if(serialPlotter != null) {
if(serialPlotter.isClosed()) {
serialPlotter = null;
} else {
statusError(I18n.format("Serial monitor not available while plotter is open"));
return;
}
}
if (serialMonitor != null) {
// The serial monitor already exists
@ -2582,7 +2641,97 @@ public class Editor extends JFrame implements RunnerListener {
} while (serialMonitor.requiresAuthorization() && !success);
}
public void handlePlotter() {
if(serialMonitor != null) {
if(serialMonitor.isClosed()) {
serialMonitor = null;
} else {
statusError(I18n.format("Plotter not available while serial monitor is open"));
return;
}
}
if (serialPlotter != null) {
// The serial plotter already exists
if (serialPlotter.isClosed()) {
// If it's closed, clear the refrence to the existing
// plotter and create a new one
serialPlotter = null;
}
else {
// If it's not closed, give it the focus
try {
serialPlotter.toFront();
serialPlotter.requestFocus();
return;
} catch (Exception e) {
// noop
}
}
}
BoardPort port = Base.getDiscoveryManager().find(PreferencesData.get("serial.port"));
if (port == null) {
statusError(I18n.format("Board at {0} is not available", PreferencesData.get("serial.port")));
return;
}
serialPlotter = new SerialPlotter(port);
serialPlotter.setIconImage(getIconImage());
// If currently uploading, disable the plotter (it will be later
// enabled when done uploading)
if (uploading) {
try {
serialPlotter.suspend();
} catch (Exception e) {
statusError(e);
}
}
boolean success = false;
do {
if (serialPlotter.requiresAuthorization() && !PreferencesData.has(serialPlotter.getAuthorizationKey())) {
PasswordAuthorizationDialog dialog = new PasswordAuthorizationDialog(this, _("Type board password to access its console"));
dialog.setLocationRelativeTo(this);
dialog.setVisible(true);
if (dialog.isCancelled()) {
statusNotice(_("Unable to open serial plotter"));
return;
}
PreferencesData.set(serialPlotter.getAuthorizationKey(), dialog.getPassword());
}
try {
serialPlotter.open();
serialPlotter.setVisible(true);
success = true;
} catch (ConnectException e) {
statusError(_("Unable to connect: is the sketch using the bridge?"));
} catch (JSchException e) {
statusError(_("Unable to connect: wrong password?"));
} catch (SerialException e) {
String errorMessage = e.getMessage();
if (e.getCause() != null && e.getCause() instanceof SerialPortException) {
errorMessage += " (" + ((SerialPortException) e.getCause()).getExceptionType() + ")";
}
statusError(errorMessage);
} catch (Exception e) {
statusError(e);
} finally {
if (serialPlotter.requiresAuthorization() && !success) {
PreferencesData.remove(serialPlotter.getAuthorizationKey());
}
}
} while (serialPlotter.requiresAuthorization() && !success);
}
private void handleBurnBootloader() {
console.clear();

View File

@ -22,7 +22,7 @@ import java.io.OutputStream;
import static processing.app.I18n._;
@SuppressWarnings("serial")
public class NetworkMonitor extends AbstractMonitor implements MessageConsumer {
public class NetworkMonitor extends AbstractTextMonitor implements MessageConsumer {
private static final int MAX_CONNECTION_ATTEMPTS = 5;

View File

@ -28,7 +28,7 @@ import java.awt.event.ActionListener;
import static processing.app.I18n._;
@SuppressWarnings("serial")
public class SerialMonitor extends AbstractMonitor {
public class SerialMonitor extends AbstractTextMonitor {
private Serial serial;
private int serialRate;

View File

@ -0,0 +1,242 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
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
*/
package processing.app;
import cc.arduino.packages.BoardPort;
import processing.app.legacy.PApplet;
import processing.app.debug.MessageConsumer;
import processing.app.helpers.*;
import static processing.app.I18n._;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.text.*;
public class SerialPlotter extends AbstractMonitor {
private StringBuffer messageBuffer;
private CircularBuffer buffer;
private GraphPanel graphPanel;
private JComboBox serialRates;
private Serial serial;
private int serialRate;
private class GraphPanel extends JPanel {
private double minY, maxY, rangeY;
private Rectangle bounds;
private int xOffset;
private Font font;
private Color graphColor;
public GraphPanel() {
font = Theme.getFont("console.font");
graphColor = Theme.getColor("header.bgcolor");
xOffset = 20;
}
@Override
public void paintComponent(Graphics g1) {
Graphics2D g = (Graphics2D)g1;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setFont(font);
super.paintComponent(g);
bounds = g.getClipBounds();
setBackground(Color.WHITE);
if(buffer.isEmpty()) {
return;
}
minY = buffer.min();
maxY = buffer.max();
Ticks ticks = new Ticks(minY, maxY, 3);
minY = Math.min(minY, ticks.getTick(0));
maxY = Math.max(maxY, ticks.getTick(ticks.getTickCount() - 1));
rangeY = maxY - minY;
minY -= 0.05 * rangeY;
maxY += 0.05 * rangeY;
rangeY = maxY - minY;
g.setStroke(new BasicStroke(1.0f));
FontMetrics fm = g.getFontMetrics();
for(int i = 0; i < ticks.getTickCount(); ++i) {
double tick = ticks.getTick(i);
Rectangle2D fRect = fm.getStringBounds(String.valueOf(tick), g);
xOffset = Math.max(xOffset, (int)fRect.getWidth() + 15);
// draw tick
g.drawLine(xOffset - 5, (int)transformY(tick), xOffset + 2, (int)transformY(tick));
// draw tick label
g.drawString(String.valueOf(tick), xOffset - (int)fRect.getWidth() - 10, transformY(tick) - (float)fRect.getHeight() * 0.5f + fm.getAscent());
}
g.drawLine(bounds.x + xOffset, bounds.y + 5, bounds.x + xOffset, bounds.y + bounds.height - 10);
g.setTransform(AffineTransform.getTranslateInstance(xOffset, 0));
float xstep = (float)(bounds.width - xOffset) / (float)buffer.capacity();
g.setColor(graphColor);
g.setStroke(new BasicStroke(0.75f));
for(int i = 0; i < buffer.size() - 1; ++i) {
g.drawLine(
(int)(i * xstep), (int)transformY(buffer.get(i)),
(int)((i + 1) * xstep), (int)transformY(buffer.get(i + 1))
);
}
}
@Override
public Dimension getMinimumSize() {
return new Dimension(200, 100);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(500, 250);
}
private float transformY(double rawY) {
return (float)(5 + (bounds.height - 10) * (1.0 - (rawY - minY) / rangeY));
}
}
public SerialPlotter(BoardPort port) {
super(port);
serialRate = PreferencesData.getInteger("serial.debug_rate");
serialRates.setSelectedItem(serialRate + " " + _("baud"));
onSerialRateChange(new ActionListener() {
public void actionPerformed(ActionEvent event) {
String wholeString = (String) serialRates.getSelectedItem();
String rateString = wholeString.substring(0, wholeString.indexOf(' '));
serialRate = Integer.parseInt(rateString);
PreferencesData.set("serial.debug_rate", rateString);
try {
close();
Thread.sleep(100); // Wait for serial port to properly close
open();
} catch (InterruptedException e) {
// noop
} catch (Exception e) {
System.err.println(e);
}
}
});
messageBuffer = new StringBuffer();
}
protected void onCreateWindow(Container mainPane) {
mainPane.setLayout(new BorderLayout());
Font consoleFont = Theme.getFont("console.font");
Font editorFont = PreferencesData.getFont("editor.font");
Font font = new Font(consoleFont.getName(), consoleFont.getStyle(), editorFont.getSize());
buffer = new CircularBuffer(500);
graphPanel = new GraphPanel();
mainPane.add(graphPanel, BorderLayout.CENTER);
JPanel pane = new JPanel();
pane.setLayout(new BoxLayout(pane, BoxLayout.X_AXIS));
pane.setBorder(new EmptyBorder(4, 4, 4, 4));
String[] serialRateStrings = {
"300","1200","2400","4800","9600","14400",
"19200","28800","38400","57600","115200"
};
serialRates = new JComboBox();
for (int i = 0; i < serialRateStrings.length; i++)
serialRates.addItem(serialRateStrings[i] + " " + _("baud"));
serialRates.setMaximumSize(serialRates.getMinimumSize());
pane.add(Box.createRigidArea(new Dimension(8, 0)));
pane.add(serialRates);
mainPane.add(pane, BorderLayout.SOUTH);
}
protected void onEnableWindow(boolean enable)
{
serialRates.setEnabled(enable);
}
public void onSerialRateChange(ActionListener listener) {
serialRates.addActionListener(listener);
}
public void message(final String s) {
messageBuffer.append(s);
while(true) {
int linebreak = messageBuffer.indexOf("\n");
if(linebreak == -1) {
break;
}
String line = messageBuffer.substring(0, linebreak);
line = line.trim();
messageBuffer.delete(0, linebreak + 1);
try {
double value = Double.valueOf(line);
buffer.add(value);
} catch(NumberFormatException e) {
continue; // ignore lines that can't be cast to a number
}
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
SerialPlotter.this.repaint();
}});
}
public void open() throws Exception {
super.open();
if (serial != null) return;
serial = new Serial(getBoardPort().getAddress(), serialRate) {
@Override
protected void message(char buff[], int n) {
addToUpdateBuffer(buff, n);
}
};
}
public void close() throws Exception {
if (serial != null) {
super.close();
int[] location = getPlacement();
String locationStr = PApplet.join(PApplet.str(location), ",");
PreferencesData.set("last.serial.location", locationStr);
serial.dispose();
serial = null;
}
}
}

View File

@ -0,0 +1,81 @@
package processing.app.helpers;
import java.util.NoSuchElementException;
public class CircularBuffer {
private double[] elements;
private int start = -1;
private int end = -1;
private int capacity;
public void add(double num) {
end = (end + 1) % capacity;
elements[end] = num;
if(start == end || start == -1) {
start = (start + 1) % capacity;
}
}
public double get(int index) {
if(index >= capacity) {
throw new IndexOutOfBoundsException();
}
if(index >= size()) {
throw new IndexOutOfBoundsException();
}
return elements[(start + index) % capacity];
}
public boolean isEmpty() {
return start == -1 && end == -1;
}
public void clear() {
start = end = -1;
}
public CircularBuffer(int capacity) {
this.capacity = capacity;
elements = new double[capacity];
}
public double min() {
if(size() == 0) {
throw new NoSuchElementException();
}
double out = get(0);
for(int i = 1; i < size(); ++i) {
out = Math.min(out, get(i));
}
return out;
}
public double max() {
if(size() == 0) {
throw new NoSuchElementException();
}
double out = get(0);
for(int i = 1; i < size(); ++i) {
out = Math.max(out, get(i));
}
return out;
}
public int size() {
if(end == -1) {
return 0;
}
return (end - start + capacity) % capacity + 1;
}
public int capacity() {
return capacity;
}
}

View File

@ -0,0 +1,46 @@
package processing.app.helpers;
public class Ticks {
private double tickMin;
private double tickMax;
private double tickStep;
private int tickCount;
private double[] ticks;
public Ticks(double min, double max, int tickCount) {
double range = max - min;
double exp = Math.floor(Math.log10(range / (tickCount - 1)));
double scale = Math.pow(10, exp);
double rawTickStep = (range / (tickCount - 1)) / scale;
for(double potentialStep : new double[] {1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0}) {
if(potentialStep < rawTickStep) {
continue;
}
tickStep = potentialStep * scale;
tickMin = tickStep * Math.floor(min / tickStep);
tickMax = tickMin + tickStep * (tickCount - 1);
if(tickMax >= max) {
break;
}
}
tickCount -= (int)Math.floor((tickMax - max) / tickStep);
this.tickCount = tickCount;
ticks = new double[tickCount];
for(int i = 0; i < tickCount; ++i) {
ticks[i] = tickMin + i * tickStep;
}
}
public double getTick(int i) {
return ticks[i];
}
public int getTickCount() {
return tickCount;
}
}