forked from I2P_Developers/i2p.i2p
partial impl of the gui, still a few things left to do:
- implement some validation on the state files loaded - reenable delete and updates to refresh - integrate the real chart code (currently just plain text instead of the graphs) - gui updates i wont spend more than another day on this during the testnet, but i want to get it plotting before continuing.
This commit is contained in:
@@ -1,11 +1,17 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project basedir="." default="all" name="heartbeat">
|
<project basedir="." default="all" name="heartbeat">
|
||||||
<target name="all" depends="clean, build" />
|
<target name="all" depends="clean, buildGUI" />
|
||||||
<target name="build" depends="builddep, jar" />
|
<target name="build" depends="builddep, jar" />
|
||||||
|
<target name="buildGUI" depends="build, jarGUI" />
|
||||||
<target name="builddep">
|
<target name="builddep">
|
||||||
<ant dir="../../../core/java/" target="build" />
|
<ant dir="../../../core/java/" target="build" />
|
||||||
</target>
|
</target>
|
||||||
<target name="compile">
|
<target name="compile">
|
||||||
|
<mkdir dir="./build" />
|
||||||
|
<mkdir dir="./build/obj" />
|
||||||
|
<javac srcdir="./src" debug="true" destdir="./build/obj" excludes="src/net/i2p/heartbeat/gui/**/*.java" classpath="../../../core/java/build/i2p.jar" />
|
||||||
|
</target>
|
||||||
|
<target name="compileGUI">
|
||||||
<mkdir dir="./build" />
|
<mkdir dir="./build" />
|
||||||
<mkdir dir="./build/obj" />
|
<mkdir dir="./build/obj" />
|
||||||
<javac srcdir="./src" debug="true" destdir="./build/obj" classpath="../../../core/java/build/i2p.jar" />
|
<javac srcdir="./src" debug="true" destdir="./build/obj" classpath="../../../core/java/build/i2p.jar" />
|
||||||
@@ -18,6 +24,14 @@
|
|||||||
</manifest>
|
</manifest>
|
||||||
</jar>
|
</jar>
|
||||||
</target>
|
</target>
|
||||||
|
<target name="jarGUI" depends="compileGUI">
|
||||||
|
<jar destfile="./build/heartbeatGUI.jar" basedir="./build/obj" includes="**/*.class">
|
||||||
|
<manifest>
|
||||||
|
<attribute name="Main-Class" value="net.i2p.heartbeat.gui.HeartbeatMonitor" />
|
||||||
|
<attribute name="Class-Path" value="i2p.jar heartbeatGUI.jar" />
|
||||||
|
</manifest>
|
||||||
|
</jar>
|
||||||
|
</target>
|
||||||
<target name="javadoc">
|
<target name="javadoc">
|
||||||
<mkdir dir="./build" />
|
<mkdir dir="./build" />
|
||||||
<mkdir dir="./build/javadoc" />
|
<mkdir dir="./build/javadoc" />
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package net.i2p.heartbeat;
|
package net.i2p.heartbeat;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
@@ -85,10 +86,19 @@ public class ClientConfig {
|
|||||||
public ClientConfig() {
|
public ClientConfig() {
|
||||||
this(null, null, null, -1, -1, -1, -1, 0, null, null);
|
this(null, null, null, -1, -1, -1, -1, 0, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a dummy client config to be fetched from the specified location
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public ClientConfig(String location) {
|
||||||
|
this(null, null, location, -1, -1, -1, -1, 0, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param peer who we will test against
|
* @param peer who we will test against
|
||||||
* @param us who we are
|
* @param us who we are
|
||||||
|
* @param statLocation where the stat data should be stored/fetched
|
||||||
* @param duration how many minutes to keep events for
|
* @param duration how many minutes to keep events for
|
||||||
* @param statFreq how often to write out stats
|
* @param statFreq how often to write out stats
|
||||||
* @param sendFreq how often to send pings
|
* @param sendFreq how often to send pings
|
||||||
@@ -97,11 +107,11 @@ public class ClientConfig {
|
|||||||
* @param comment describe this test
|
* @param comment describe this test
|
||||||
* @param averagePeriods list of minutes to summarize over
|
* @param averagePeriods list of minutes to summarize over
|
||||||
*/
|
*/
|
||||||
public ClientConfig(Destination peer, Destination us, String statFile, int duration, int statFreq, int sendFreq,
|
public ClientConfig(Destination peer, Destination us, String statLocation, int duration, int statFreq, int sendFreq,
|
||||||
int sendSize, int numHops, String comment, int averagePeriods[]) {
|
int sendSize, int numHops, String comment, int averagePeriods[]) {
|
||||||
_peer = peer;
|
_peer = peer;
|
||||||
_us = us;
|
_us = us;
|
||||||
_statFile = statFile;
|
_statFile = statLocation;
|
||||||
_statDuration = duration;
|
_statDuration = duration;
|
||||||
_statFrequency = statFreq;
|
_statFrequency = statFreq;
|
||||||
_sendFrequency = sendFreq;
|
_sendFrequency = sendFreq;
|
||||||
@@ -273,6 +283,30 @@ public class ClientConfig {
|
|||||||
public void setAveragePeriods(int periods[]) {
|
public void setAveragePeriods(int periods[]) {
|
||||||
_averagePeriods = periods;
|
_averagePeriods = periods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure we're keeping track of the average over the given time period.
|
||||||
|
*
|
||||||
|
* @param minutes how many minutes to monitor
|
||||||
|
*/
|
||||||
|
public void addAveragePeriod(int minutes) {
|
||||||
|
if (_averagePeriods != null) {
|
||||||
|
for (int i = 0; i < _averagePeriods.length; i++) {
|
||||||
|
if (_averagePeriods[i] == minutes)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int numPeriods = 1;
|
||||||
|
if (_averagePeriods != null)
|
||||||
|
numPeriods += _averagePeriods.length;
|
||||||
|
int periods[] = new int[numPeriods];
|
||||||
|
if (_averagePeriods != null)
|
||||||
|
System.arraycopy(_averagePeriods, 0, periods, 0, _averagePeriods.length);
|
||||||
|
periods[periods.length-1] = minutes;
|
||||||
|
Arrays.sort(periods);
|
||||||
|
_averagePeriods = periods;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves how many hops this test engine is configured to use for its outbound and inbound tunnels
|
* Retrieves how many hops this test engine is configured to use for its outbound and inbound tunnels
|
||||||
|
@@ -103,6 +103,7 @@ public class PeerData {
|
|||||||
public long getSessionStart() {
|
public long getSessionStart() {
|
||||||
return _sessionStart;
|
return _sessionStart;
|
||||||
}
|
}
|
||||||
|
public void setSessionStart(long when) { _sessionStart = when; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* how many pings have we sent for this test?
|
* how many pings have we sent for this test?
|
||||||
@@ -211,13 +212,17 @@ public class PeerData {
|
|||||||
data.setPongReceived(now);
|
data.setPongReceived(now);
|
||||||
data.setPongSent(pongSent);
|
data.setPongSent(pongSent);
|
||||||
data.setWasPonged(true);
|
data.setWasPonged(true);
|
||||||
_dataPoints.put(new Long(dateSent), data);
|
addDataPoint(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_sendRate.addData(pongSent - dateSent, 0);
|
_sendRate.addData(pongSent - dateSent, 0);
|
||||||
_receiveRate.addData(now - pongSent, 0);
|
_receiveRate.addData(now - pongSent, 0);
|
||||||
_lifetimeReceived++;
|
_lifetimeReceived++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void addDataPoint(EventDataPoint data) {
|
||||||
|
_dataPoints.put(new Long(data.getPingSent()), data);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* drop all datapoints outside the window we're watching, and timeout all
|
* drop all datapoints outside the window we're watching, and timeout all
|
||||||
|
@@ -2,6 +2,7 @@ package net.i2p.heartbeat;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.text.DecimalFormatSymbols;
|
import java.text.DecimalFormatSymbols;
|
||||||
@@ -17,7 +18,7 @@ import net.i2p.util.Log;
|
|||||||
* Actually write out the stats for peer test
|
* Actually write out the stats for peer test
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class PeerDataWriter {
|
public class PeerDataWriter {
|
||||||
private final static Log _log = new Log(PeerDataWriter.class);
|
private final static Log _log = new Log(PeerDataWriter.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,18 +29,11 @@ class PeerDataWriter {
|
|||||||
*/
|
*/
|
||||||
public boolean persist(PeerData data) {
|
public boolean persist(PeerData data) {
|
||||||
String filename = data.getConfig().getStatFile();
|
String filename = data.getConfig().getStatFile();
|
||||||
String header = getHeader(data);
|
|
||||||
File statFile = new File(filename);
|
File statFile = new File(filename);
|
||||||
FileOutputStream fos = null;
|
FileOutputStream fos = null;
|
||||||
try {
|
try {
|
||||||
fos = new FileOutputStream(statFile);
|
fos = new FileOutputStream(statFile);
|
||||||
fos.write(header.getBytes());
|
persist(data, fos);
|
||||||
fos.write("#action\tstatus\tdate and time sent \tsendMs\treplyMs\n".getBytes());
|
|
||||||
for (Iterator iter = data.getDataPoints().iterator(); iter.hasNext();) {
|
|
||||||
PeerData.EventDataPoint point = (PeerData.EventDataPoint) iter.next();
|
|
||||||
String line = getEvent(point);
|
|
||||||
fos.write(line.getBytes());
|
|
||||||
}
|
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
if (_log.shouldLog(Log.ERROR))
|
if (_log.shouldLog(Log.ERROR))
|
||||||
_log.error("Error persisting the peer data for "
|
_log.error("Error persisting the peer data for "
|
||||||
@@ -53,6 +47,19 @@ class PeerDataWriter {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean persist(PeerData data, OutputStream out) throws IOException {
|
||||||
|
String header = getHeader(data);
|
||||||
|
|
||||||
|
out.write(header.getBytes());
|
||||||
|
out.write("#action\tstatus\tdate and time sent \tsendMs\treplyMs\n".getBytes());
|
||||||
|
for (Iterator iter = data.getDataPoints().iterator(); iter.hasNext();) {
|
||||||
|
PeerData.EventDataPoint point = (PeerData.EventDataPoint) iter.next();
|
||||||
|
String line = getEvent(point);
|
||||||
|
out.write(line.getBytes());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private String getHeader(PeerData data) {
|
private String getHeader(PeerData data) {
|
||||||
StringBuffer buf = new StringBuffer(1024);
|
StringBuffer buf = new StringBuffer(1024);
|
||||||
|
@@ -0,0 +1,93 @@
|
|||||||
|
package net.i2p.heartbeat.gui;
|
||||||
|
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JTabbedPane;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the control widgets (refresh/load/snapshot and the
|
||||||
|
* tabbed panel with the plot config data)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class HeartbeatControlPane extends JPanel {
|
||||||
|
private final static Log _log = new Log(HeartbeatControlPane.class);
|
||||||
|
private HeartbeatMonitorGUI _gui;
|
||||||
|
private JTabbedPane _configPane;
|
||||||
|
private final static Color WHITE = new Color(255, 255, 255);
|
||||||
|
private final static Color LIGHT_BLUE = new Color(180, 180, 255);
|
||||||
|
private final static Color BLACK = new Color(0, 0, 0);
|
||||||
|
private Color _background = WHITE;
|
||||||
|
private Color _foreground = BLACK;
|
||||||
|
|
||||||
|
public HeartbeatControlPane(HeartbeatMonitorGUI gui) {
|
||||||
|
_gui = gui;
|
||||||
|
initializeComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTest(PeerPlotConfig config) {
|
||||||
|
_configPane.addTab(config.getTitle(), null, new JScrollPane(new PeerPlotConfigPane(config, this)), config.getSummary());
|
||||||
|
_configPane.setBackgroundAt(_configPane.getTabCount()-1, _background);
|
||||||
|
_configPane.setForegroundAt(_configPane.getTabCount()-1, _foreground);
|
||||||
|
}
|
||||||
|
public void removeTest(PeerPlotConfig config) {
|
||||||
|
_gui.getMonitor().getState().removeTest(config);
|
||||||
|
int index = _configPane.indexOfTab(config.getTitle());
|
||||||
|
if (index >= 0)
|
||||||
|
_configPane.removeTabAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testsUpdated() {
|
||||||
|
List knownNames = new ArrayList(8);
|
||||||
|
for (int i = 0; i < _gui.getMonitor().getState().getTestCount(); i++) {
|
||||||
|
PeerPlotState state = _gui.getMonitor().getState().getTest(i);
|
||||||
|
String title = state.getPlotConfig().getTitle();
|
||||||
|
knownNames.add(state.getPlotConfig().getTitle());
|
||||||
|
if (_configPane.indexOfTab(title) >= 0) {
|
||||||
|
_log.debug("We already know about [" + title + "]");
|
||||||
|
} else {
|
||||||
|
_log.info("The test [" + title + "] is new to us");
|
||||||
|
PeerPlotConfigPane pane = new PeerPlotConfigPane(state.getPlotConfig(), this);
|
||||||
|
_configPane.addTab(state.getPlotConfig().getTitle(), null, new JScrollPane(pane), state.getPlotConfig().getSummary());
|
||||||
|
_configPane.setBackgroundAt(_configPane.getTabCount()-1, _background);
|
||||||
|
_configPane.setForegroundAt(_configPane.getTabCount()-1, _foreground);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List toRemove = new ArrayList(4);
|
||||||
|
for (int i = 0; i < _configPane.getTabCount(); i++) {
|
||||||
|
if (knownNames.contains(_configPane.getTitleAt(i))) {
|
||||||
|
// noop
|
||||||
|
} else {
|
||||||
|
toRemove.add(_configPane.getTitleAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < toRemove.size(); i++) {
|
||||||
|
String title = (String)toRemove.get(i);
|
||||||
|
_log.info("Removing test [" + title + "]");
|
||||||
|
_configPane.removeTabAt(_configPane.indexOfTab(title));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeComponents() {
|
||||||
|
if (_gui != null)
|
||||||
|
setBackground(_gui.getBackground());
|
||||||
|
else
|
||||||
|
setBackground(_background);
|
||||||
|
setLayout(new BorderLayout());
|
||||||
|
HeartbeatMonitorCommandBar bar = new HeartbeatMonitorCommandBar(_gui);
|
||||||
|
bar.setBackground(getBackground());
|
||||||
|
add(bar, BorderLayout.NORTH);
|
||||||
|
_configPane = new JTabbedPane(JTabbedPane.LEFT);
|
||||||
|
_configPane.setBackground(_background);
|
||||||
|
//add(_configPane, BorderLayout.CENTER);
|
||||||
|
add(_configPane, BorderLayout.SOUTH);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,64 @@
|
|||||||
|
package net.i2p.heartbeat.gui;
|
||||||
|
|
||||||
|
import net.i2p.util.I2PThread;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
|
public class HeartbeatMonitor implements PeerPlotStateFetcher.FetchStateReceptor {
|
||||||
|
private final static Log _log = new Log(HeartbeatMonitor.class);
|
||||||
|
private HeartbeatMonitorState _state;
|
||||||
|
private HeartbeatMonitorGUI _gui;
|
||||||
|
|
||||||
|
public HeartbeatMonitor() { this(null); }
|
||||||
|
public HeartbeatMonitor(String configFilename) {
|
||||||
|
_state = new HeartbeatMonitorState(configFilename);
|
||||||
|
_gui = new HeartbeatMonitorGUI(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runMonitor() {
|
||||||
|
loadConfig();
|
||||||
|
I2PThread t = new I2PThread(new HeartbeatMonitorRunner(this));
|
||||||
|
t.setName("HeartbeatMonitor");
|
||||||
|
t.setDaemon(false);
|
||||||
|
t.start();
|
||||||
|
_log.debug("Monitor started");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** give us all the data/config available */
|
||||||
|
HeartbeatMonitorState getState() { return _state; }
|
||||||
|
|
||||||
|
/** for all of the peer tests being monitored, refetch the data and rerender */
|
||||||
|
void refetchData() {
|
||||||
|
_log.debug("Refetching data");
|
||||||
|
for (int i = 0; i < _state.getTestCount(); i++)
|
||||||
|
PeerPlotStateFetcher.fetchPeerPlotState(this, _state.getTest(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** (re)load the config defining what peer tests we are monitoring (and how to render) */
|
||||||
|
void loadConfig() {
|
||||||
|
//for (int i = 0; i < 10; i++) {
|
||||||
|
// load("fake" + i);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load(String location) {
|
||||||
|
PeerPlotConfig cfg = new PeerPlotConfig(location);
|
||||||
|
PeerPlotState state = new PeerPlotState(cfg);
|
||||||
|
PeerPlotStateFetcher.fetchPeerPlotState(this, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void peerPlotStateFetched(PeerPlotState state) {
|
||||||
|
_state.addTest(state);
|
||||||
|
_gui.stateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** store the config defining what peer tests we are monitoring (and how to render) */
|
||||||
|
void storeConfig() {}
|
||||||
|
|
||||||
|
public static void main(String args[]) {
|
||||||
|
Thread.currentThread().setName("HeartbeatMonitor.main");
|
||||||
|
if (args.length == 1)
|
||||||
|
new HeartbeatMonitor(args[0]).runMonitor();
|
||||||
|
else
|
||||||
|
new HeartbeatMonitor().runMonitor();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,62 @@
|
|||||||
|
package net.i2p.heartbeat.gui;
|
||||||
|
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JComboBox;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JFileChooser;
|
||||||
|
import javax.swing.DefaultComboBoxModel;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ItemEvent;
|
||||||
|
import java.awt.event.ItemListener;
|
||||||
|
|
||||||
|
class HeartbeatMonitorCommandBar extends JPanel {
|
||||||
|
private HeartbeatMonitorGUI _gui;
|
||||||
|
private JComboBox _refreshRate;
|
||||||
|
private JTextField _location;
|
||||||
|
|
||||||
|
public HeartbeatMonitorCommandBar(HeartbeatMonitorGUI gui) {
|
||||||
|
_gui = gui;
|
||||||
|
initializeComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshChanged(ItemEvent evt) {}
|
||||||
|
private void loadCalled() {
|
||||||
|
_gui.getMonitor().load(_location.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void browseCalled() {
|
||||||
|
JFileChooser chooser = new JFileChooser(_location.getText());
|
||||||
|
chooser.setBackground(_gui.getBackground());
|
||||||
|
chooser.setMultiSelectionEnabled(false);
|
||||||
|
int rv = chooser.showDialog(this, "Load");
|
||||||
|
if (rv == JFileChooser.APPROVE_OPTION)
|
||||||
|
_gui.getMonitor().load(chooser.getSelectedFile().getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeComponents() {
|
||||||
|
_refreshRate = new JComboBox(new DefaultComboBoxModel(new Object[] {"10 second refresh", "30 second refresh", "1 minute refresh", "5 minute refresh"}));
|
||||||
|
_refreshRate.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent evt) { refreshChanged(evt); } });
|
||||||
|
_refreshRate.setEnabled(false);
|
||||||
|
_refreshRate.setBackground(_gui.getBackground());
|
||||||
|
add(_refreshRate);
|
||||||
|
JLabel loadLabel = new JLabel("Load from: ");
|
||||||
|
loadLabel.setBackground(_gui.getBackground());
|
||||||
|
add(loadLabel);
|
||||||
|
_location = new JTextField(20);
|
||||||
|
_location.setToolTipText("Either specify a local filename or a fully qualified URL");
|
||||||
|
_location.setBackground(_gui.getBackground());
|
||||||
|
add(_location);
|
||||||
|
JButton browse = new JButton("Browse...");
|
||||||
|
browse.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { browseCalled(); } });
|
||||||
|
browse.setBackground(_gui.getBackground());
|
||||||
|
add(browse);
|
||||||
|
JButton load = new JButton("Load");
|
||||||
|
load.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { loadCalled(); } });
|
||||||
|
load.setBackground(_gui.getBackground());
|
||||||
|
add(load);
|
||||||
|
setBackground(_gui.getBackground());
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,89 @@
|
|||||||
|
package net.i2p.heartbeat.gui;
|
||||||
|
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JMenu;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
import javax.swing.JMenuBar;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
|
||||||
|
class HeartbeatMonitorGUI extends JFrame {
|
||||||
|
private HeartbeatMonitor _monitor;
|
||||||
|
private HeartbeatPlotPane _plotPane;
|
||||||
|
private HeartbeatControlPane _controlPane;
|
||||||
|
private final static Color WHITE = new Color(255, 255, 255);
|
||||||
|
private Color _background = WHITE;
|
||||||
|
|
||||||
|
public HeartbeatMonitorGUI(HeartbeatMonitor monitor) {
|
||||||
|
super("Heartbeat Monitor");
|
||||||
|
_monitor = monitor;
|
||||||
|
initializeComponents();
|
||||||
|
pack();
|
||||||
|
setResizable(false);
|
||||||
|
setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
HeartbeatMonitor getMonitor() { return _monitor; }
|
||||||
|
|
||||||
|
/** build up all our widgets */
|
||||||
|
private void initializeComponents() {
|
||||||
|
getContentPane().setLayout(new BorderLayout());
|
||||||
|
|
||||||
|
setBackground(_background);
|
||||||
|
|
||||||
|
_plotPane = new HeartbeatPlotPane(this);
|
||||||
|
_plotPane.setBackground(_background);
|
||||||
|
JScrollPane pane = new JScrollPane(_plotPane);
|
||||||
|
pane.setBackground(_background);
|
||||||
|
getContentPane().add(pane, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
_controlPane = new HeartbeatControlPane(this);
|
||||||
|
_controlPane.setBackground(_background);
|
||||||
|
getContentPane().add(_controlPane, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
initializeMenus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stateUpdated() {
|
||||||
|
_controlPane.testsUpdated();
|
||||||
|
_plotPane.stateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void exitCalled() {
|
||||||
|
_monitor.getState().setWasKilled(true);
|
||||||
|
setVisible(false);
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
private void loadConfigCalled() {}
|
||||||
|
private void saveConfigCalled() {}
|
||||||
|
private void loadSnapshotCalled() {}
|
||||||
|
private void saveSnapshotCalled() {}
|
||||||
|
|
||||||
|
private void initializeMenus() {
|
||||||
|
JMenuBar bar = new JMenuBar();
|
||||||
|
JMenu fileMenu = new JMenu("File");
|
||||||
|
JMenuItem loadConfig = new JMenuItem("Load config");
|
||||||
|
loadConfig.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { loadConfigCalled(); } });
|
||||||
|
JMenuItem saveConfig = new JMenuItem("Save config");
|
||||||
|
saveConfig.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { saveConfigCalled(); } });
|
||||||
|
JMenuItem saveSnapshot = new JMenuItem("Save snapshot");
|
||||||
|
saveSnapshot.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { saveSnapshotCalled(); } });
|
||||||
|
JMenuItem loadSnapshot = new JMenuItem("Load snapshot");
|
||||||
|
loadSnapshot.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { loadSnapshotCalled(); } });
|
||||||
|
JMenuItem exit = new JMenuItem("Exit");
|
||||||
|
exit.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { exitCalled(); } });
|
||||||
|
|
||||||
|
fileMenu.add(loadConfig);
|
||||||
|
fileMenu.add(saveConfig);
|
||||||
|
fileMenu.add(loadSnapshot);
|
||||||
|
fileMenu.add(saveSnapshot);
|
||||||
|
fileMenu.add(exit);
|
||||||
|
bar.add(fileMenu);
|
||||||
|
setJMenuBar(bar);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,26 @@
|
|||||||
|
package net.i2p.heartbeat.gui;
|
||||||
|
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Periodically fire off necessary events (instructing the heartbeat monitor when
|
||||||
|
* to refetch the data, etc). This is the only active thread in the heartbeat
|
||||||
|
* monitor (outside the swing/jvm threads)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class HeartbeatMonitorRunner implements Runnable {
|
||||||
|
private final static Log _log = new Log(HeartbeatMonitorRunner.class);
|
||||||
|
private HeartbeatMonitor _monitor;
|
||||||
|
|
||||||
|
public HeartbeatMonitorRunner(HeartbeatMonitor monitor) {
|
||||||
|
_monitor = monitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
while (!_monitor.getState().getWasKilled()) {
|
||||||
|
_monitor.refetchData();
|
||||||
|
try { Thread.sleep(_monitor.getState().getRefreshRateMs()); } catch (InterruptedException ie) {}
|
||||||
|
}
|
||||||
|
_log.info("Stopping the heartbeat monitor runner");
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,67 @@
|
|||||||
|
package net.i2p.heartbeat.gui;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* manage the current state of the GUI - all data points, as well as any
|
||||||
|
* rendering or configuration options.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class HeartbeatMonitorState {
|
||||||
|
private String _configFile;
|
||||||
|
private List _peerPlotState;
|
||||||
|
private int _currentPeerPlotConfig;
|
||||||
|
private int _refreshRateMs;
|
||||||
|
private boolean _killed;
|
||||||
|
|
||||||
|
/** by default, refresh every 30 seconds */
|
||||||
|
private final static int DEFAULT_REFRESH_RATE = 30*1000;
|
||||||
|
/** where do we load/store config info from? */
|
||||||
|
private final static String DEFAULT_CONFIG_FILE = "heartbeatMonitor.config";
|
||||||
|
|
||||||
|
public HeartbeatMonitorState() { this(DEFAULT_CONFIG_FILE); }
|
||||||
|
public HeartbeatMonitorState(String configFile) {
|
||||||
|
_peerPlotState = Collections.synchronizedList(new ArrayList());
|
||||||
|
_refreshRateMs = DEFAULT_REFRESH_RATE;
|
||||||
|
_configFile = configFile;
|
||||||
|
_killed = false;
|
||||||
|
_currentPeerPlotConfig = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** how many tests are we monitoring? */
|
||||||
|
public int getTestCount() { return _peerPlotState.size(); }
|
||||||
|
public PeerPlotState getTest(int peer) { return (PeerPlotState)_peerPlotState.get(peer); }
|
||||||
|
public void addTest(PeerPlotState peerState) {
|
||||||
|
if (!_peerPlotState.contains(peerState))
|
||||||
|
_peerPlotState.add(peerState);
|
||||||
|
}
|
||||||
|
public void removeTest(PeerPlotState peerState) { _peerPlotState.remove(peerState); }
|
||||||
|
|
||||||
|
public void removeTest(PeerPlotConfig peerConfig) {
|
||||||
|
for (int i = 0; i < getTestCount(); i++) {
|
||||||
|
PeerPlotState state = getTest(i);
|
||||||
|
if (state.getPlotConfig() == peerConfig) {
|
||||||
|
removeTest(state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** which of the tests are we currently editing/viewing? */
|
||||||
|
public int getPeerPlotConfig() { return _currentPeerPlotConfig; }
|
||||||
|
public void setPeerPlotConfig(int whichTest) { _currentPeerPlotConfig = whichTest; }
|
||||||
|
|
||||||
|
/** how frequently should we update the data? */
|
||||||
|
public int getRefreshRateMs() { return _refreshRateMs; }
|
||||||
|
public void setRefreshRateMs(int ms) { _refreshRateMs = ms; }
|
||||||
|
|
||||||
|
/** where is our config stored? */
|
||||||
|
public String getConfigFile() { return _configFile; }
|
||||||
|
public void setConfigFile(String filename) { _configFile = filename; }
|
||||||
|
|
||||||
|
/** have we been shut down? */
|
||||||
|
public boolean getWasKilled() { return _killed; }
|
||||||
|
public void setWasKilled(boolean killed) { _killed = killed; }
|
||||||
|
}
|
@@ -0,0 +1,59 @@
|
|||||||
|
package net.i2p.heartbeat.gui;
|
||||||
|
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JTextArea;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import net.i2p.heartbeat.PeerDataWriter;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the graph and legend
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class HeartbeatPlotPane extends JPanel {
|
||||||
|
private final static Log _log = new Log(HeartbeatPlotPane.class);
|
||||||
|
private HeartbeatMonitorGUI _gui;
|
||||||
|
private JTextArea _text;
|
||||||
|
|
||||||
|
public HeartbeatPlotPane(HeartbeatMonitorGUI gui) {
|
||||||
|
_gui = gui;
|
||||||
|
initializeComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stateUpdated() {
|
||||||
|
StringBuffer buf = new StringBuffer(32*1024);
|
||||||
|
PeerDataWriter writer = new PeerDataWriter();
|
||||||
|
|
||||||
|
for (int i = 0; i < _gui.getMonitor().getState().getTestCount(); i++) {
|
||||||
|
StaticPeerData data = _gui.getMonitor().getState().getTest(i).getCurrentData();
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
|
||||||
|
try {
|
||||||
|
writer.persist(data, baos);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
_log.error("wtf, error writing to a byte array?", ioe);
|
||||||
|
}
|
||||||
|
buf.append(new String(baos.toByteArray())).append("\n\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
_text.setText(buf.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeComponents() {
|
||||||
|
setBackground(new Color(255, 255, 255));
|
||||||
|
//Dimension size = new Dimension(800, 600);
|
||||||
|
_text = new JTextArea("",30,80); // 16, 60);
|
||||||
|
_text.setAutoscrolls(true);
|
||||||
|
_text.setEditable(false);
|
||||||
|
// _text.setLineWrap(true);
|
||||||
|
// add(new JScrollPane(_text));
|
||||||
|
add(_text);
|
||||||
|
//add(new JScrollPane(_text, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS));
|
||||||
|
//setPreferredSize(size);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,17 @@
|
|||||||
package net.i2p.heartbeat.gui;
|
package net.i2p.heartbeat.gui;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
|
||||||
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.heartbeat.ClientConfig;
|
import net.i2p.heartbeat.ClientConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -7,24 +19,197 @@ import net.i2p.heartbeat.ClientConfig;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class PeerPlotConfig {
|
class PeerPlotConfig {
|
||||||
|
/** where can we find the current state/data (either as a filename or a URL)? */
|
||||||
|
private String _location;
|
||||||
|
/** what test are we defining the plot data for? */
|
||||||
private ClientConfig _config;
|
private ClientConfig _config;
|
||||||
|
/** how should we render the current data set? */
|
||||||
|
private PlotSeriesConfig _currentSeriesConfig;
|
||||||
|
/** how should we render the various averages available? */
|
||||||
|
private List _averageSeriesConfigs;
|
||||||
|
private Set _listeners;
|
||||||
|
private boolean _disabled;
|
||||||
|
|
||||||
private final static void foo() {
|
public PeerPlotConfig(String location) {
|
||||||
// bar
|
this(location, null, null, null);
|
||||||
if (true) {
|
}
|
||||||
// baz
|
|
||||||
|
public PeerPlotConfig(String location, ClientConfig config, PlotSeriesConfig currentSeriesConfig, List averageSeriesConfigs) {
|
||||||
|
_location = location;
|
||||||
|
if (config == null)
|
||||||
|
config = new ClientConfig(location);
|
||||||
|
_config = config;
|
||||||
|
if (currentSeriesConfig != null)
|
||||||
|
_currentSeriesConfig = currentSeriesConfig;
|
||||||
|
else
|
||||||
|
_currentSeriesConfig = new PlotSeriesConfig(0);
|
||||||
|
|
||||||
|
if (averageSeriesConfigs != null) {
|
||||||
|
_averageSeriesConfigs = averageSeriesConfigs;
|
||||||
|
} else {
|
||||||
|
rebuildAverageSeriesConfigs();
|
||||||
}
|
}
|
||||||
// baf
|
_listeners = Collections.synchronizedSet(new HashSet(2));
|
||||||
|
_disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rebuildAverageSeriesConfigs() {
|
||||||
|
int periods[] = _config.getAveragePeriods();
|
||||||
|
if (periods == null) {
|
||||||
|
_averageSeriesConfigs = Collections.synchronizedList(new ArrayList(0));
|
||||||
|
} else {
|
||||||
|
Arrays.sort(periods);
|
||||||
|
_averageSeriesConfigs = Collections.synchronizedList(new ArrayList(periods.length));
|
||||||
|
for (int i = 0; i < periods.length; i++) {
|
||||||
|
_averageSeriesConfigs.add(new PlotSeriesConfig(periods[i]*60*1000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAverage(int minutes) {
|
||||||
|
_config.addAveragePeriod(minutes);
|
||||||
|
|
||||||
|
TreeMap ordered = new TreeMap();
|
||||||
|
for (int i = 0; i < _averageSeriesConfigs.size(); i++) {
|
||||||
|
PlotSeriesConfig cfg = (PlotSeriesConfig)_averageSeriesConfigs.get(i);
|
||||||
|
ordered.put(new Long(cfg.getPeriod()), cfg);
|
||||||
|
}
|
||||||
|
ordered.put(new Long(minutes*60*1000), new PlotSeriesConfig(minutes*60*1000));
|
||||||
|
|
||||||
|
List cfgs = Collections.synchronizedList(new ArrayList(ordered.size()));
|
||||||
|
for (Iterator iter = ordered.values().iterator(); iter.hasNext(); )
|
||||||
|
cfgs.add(iter.next());
|
||||||
|
|
||||||
|
_averageSeriesConfigs = cfgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where is the current state data supposed to be found? This must either be a
|
||||||
|
* local file path or a URL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public String getLocation() { return _location; }
|
||||||
|
public void setLocation(String location) {
|
||||||
|
_location = location;
|
||||||
|
fireUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** What are we configuring? */
|
||||||
|
public ClientConfig getClientConfig() { return _config; }
|
||||||
|
public void setClientConfig(ClientConfig config) {
|
||||||
|
_config = config;
|
||||||
|
fireUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** How do we want to render the current data set? */
|
||||||
|
public PlotSeriesConfig getCurrentSeriesConfig() { return _currentSeriesConfig; }
|
||||||
|
public void setCurrentSeriesConfig(PlotSeriesConfig config) {
|
||||||
|
_currentSeriesConfig = config;
|
||||||
|
fireUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** How do we want to render the averages? */
|
||||||
|
public List getAverageSeriesConfigs() { return _averageSeriesConfigs; }
|
||||||
|
public void setAverageSeriesConfigs(List configs) { _averageSeriesConfigs = configs; }
|
||||||
|
|
||||||
|
/** four char description of the peer */
|
||||||
|
public String getPeerName() {
|
||||||
|
Destination peer = getClientConfig().getPeer();
|
||||||
|
if (peer == null)
|
||||||
|
return "????";
|
||||||
|
else
|
||||||
|
return peer.calculateHash().toBase64().substring(0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
// moo
|
public String getTitle() { return getPeerName() + '.' + getSize() + '.' + getClientConfig().getSendFrequency(); }
|
||||||
|
public String getSummary() {
|
||||||
private final static void biff() {
|
return "Send peer " + getPeerName() + ' ' + getSize() + " every " +
|
||||||
// b0nk
|
getClientConfig().getSendFrequency() + " seconds through " +
|
||||||
if (false) {
|
getClientConfig().getNumHops() + "-hop tunnels";
|
||||||
// boink
|
}
|
||||||
}
|
|
||||||
// b00m
|
private String getSize() {
|
||||||
|
int bytes = getClientConfig().getSendSize();
|
||||||
|
if (bytes < 1024)
|
||||||
|
return bytes + "b";
|
||||||
|
else
|
||||||
|
return bytes/1024 + "kb";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** we've got someone who wants to be notified of changes to the plot config */
|
||||||
|
public void addListener(UpdateListener lsnr) { _listeners.add(lsnr); }
|
||||||
|
public void removeListener(UpdateListener lsnr) { _listeners.remove(lsnr); }
|
||||||
|
|
||||||
|
void fireUpdate() {
|
||||||
|
if (_disabled) return;
|
||||||
|
for (Iterator iter = _listeners.iterator(); iter.hasNext(); ) {
|
||||||
|
((UpdateListener)iter.next()).configUpdated(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disableEvents() { _disabled = true; }
|
||||||
|
public void enableEvents() { _disabled = false; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How do we want to render a particular dataset (either the current or the averaged values)?
|
||||||
|
*/
|
||||||
|
public class PlotSeriesConfig {
|
||||||
|
private long _period;
|
||||||
|
private boolean _plotSendTime;
|
||||||
|
private boolean _plotReceiveTime;
|
||||||
|
private boolean _plotLostMessages;
|
||||||
|
private Color _plotLineColor;
|
||||||
|
|
||||||
|
public PlotSeriesConfig(long period) {
|
||||||
|
this(period, false, false, false, null);
|
||||||
|
}
|
||||||
|
public PlotSeriesConfig(long period, boolean plotSend, boolean plotReceive, boolean plotLost, Color plotColor) {
|
||||||
|
_period = period;
|
||||||
|
_plotSendTime = plotSend;
|
||||||
|
_plotReceiveTime = plotReceive;
|
||||||
|
_plotLostMessages = plotLost;
|
||||||
|
_plotLineColor = plotColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PeerPlotConfig getPlotConfig() { return PeerPlotConfig.this; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What period is this series config describing?
|
||||||
|
*
|
||||||
|
* @return 0 for current, otherwise # milliseconds that are being averaged over
|
||||||
|
*/
|
||||||
|
public long getPeriod() { return _period; }
|
||||||
|
public void setPeriod(long period) {
|
||||||
|
_period = period;
|
||||||
|
fireUpdate();
|
||||||
|
}
|
||||||
|
/** Should we render the time to send (ping to peer)? */
|
||||||
|
public boolean getPlotSendTime() { return _plotSendTime; }
|
||||||
|
public void setPlotSendTime(boolean shouldPlot) {
|
||||||
|
_plotSendTime = shouldPlot;
|
||||||
|
fireUpdate();
|
||||||
|
}
|
||||||
|
/** Should we render the time to receive (peer pong to us)? */
|
||||||
|
public boolean getPlotReceiveTime() { return _plotReceiveTime; }
|
||||||
|
public void setPlotReceiveTime(boolean shouldPlot) {
|
||||||
|
_plotReceiveTime = shouldPlot;
|
||||||
|
fireUpdate();
|
||||||
|
}
|
||||||
|
/** Should we render the number of messages lost (ping sent, no pong received in time)? */
|
||||||
|
public boolean getPlotLostMessages() { return _plotLostMessages; }
|
||||||
|
public void setPlotLostMessages(boolean shouldPlot) {
|
||||||
|
_plotLostMessages = shouldPlot;
|
||||||
|
fireUpdate();
|
||||||
|
}
|
||||||
|
/** What color should we plot the data with? */
|
||||||
|
public Color getPlotLineColor() { return _plotLineColor; }
|
||||||
|
public void setPlotLineColor(Color color) {
|
||||||
|
_plotLineColor = color;
|
||||||
|
fireUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface UpdateListener {
|
||||||
|
void configUpdated(PeerPlotConfig config);
|
||||||
}
|
}
|
||||||
// baa
|
|
||||||
}
|
}
|
@@ -0,0 +1,339 @@
|
|||||||
|
package net.i2p.heartbeat.gui;
|
||||||
|
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JCheckBox;
|
||||||
|
import javax.swing.JComboBox;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
import javax.swing.JTextArea;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.DefaultComboBoxModel;
|
||||||
|
import javax.swing.ComboBoxModel;
|
||||||
|
import javax.swing.JColorChooser;
|
||||||
|
import javax.swing.border.LineBorder;
|
||||||
|
import javax.swing.border.BevelBorder;
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.GridBagLayout;
|
||||||
|
import java.awt.GridBagConstraints;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
class PeerPlotConfigPane extends JPanel implements PeerPlotConfig.UpdateListener {
|
||||||
|
private final static Log _log = new Log(PeerPlotConfigPane.class);
|
||||||
|
private PeerPlotConfig _config;
|
||||||
|
private HeartbeatControlPane _parent;
|
||||||
|
private JLabel _title;
|
||||||
|
private JButton _delete;
|
||||||
|
private JLabel _fromLabel;
|
||||||
|
private JTextField _from;
|
||||||
|
private JTextArea _comments;
|
||||||
|
private JLabel _peerLabel;
|
||||||
|
private JTextField _peerKey;
|
||||||
|
private JLabel _localLabel;
|
||||||
|
private JTextField _localKey;
|
||||||
|
private OptionLine _options[];
|
||||||
|
private Random _rnd = new Random();
|
||||||
|
private final static Color WHITE = new Color(255, 255, 255);
|
||||||
|
private Color _background = WHITE;
|
||||||
|
|
||||||
|
public PeerPlotConfigPane(PeerPlotConfig config, HeartbeatControlPane pane) {
|
||||||
|
_config = config;
|
||||||
|
_parent = pane;
|
||||||
|
if (_parent != null)
|
||||||
|
_background = _parent.getBackground();
|
||||||
|
_config.addListener(this);
|
||||||
|
initializeComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** called when the user wants to stop monitoring this test */
|
||||||
|
private void delete() {
|
||||||
|
_parent.removeTest(_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeComponents() {
|
||||||
|
buildComponents();
|
||||||
|
placeComponents(this);
|
||||||
|
refreshView();
|
||||||
|
//setBorder(new BevelBorder(BevelBorder.RAISED));
|
||||||
|
setBackground(_background);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** place all the gui components onto the given panel */
|
||||||
|
private void placeComponents(JPanel body) {
|
||||||
|
body.setLayout(new GridBagLayout());
|
||||||
|
GridBagConstraints cts = new GridBagConstraints();
|
||||||
|
|
||||||
|
// row 0: title + delete
|
||||||
|
cts.gridx = 0;
|
||||||
|
cts.gridy = 0;
|
||||||
|
cts.gridwidth = 5;
|
||||||
|
cts.anchor = GridBagConstraints.WEST;
|
||||||
|
cts.fill = GridBagConstraints.NONE;
|
||||||
|
body.add(_title, cts);
|
||||||
|
cts.gridx = 5;
|
||||||
|
cts.gridwidth = 1;
|
||||||
|
cts.anchor = GridBagConstraints.NORTHWEST;
|
||||||
|
cts.fill = GridBagConstraints.BOTH;
|
||||||
|
body.add(_delete, cts);
|
||||||
|
|
||||||
|
// row 1: from + location
|
||||||
|
cts.gridx = 0;
|
||||||
|
cts.gridy = 1;
|
||||||
|
cts.gridwidth = 1;
|
||||||
|
cts.fill = GridBagConstraints.NONE;
|
||||||
|
body.add(_fromLabel, cts);
|
||||||
|
cts.gridx = 1;
|
||||||
|
cts.gridwidth = 5;
|
||||||
|
cts.fill = GridBagConstraints.BOTH;
|
||||||
|
body.add(_from, cts);
|
||||||
|
|
||||||
|
// row 2: comment
|
||||||
|
cts.gridx = 0;
|
||||||
|
cts.gridy = 2;
|
||||||
|
cts.gridwidth = 6;
|
||||||
|
cts.fill = GridBagConstraints.BOTH;
|
||||||
|
body.add(_comments, cts);
|
||||||
|
|
||||||
|
// row 3: peer + peerKey
|
||||||
|
cts.gridx = 0;
|
||||||
|
cts.gridy = 3;
|
||||||
|
cts.gridwidth = 1;
|
||||||
|
cts.fill = GridBagConstraints.NONE;
|
||||||
|
body.add(_peerLabel, cts);
|
||||||
|
cts.gridx = 1;
|
||||||
|
cts.gridwidth = 5;
|
||||||
|
cts.fill = GridBagConstraints.BOTH;
|
||||||
|
body.add(_peerKey, cts);
|
||||||
|
|
||||||
|
// row 4: local + localKey
|
||||||
|
cts.gridx = 0;
|
||||||
|
cts.gridy = 4;
|
||||||
|
cts.gridwidth = 1;
|
||||||
|
cts.fill = GridBagConstraints.NONE;
|
||||||
|
body.add(_localLabel, cts);
|
||||||
|
cts.gridx = 1;
|
||||||
|
cts.gridwidth = 5;
|
||||||
|
cts.fill = GridBagConstraints.BOTH;
|
||||||
|
body.add(_localKey, cts);
|
||||||
|
|
||||||
|
// row 5-N: data row
|
||||||
|
for (int i = 0; i < _options.length; i++) {
|
||||||
|
cts.gridx = 0;
|
||||||
|
cts.gridy = 5 + i;
|
||||||
|
cts.gridwidth = 1;
|
||||||
|
cts.fill = GridBagConstraints.NONE;
|
||||||
|
cts.anchor = GridBagConstraints.WEST;
|
||||||
|
if (_options[i]._durationMinutes <= 0)
|
||||||
|
body.add(new JLabel("Data: "), cts);
|
||||||
|
else
|
||||||
|
body.add(new JLabel(_options[i]._durationMinutes + "m avg: "), cts);
|
||||||
|
|
||||||
|
cts.gridx = 1;
|
||||||
|
body.add(_options[i]._send, cts);
|
||||||
|
cts.gridx = 2;
|
||||||
|
body.add(_options[i]._recv, cts);
|
||||||
|
cts.gridx = 3;
|
||||||
|
body.add(_options[i]._lost, cts);
|
||||||
|
cts.gridx = 4;
|
||||||
|
body.add(_options[i]._all, cts);
|
||||||
|
cts.gridx = 5;
|
||||||
|
body.add(_options[i]._color, cts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** build all of the gui components */
|
||||||
|
private void buildComponents() {
|
||||||
|
_title = new JLabel(_config.getSummary());
|
||||||
|
_title.setBackground(_background);
|
||||||
|
_delete = new JButton("Delete");
|
||||||
|
_delete.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { delete(); } });
|
||||||
|
_delete.setEnabled(false);
|
||||||
|
_delete.setBackground(_background);
|
||||||
|
_fromLabel = new JLabel("Location: ");
|
||||||
|
_fromLabel.setBackground(_background);
|
||||||
|
_from = new JTextField(_config.getLocation());
|
||||||
|
_from.setEditable(false);
|
||||||
|
_from.setBackground(_background);
|
||||||
|
_comments = new JTextArea(_config.getClientConfig().getComment(), 2, 20);
|
||||||
|
// _comments = new JTextArea(_config.getClientConfig().getComment(), 2, 40);
|
||||||
|
_comments.setEditable(false);
|
||||||
|
_comments.setBackground(_background);
|
||||||
|
_peerLabel = new JLabel("Peer: ");
|
||||||
|
_peerLabel.setBackground(_background);
|
||||||
|
_peerKey = new JTextField(_config.getClientConfig().getPeer().toBase64(), 8);
|
||||||
|
_peerKey.setBackground(_background);
|
||||||
|
_localLabel = new JLabel("Local: ");
|
||||||
|
_localLabel.setBackground(_background);
|
||||||
|
_localKey = new JTextField(_config.getClientConfig().getUs().toBase64(), 8);
|
||||||
|
_localKey.setBackground(_background);
|
||||||
|
|
||||||
|
int averagedPeriods[] = _config.getClientConfig().getAveragePeriods();
|
||||||
|
if (averagedPeriods == null)
|
||||||
|
averagedPeriods = new int[0];
|
||||||
|
|
||||||
|
_options = new OptionLine[1 + averagedPeriods.length];
|
||||||
|
_options[0] = new OptionLine(0);
|
||||||
|
for (int i = 0; i < averagedPeriods.length; i++) {
|
||||||
|
_options[1+i] = new OptionLine(averagedPeriods[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** the settings have changed - revise */
|
||||||
|
private void refreshView() {
|
||||||
|
for (int i = 0; i < _options.length; i++) {
|
||||||
|
PeerPlotConfig.PlotSeriesConfig cfg = getConfig(_options[i]._durationMinutes);
|
||||||
|
if (cfg == null) {
|
||||||
|
_log.warn("Config for minutes " + _options[i]._durationMinutes + " was not found?");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_log.debug("Refreshing view for minutes ["+ _options[i]._durationMinutes + "]: send [" +
|
||||||
|
_options[i]._send.isSelected() + "/" + cfg.getPlotSendTime() + "] recv [" +
|
||||||
|
_options[i]._recv.isSelected() + "/" + cfg.getPlotReceiveTime() + "] lost [" +
|
||||||
|
_options[i]._lost.isSelected() + "/" + cfg.getPlotLostMessages() + "]");
|
||||||
|
_options[i]._send.setSelected(cfg.getPlotSendTime());
|
||||||
|
_options[i]._recv.setSelected(cfg.getPlotReceiveTime());
|
||||||
|
_options[i]._lost.setSelected(cfg.getPlotLostMessages());
|
||||||
|
if (cfg.getPlotLineColor() != null)
|
||||||
|
_options[i]._color.setBackground(cfg.getPlotLineColor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** find the right config for the given period, or null if none exist */
|
||||||
|
private PeerPlotConfig.PlotSeriesConfig getConfig(int minutes) {
|
||||||
|
if (minutes <= 0)
|
||||||
|
return _config.getCurrentSeriesConfig();
|
||||||
|
|
||||||
|
List configs = _config.getAverageSeriesConfigs();
|
||||||
|
for (int i = 0; i < configs.size(); i++) {
|
||||||
|
PeerPlotConfig.PlotSeriesConfig cfg = (PeerPlotConfig.PlotSeriesConfig)configs.get(i);
|
||||||
|
if (cfg.getPeriod() == minutes * 60*1000)
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** notified that the config has been updated */
|
||||||
|
public void configUpdated(PeerPlotConfig config) { refreshView(); }
|
||||||
|
|
||||||
|
private class ChooseColor implements ActionListener {
|
||||||
|
private int _minutes;
|
||||||
|
private JButton _button;
|
||||||
|
|
||||||
|
public ChooseColor(int minutes, JButton button) {
|
||||||
|
_minutes = minutes;
|
||||||
|
_button = button;
|
||||||
|
}
|
||||||
|
public void actionPerformed(ActionEvent evt) {
|
||||||
|
PeerPlotConfig.PlotSeriesConfig cfg = getConfig(_minutes);
|
||||||
|
Color origColor = null;
|
||||||
|
if (cfg != null)
|
||||||
|
origColor = cfg.getPlotLineColor();
|
||||||
|
Color color = JColorChooser.showDialog(PeerPlotConfigPane.this, "What color should this line be?", origColor);
|
||||||
|
if (color != null) {
|
||||||
|
if (cfg != null)
|
||||||
|
cfg.setPlotLineColor(color);
|
||||||
|
_button.setBackground(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OptionLine {
|
||||||
|
int _durationMinutes;
|
||||||
|
JCheckBox _send;
|
||||||
|
JCheckBox _recv;
|
||||||
|
JCheckBox _lost;
|
||||||
|
JCheckBox _all;
|
||||||
|
JButton _color;
|
||||||
|
|
||||||
|
public OptionLine(int durationMinutes) {
|
||||||
|
_durationMinutes = durationMinutes;
|
||||||
|
_send = new JCheckBox("send time");
|
||||||
|
_send.setBackground(_background);
|
||||||
|
_recv = new JCheckBox("receive time");
|
||||||
|
_recv.setBackground(_background);
|
||||||
|
_lost = new JCheckBox("lost messages");
|
||||||
|
_lost.setBackground(_background);
|
||||||
|
_all = new JCheckBox("all");
|
||||||
|
_all.setBackground(_background);
|
||||||
|
_color = new JButton("color");
|
||||||
|
int r = _rnd.nextInt(255);
|
||||||
|
if (r < 0) r = -r;
|
||||||
|
int g = _rnd.nextInt(255);
|
||||||
|
if (g < 0) g = -g;
|
||||||
|
int b = _rnd.nextInt(255);
|
||||||
|
if (b < 0) b = -b;
|
||||||
|
_color.setBackground(new Color(r, g, b));
|
||||||
|
|
||||||
|
_send.addActionListener(new UpdateListener(OptionLine.this, _durationMinutes));
|
||||||
|
_recv.addActionListener(new UpdateListener(OptionLine.this, _durationMinutes));
|
||||||
|
_lost.addActionListener(new UpdateListener(OptionLine.this, _durationMinutes));
|
||||||
|
_all.addActionListener(new UpdateListener(OptionLine.this, _durationMinutes));
|
||||||
|
_color.addActionListener(new ChooseColor(durationMinutes, _color));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UpdateListener implements ActionListener {
|
||||||
|
private OptionLine _line;
|
||||||
|
private int _minutes;
|
||||||
|
public UpdateListener(OptionLine line, int minutes) {
|
||||||
|
_line = line;
|
||||||
|
_minutes = minutes;
|
||||||
|
}
|
||||||
|
public void actionPerformed(ActionEvent evt) {
|
||||||
|
PeerPlotConfig.PlotSeriesConfig cfg = getConfig(_minutes);
|
||||||
|
if (cfg == null) {
|
||||||
|
_log.error("wtf, why is there no config for " + _minutes + "?");
|
||||||
|
|
||||||
|
List configs = _config.getAverageSeriesConfigs();
|
||||||
|
for (int i = 0; i < configs.size(); i++) {
|
||||||
|
PeerPlotConfig.PlotSeriesConfig conf = (PeerPlotConfig.PlotSeriesConfig)configs.get(i);
|
||||||
|
_log.debug("We know about " + conf.getPeriod());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.getPlotConfig().disableEvents();
|
||||||
|
_log.debug("Updating data for minutes ["+ _line._durationMinutes + "]: send [" +
|
||||||
|
_line._send.isSelected() + "/" + cfg.getPlotSendTime() + "] recv [" +
|
||||||
|
_line._recv.isSelected() + "/" + cfg.getPlotReceiveTime() + "] lost [" +
|
||||||
|
_line._lost.isSelected() + "/" + cfg.getPlotLostMessages() + "]");
|
||||||
|
|
||||||
|
boolean force = _line._all.isSelected();
|
||||||
|
cfg.setPlotSendTime(_line._send.isSelected() || force);
|
||||||
|
cfg.setPlotReceiveTime(_line._recv.isSelected() || force);
|
||||||
|
cfg.setPlotLostMessages(_line._lost.isSelected() || force);
|
||||||
|
cfg.getPlotConfig().enableEvents();
|
||||||
|
cfg.getPlotConfig().fireUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final static void main(String args[]) {
|
||||||
|
Test t = new Test();
|
||||||
|
t.runTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static class Test implements PeerPlotStateFetcher.FetchStateReceptor {
|
||||||
|
public void runTest() {
|
||||||
|
PeerPlotConfig cfg = new PeerPlotConfig("C:\\testnet\\r2\\heartbeatStat_10s_30kb.txt");
|
||||||
|
PeerPlotState state = new PeerPlotState(cfg);
|
||||||
|
PeerPlotStateFetcher.fetchPeerPlotState(this, state);
|
||||||
|
try { Thread.sleep(60*1000); } catch (InterruptedException ie) {}
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void peerPlotStateFetched(PeerPlotState state) {
|
||||||
|
javax.swing.JFrame f = new javax.swing.JFrame("Test");
|
||||||
|
f.getContentPane().add(new JScrollPane(new PeerPlotConfigPane(state.getPlotConfig(), null)));
|
||||||
|
f.pack();
|
||||||
|
f.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,58 @@
|
|||||||
|
package net.i2p.heartbeat.gui;
|
||||||
|
|
||||||
|
import net.i2p.heartbeat.PeerData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current data + plot config for a particular test
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class PeerPlotState {
|
||||||
|
private StaticPeerData _currentData;
|
||||||
|
private PeerPlotConfig _plotConfig;
|
||||||
|
|
||||||
|
public PeerPlotState() {
|
||||||
|
this(null, null);
|
||||||
|
}
|
||||||
|
public PeerPlotState(PeerPlotConfig config) {
|
||||||
|
this(config, new StaticPeerData(config.getClientConfig()));
|
||||||
|
}
|
||||||
|
public PeerPlotState(PeerPlotConfig config, StaticPeerData data) {
|
||||||
|
_plotConfig = config;
|
||||||
|
_currentData = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAverage(int minutes, int sendMs, int recvMs, int lost) {
|
||||||
|
// make sure we've got the config entry for the average
|
||||||
|
_plotConfig.addAverage(minutes);
|
||||||
|
// add the data point...
|
||||||
|
_currentData.addAverage(minutes, sendMs, recvMs, lost);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* we successfully got a ping/pong through
|
||||||
|
*
|
||||||
|
* @param sendTime when did the ping get sent?
|
||||||
|
* @param sendMs how much later did the peer receive the ping?
|
||||||
|
* @param recvMs how much later than that did we receive the pong?
|
||||||
|
*/
|
||||||
|
public void addSuccess(long sendTime, int sendMs, int recvMs) {
|
||||||
|
_currentData.addData(sendTime, sendMs, recvMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* we lost a ping/pong
|
||||||
|
*
|
||||||
|
* @param sendTime when did we send the ping?
|
||||||
|
*/
|
||||||
|
public void addLost(long sendTime) {
|
||||||
|
_currentData.addData(sendTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** data set to render */
|
||||||
|
public StaticPeerData getCurrentData() { return _currentData; }
|
||||||
|
public void setCurrentData(StaticPeerData data) { _currentData = data; }
|
||||||
|
|
||||||
|
/** configuration options on how to render the data set */
|
||||||
|
public PeerPlotConfig getPlotConfig() { return _plotConfig; }
|
||||||
|
public void setPlotConfig(PeerPlotConfig config) { _plotConfig = config; }
|
||||||
|
}
|
@@ -0,0 +1,350 @@
|
|||||||
|
package net.i2p.heartbeat.gui;
|
||||||
|
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
import net.i2p.util.I2PThread;
|
||||||
|
|
||||||
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.data.DataFormatException;
|
||||||
|
|
||||||
|
import net.i2p.heartbeat.ClientConfig;
|
||||||
|
import net.i2p.heartbeat.PeerData;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
class PeerPlotStateFetcher {
|
||||||
|
private final static Log _log = new Log(PeerPlotStateFetcher.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch and fill the specified state structure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static void fetchPeerPlotState(FetchStateReceptor receptor, PeerPlotState state) {
|
||||||
|
I2PThread t = new I2PThread(new Fetcher(receptor, state));
|
||||||
|
t.setDaemon(true);
|
||||||
|
t.setName("Fetch state from " + state.getPlotConfig().getLocation());
|
||||||
|
t.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface FetchStateReceptor {
|
||||||
|
void peerPlotStateFetched(PeerPlotState state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Fetcher implements Runnable {
|
||||||
|
private PeerPlotState _state;
|
||||||
|
private FetchStateReceptor _receptor;
|
||||||
|
public Fetcher(FetchStateReceptor receptor, PeerPlotState state) {
|
||||||
|
_state = state;
|
||||||
|
_receptor = receptor;
|
||||||
|
}
|
||||||
|
public void run() {
|
||||||
|
String loc = _state.getPlotConfig().getLocation();
|
||||||
|
_log.debug("Load called [" + loc + "]");
|
||||||
|
InputStream in = null;
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
URL location = new URL(loc);
|
||||||
|
in = location.openStream();
|
||||||
|
} catch (MalformedURLException mue) {
|
||||||
|
_log.debug("Not a url [" + loc + "]");
|
||||||
|
in = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in == null)
|
||||||
|
in = new FileInputStream(loc);
|
||||||
|
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
|
||||||
|
String line = null;
|
||||||
|
while ( (line = reader.readLine()) != null) {
|
||||||
|
handleLine(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid())
|
||||||
|
_receptor.peerPlotStateFetched(_state);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
_log.error("Error retrieving from the location [" + loc + "]", ioe);
|
||||||
|
} finally {
|
||||||
|
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check to make sure we've got everything we need
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
boolean valid() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* handle a line from the data set - these can be formatted in one of the
|
||||||
|
* following ways. <p />
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* peer khWYqCETu9YtPUvGV92ocsbEW5DezhKlIG7ci8RLX3g=
|
||||||
|
* local u-9hlR1ik2hemXf0HvKMfeRgrS86CbNQh25e7XBhaQE=
|
||||||
|
* peerDest [base 64 of the full destination]
|
||||||
|
* localDest [base 64 of the full destination]
|
||||||
|
* numTunnelHops 2
|
||||||
|
* comment Test with localhost sending 30KB every 20 seconds
|
||||||
|
* sendFrequency 20
|
||||||
|
* sendSize 30720
|
||||||
|
* sessionStart 20040409.22:51:10.915
|
||||||
|
* currentTime 20040409.23:31:39.607
|
||||||
|
* numPending 2
|
||||||
|
* lifetimeSent 118
|
||||||
|
* lifetimeRecv 113
|
||||||
|
* #averages minutes sendMs recvMs numLost
|
||||||
|
* periodAverage 1 1843 771 0
|
||||||
|
* periodAverage 5 786 752 1
|
||||||
|
* periodAverage 30 855 735 3
|
||||||
|
* #action status date and time sent sendMs replyMs
|
||||||
|
* EVENT OK 20040409.23:21:44.742 691 670
|
||||||
|
* EVENT OK 20040409.23:22:05.201 671 581
|
||||||
|
* EVENT OK 20040409.23:22:26.301 1182 1452
|
||||||
|
* EVENT OK 20040409.23:22:47.322 24304 1723
|
||||||
|
* EVENT OK 20040409.23:23:08.232 2293 1081
|
||||||
|
* EVENT OK 20040409.23:23:29.332 1392 641
|
||||||
|
* EVENT OK 20040409.23:23:50.262 641 761
|
||||||
|
* EVENT OK 20040409.23:24:11.102 651 701
|
||||||
|
* EVENT OK 20040409.23:24:31.401 841 621
|
||||||
|
* EVENT OK 20040409.23:24:52.061 651 681
|
||||||
|
* EVENT OK 20040409.23:25:12.480 701 1623
|
||||||
|
* EVENT OK 20040409.23:25:32.990 1442 1212
|
||||||
|
* EVENT OK 20040409.23:25:54.230 591 631
|
||||||
|
* EVENT OK 20040409.23:26:14.620 620 691
|
||||||
|
* EVENT OK 20040409.23:26:35.199 1793 1432
|
||||||
|
* EVENT OK 20040409.23:26:56.570 661 641
|
||||||
|
* EVENT OK 20040409.23:27:17.200 641 660
|
||||||
|
* EVENT OK 20040409.23:27:38.120 611 921
|
||||||
|
* EVENT OK 20040409.23:27:58.699 831 621
|
||||||
|
* EVENT OK 20040409.23:28:19.559 801 661
|
||||||
|
* EVENT OK 20040409.23:28:40.279 601 611
|
||||||
|
* EVENT OK 20040409.23:29:00.648 601 621
|
||||||
|
* EVENT OK 20040409.23:29:21.288 701 661
|
||||||
|
* EVENT LOST 20040409.23:29:41.828
|
||||||
|
* EVENT LOST 20040409.23:30:02.327
|
||||||
|
* EVENT LOST 20040409.23:30:22.656
|
||||||
|
* EVENT OK 20040409.23:31:24.305 1843 771
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
private void handleLine(String line) {
|
||||||
|
if (line.startsWith("peerDest"))
|
||||||
|
handlePeerDest(line);
|
||||||
|
else if (line.startsWith("localDest"))
|
||||||
|
handleLocalDest(line);
|
||||||
|
else if (line.startsWith("numTunnelHops"))
|
||||||
|
handleNumTunnelHops(line);
|
||||||
|
else if (line.startsWith("comment"))
|
||||||
|
handleComment(line);
|
||||||
|
else if (line.startsWith("sendFrequency"))
|
||||||
|
handleSendFrequency(line);
|
||||||
|
else if (line.startsWith("sendSize"))
|
||||||
|
handleSendSize(line);
|
||||||
|
else if (line.startsWith("periodAverage"))
|
||||||
|
handlePeriodAverage(line);
|
||||||
|
else if (line.startsWith("EVENT"))
|
||||||
|
handleEvent(line);
|
||||||
|
else if (line.startsWith("numPending"))
|
||||||
|
handleNumPending(line);
|
||||||
|
else if (line.startsWith("sessionStart"))
|
||||||
|
handleSessionStart(line);
|
||||||
|
else
|
||||||
|
_log.debug("Not handled: " + line);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handlePeerDest(String line) {
|
||||||
|
StringTokenizer tok = new StringTokenizer(line);
|
||||||
|
tok.nextToken(); // ignore;
|
||||||
|
String destKey = tok.nextToken();
|
||||||
|
try {
|
||||||
|
Destination d = new Destination();
|
||||||
|
d.fromBase64(destKey);
|
||||||
|
_state.getPlotConfig().getClientConfig().setPeer(d);
|
||||||
|
_log.debug("Setting the peer to " + d.calculateHash().toBase64());
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
_log.error("Unable to parse the peerDest line: [" + line + "]", dfe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleLocalDest(String line) {
|
||||||
|
StringTokenizer tok = new StringTokenizer(line);
|
||||||
|
tok.nextToken(); // ignore;
|
||||||
|
String destKey = tok.nextToken();
|
||||||
|
try {
|
||||||
|
Destination d = new Destination();
|
||||||
|
d.fromBase64(destKey);
|
||||||
|
_state.getPlotConfig().getClientConfig().setUs(d);
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
_log.error("Unable to parse the localDest line: [" + line + "]", dfe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleComment(String line) {
|
||||||
|
StringTokenizer tok = new StringTokenizer(line);
|
||||||
|
tok.nextToken(); // ignore;
|
||||||
|
StringBuffer buf = new StringBuffer(line.length()-32);
|
||||||
|
while (tok.hasMoreTokens())
|
||||||
|
buf.append(tok.nextToken()).append(' ');
|
||||||
|
_state.getPlotConfig().getClientConfig().setComment(buf.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleNumTunnelHops(String line) {
|
||||||
|
StringTokenizer tok = new StringTokenizer(line);
|
||||||
|
tok.nextToken(); // ignore;
|
||||||
|
String num = tok.nextToken();
|
||||||
|
try {
|
||||||
|
int val = Integer.parseInt(num);
|
||||||
|
_state.getPlotConfig().getClientConfig().setNumHops(val);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
_log.error("Unable to parse the numTunnelHops line: [" + line + "]", nfe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleNumPending(String line) {
|
||||||
|
StringTokenizer tok = new StringTokenizer(line);
|
||||||
|
tok.nextToken(); // ignore;
|
||||||
|
String num = tok.nextToken();
|
||||||
|
try {
|
||||||
|
int val = Integer.parseInt(num);
|
||||||
|
_state.getCurrentData().setPendingCount(val);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
_log.error("Unable to parse the numPending line: [" + line + "]", nfe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleSendFrequency(String line) {
|
||||||
|
StringTokenizer tok = new StringTokenizer(line);
|
||||||
|
tok.nextToken(); // ignore;
|
||||||
|
String num = tok.nextToken();
|
||||||
|
try {
|
||||||
|
int val = Integer.parseInt(num);
|
||||||
|
_state.getPlotConfig().getClientConfig().setSendFrequency(val);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
_log.error("Unable to parse the sendFrequency line: [" + line + "]", nfe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleSendSize(String line) {
|
||||||
|
StringTokenizer tok = new StringTokenizer(line);
|
||||||
|
tok.nextToken(); // ignore;
|
||||||
|
String num = tok.nextToken();
|
||||||
|
try {
|
||||||
|
int val = Integer.parseInt(num);
|
||||||
|
_state.getPlotConfig().getClientConfig().setSendSize(val);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
_log.error("Unable to parse the sendSize line: [" + line + "]", nfe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleSessionStart(String line) {
|
||||||
|
StringTokenizer tok = new StringTokenizer(line);
|
||||||
|
tok.nextToken(); // ignore;
|
||||||
|
String date = tok.nextToken();
|
||||||
|
try {
|
||||||
|
long when = getDate(date);
|
||||||
|
_state.getCurrentData().setSessionStart(when);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
_log.error("Unable to parse the sessionStart line: [" + line + "]", nfe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handlePeriodAverage(String line) {
|
||||||
|
StringTokenizer tok = new StringTokenizer(line);
|
||||||
|
tok.nextToken(); // ignore;
|
||||||
|
try {
|
||||||
|
// periodAverage minutes sendMs recvMs numLost
|
||||||
|
int min = Integer.parseInt(tok.nextToken());
|
||||||
|
int send = Integer.parseInt(tok.nextToken());
|
||||||
|
int recv = Integer.parseInt(tok.nextToken());
|
||||||
|
int lost = Integer.parseInt(tok.nextToken());
|
||||||
|
_state.addAverage(min, send, recv, lost);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
_log.error("Unable to parse the sendSize line: [" + line + "]", nfe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleEvent(String line) {
|
||||||
|
StringTokenizer tok = new StringTokenizer(line);
|
||||||
|
|
||||||
|
// * EVENT OK 20040409.23:29:21.288 701 661
|
||||||
|
// * EVENT LOST 20040409.23:29:41.828
|
||||||
|
tok.nextToken(); // ignore first two
|
||||||
|
tok.nextToken();
|
||||||
|
try {
|
||||||
|
long when = getDate(tok.nextToken());
|
||||||
|
if (when < 0) {
|
||||||
|
_log.error("Invalid EVENT line: [" + line + "]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tok.hasMoreTokens()) {
|
||||||
|
int sendMs = Integer.parseInt(tok.nextToken());
|
||||||
|
int recvMs = Integer.parseInt(tok.nextToken());
|
||||||
|
_state.addSuccess(when, sendMs, recvMs);
|
||||||
|
} else {
|
||||||
|
_state.addLost(when);
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
_log.error("Unable to parse the EVENT line: [" + line + "]", nfe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final SimpleDateFormat _fmt = new SimpleDateFormat("yyyyMMdd.HH:mm:ss.SSS", Locale.UK);
|
||||||
|
private long getDate(String date) {
|
||||||
|
synchronized (_fmt) {
|
||||||
|
try {
|
||||||
|
return _fmt.parse(date).getTime();
|
||||||
|
} catch (ParseException pe) {
|
||||||
|
_log.error("Unable to parse the date [" + date + "]", pe);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fakeRun() {
|
||||||
|
try {
|
||||||
|
Destination peer = new Destination();
|
||||||
|
Destination us = new Destination();
|
||||||
|
peer.fromBase64("3RPLOkQGlq8anNyNWhjbMyHxpAvUyUJKbiUejI80DnPR59T3blc7-XrBhQ2iPbf-BRAR~v1j34Kpba1eDyhPk2gevsE6ULO1irarJ3~C9WcQH2wAbNiVwfWqbh6onQ~YmkSpGNwGHD6ytwbvTyXeBJ" +
|
||||||
|
"cS8e6gmfNN-sYLn1aQu8UqWB3D6BmTfLtyS3eqWVk66Nrzmwy8E1Hvq5z~1lukYb~cyiDO1oZHAOLyUQtd9eN16yJY~2SRG8LiscpPMl9nSJUr6fmXMUubW-M7QGFH82Om-735PJUk6WMy1Hi9Vgh4Pxhdl7g" +
|
||||||
|
"fqGRWioFABdhcypb7p1Ca77p73uabLDFK-SjIYmdj7TwSdbNa6PCmzEvCEW~IZeZmnZC5B6pK30AdmD9vc641wUGce9xTJVfNRupf5L7pSsVIISix6FkKQk-FTW2RsZKLbuMCYMaPzLEx5gzODEqtI6Jf2teM" +
|
||||||
|
"d5xCz51RPayDJl~lJ-W0IWYfosnjM~KxYaqc4agviBuF5ZWeAAAA");
|
||||||
|
us.fromBase64("W~JFpqSH8uopylox2V5hMbpcHSsb-dJkSKvdJ1vj~KQcUFJWXFyfbetBAukcGH5S559aK9oslU0qbVoMDlJITVC4OXfXSnVbJBP1IhsK8SvjSYicjmIi2fA~k4HvSh9Wxu~bg8yo~jgfHA8tjYpp" +
|
||||||
|
"K9QKc56BpkJb~hx0nNGy4Ny9eW~6A5AwAmHvwdt5NqcREYRMjRd63dMGm8BcEe-6FbOyMo3dnIFcETWAe8TCeoMxm~S1n~6Jlinw3ETxv-L6lQkhFFWnC5zyzQ~4JhVxxT3taTMYXg8td4CBGmrS078jcjW63" +
|
||||||
|
"rlSiQgZBlYfN3iEYmurhuIEV9NXRcmnMrBOQUAoXPpVuRIxJbaQNDL71FO2iv424n4YjKs84suAho34GGQKq7WoL5V5KQgihfcl0f~xne-qP3FtpoPFeyA9x-sA2JWDAsxoZlfvgkiP5eyOn23prT9TJK47HC" +
|
||||||
|
"VilHSV11uTVaC4Jc5YsjoBCZadWbgQnMCKlZ4jk-bLE1PSWLg7AAAA");
|
||||||
|
_state.getPlotConfig().getClientConfig().setPeer(peer);
|
||||||
|
_state.getPlotConfig().getClientConfig().setUs(us);
|
||||||
|
_state.getPlotConfig().getClientConfig().setNumHops(2);
|
||||||
|
_state.getPlotConfig().getClientConfig().setComment("we do stuff\nreally nifty stuff. really");
|
||||||
|
_state.getPlotConfig().getClientConfig().setAveragePeriods(new int[] { 1, 5, 30, 60 });
|
||||||
|
int rnd = new java.util.Random().nextInt();
|
||||||
|
if (rnd > 0)
|
||||||
|
rnd = rnd % 10;
|
||||||
|
else
|
||||||
|
rnd = (-rnd) % 10;
|
||||||
|
_state.getPlotConfig().getClientConfig().setSendFrequency(rnd);
|
||||||
|
_state.getPlotConfig().getClientConfig().setSendSize(16*1024);
|
||||||
|
_state.getPlotConfig().getClientConfig().setStatDuration(10);
|
||||||
|
_state.getPlotConfig().rebuildAverageSeriesConfigs();
|
||||||
|
_state.setCurrentData(new StaticPeerData(_state.getPlotConfig().getClientConfig()));
|
||||||
|
|
||||||
|
_receptor.peerPlotStateFetched(_state);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,95 @@
|
|||||||
|
package net.i2p.heartbeat.gui;
|
||||||
|
|
||||||
|
import net.i2p.heartbeat.PeerData;
|
||||||
|
import net.i2p.heartbeat.ClientConfig;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raw data points for a test
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class StaticPeerData extends PeerData {
|
||||||
|
private int _pending;
|
||||||
|
/** Integer (period, in minutes) to Integer (milliseconds) for sending a ping */
|
||||||
|
private Map _averageSendTimes;
|
||||||
|
/** Integer (period, in minutes) to Integer (milliseconds) for receiving a pong */
|
||||||
|
private Map _averageReceiveTimes;
|
||||||
|
/** Integer (period, in minutes) to Integer (num messages) of how many messages were lost on average */
|
||||||
|
private Map _lostMessages;
|
||||||
|
|
||||||
|
public StaticPeerData(ClientConfig config) {
|
||||||
|
super(config);
|
||||||
|
_averageSendTimes = new HashMap(4);
|
||||||
|
_averageReceiveTimes = new HashMap(4);
|
||||||
|
_lostMessages = new HashMap(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void addAverage(int minutes, int sendMs, int recvMs, int lost) {
|
||||||
|
_averageSendTimes.put(new Integer(minutes), new Integer(sendMs));
|
||||||
|
_averageReceiveTimes.put(new Integer(minutes), new Integer(recvMs));
|
||||||
|
_lostMessages.put(new Integer(minutes), new Integer(lost));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPendingCount(int numPending) { _pending = numPending; }
|
||||||
|
public void setSessionStart(long when) { super.setSessionStart(when); }
|
||||||
|
|
||||||
|
public void addData(long sendTime, int sendMs, int recvMs) {
|
||||||
|
PeerData.EventDataPoint dataPoint = new PeerData.EventDataPoint(sendTime);
|
||||||
|
dataPoint.setPongSent(sendTime + sendMs);
|
||||||
|
dataPoint.setPongReceived(sendTime + sendMs + recvMs);
|
||||||
|
dataPoint.setWasPonged(true);
|
||||||
|
addDataPoint(dataPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addData(long sendTime) {
|
||||||
|
PeerData.EventDataPoint dataPoint = new PeerData.EventDataPoint(sendTime);
|
||||||
|
dataPoint.setWasPonged(false);
|
||||||
|
addDataPoint(dataPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* how many pings are still outstanding?
|
||||||
|
* @return the number of pings outstanding
|
||||||
|
*/
|
||||||
|
public int getPendingCount() { return _pending; }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* average time to send over the given period.
|
||||||
|
*
|
||||||
|
* @param period number of minutes to retrieve the average for
|
||||||
|
* @return milliseconds average, or -1 if we dont track that period
|
||||||
|
*/
|
||||||
|
public double getAverageSendTime(int period) {
|
||||||
|
return ((Integer)_averageSendTimes.get(new Integer(period))).doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* average time to receive over the given period.
|
||||||
|
*
|
||||||
|
* @param period number of minutes to retrieve the average for
|
||||||
|
* @return milliseconds average, or -1 if we dont track that period
|
||||||
|
*/
|
||||||
|
public double getAverageReceiveTime(int period) {
|
||||||
|
return ((Integer)_averageReceiveTimes.get(new Integer(period))).doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* number of lost messages over the given period.
|
||||||
|
*
|
||||||
|
* @param period number of minutes to retrieve the average for
|
||||||
|
* @return number of lost messages in the period, or -1 if we dont track that period
|
||||||
|
*/
|
||||||
|
public double getLostMessages(int period) {
|
||||||
|
return ((Integer)_lostMessages.get(new Integer(period))).doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanup() {}
|
||||||
|
}
|
Reference in New Issue
Block a user