Skip to content

Commit

Permalink
Add serial monitor send/receive encoding options
Browse files Browse the repository at this point in the history
- Add "send as <encoding>" dropdown menu.
- Add "receive as <encoding>" dropdown menu.

Sending and receiving can be done in any encoding specified in StandardCharsets with the additional option to send comma-separated bytes and receive newline-separated bytes directly.

This fixes #4452 and offers an easy implementation for issue #4632.
  • Loading branch information
Pieter12345 committed Mar 18, 2019
1 parent 74f93fe commit d73de65
Show file tree
Hide file tree
Showing 4 changed files with 259 additions and 49 deletions.
55 changes: 53 additions & 2 deletions app/src/processing/app/AbstractTextMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.StringTokenizer;
Expand Down Expand Up @@ -40,6 +42,8 @@ public abstract class AbstractTextMonitor extends AbstractMonitor {
protected JButton clearButton;
protected JCheckBox autoscrollBox;
protected JCheckBox addTimeStampBox;
protected JComboBox<String> sendEncoding;
protected JComboBox<String> receiveEncoding;
protected JComboBox lineEndings;
protected JComboBox serialRates;

Expand Down Expand Up @@ -103,6 +107,45 @@ public void windowGainedFocus(WindowEvent e) {
minimumSize.setSize(minimumSize.getWidth() / 3, minimumSize.getHeight());
noLineEndingAlert.setMinimumSize(minimumSize);

String sendAs = tr("Send as") + " ";
sendEncoding = new JComboBox<>(new String[] {
sendAs + EncodingOption.SYSTEM_DEFAULT,
sendAs + EncodingOption.BYTES,
sendAs + EncodingOption.UTF_8,
sendAs + EncodingOption.UTF_16,
sendAs + EncodingOption.UTF_16BE,
sendAs + EncodingOption.UTF_16LE,
sendAs + EncodingOption.ISO_8859_1,
sendAs + EncodingOption.US_ASCII});
sendEncoding.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
PreferencesData.set("serial.send_encoding", sendEncoding.getItemAt(
sendEncoding.getSelectedIndex()).substring(sendAs.length()));
// sendEncoding.setForeground(pane.getBackground());
}
});
String sendEncodingStr = PreferencesData.get("serial.send_encoding");
if (sendEncodingStr != null) {
sendEncoding.setSelectedItem(sendAs + sendEncodingStr);
}
sendEncoding.setMaximumSize(sendEncoding.getMinimumSize());

String receiveAs = tr("Receive as") + " ";
receiveEncoding = new JComboBox<>(new String[] {
receiveAs + EncodingOption.SYSTEM_DEFAULT,
receiveAs + EncodingOption.BYTES,
receiveAs + EncodingOption.UTF_8,
receiveAs + EncodingOption.UTF_16,
receiveAs + EncodingOption.UTF_16BE,
receiveAs + EncodingOption.UTF_16LE,
receiveAs + EncodingOption.ISO_8859_1,
receiveAs + EncodingOption.US_ASCII});
String receiveEncodingStr = PreferencesData.get("serial.receive_encoding");
if (receiveEncodingStr != null) {
receiveEncoding.setSelectedItem(receiveAs + receiveEncodingStr);
}
receiveEncoding.setMaximumSize(receiveEncoding.getMinimumSize());

lineEndings = new JComboBox(new String[]{tr("No line ending"), tr("Newline"), tr("Carriage return"), tr("Both NL & CR")});
lineEndings.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
Expand All @@ -121,21 +164,23 @@ public void actionPerformed(ActionEvent e) {
PreferencesData.setBoolean("serial.show_timestamp", addTimeStampBox.isSelected());
}
});

lineEndings.setMaximumSize(lineEndings.getMinimumSize());

serialRates = new JComboBox();
for (String rate : serialRateStrings) {
serialRates.addItem(rate + " " + tr("baud"));
}

serialRates.setMaximumSize(serialRates.getMinimumSize());

pane.add(autoscrollBox);
pane.add(addTimeStampBox);
pane.add(Box.createHorizontalGlue());
pane.add(noLineEndingAlert);
pane.add(Box.createRigidArea(new Dimension(8, 0)));
pane.add(sendEncoding);
pane.add(Box.createRigidArea(new Dimension(8, 0)));
pane.add(receiveEncoding);
pane.add(Box.createRigidArea(new Dimension(8, 0)));
pane.add(lineEndings);
pane.add(Box.createRigidArea(new Dimension(8, 0)));
pane.add(serialRates);
Expand All @@ -154,6 +199,8 @@ protected void onEnableWindow(boolean enable)
sendButton.setEnabled(enable);
autoscrollBox.setEnabled(enable);
addTimeStampBox.setEnabled(enable);
sendEncoding.setEnabled(enable);
receiveEncoding.setEnabled(enable);
lineEndings.setEnabled(enable);
serialRates.setEnabled(enable);
}
Expand All @@ -171,6 +218,10 @@ public void onSerialRateChange(ActionListener listener) {
serialRates.addActionListener(listener);
}

public void onReceiveEncodingChange(ActionListener listener) {
receiveEncoding.addActionListener(listener);
}

public void message(String msg) {
SwingUtilities.invokeLater(() -> updateTextArea(msg));
}
Expand Down
77 changes: 77 additions & 0 deletions app/src/processing/app/EncodingOption.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package processing.app;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
* Represents the encoding option for decoding or encoding bytes that are
* read from or written to a stream.
*/
public enum EncodingOption {

/**
* The system default character set as returned by
* {@link Charset#defaultCharset()}.
*/
SYSTEM_DEFAULT(Charset.defaultCharset()),

/**
* Comma separated unsigned byte representation.
*/
BYTES(null),

UTF_8(StandardCharsets.UTF_8),
UTF_16(StandardCharsets.UTF_16),
UTF_16BE(StandardCharsets.UTF_16BE),
UTF_16LE(StandardCharsets.UTF_16LE),
ISO_8859_1(StandardCharsets.ISO_8859_1),
US_ASCII(StandardCharsets.US_ASCII);

private final Charset charset;

private EncodingOption(Charset charset) {
this.charset = charset;
}

public Charset getCharset() {
return this.charset;
}

@Override
public String toString() {
switch (this) {
case SYSTEM_DEFAULT:
case BYTES:
return this.name().replace('_', ' ').toLowerCase();
default:
return this.charset.name();
}
}

/**
* Gets the {@link EncodingOption} with the given name.
* The name match is case-insensitive and
* whitespaces/dashes are interpreted as '_'.
* @param name - The name of the {@link EncodingOption}.
* @return The matching {@link EncodingOption}
* or null when no such option exists.
*/
public static EncodingOption forName(String name) {
if (name == null) {
return null;
}
try {
return EncodingOption.valueOf(
name.replace(' ', '_').replace('-', '_').toUpperCase());
} catch (IllegalArgumentException e) {
return null;
}
// name = name.replace(' ', '_').replace('-', '_');
// for (EncodingOption option : EncodingOption.values()) {
// if (option.name().equalsIgnoreCase(name)) {
// return option;
// }
// }
// return null;
}
}
83 changes: 79 additions & 4 deletions app/src/processing/app/SerialMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.nio.charset.Charset;

import static processing.app.I18n.tr;

Expand All @@ -33,6 +34,11 @@ public class SerialMonitor extends AbstractTextMonitor {
private Serial serial;
private int serialRate;

private static final EncodingOption DEFAULT_SEND_ENCODING =
EncodingOption.UTF_8;
private static final EncodingOption DEFAULT_RECEIVE_ENCODING =
EncodingOption.UTF_8;

public SerialMonitor(BoardPort port) {
super(port);

Expand All @@ -56,6 +62,21 @@ public void actionPerformed(ActionEvent event) {
}
});

onReceiveEncodingChange(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String receiveAs = tr("Receive as") + " ";
String selectedEncodingStr = receiveEncoding.getItemAt(
receiveEncoding.getSelectedIndex()).substring(receiveAs.length());
Charset selectedCharset =
EncodingOption.forName(selectedEncodingStr).getCharset();
if (serial.getCharset() != selectedCharset) {
serial.resetDecoding(selectedCharset);
PreferencesData.set("serial.receive_encoding", selectedEncodingStr);
}
}
});

onSendCommand(new ActionListener() {
public void actionPerformed(ActionEvent e) {
send(textField.getText());
Expand Down Expand Up @@ -85,11 +106,48 @@ private void send(String s) {
default:
break;
}
if ("".equals(s) && lineEndings.getSelectedIndex() == 0 && !PreferencesData.has("runtime.line.ending.alert.notified")) {
if ("".equals(s) && lineEndings.getSelectedIndex() == 0
&& !PreferencesData.has("runtime.line.ending.alert.notified")) {
noLineEndingAlert.setForeground(Color.RED);
PreferencesData.set("runtime.line.ending.alert.notified", "true");
}
serial.write(s);
EncodingOption encodingOption =
EncodingOption.forName(PreferencesData.get("serial.send_encoding"));
if (encodingOption == null) {
encodingOption = DEFAULT_SEND_ENCODING;
}
Charset charSet = encodingOption.getCharset();
byte[] bytes;
if (charSet != null) {
bytes = s.getBytes(encodingOption.getCharset());
} else {
switch (encodingOption) {
case BYTES:
String[] split = s.split(",");
bytes = new byte[split.length];
for (int i = 0; i < split.length; i++) {
String valStr = split[i].trim();
try {
int val = Integer.parseInt(valStr);
if (val < 0x00 || val > 0xFF) {
this.message("\n[ERROR] Invalid byte value given: "
+ val + ". Byte values are in range [0-255].\n");
return;
}
bytes[i] = (byte) val;
} catch (NumberFormatException e) {
this.message("\n[ERROR] Invalid byte value given: " + valStr
+ ". Byte values are numbers in range [0-255].\n");
return;
}
}
break;
default:
throw new Error(
"Unsupported 'send as' encoding option: " + encodingOption);
}
}
serial.write(bytes);
}
}

Expand All @@ -98,10 +156,27 @@ public void open() throws Exception {

if (serial != null) return;

serial = new Serial(getBoardPort().getAddress(), serialRate) {
EncodingOption encodingOption =
EncodingOption.forName(PreferencesData.get("serial.receive_encoding"));
if (encodingOption == null) {
encodingOption = DEFAULT_RECEIVE_ENCODING;
}
serial = new Serial(
getBoardPort().getAddress(), serialRate, encodingOption.getCharset()) {
@Override
protected void message(char buff[], int n) {
addToUpdateBuffer(buff, n);
if (serial.getCharset() == null) {
if(buff.length != 0) {
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < n; i++) {
strBuilder.append(buff[i] & 0xFF).append("\n");
}
addToUpdateBuffer(
strBuilder.toString().toCharArray(), strBuilder.length());
}
} else {
addToUpdateBuffer(buff, n);
}
}
};
}
Expand Down
Loading

0 comments on commit d73de65

Please sign in to comment.