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 29, 2019
1 parent a87024d commit 3485f6d
Show file tree
Hide file tree
Showing 4 changed files with 255 additions and 49 deletions.
53 changes: 51 additions & 2 deletions app/src/processing/app/AbstractTextMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,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<String> lineEndings;
protected JComboBox<String> serialRates;

Expand Down Expand Up @@ -105,6 +107,44 @@ 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()));
}
});
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<String>(new String[]{tr("No line ending"), tr("Newline"), tr("Carriage return"), tr("Both NL & CR")});
lineEndings.addActionListener((ActionEvent event) -> {
PreferencesData.setInteger("serial.line_ending", lineEndings.getSelectedIndex());
Expand All @@ -127,6 +167,10 @@ public void windowGainedFocus(WindowEvent e) {
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 @@ -139,15 +183,16 @@ public void windowGainedFocus(WindowEvent e) {
}

@Override
protected void onEnableWindow(boolean enable)
{
protected void onEnableWindow(boolean enable) {
textArea.setEnabled(enable);
clearButton.setEnabled(enable);
scrollPane.setEnabled(enable);
textField.setEnabled(enable);
sendButton.setEnabled(enable);
autoscrollBox.setEnabled(enable);
addTimeStampBox.setEnabled(enable);
sendEncoding.setEnabled(enable);
receiveEncoding.setEnabled(enable);
lineEndings.setEnabled(enable);
serialRates.setEnabled(enable);
}
Expand All @@ -165,6 +210,10 @@ public void onSerialRateChange(ActionListener listener) {
serialRates.addActionListener(listener);
}

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

@Override
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;
}
}
81 changes: 77 additions & 4 deletions app/src/processing/app/SerialMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.nio.charset.Charset;

import static processing.app.I18n.tr;

Expand All @@ -32,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(Base base, BoardPort port) {
super(base, port);

Expand All @@ -53,6 +60,18 @@ public SerialMonitor(Base base, BoardPort port) {
}
});

onReceiveEncodingChange((ActionEvent event) -> {
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((ActionEvent event) -> {
send(textField.getText());
textField.setText("");
Expand All @@ -76,11 +95,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 @@ -90,10 +146,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 3485f6d

Please sign in to comment.