log listener plugin update: support for regexp filtering + nicer ui

+ minor tooltip fix in radio logger
This commit is contained in:
nifi 2009-06-12 14:34:29 +00:00
parent b2c0df08af
commit e7c13890ee
2 changed files with 252 additions and 130 deletions

View file

@ -26,22 +26,55 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: LogListener.java,v 1.13 2009/05/26 14:27:00 fros4943 Exp $
* $Id: LogListener.java,v 1.14 2009/06/12 14:34:29 nifi Exp $
*/
package se.sics.cooja.plugins;
import java.awt.BorderLayout;
import java.awt.Insets;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.util.*;
import javax.swing.*;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Observable;
import java.util.Observer;
import java.util.Vector;
import java.util.regex.PatternSyntaxException;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.RowFilter;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import org.apache.log4j.Logger;
import org.jdom.Element;
import se.sics.cooja.*;
import se.sics.cooja.ClassDescription;
import se.sics.cooja.GUI;
import se.sics.cooja.Mote;
import se.sics.cooja.PluginType;
import se.sics.cooja.Simulation;
import se.sics.cooja.VisPlugin;
import se.sics.cooja.dialogs.TableColumnAdjuster;
import se.sics.cooja.interfaces.Log;
import se.sics.cooja.interfaces.MoteID;
/**
* A simple mote log listener.
@ -55,15 +88,25 @@ public class LogListener extends VisPlugin {
private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(LogListener.class);
private JTextArea logTextArea;
private final static int COLUMN_TIME = 0;
private final static int COLUMN_FROM = 1;
private final static int COLUMN_DATA = 2;
private final static String[] COLUMN_NAMES = {
"Time",
"Mote",
"Data"
};
private final JTable logTable;
private TableRowSorter<TableModel> logFilter;
private ArrayList<LogData> logs = new ArrayList<LogData>();
private Observer logObserver;
private Simulation simulation;
private String filterText = "";
private JTextField filterTextField = null;
private String[] logCache = new String[256];
private int logPos;
private Color filterTextFieldBackground;
/**
* Create a new simulation control panel.
@ -75,47 +118,120 @@ public class LogListener extends VisPlugin {
simulation = simulationToControl;
int nrLogs = 0;
final AbstractTableModel model = new AbstractTableModel() {
private static final long serialVersionUID = 3065150390849332924L;
public String getColumnName(int col) {
return COLUMN_NAMES[col];
}
public int getRowCount() {
return logs.size();
}
public int getColumnCount() {
return COLUMN_NAMES.length;
}
public Object getValueAt(int row, int col) {
LogData log = logs.get(row);
if (col == COLUMN_TIME) {
return log.time;
} else if (col == COLUMN_FROM) {
return log.moteID;
} else if (col == COLUMN_DATA) {
return log.data;
}
return null;
}
};
logTable = new JTable(model) {
private static final long serialVersionUID = -930616018336483196L;
public String getToolTipText(MouseEvent e) {
java.awt.Point p = e.getPoint();
int rowIndex = rowAtPoint(p);
int colIndex = columnAtPoint(p);
int columnIndex = convertColumnIndexToModel(colIndex);
if (rowIndex < 0 || columnIndex < 0) {
return super.getToolTipText(e);
}
Object v = getValueAt(rowIndex, columnIndex);
if (v != null) {
String t = v.toString();
if (t.length() > 60) {
StringBuilder sb = new StringBuilder();
sb.append("<html>");
do {
sb.append(t.substring(0, 60)).append("<br>");
t = t.substring(60);
} while (t.length() > 60);
return sb.append(t).append("</html>").toString();
}
}
return super.getToolTipText(e);
}
};
logTable.setFillsViewportHeight(true);
logTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
logTable.setFont(new Font("Monospaced", Font.PLAIN, 12));
logFilter = new TableRowSorter<TableModel>(model);
for (int i = 0, n = model.getColumnCount(); i < n; i++) {
logFilter.setSortable(i, false);
}
logTable.setRowSorter(logFilter);
JPopupMenu popupMenu = new JPopupMenu();
JMenuItem clearItem = new JMenuItem("Clear");
clearItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int size = logs.size();
if (size > 0) {
logs.clear();
model.fireTableRowsDeleted(0, size - 1);
}
}
});
popupMenu.add(clearItem);
logTable.setComponentPopupMenu(popupMenu);
TableColumnAdjuster adjuster = new TableColumnAdjuster(logTable);
adjuster.setDynamicAdjustment(true);
adjuster.packColumns();
// Log observer
logObserver = new Observer() {
public void update(Observable obs, Object obj) {
if (logTextArea == null) {
return;
}
Mote mote = (Mote) obj;
Log moteLogInterface = (Log) obs;
String lastMessage = moteLogInterface.getLastLogMessage();
if (lastMessage.length() > 0 && lastMessage.charAt(lastMessage.length() - 1) == '\n') {
lastMessage = lastMessage.substring(0, lastMessage.length() - 1);
}
String outputString = "TIME:" + simulation.getSimulationTimeMillis() + "\t";
if (mote != null && mote.getInterfaces().getMoteID() != null) {
outputString += "ID:" + mote.getInterfaces().getMoteID().getMoteID() + "\t";
}
outputString += lastMessage;
if (logCache.length > 1) {
synchronized (logCache) {
logCache[logPos] = outputString;
logPos = (logPos + 1) % logCache.length;
}
}
// Match against filter (if any)
if (filterText.length() > 0 && !outputString.contains(filterText)) {
return;
}
final String str = outputString;
SwingUtilities.invokeLater(new Runnable() {
final LogData data =
new LogData("ID:" + getMoteID(mote),
simulation.getSimulationTimeMillis(), lastMessage);
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
int len = logTextArea.getDocument().getLength();
if (len == 0) {
logTextArea.append(str);
} else {
logTextArea.append('\n' + str);
len++;
// Check if the last row is visible
boolean isVisible = false;
int rowCount = logTable.getRowCount();
if (rowCount > 0) {
Rectangle visible = logTable.getVisibleRect();
isVisible = visible.y + visible.height >= logTable.getHeight();
}
int index = logs.size();
logs.add(data);
model.fireTableRowsInserted(index, index);
if (isVisible) {
logTable.scrollRectToVisible(new Rectangle(0, logTable.getHeight() - 2, 1, logTable.getHeight()));
}
logTextArea.setCaretPosition(len + str.length());
}
});
}
@ -145,104 +261,68 @@ public class LogListener extends VisPlugin {
});
// Main panel
logTextArea = new JTextArea(8,50);
logTextArea.setMargin(new Insets(5,5,5,5));
logTextArea.setEditable(false);
logTextArea.setCursor(null);
JPanel filterPanel = new JPanel();
filterPanel.setLayout(new BoxLayout(filterPanel, BoxLayout.X_AXIS));
filterTextField = new JTextField("");
filterTextFieldBackground = filterTextField.getBackground();
filterPanel.add(Box.createHorizontalStrut(2));
filterPanel.add(new JLabel("Filter on string: "));
filterPanel.add(filterTextField);
filterTextField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String oldFilterText = filterText;
filterText = filterTextField.getText();
if (filterText == null) {
filterText = "";
}
if (!filterText.equals(oldFilterText) && logCache.length > 1) {
// Update from log cache
StringBuilder sb = new StringBuilder();
synchronized(logCache) {
int pos = logPos;
int count;
if(logCache[pos] != null) {
// Log cache has wrapped
count = logCache.length;
} else {
// Log cache has not wrapped yet
count = pos;
pos = 0;
}
for(int i = 0; i < count; i++) {
if (filterText.length() == 0 || logCache[pos].contains(filterText)) {
if (sb.length() > 0) {
sb.append('\n');
}
sb.append(logCache[pos]);
}
pos = (pos + 1) % logCache.length;
}
}
logTextArea.setText(sb.toString());
logTextArea.setCaretPosition(logTextArea.getDocument().getLength());
}
setFilter(filterText);
// Ensure last row is visible
logTable.scrollRectToVisible(new Rectangle(0, logTable.getHeight() - 2, 1, logTable.getHeight()));
}
});
filterPanel.add(Box.createHorizontalStrut(2));
JButton saveButton;
filterPanel.add(saveButton = new JButton("Save log"));
saveButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
JFileChooser fc = new JFileChooser();
if (!GUI.isVisualizedInApplet()) {
JButton saveButton = new JButton("Save log");
filterPanel.add(saveButton);
saveButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
JFileChooser fc = new JFileChooser();
int returnVal = fc.showSaveDialog(GUI.getTopParentContainer());
if (returnVal == JFileChooser.APPROVE_OPTION) {
File saveFile = fc.getSelectedFile();
int returnVal = fc.showSaveDialog(GUI.getTopParentContainer());
if (returnVal == JFileChooser.APPROVE_OPTION) {
File saveFile = fc.getSelectedFile();
if (saveFile.exists()) {
String s1 = "Overwrite";
String s2 = "Cancel";
Object[] options = { s1, s2 };
int n = JOptionPane.showOptionDialog(
GUI.getTopParentContainer(),
"A file with the same name already exists.\nDo you want to remove it?",
"Overwrite existing file?", JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE, null, options, s1);
if (n != JOptionPane.YES_OPTION) {
return;
}
}
if (!saveFile.exists() || saveFile.canWrite()) {
try {
BufferedWriter outStream = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(
saveFile)));
outStream.write(logTextArea.getText());
outStream.close();
} catch (Exception ex) {
logger.fatal("Could not write to file: " + saveFile);
return;
if (saveFile.exists()) {
String s1 = "Overwrite";
String s2 = "Cancel";
Object[] options = { s1, s2 };
int n = JOptionPane.showOptionDialog(
GUI.getTopParentContainer(),
"A file with the same name already exists.\nDo you want to remove it?",
"Overwrite existing file?", JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE, null, options, s1);
if (n != JOptionPane.YES_OPTION) {
return;
}
}
} else {
logger.fatal("No write access to file");
if (!saveFile.exists() || saveFile.canWrite()) {
try {
PrintWriter outStream = new PrintWriter(new FileWriter(saveFile));
for(LogData data : logs) {
outStream.println("Time: " + data.time + " " + data.moteID + " " + data.data);
}
outStream.close();
} catch (Exception ex) {
logger.fatal("Could not write to file: " + saveFile);
return;
}
} else {
logger.fatal("No write access to file");
}
}
}
}
});
if (GUI.isVisualizedInApplet()) {
saveButton.setToolTipText("Not available in applet");
saveButton.setEnabled(false);
});
}
getContentPane().add(BorderLayout.CENTER, new JScrollPane(logTextArea));
getContentPane().add(BorderLayout.CENTER, new JScrollPane(logTable));
getContentPane().add(BorderLayout.SOUTH, filterPanel);
setTitle("Log Listener - Listening on " + nrLogs + " mote logs");
@ -258,6 +338,14 @@ public class LogListener extends VisPlugin {
}
private String getMoteID(Mote mote) {
MoteID moteID = mote.getInterfaces().getMoteID();
if (moteID != null) {
return Integer.toString(moteID.getMoteID());
}
return mote.toString();
}
public void closePlugin() {
// Remove log observer from all log interfaces
for (int i=0; i < simulation.getMotesCount(); i++) {
@ -276,9 +364,9 @@ public class LogListener extends VisPlugin {
element = new Element("filter");
element.setText(filterText);
config.add(element);
element = new Element("history");
element.setText("" + logCache.length);
config.add(element);
// element = new Element("history");
// element.setText("" + logCache.length);
// config.add(element);
return config;
}
@ -289,19 +377,50 @@ public class LogListener extends VisPlugin {
String name = element.getName();
if ("filter".equals(name)) {
filterText = element.getText();
filterTextField.setText(filterText);
EventQueue.invokeLater(new Runnable() {
public void run() {
filterTextField.setText(filterText);
setFilter(filterText);
}
});
} else if ("history".equals(name)) {
try {
int size = Integer.parseInt(element.getText().trim());
logCache = new String[size];
logPos = 0;
} catch (Exception e) {
// Ignore malformed history size
}
// try {
// int size = Integer.parseInt(element.getText().trim());
// } catch (Exception e) {
// // Ignore malformed history size
// }
}
}
return true;
}
private void setFilter(String text) {
try {
if (text != null && text.length() > 0) {
logFilter.setRowFilter(RowFilter.regexFilter(text));
} else {
logFilter.setRowFilter(null);
}
filterTextField.setBackground(filterTextFieldBackground);
filterTextField.setToolTipText(null);
} catch (PatternSyntaxException e) {
filterTextField.setBackground(Color.red);
filterTextField.setToolTipText("Syntax error: " + e.getMessage());
}
}
private static class LogData {
public final String moteID;
public final long time;
public final String data;
public LogData(String moteID, long time, String data) {
this.moteID = moteID;
this.time = time;
this.data = data;
}
}
}

View file

@ -26,7 +26,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: RadioLogger.java,v 1.20 2009/06/12 14:12:59 nifi Exp $
* $Id: RadioLogger.java,v 1.21 2009/06/12 14:34:29 nifi Exp $
*/
package se.sics.cooja.plugins;
@ -170,6 +170,9 @@ public class RadioLogger extends VisPlugin {
int rowIndex = rowAtPoint(p);
int colIndex = columnAtPoint(p);
int realColumnIndex = convertColumnIndexToModel(colIndex);
if (rowIndex < 0 || realColumnIndex < 0) {
return super.getToolTipText(e);
}
RadioConnectionLog conn = connections.get(rowIndex);
if (realColumnIndex == COLUMN_TIME) {