added new optional features: time formatting, hide-duplicate-packets, hide-airshot-packets

This commit is contained in:
Fredrik Osterlind 2013-08-14 12:32:23 +02:00
parent bda04947e7
commit 492cd5f721

View file

@ -40,20 +40,24 @@ import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Observable;
import java.util.Observer;
import java.util.Properties;
import java.util.regex.PatternSyntaxException;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFileChooser;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
@ -68,9 +72,12 @@ import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.KeyStroke;
import javax.swing.RowFilter;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import org.apache.log4j.Logger;
import org.jdom.Element;
@ -116,9 +123,11 @@ public class RadioLogger extends VisPlugin {
private JSplitPane splitPane;
private JTextPane verboseBox = null;
private boolean formatTimeString = true;
private final static String[] COLUMN_NAMES = {
"No.",
"Time",
"No. ",
"Time ms",
"From",
"To",
"Data"
@ -126,6 +135,7 @@ public class RadioLogger extends VisPlugin {
private final Simulation simulation;
private final JTable dataTable;
private TableRowSorter<TableModel> logFilter;
private ArrayList<RadioConnectionLog> connections = new ArrayList<RadioConnectionLog>();
private RadioMedium radioMedium;
private Observer radioMediumObserver;
@ -149,7 +159,7 @@ public class RadioLogger extends VisPlugin {
JMenu fileMenu = new JMenu("File");
JMenu editMenu = new JMenu("Edit");
JMenu analyzerMenu = new JMenu("Analyzer");
JMenu payloadMenu = new JMenu("Payload");
JMenu payloadMenu = new JMenu("View");
menuBar.add(fileMenu);
menuBar.add(editMenu);
@ -169,11 +179,15 @@ public class RadioLogger extends VisPlugin {
lowpanAnalyzersPcap.add(new IPHCPacketAnalyzer());
lowpanAnalyzersPcap.add(new IPv6PacketAnalyzer());
lowpanAnalyzersPcap.add(new ICMPv6Analyzer());
model = new AbstractTableModel() {
private static final long serialVersionUID = 1692207305977527004L;
public String getColumnName(int col) {
if (col == COLUMN_TIME && formatTimeString) {
return "Time";
}
return COLUMN_NAMES[col];
}
@ -186,10 +200,19 @@ public class RadioLogger extends VisPlugin {
}
public Object getValueAt(int row, int col) {
if (row < 0 || row >= connections.size()) {
return "";
}
RadioConnectionLog conn = connections.get(row);
if (col == COLUMN_NO) {
return Long.toString(row + 1);
if (!showDuplicates && conn.hides > 0) {
return (String) "" + (row + 1) + "+" + conn.hides;
}
return (String) "" + (row + 1);
} else if (col == COLUMN_TIME) {
if (formatTimeString) {
return LogListener.getFormattedTime(conn.startTime);
}
return Long.toString(conn.startTime / Simulation.MILLISECOND);
} else if (col == COLUMN_FROM) {
return "" + conn.connection.getSource().getMote().getID();
@ -251,14 +274,19 @@ public class RadioLogger extends VisPlugin {
public String getToolTipText(MouseEvent e) {
java.awt.Point p = e.getPoint();
int rowIndex = rowAtPoint(p);
if (rowIndex < 0) {
return super.getToolTipText(e);
}
int modelRowIndex = convertRowIndexToModel(rowIndex);
int colIndex = columnAtPoint(p);
int realColumnIndex = convertColumnIndexToModel(colIndex);
if (rowIndex < 0 || realColumnIndex < 0) {
int modelColumnIndex = convertColumnIndexToModel(colIndex);
if (modelRowIndex < 0 || modelColumnIndex < 0) {
return super.getToolTipText(e);
}
RadioConnectionLog conn = connections.get(rowIndex);
if (realColumnIndex == COLUMN_TIME) {
/* TODO This entry may represent several hidden connections */
RadioConnectionLog conn = connections.get(modelRowIndex);
if (modelColumnIndex == COLUMN_TIME) {
return
"<html>" +
"Start time (us): " + conn.startTime +
@ -267,9 +295,9 @@ public class RadioLogger extends VisPlugin {
"<br><br>" +
"Duration (us): " + (conn.endTime - conn.startTime) +
"</html>";
} else if (realColumnIndex == COLUMN_FROM) {
} else if (modelColumnIndex == COLUMN_FROM) {
return conn.connection.getSource().getMote().toString();
} else if (realColumnIndex == COLUMN_TO) {
} else if (modelColumnIndex == COLUMN_TO) {
Radio[] dests = conn.connection.getDestinations();
if (dests.length == 0) {
return "No destinations";
@ -286,7 +314,7 @@ public class RadioLogger extends VisPlugin {
}
tip.append("</html>");
return tip.toString();
} else if (realColumnIndex == COLUMN_DATA) {
} else if (modelColumnIndex == COLUMN_DATA) {
if (conn.tooltip == null) {
prepareTooltipString(conn);
}
@ -296,6 +324,21 @@ public class RadioLogger extends VisPlugin {
}
};
/* Toggle time format */
dataTable.getTableHeader().addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
int colIndex = dataTable.columnAtPoint(e.getPoint());
int columnIndex = dataTable.convertColumnIndexToModel(colIndex);
if (columnIndex != COLUMN_TIME) {
return;
}
formatTimeString = !formatTimeString;
dataTable.getColumnModel().getColumn(COLUMN_TIME).setHeaderValue(
dataTable.getModel().getColumnName(COLUMN_TIME));
repaint();
}
});
dataTable.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
@ -314,11 +357,21 @@ public class RadioLogger extends VisPlugin {
}
});
logFilter = new TableRowSorter<TableModel>(model);
for (int i = 0, n = model.getColumnCount(); i < n; i++) {
logFilter.setSortable(i, false);
}
dataTable.setRowSorter(logFilter);
dataTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
int row = dataTable.getSelectedRow();
if (row >= 0) {
RadioConnectionLog conn = connections.get(row);
if (row < 0) {
return;
}
int modelRowIndex = dataTable.convertRowIndexToModel(row);
if (modelRowIndex >= 0) {
RadioConnectionLog conn = connections.get(modelRowIndex);
if (conn.tooltip == null) {
prepareTooltipString(conn);
}
@ -338,6 +391,16 @@ public class RadioLogger extends VisPlugin {
editMenu.add(new JMenuItem(clearAction));
payloadMenu.add(new JMenuItem(aliasAction));
payloadMenu.add(new JCheckBoxMenuItem(showDuplicatesAction) {
public boolean isSelected() {
return showDuplicates;
}
});
payloadMenu.add(new JCheckBoxMenuItem(hideNoDestinationAction) {
public boolean isSelected() {
return hideNoDestinationPackets;
}
});
fileMenu.add(new JMenuItem(saveAction));
@ -369,12 +432,14 @@ public class RadioLogger extends VisPlugin {
group.add(rbMenuItem);
analyzerMenu.add(rbMenuItem);
/* Load additional analyzers specified by projects (cooja.config) */
String[] projectAnalyzerSuites =
gui.getProjectConfig().getStringArrayValue(RadioLogger.class, "ANALYZERS");
if (projectAnalyzerSuites != null) {
for (String suiteName: projectAnalyzerSuites) {
if (suiteName == null || suiteName.trim().isEmpty()) {
continue;
}
Class<? extends RadioLoggerAnalyzerSuite> suiteClass =
gui.tryLoadClass(RadioLogger.this, RadioLoggerAnalyzerSuite.class, suiteName);
try {
@ -383,7 +448,7 @@ public class RadioLogger extends VisPlugin {
rbMenuItem = new JRadioButtonMenuItem(createAnalyzerAction(
suite.getDescription(), suiteName, suiteAnalyzers, false));
group.add(rbMenuItem);
popupMenu.add(rbMenuItem);
analyzerMenu.add(rbMenuItem);
logger.debug("Loaded radio logger analyzers: " + suite.getDescription());
} catch (InstantiationException e1) {
logger.warn("Failed to load analyzer suite '" + suiteName + "': " + e1.getMessage());
@ -457,7 +522,7 @@ public class RadioLogger extends VisPlugin {
if (isVisible) {
dataTable.scrollRectToVisible(dataTable.getCellRect(dataTable.getRowCount() - 1, 0, true));
}
setTitle("Radio messages: " + dataTable.getRowCount() + " messages seen");
setTitle("Radio messages: showing " + dataTable.getRowCount() + "/" + connections.size() + " packets");
}
});
}
@ -471,6 +536,11 @@ public class RadioLogger extends VisPlugin {
}
}
public void startPlugin() {
super.startPlugin();
rebuildAllEntries();
}
private void searchSelectNext(String text, boolean reverse) {
if (text.isEmpty()) {
return;
@ -514,18 +584,75 @@ public class RadioLogger extends VisPlugin {
public void trySelectTime(final long time) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
for (int i=0; i < connections.size(); i++) {
if (connections.get(i).endTime < time) {
continue;
}
dataTable.scrollRectToVisible(dataTable.getCellRect(i, 0, true));
dataTable.setRowSelectionInterval(i, i);
if (dataTable.getRowCount() == 0) {
return;
}
for (int ai=0; ai < model.getRowCount(); ai++) {
int index = dataTable.convertRowIndexToModel(ai);
if (connections.get(index).endTime < time) {
continue;
}
dataTable.scrollRectToVisible(dataTable.getCellRect(ai, 0, true));
dataTable.setRowSelectionInterval(ai, ai);
return;
}
dataTable.scrollRectToVisible(dataTable.getCellRect(dataTable.getRowCount()-1, 0, true));
dataTable.setRowSelectionInterval(dataTable.getRowCount()-1, dataTable.getRowCount()-1);
}
});
}
private void applyFilter() {
for(RadioConnectionLog conn: connections) {
conn.data = null;
conn.tooltip = null;
conn.hides = 0;
conn.hiddenBy = null;
}
try {
logFilter.setRowFilter(null);
RowFilter<Object, Object> filter = new RowFilter<Object, Object>() {
public boolean include(RowFilter.Entry<? extends Object, ? extends Object> entry) {
int row = (Integer) entry.getIdentifier();
RadioConnectionLog current = connections.get(row);
byte[] currentData = current.packet.getPacketData();
if (!showDuplicates && row > 0) {
RadioConnectionLog previous = connections.get(row-1);
byte[] previousData = previous.packet.getPacketData();
if (!showDuplicates &&
Arrays.equals(previousData, currentData) &&
previous.connection.getSource() == current.connection.getSource() &&
Arrays.equals(previous.connection.getAllDestinations(), current.connection.getAllDestinations())) {
if (connections.get(row-1).hiddenBy == null) {
connections.get(row-1).hides++;
connections.get(row).hiddenBy = connections.get(row-1);
} else {
connections.get(row-1).hiddenBy.hides++;
connections.get(row).hiddenBy = connections.get(row-1).hiddenBy;
}
return false;
}
}
if (hideNoDestinationPackets) {
if (current.connection.getDestinations().length == 0) {
return false;
}
}
return true;
}
};
logFilter.setRowFilter(filter);
} catch (PatternSyntaxException e) {
logFilter.setRowFilter(null);
logger.warn("Error when setting table filter: " + e.getMessage());
}
}
private void prepareDataString(RadioConnectionLog conn) {
byte[] data;
if (conn.packet == null) {
@ -646,6 +773,19 @@ public class RadioLogger extends VisPlugin {
element.addContent(Integer.toString(splitPane.getDividerLocation()));
config.add(element);
if (formatTimeString) {
element = new Element("formatted_time");
config.add(element);
}
element = new Element("showdups");
element.addContent(Boolean.toString(showDuplicates));
config.add(element);
element = new Element("hidenodests");
element.addContent(Boolean.toString(hideNoDestinationPackets));
config.add(element);
if (analyzerName != null && analyzers != null) {
element = new Element("analyzers");
element.setAttribute("name", analyzerName);
@ -676,6 +816,12 @@ public class RadioLogger extends VisPlugin {
aliases.put(payload, alias);
} else if ("split".equals(name)) {
splitPane.setDividerLocation(Integer.parseInt(element.getText()));
} else if ("formatted_time".equals(name)) {
formatTimeString = true;
} else if ("showdups".equals(name)) {
showDuplicates = Boolean.parseBoolean(element.getText());
} else if ("hidenodests".equals(name)) {
hideNoDestinationPackets = Boolean.parseBoolean(element.getText());
} else if ("analyzers".equals(name)) {
String analyzerName = element.getAttributeValue("name");
final Action action;
@ -697,6 +843,9 @@ public class RadioLogger extends VisPlugin {
long endTime;
RadioConnection connection;
RadioPacket packet;
RadioConnectionLog hiddenBy = null;
int hides = 0;
String data = null;
String tooltip = null;
@ -729,6 +878,18 @@ public class RadioLogger extends VisPlugin {
return sb.toString();
}
private void rebuildAllEntries() {
applyFilter();
if (connections.size() > 0) {
model.fireTableRowsUpdated(0, connections.size() - 1);
}
verboseBox.setText("");
setTitle("Radio messages: showing " + dataTable.getRowCount() + "/" + connections.size() + " packets");
simulation.getGUI().getDesktopPane().repaint();
}
private Action createAnalyzerAction(String name, final String actionName,
final ArrayList<PacketAnalyzer> analyzerList, boolean selected) {
Action action = new AbstractAction(name) {
@ -738,16 +899,7 @@ public class RadioLogger extends VisPlugin {
if (analyzers != analyzerList) {
analyzers = analyzerList;
analyzerName = actionName;
if (connections.size() > 0) {
// Remove the cached values
for(int i = 0; i < connections.size(); i++) {
RadioConnectionLog conn = connections.get(i);
conn.data = null;
conn.tooltip = null;
}
model.fireTableRowsUpdated(0, connections.size() - 1);
}
verboseBox.setText("");
rebuildAllEntries();
}
}
};
@ -764,7 +916,7 @@ public class RadioLogger extends VisPlugin {
if (size > 0) {
connections.clear();
model.fireTableRowsDeleted(0, size - 1);
setTitle("Radio Logger: " + dataTable.getRowCount() + " packets");
setTitle("Radio messages: showing " + dataTable.getRowCount() + "/" + connections.size() + " packets");
}
}
};
@ -779,11 +931,8 @@ public class RadioLogger extends VisPlugin {
StringBuilder sb = new StringBuilder();
for (int i: selectedRows) {
sb.append(i + 1).append('\t');
sb.append(dataTable.getValueAt(i, COLUMN_TIME)).append('\t');
sb.append(dataTable.getValueAt(i, COLUMN_FROM)).append('\t');
sb.append(getDestString(connections.get(i))).append('\t');
sb.append(dataTable.getValueAt(i, COLUMN_DATA)).append('\n');
int iModel = dataTable.convertRowIndexToModel(i);
sb.append(connections.get(iModel).toString() + "\n");
}
StringSelection stringSelection = new StringSelection(sb.toString());
@ -799,11 +948,7 @@ public class RadioLogger extends VisPlugin {
StringBuilder sb = new StringBuilder();
for(int i=0; i < connections.size(); i++) {
sb.append("" + (i + 1) + '\t');
sb.append("" + dataTable.getValueAt(i, COLUMN_TIME) + '\t');
sb.append("" + dataTable.getValueAt(i, COLUMN_FROM) + '\t');
sb.append("" + getDestString(connections.get(i)) + '\t');
sb.append("" + dataTable.getValueAt(i, COLUMN_DATA) + '\n');
sb.append(connections.get(i).toString() + "\n");
}
StringSelection stringSelection = new StringSelection(sb.toString());
@ -844,11 +989,7 @@ public class RadioLogger extends VisPlugin {
try {
PrintWriter outStream = new PrintWriter(new FileWriter(saveFile));
for(int i=0; i < connections.size(); i++) {
outStream.print("" + (i + 1) + '\t');
outStream.print("" + dataTable.getValueAt(i, COLUMN_TIME) + '\t');
outStream.print("" + dataTable.getValueAt(i, COLUMN_FROM) + '\t');
outStream.print("" + getDestString(connections.get(i)) + '\t');
outStream.print("" + dataTable.getValueAt(i, COLUMN_DATA) + '\n');
outStream.print(connections.get(i).toString() + "\n");
}
outStream.close();
} catch (Exception ex) {
@ -864,6 +1005,9 @@ public class RadioLogger extends VisPlugin {
public void actionPerformed(ActionEvent e) {
int selectedRow = dataTable.getSelectedRow();
if (selectedRow < 0) return;
selectedRow = dataTable.convertRowIndexToModel(selectedRow);
if (selectedRow < 0) return;
long time = connections.get(selectedRow).startTime;
Plugin[] plugins = simulation.getGUI().getStartedPlugins();
@ -884,6 +1028,9 @@ public class RadioLogger extends VisPlugin {
public void actionPerformed(ActionEvent e) {
int selectedRow = dataTable.getSelectedRow();
if (selectedRow < 0) return;
selectedRow = dataTable.convertRowIndexToModel(selectedRow);
if (selectedRow < 0) return;
long time = connections.get(selectedRow).startTime;
Plugin[] plugins = simulation.getGUI().getStartedPlugins();
@ -917,6 +1064,8 @@ public class RadioLogger extends VisPlugin {
public void actionPerformed(ActionEvent e) {
int selectedRow = dataTable.getSelectedRow();
if (selectedRow < 0) return;
selectedRow = dataTable.convertRowIndexToModel(selectedRow);
if (selectedRow < 0) return;
String current = "";
if (aliases != null && aliases.get(connections.get(selectedRow).data) != null) {
@ -961,6 +1110,22 @@ public class RadioLogger extends VisPlugin {
}
};
private boolean showDuplicates = false;
private AbstractAction showDuplicatesAction = new AbstractAction("Show duplicates") {
public void actionPerformed(ActionEvent e) {
showDuplicates = !showDuplicates;
rebuildAllEntries();
}
};
private boolean hideNoDestinationPackets = false;
private AbstractAction hideNoDestinationAction = new AbstractAction("Hide airshots") {
public void actionPerformed(ActionEvent e) {
hideNoDestinationPackets = !hideNoDestinationPackets;
rebuildAllEntries();
}
};
public String getConnectionsString() {
StringBuilder sb = new StringBuilder();
RadioConnectionLog[] cs = connections.toArray(new RadioConnectionLog[0]);