Skip to content

Fix issue #8055 missing timestamps on serial monitor #8088

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .classpath
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
<classpathentry kind="lib" path="app/test-lib/fest-swing-1.2.jar"/>
<classpathentry kind="lib" path="app/test-lib/fest-util-1.1.2.jar"/>
<classpathentry kind="lib" path="app/test-lib/jcip-annotations-1.0.jar"/>
<classpathentry kind="lib" path="app/test-lib/mockito-core-2.23.0.jar"/>
<classpathentry kind="lib" path="app/test-lib/byte-buddy-1.9.1.jar"/>
<classpathentry kind="lib" path="app/test-lib/objenesis-3.0.jar"/>
<classpathentry kind="lib" path="app/lib/commons-codec-1.7.jar"/>
<classpathentry kind="lib" path="app/lib/commons-exec-1.1.jar"/>
<classpathentry kind="lib" path="app/lib/commons-httpclient-3.1.jar"/>
Expand Down
3 changes: 3 additions & 0 deletions app/.classpath
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,7 @@
<classpathentry kind="lib" path="test-lib/fest-swing-1.2.jar"/>
<classpathentry kind="lib" path="test-lib/fest-util-1.1.2.jar"/>
<classpathentry kind="lib" path="test-lib/jcip-annotations-1.0.jar"/>
<classpathentry kind="lib" path="test-lib/mockito-core-2.23.0.jar"/>
<classpathentry kind="lib" path="test-lib/byte-buddy-1.9.1.jar"/>
<classpathentry kind="lib" path="test-lib/objenesis-3.0.jar"/>
</classpath>
94 changes: 54 additions & 40 deletions app/src/processing/app/AbstractTextMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.StringTokenizer;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.swing.Box;
import javax.swing.BoxLayout;
Expand Down Expand Up @@ -43,21 +44,18 @@ public abstract class AbstractTextMonitor extends AbstractMonitor {
protected JComboBox lineEndings;
protected JComboBox serialRates;

private SimpleDateFormat logDateFormat;

public AbstractTextMonitor(BoardPort boardPort) {
super(boardPort);
logDateFormat = new SimpleDateFormat("HH:mm:ss.SSS -> ");
}

protected void onCreateWindow(Container mainPane) {
Font consoleFont = Theme.getFont("console.font");
Font editorFont = PreferencesData.getFont("editor.font");
Font font = Theme.scale(new Font(consoleFont.getName(), consoleFont.getStyle(), editorFont.getSize()));

mainPane.setLayout(new BorderLayout());

textArea = new TextAreaFIFO(8000000);
textArea = new TextAreaFIFO(8_000_000);
textArea.setRows(16);
textArea.setColumns(40);
textArea.setEditable(false);
Expand All @@ -70,7 +68,7 @@ protected void onCreateWindow(Container mainPane) {
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));
Expand Down Expand Up @@ -165,49 +163,65 @@ public void onSendCommand(ActionListener listener) {
textField.addActionListener(listener);
sendButton.addActionListener(listener);
}

public void onClearCommand(ActionListener listener) {
clearButton.addActionListener(listener);
}

public void onSerialRateChange(ActionListener listener) {
serialRates.addActionListener(listener);
}

public void message(final String s) {
SwingUtilities.invokeLater(new Runnable() {
// Pre-allocate all objects used for streaming data
Date t = new Date();
String now;
StringBuilder out = new StringBuilder(16384);
boolean isStartingLine = false;

public void run() {
if (addTimeStampBox.isSelected()) {
t.setTime(System.currentTimeMillis());
now = logDateFormat.format(t);
out.setLength(0);

StringTokenizer tokenizer = new StringTokenizer(s, "\n", true);
while (tokenizer.hasMoreTokens()) {
if (isStartingLine) {
out.append(now);
}
String token = tokenizer.nextToken();
out.append(token);
// tokenizer returns "\n" as a single token
isStartingLine = token.charAt(0) == '\n';
}

textArea.append(out.toString());
} else {
textArea.append(s);
}

if (autoscrollBox.isSelected()) {
textArea.setCaretPosition(textArea.getDocument().getLength());
public void message(final String msg) {
SwingUtilities.invokeLater(new UpdateTextAreaAction(textArea,
addTimeStampBox.isSelected(),
autoscrollBox.isSelected(),
msg));
}

static class UpdateTextAreaAction implements Runnable {

private static final String LINE_SEPARATOR = "\n";
private static AtomicBoolean isStartingLine = new AtomicBoolean(true);

private String msg;
private boolean addTimeStamp;
private boolean doAutoscroll;
private TextAreaFIFO textArea;

UpdateTextAreaAction(TextAreaFIFO textArea, boolean addTimeStamp,
boolean doAutoscroll, String msg) {
this.msg = msg;
this.textArea = textArea;
this.addTimeStamp = addTimeStamp;
this.doAutoscroll = doAutoscroll;
}

public void run() {
if (addTimeStamp) {
textArea.append(addTimestamps(msg));
} else {
textArea.append(msg);
}
if (doAutoscroll) {
textArea.setCaretPosition(textArea.getDocument().getLength());
}
}

private String addTimestamps(String text) {
String now = new SimpleDateFormat("HH:mm:ss.SSS -> ").format(new Date());
final StringBuilder sb = new StringBuilder(text.length() + now.length());
StringTokenizer tokenizer = new StringTokenizer(text, LINE_SEPARATOR, true);
while (tokenizer.hasMoreTokens()) {
if (isStartingLine.get()) {
sb.append(now);
}
String token = tokenizer.nextToken();
sb.append(token);
// tokenizer returns "\n" as a single token
isStartingLine.set(token.equals(LINE_SEPARATOR));
}
});
return sb.toString();
}
}
}
Binary file added app/test-lib/byte-buddy-1.9.1.jar
Binary file not shown.
Binary file added app/test-lib/mockito-core-2.23.0.jar
Binary file not shown.
Binary file added app/test-lib/objenesis-3.0.jar
Binary file not shown.
100 changes: 100 additions & 0 deletions app/test/processing/app/UpdateTextAreaActionTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package processing.app;

import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;

import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.*;

public class UpdateTextAreaActionTest {

private static final String TIMESTAMP_REGEX = "\\d\\d:\\d\\d:\\d\\d.\\d\\d\\d";
private TextAreaFIFO textAreaFIFO;
private ArgumentCaptor<String> text;

@Before public void setUp() {
textAreaFIFO = mock(TextAreaFIFO.class);
text = ArgumentCaptor.forClass(String.class);
sendNewLineInOrderToHaveCleanTestStart();
}

@Test
public void noTimestampAdded() {
// given
AbstractTextMonitor.UpdateTextAreaAction action = new AbstractTextMonitor.UpdateTextAreaAction(
textAreaFIFO, false, false, "line1\nline2\r\nline3");

// when
action.run();

//then
verify(textAreaFIFO, atLeastOnce()).append(text.capture());
assertThat(text.getValue()).startsWith("line1");
}

@Test
public void all3LinesHaveTimestampAdded() {
// given
AbstractTextMonitor.UpdateTextAreaAction action = new AbstractTextMonitor.UpdateTextAreaAction(
textAreaFIFO, true, false, "line1\nline2\r\nline3");

// when
action.run();

//then
verify(textAreaFIFO, atLeastOnce()).append(text.capture());
assertThat(text.getValue()).matches(TIMESTAMP_REGEX + " -> line1\\n"
+ TIMESTAMP_REGEX + " -> line2\\r\\n"
+ TIMESTAMP_REGEX + " -> line3");
}

@Test
public void emptyLinesHaveTimestampToo() {
// given
AbstractTextMonitor.UpdateTextAreaAction action = new AbstractTextMonitor.UpdateTextAreaAction(
textAreaFIFO, true, false, "line_1\n\nline_2");

// when
action.run();

//then
verify(textAreaFIFO, atLeastOnce()).append(text.capture());
assertThat(text.getValue()).matches(TIMESTAMP_REGEX + " -> line_1\\n"
+ TIMESTAMP_REGEX + " -> \\n"
+ TIMESTAMP_REGEX + " -> line_2");
}

@Test
public void newLinesAreRememberedWhenNewBufferIsUsed() {
// given #1
AbstractTextMonitor.UpdateTextAreaAction action;
action = new AbstractTextMonitor.UpdateTextAreaAction(
textAreaFIFO, true, false, "first line without newline");

// when #1
action.run();

//then #1
verify(textAreaFIFO, atLeastOnce()).append(text.capture());
assertThat(text.getValue()).matches(TIMESTAMP_REGEX + " -> first line without newline");

// given #2
action = new AbstractTextMonitor.UpdateTextAreaAction(
textAreaFIFO, true, false, "more text for first line");

// when #2
action.run();

//then #2
verify(textAreaFIFO, atLeastOnce()).append(text.capture());
assertThat(text.getValue()).matches("more text for first line");
}


private void sendNewLineInOrderToHaveCleanTestStart() {
AbstractTextMonitor.UpdateTextAreaAction action = new AbstractTextMonitor.UpdateTextAreaAction(
textAreaFIFO, true, false, "\n");
action.run();
}
}