From 33782859b81ed8742153063377287d7dba47657e Mon Sep 17 00:00:00 2001 From: jrandom Date: Tue, 13 Apr 2004 03:24:04 +0000 Subject: [PATCH] the heartbeat engine and gui are good 'nuff for now (while i want to spend another few days on it, there are more pressing things and this will meet my needs for testnet). the engine works as before, and the gui now actually plots out and follows the chart over time. The gui does have a lot of things left to be done before it can be adopted by joe sixpack - * load/store the URLs being monitored so you don't have to reenter them on each startup * clear out the x axis on refetch (now it just keeps growing, which is good and bad) * adjustable refresh rate * implement snapshots (saving all the current state files to a dir, and allowing that dir to be loaded all at once) * beautification (override the colors, etc) the net.i2p.heartbeat.** code is all public domain, BUT net.i2p.heartbeat.gui.JFreeChart* classes depend on the LGPL'ed jfreechart code, which in turn depends on apache licensed code (log4j). for the time being, this code doesn't include the jfreechart code (and dependencies), but the ant task in apps/heartbeat/java 'fetchJfreechart' downloads it and places it under apps/heartbeat/java/lib, after which you can build the GUI by running the ant task 'buildGUI' (buildGUI / etc are NOT in the standard build process). once we figure out all the details to comply with the requirements of log4j's license we'll do so. but for now, the above works. --- apps/heartbeat/java/build.xml | 34 ++- .../src/net/i2p/heartbeat/ClientEngine.java | 1 + .../java/src/net/i2p/heartbeat/Heartbeat.java | 2 + .../src/net/i2p/heartbeat/I2PAdapter.java | 31 ++- .../heartbeat/gui/HeartbeatControlPane.java | 1 + .../i2p/heartbeat/gui/HeartbeatMonitor.java | 16 +- .../gui/HeartbeatMonitorCommandBar.java | 2 +- .../heartbeat/gui/HeartbeatMonitorGUI.java | 13 +- .../i2p/heartbeat/gui/HeartbeatPlotPane.java | 8 +- .../i2p/heartbeat/gui/JFreeChartAdapter.java | 234 ++++++++++++++++++ .../gui/JFreeChartHeartbeatPlotPane.java | 58 +++++ .../net/i2p/heartbeat/gui/PeerPlotConfig.java | 11 +- .../i2p/heartbeat/gui/PeerPlotConfigPane.java | 24 +- .../net/i2p/heartbeat/gui/StaticPeerData.java | 18 +- 14 files changed, 412 insertions(+), 41 deletions(-) create mode 100644 apps/heartbeat/java/src/net/i2p/heartbeat/gui/JFreeChartAdapter.java create mode 100644 apps/heartbeat/java/src/net/i2p/heartbeat/gui/JFreeChartHeartbeatPlotPane.java diff --git a/apps/heartbeat/java/build.xml b/apps/heartbeat/java/build.xml index d506b0edf..a266f4dfe 100644 --- a/apps/heartbeat/java/build.xml +++ b/apps/heartbeat/java/build.xml @@ -1,20 +1,37 @@ - - + + + + + + + + + + - + - + + + + + + + + + + @@ -25,12 +42,16 @@ - + + + + - + + @@ -48,6 +69,7 @@ + diff --git a/apps/heartbeat/java/src/net/i2p/heartbeat/ClientEngine.java b/apps/heartbeat/java/src/net/i2p/heartbeat/ClientEngine.java index 0f85eae8d..a0ef6232d 100644 --- a/apps/heartbeat/java/src/net/i2p/heartbeat/ClientEngine.java +++ b/apps/heartbeat/java/src/net/i2p/heartbeat/ClientEngine.java @@ -4,6 +4,7 @@ import net.i2p.data.Destination; import net.i2p.util.Clock; import net.i2p.util.I2PThread; import net.i2p.util.Log; +import net.i2p.util.Clock; /** * Responsible for actually conducting the tests, coordinating the storing of the diff --git a/apps/heartbeat/java/src/net/i2p/heartbeat/Heartbeat.java b/apps/heartbeat/java/src/net/i2p/heartbeat/Heartbeat.java index 70ad6313f..5a6fe476a 100644 --- a/apps/heartbeat/java/src/net/i2p/heartbeat/Heartbeat.java +++ b/apps/heartbeat/java/src/net/i2p/heartbeat/Heartbeat.java @@ -30,6 +30,8 @@ import net.i2p.util.Log; * # where our private destination keys are located - if this doesn't exist, * # a new one will be created and saved there (by default, heartbeat.keys) * privateDestinationFile=heartbeat_r2.keys + * # where do we want to export the plain base64 of our destination? + * publicDestinationFile=heartbeat_r2.txt * * ## peer tests configured below: * diff --git a/apps/heartbeat/java/src/net/i2p/heartbeat/I2PAdapter.java b/apps/heartbeat/java/src/net/i2p/heartbeat/I2PAdapter.java index 015a96840..46eb8c6a2 100644 --- a/apps/heartbeat/java/src/net/i2p/heartbeat/I2PAdapter.java +++ b/apps/heartbeat/java/src/net/i2p/heartbeat/I2PAdapter.java @@ -37,6 +37,8 @@ class I2PAdapter { private int _numHops; /** filename containing the heartbeat engine's private destination info */ private String _privateDestFile; + /** filename to store the heartbeat engine's public destination in base64*/ + private String _publicDestFile; /** our destination */ private Destination _localDest; /** who do we tell? */ @@ -53,6 +55,10 @@ class I2PAdapter { private static final String DEST_FILE_PROP = "privateDestinationFile"; /** by default, the private destination data is in "heartbeat.keys" */ private static final String DEST_FILE_DEFAULT = "heartbeat.keys"; + /** where will we export the public destination in base 64? */ + private static final String PUBLIC_DEST_FILE_PROP = "publicDestinationFile"; + /** where will we export the public destination in base 64? */ + private static final String PUBLIC_DEST_FILE_DEFAULT = "heartbeat.txt"; /** This config property defines where the I2P router is */ private static final String I2CP_HOST_PROP = "i2cpHost"; /** by default, the I2P host is "localhost" */ @@ -72,6 +78,7 @@ class I2PAdapter { */ public I2PAdapter() { _privateDestFile = null; + _publicDestFile = null; _i2cpHost = null; _i2cpPort = -1; _localDest = null; @@ -126,6 +133,7 @@ class I2PAdapter { */ void loadConfig(Properties props) { String privDestFile = props.getProperty(DEST_FILE_PROP, DEST_FILE_DEFAULT); + String pubDestFile = props.getProperty(PUBLIC_DEST_FILE_PROP, PUBLIC_DEST_FILE_DEFAULT); String host = props.getProperty(I2CP_HOST_PROP, I2CP_HOST_DEFAULT); String port = props.getProperty(I2CP_PORT_PROP, "" + I2CP_PORT_DEFAULT); String numHops = props.getProperty(NUMHOPS_PROP, "" + NUMHOPS_DEFAULT); @@ -151,6 +159,7 @@ class I2PAdapter { _numHops = hops; _privateDestFile = privDestFile; + _publicDestFile = pubDestFile; _i2cpHost = host; _i2cpPort = portNum; } @@ -165,6 +174,12 @@ class I2PAdapter { } else { props.setProperty(DEST_FILE_PROP, DEST_FILE_DEFAULT); } + + if (_publicDestFile != null) { + props.setProperty(PUBLIC_DEST_FILE_PROP, _publicDestFile); + } else { + props.setProperty(PUBLIC_DEST_FILE_PROP, PUBLIC_DEST_FILE_DEFAULT); + } if (_i2cpHost != null) { props.setProperty(I2CP_HOST_PROP, _i2cpHost); @@ -406,6 +421,18 @@ class I2PAdapter { if (_log.shouldLog(Log.INFO)) { _log.info("Existing destination loaded: [" + us.toBase64() + "]"); } + + FileOutputStream fos = null; + try { + fos = new FileOutputStream(_publicDestFile); + fos.write(us.toBase64().getBytes()); + fos.flush(); + } catch (IOException fioe) { + _log.error("Error writing out the plain destination to [" + _publicDestFile + "]", fioe); + } finally { + if (fos != null) try { fos.close(); } catch (IOException fioe) {} + } + } catch (IOException ioe) { if (fin != null) try { fin.close(); @@ -466,7 +493,9 @@ class I2PAdapter { */ private Properties getOptions() { Properties props = new Properties(); - props.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT); + // this should be BEST_EFFORT, but i'm too lazy to update the code to handle tracking + // sessionTags and sessionKeys, marking them as delivered on pong. + props.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED); props.setProperty(I2PClient.PROP_TCP_HOST, _i2cpHost); props.setProperty(I2PClient.PROP_TCP_PORT, _i2cpPort + ""); props.setProperty("tunnels.depthInbound", "" + _numHops); diff --git a/apps/heartbeat/java/src/net/i2p/heartbeat/gui/HeartbeatControlPane.java b/apps/heartbeat/java/src/net/i2p/heartbeat/gui/HeartbeatControlPane.java index 813f3a151..d2d69c32b 100644 --- a/apps/heartbeat/java/src/net/i2p/heartbeat/gui/HeartbeatControlPane.java +++ b/apps/heartbeat/java/src/net/i2p/heartbeat/gui/HeartbeatControlPane.java @@ -43,6 +43,7 @@ class HeartbeatControlPane extends JPanel { _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); + _gui.pack(); } /** diff --git a/apps/heartbeat/java/src/net/i2p/heartbeat/gui/HeartbeatMonitor.java b/apps/heartbeat/java/src/net/i2p/heartbeat/gui/HeartbeatMonitor.java index a4ac94c4c..57a7999ab 100644 --- a/apps/heartbeat/java/src/net/i2p/heartbeat/gui/HeartbeatMonitor.java +++ b/apps/heartbeat/java/src/net/i2p/heartbeat/gui/HeartbeatMonitor.java @@ -6,14 +6,16 @@ import net.i2p.util.Log; /** * The HeartbeatMonitor, complete with main()! Act now, and it's only 5 easy * payments of $19.95 (plus shipping and handling)! You heard me, only _5_ - * easy payments of $19.95 (plus shipping and handling)! + * easy payments of $19.95 (plus shipping and handling)!

* * (fine print: something about some states in the US requiring the addition - * of sales tax... or something) + * of sales tax... or something)

* - * (finer print: Satan owns you. Deal with it.) + * (finer print: Satan owns you. Deal with it.)

+ * + * (even finer print: usage: HeartbeatMonitor [configFilename]) */ -public class HeartbeatMonitor implements PeerPlotStateFetcher.FetchStateReceptor { +public class HeartbeatMonitor implements PeerPlotStateFetcher.FetchStateReceptor, PeerPlotConfig.UpdateListener { private final static Log _log = new Log(HeartbeatMonitor.class); private HeartbeatMonitorState _state; private HeartbeatMonitorGUI _gui; @@ -73,6 +75,7 @@ public class HeartbeatMonitor implements PeerPlotStateFetcher.FetchStateReceptor */ public void load(String location) { PeerPlotConfig cfg = new PeerPlotConfig(location); + cfg.addListener(this); PeerPlotState state = new PeerPlotState(cfg); PeerPlotStateFetcher.fetchPeerPlotState(this, state); } @@ -99,4 +102,9 @@ public class HeartbeatMonitor implements PeerPlotStateFetcher.FetchStateReceptor else new HeartbeatMonitor().runMonitor(); } + + public void configUpdated(PeerPlotConfig config) { + _log.debug("Config updated, revamping the gui"); + _gui.stateUpdated(); + } } \ No newline at end of file diff --git a/apps/heartbeat/java/src/net/i2p/heartbeat/gui/HeartbeatMonitorCommandBar.java b/apps/heartbeat/java/src/net/i2p/heartbeat/gui/HeartbeatMonitorCommandBar.java index 0ca18f174..c95aa68a3 100644 --- a/apps/heartbeat/java/src/net/i2p/heartbeat/gui/HeartbeatMonitorCommandBar.java +++ b/apps/heartbeat/java/src/net/i2p/heartbeat/gui/HeartbeatMonitorCommandBar.java @@ -46,7 +46,7 @@ class HeartbeatMonitorCommandBar extends JPanel { _refreshRate.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent evt) { refreshChanged(evt); } }); _refreshRate.setEnabled(false); _refreshRate.setBackground(_gui.getBackground()); - add(_refreshRate); + //add(_refreshRate); JLabel loadLabel = new JLabel("Load from: "); loadLabel.setBackground(_gui.getBackground()); add(loadLabel); diff --git a/apps/heartbeat/java/src/net/i2p/heartbeat/gui/HeartbeatMonitorGUI.java b/apps/heartbeat/java/src/net/i2p/heartbeat/gui/HeartbeatMonitorGUI.java index 80025e1df..08b3d9e40 100644 --- a/apps/heartbeat/java/src/net/i2p/heartbeat/gui/HeartbeatMonitorGUI.java +++ b/apps/heartbeat/java/src/net/i2p/heartbeat/gui/HeartbeatMonitorGUI.java @@ -10,6 +10,7 @@ import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JScrollPane; +import javax.swing.JSplitPane; class HeartbeatMonitorGUI extends JFrame { private HeartbeatMonitor _monitor; @@ -27,7 +28,7 @@ class HeartbeatMonitorGUI extends JFrame { _monitor = monitor; initializeComponents(); pack(); - setResizable(false); + //setResizable(false); setVisible(true); } @@ -39,16 +40,18 @@ class HeartbeatMonitorGUI extends JFrame { setBackground(_background); - _plotPane = new HeartbeatPlotPane(this); + _plotPane = new JFreeChartHeartbeatPlotPane(this); // new HeartbeatPlotPane(this); _plotPane.setBackground(_background); - JScrollPane pane = new JScrollPane(_plotPane); - pane.setBackground(_background); - getContentPane().add(pane, BorderLayout.CENTER); + //JScrollPane pane = new JScrollPane(_plotPane); + //pane.setBackground(_background); + getContentPane().add(new JScrollPane(_plotPane), BorderLayout.CENTER); _controlPane = new HeartbeatControlPane(this); _controlPane.setBackground(_background); getContentPane().add(_controlPane, BorderLayout.SOUTH); + //JSplitPane pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, new JScrollPane(_plotPane), new JScrollPane(_controlPane)); + //getContentPane().add(pane, BorderLayout.CENTER); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); initializeMenus(); } diff --git a/apps/heartbeat/java/src/net/i2p/heartbeat/gui/HeartbeatPlotPane.java b/apps/heartbeat/java/src/net/i2p/heartbeat/gui/HeartbeatPlotPane.java index 2fb1f64e8..699f7c8a4 100644 --- a/apps/heartbeat/java/src/net/i2p/heartbeat/gui/HeartbeatPlotPane.java +++ b/apps/heartbeat/java/src/net/i2p/heartbeat/gui/HeartbeatPlotPane.java @@ -15,7 +15,7 @@ import net.i2p.util.Log; */ class HeartbeatPlotPane extends JPanel { private final static Log _log = new Log(HeartbeatPlotPane.class); - private HeartbeatMonitorGUI _gui; + protected HeartbeatMonitorGUI _gui; private JTextArea _text; /** @@ -48,9 +48,9 @@ class HeartbeatPlotPane extends JPanel { _text.setText(buf.toString()); } - private void initializeComponents() { - setBackground(new Color(255, 255, 255)); - // Dimension size = new Dimension(800, 600); + protected void initializeComponents() { + setBackground(_gui.getBackground()); + //Dimension size = new Dimension(800, 600); _text = new JTextArea("",30,80); // 16, 60); _text.setAutoscrolls(true); _text.setEditable(false); diff --git a/apps/heartbeat/java/src/net/i2p/heartbeat/gui/JFreeChartAdapter.java b/apps/heartbeat/java/src/net/i2p/heartbeat/gui/JFreeChartAdapter.java new file mode 100644 index 000000000..cb56efd2e --- /dev/null +++ b/apps/heartbeat/java/src/net/i2p/heartbeat/gui/JFreeChartAdapter.java @@ -0,0 +1,234 @@ +package net.i2p.heartbeat.gui; + +import net.i2p.heartbeat.PeerData; +import net.i2p.util.Log; + +import org.jfree.data.XYSeries; +import org.jfree.data.XYSeriesCollection; +import org.jfree.data.MovingAverage; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.plot.Plot; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.axis.DateAxis; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.renderer.XYLineAndShapeRenderer; +import org.jfree.chart.renderer.XYItemRenderer; +import org.jfree.chart.renderer.XYDotRenderer; +import java.util.List; + +import javax.swing.JPanel; +import java.awt.Font; +import java.awt.Color; + +class JFreeChartAdapter { + private final static Log _log = new Log(JFreeChartAdapter.class); + private final static Color WHITE = new Color(255, 255, 255); + + ChartPanel createPanel(HeartbeatMonitorState state) { + ChartPanel panel = new ChartPanel(createChart(state)); + panel.setDisplayToolTips(true); + panel.setEnforceFileExtensions(true); + panel.setHorizontalZoom(true); + panel.setVerticalZoom(true); + panel.setMouseZoomable(true, true); + panel.getChart().setBackgroundPaint(WHITE); + return panel; + } + + JFreeChart createChart(HeartbeatMonitorState state) { + Plot plot = createPlot(state); + JFreeChart chart = new JFreeChart("I2P Heartbeat performance", Font.getFont("arial"), plot, true); + return chart; + } + + void updateChart(ChartPanel panel, HeartbeatMonitorState state) { + XYPlot plot = (XYPlot)panel.getChart().getPlot(); + plot.setDataset(getCollection(state)); + updateLines(plot, state); + } + + private long getFirst(HeartbeatMonitorState state) { + long first = -1; + for (int i = 0; i < state.getTestCount(); i++) { + List dataPoints = state.getTest(i).getCurrentData().getDataPoints(); + if ( (dataPoints != null) && (dataPoints.size() > 0) ) { + PeerData.EventDataPoint data = (PeerData.EventDataPoint)dataPoints.get(0); + if ( (first < 0) || (first > data.getPingSent()) ) + first = data.getPingSent(); + } + } + return first; + } + + Plot createPlot(HeartbeatMonitorState state) { + XYItemRenderer renderer = new XYLineAndShapeRenderer(); // new XYDotRenderer(); // + XYPlot plot = new XYPlot(getCollection(state), new DateAxis(), new NumberAxis("ms"), renderer); + updateLines(plot, state); + return plot; + } + + private void updateLines(XYPlot plot, HeartbeatMonitorState state) { + if (true) return; + if (state == null) return; + for (int i = 0; i < state.getTestCount(); i++) { + PeerPlotConfig config = state.getTest(i).getPlotConfig(); + PeerPlotConfig.PlotSeriesConfig curConfig = config.getCurrentSeriesConfig(); + XYSeriesCollection col = ((XYSeriesCollection)plot.getDataset()); + for (int j = 0; j < col.getSeriesCount(); j++) { + //XYItemRenderer renderer = plot.getRendererForDataset(col.getSeries(j)); + XYItemRenderer renderer = plot.getRendererForDataset(col); + if (col.getSeriesName(j).startsWith(config.getTitle() + " send")) { + if (curConfig.getPlotSendTime()) { + //renderer.setPaint(curConfig.getPlotLineColor()); + } + } + if (col.getSeriesName(j).startsWith(config.getTitle() + " receive")) { + if (curConfig.getPlotReceiveTime()) { + //renderer.setPaint(curConfig.getPlotLineColor()); + } + } + if (col.getSeriesName(j).startsWith(config.getTitle() + " lost")) { + if (curConfig.getPlotLostMessages()) { + //renderer.setPaint(curConfig.getPlotLineColor()); + } + } + } + } + } + + XYSeriesCollection getCollection(HeartbeatMonitorState state) { + XYSeriesCollection col = new XYSeriesCollection(); + if (state != null) { + for (int i = 0; i < state.getTestCount(); i++) { + addTest(col, state.getTest(i)); + } + } else { + XYSeries series = new XYSeries("latency", false, false); + series.add(System.currentTimeMillis(), 0); + col.addSeries(series); + } + return col; + } + + void addTest(XYSeriesCollection col, PeerPlotState state) { + PeerPlotConfig config = state.getPlotConfig(); + PeerPlotConfig.PlotSeriesConfig curConfig = config.getCurrentSeriesConfig(); + addLines(col, curConfig, config.getTitle(), state.getCurrentData().getDataPoints()); + addAverageLines(col, config, config.getTitle(), state.getCurrentData()); + } + + /** + * @param config preferences for how to display this test + * @param lineName minimal name of the test (e.g. "jxHa.32KB.60s") + * @param data List of PeerData.EventDataPoint describing all of the events in the test + */ + void addLines(XYSeriesCollection col, PeerPlotConfig.PlotSeriesConfig config, String lineName, List data) { + if (config.getPlotSendTime()) { + XYSeries sendSeries = getSendSeries(data); + sendSeries.setName(lineName + " send"); + sendSeries.setDescription("milliseconds for the ping to reach the peer"); + col.addSeries(sendSeries); + } + if (config.getPlotReceiveTime()) { + XYSeries recvSeries = getReceiveSeries(data); + recvSeries.setName(lineName + " receive"); + recvSeries.setDescription("milliseconds for the peer's pong to reach the sender"); + col.addSeries(recvSeries); + } + if (config.getPlotLostMessages()) { + XYSeries lostSeries = getLostSeries(data); + lostSeries.setName(lineName + " lost"); + lostSeries.setDescription("number of ping/pong messages lost"); + col.addSeries(lostSeries); + } + } + + /** + * Add a data series for each average that we're configured to render + * + * @param config preferences for how to display this test + * @param lineName minimal name of the test (e.g. "jxHa.32KB.60s") + * @param data List of PeerData.EventDataPoint describing all of the events in the test + */ + void addAverageLines(XYSeriesCollection col, PeerPlotConfig config, String lineName, StaticPeerData data) { + if (data.getDataPointCount() <= 0) return; + PeerData.EventDataPoint start = (PeerData.EventDataPoint)data.getDataPoints().get(0); + PeerData.EventDataPoint finish = (PeerData.EventDataPoint)data.getDataPoints().get(data.getDataPointCount()-1); + + List configs = config.getAverageSeriesConfigs(); + + for (int i = 0; i < configs.size(); i++) { + PeerPlotConfig.PlotSeriesConfig cfg = (PeerPlotConfig.PlotSeriesConfig)configs.get(i); + int minutes = (int)cfg.getPeriod()/(60*1000); + if (cfg.getPlotSendTime()) { + double time = data.getAverageSendTime(minutes); + if (time > 0) { + XYSeries series = new XYSeries(lineName + " send " + minutes + "m avg [" + time + "]", false, false); + series.add(start.getPingSent(), time); + series.add(finish.getPingSent(), time); + series.setDescription("send time, averaged over the last " + minutes + " minutes"); + col.addSeries(series); + } + } + if (cfg.getPlotReceiveTime()) { + double time = data.getAverageReceiveTime(minutes); + if (time > 0) { + XYSeries series = new XYSeries(lineName + " receive " + minutes + "m avg[" + time + "]", false, false); + series.add(start.getPingSent(), time); + series.add(finish.getPingSent(), time); + series.setDescription("receive time, averaged over the last " + minutes + " minutes"); + col.addSeries(series); + } + } + if (cfg.getPlotLostMessages()) { + double num = data.getLostMessages(minutes); + if (num > 0) { + XYSeries series = new XYSeries(lineName + " lost messages (" + num + " in " + minutes + "m)", false, false); + series.add(start.getPingSent(), num); + series.add(finish.getPingSent(), num); + series.setDescription("number of messages lost in the last " + minutes + " minutes"); + col.addSeries(series); + } + } + } + } + + XYSeries getSendSeries(List data) { + XYSeries series = new XYSeries("sent", false, false); + for (int i = 0; i < data.size(); i++) { + PeerData.EventDataPoint point = (PeerData.EventDataPoint)data.get(i); + if (point.getWasPonged()) { + series.add(point.getPingSent(), point.getPongSent()-point.getPingSent()); + } else { + // series.add(data.getPingSent(), 0); + } + } + return series; + } + + XYSeries getReceiveSeries(List data) { + XYSeries series = new XYSeries("receive", false, false); + for (int i = 0; i < data.size(); i++) { + PeerData.EventDataPoint point = (PeerData.EventDataPoint)data.get(i); + if (point.getWasPonged()) { + series.add(point.getPingSent(), point.getPongReceived()-point.getPongSent()); + } else { + // series.add(data.getPingSent(), 0); + } + } + return series; + } + XYSeries getLostSeries(List data) { + XYSeries series = new XYSeries("lost", false, false); + for (int i = 0; i < data.size(); i++) { + PeerData.EventDataPoint point = (PeerData.EventDataPoint)data.get(i); + if (point.getWasPonged()) { + //series.add(point.getPingSent(), 0); + } else { + series.add(point.getPingSent(), 1); + } + } + return series; + } +} \ No newline at end of file diff --git a/apps/heartbeat/java/src/net/i2p/heartbeat/gui/JFreeChartHeartbeatPlotPane.java b/apps/heartbeat/java/src/net/i2p/heartbeat/gui/JFreeChartHeartbeatPlotPane.java new file mode 100644 index 000000000..c12fd88a0 --- /dev/null +++ b/apps/heartbeat/java/src/net/i2p/heartbeat/gui/JFreeChartHeartbeatPlotPane.java @@ -0,0 +1,58 @@ +package net.i2p.heartbeat.gui; + +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.JScrollPane; +import javax.swing.JLabel; +import java.awt.BorderLayout; +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; + +import org.jfree.chart.ChartPanel; + +/** + * Render the graph and legend + * + */ +class JFreeChartHeartbeatPlotPane extends HeartbeatPlotPane { + private final static Log _log = new Log(JFreeChartHeartbeatPlotPane.class); + private ChartPanel _panel; + private JFreeChartAdapter _adapter; + + public JFreeChartHeartbeatPlotPane(HeartbeatMonitorGUI gui) { + super(gui); + } + + public void stateUpdated() { + if (_panel == null) { + remove(0); // remove the dummy + + _adapter = new JFreeChartAdapter(); + _panel = _adapter.createPanel(_gui.getMonitor().getState()); + _panel.setBackground(_gui.getBackground()); + add(new JScrollPane(_panel), BorderLayout.CENTER); + _gui.pack(); + } else { + _adapter.updateChart(_panel, _gui.getMonitor().getState()); + //_gui.pack(); + } + } + + protected void initializeComponents() { + // noop + setLayout(new BorderLayout()); + add(new JLabel(), BorderLayout.CENTER); + //dummy.setBackground(_gui.getBackground()); + //dummy.setPreferredSize(new Dimension(800,600)); + //add(dummy); + + //add(_panel); + + } +} \ No newline at end of file diff --git a/apps/heartbeat/java/src/net/i2p/heartbeat/gui/PeerPlotConfig.java b/apps/heartbeat/java/src/net/i2p/heartbeat/gui/PeerPlotConfig.java index dcb4cf4f9..5c8fe5581 100644 --- a/apps/heartbeat/java/src/net/i2p/heartbeat/gui/PeerPlotConfig.java +++ b/apps/heartbeat/java/src/net/i2p/heartbeat/gui/PeerPlotConfig.java @@ -11,12 +11,14 @@ import java.util.Set; import java.util.TreeMap; import net.i2p.data.Destination; +import net.i2p.util.Log; import net.i2p.heartbeat.ClientConfig; /** * Configure how we want to render a particular clientConfig in the GUI */ class PeerPlotConfig { + private final static Log _log = new Log(PeerPlotConfig.class); /** 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? */ @@ -90,7 +92,9 @@ class PeerPlotConfig { 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)); + Long period = new Long(minutes*60*1000); + if (!ordered.containsKey(period)) + ordered.put(period, new PlotSeriesConfig(minutes*60*1000)); List cfgs = Collections.synchronizedList(new ArrayList(ordered.size())); for (Iterator iter = ordered.values().iterator(); iter.hasNext(); ) @@ -244,6 +248,11 @@ class PeerPlotConfig { */ public PlotSeriesConfig(long period) { this(period, false, false, false, null); + if (period <= 0) { + _plotSendTime = true; + _plotReceiveTime = true; + _plotLostMessages = true; + } } diff --git a/apps/heartbeat/java/src/net/i2p/heartbeat/gui/PeerPlotConfigPane.java b/apps/heartbeat/java/src/net/i2p/heartbeat/gui/PeerPlotConfigPane.java index 8eb815740..226eb2df8 100644 --- a/apps/heartbeat/java/src/net/i2p/heartbeat/gui/PeerPlotConfigPane.java +++ b/apps/heartbeat/java/src/net/i2p/heartbeat/gui/PeerPlotConfigPane.java @@ -195,10 +195,10 @@ class PeerPlotConfigPane extends JPanel implements PeerPlotConfig.UpdateListener _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() + "]"); + //_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()); @@ -289,13 +289,15 @@ class PeerPlotConfigPane extends JPanel implements PeerPlotConfig.UpdateListener if (g < 0) g = -g; int b = _rnd.nextInt(255); if (b < 0) b = -b; - _color.setBackground(new Color(r, g, b)); + //_color.setBackground(new Color(r, g, b)); + _color.setBackground(_background); _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)); + _color.setEnabled(false); } } @@ -318,22 +320,12 @@ class PeerPlotConfigPane extends JPanel implements PeerPlotConfig.UpdateListener */ 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() + "]"); + _line._lost.isSelected() + "/" + cfg.getPlotLostMessages() + "]: config = " + cfg); boolean force = _line._all.isSelected(); cfg.setPlotSendTime(_line._send.isSelected() || force); diff --git a/apps/heartbeat/java/src/net/i2p/heartbeat/gui/StaticPeerData.java b/apps/heartbeat/java/src/net/i2p/heartbeat/gui/StaticPeerData.java index 9b1289578..e0b36b094 100644 --- a/apps/heartbeat/java/src/net/i2p/heartbeat/gui/StaticPeerData.java +++ b/apps/heartbeat/java/src/net/i2p/heartbeat/gui/StaticPeerData.java @@ -91,7 +91,11 @@ class StaticPeerData extends PeerData { * @return milliseconds average, or -1 if we dont track that period */ public double getAverageSendTime(int period) { - return ((Integer)_averageSendTimes.get(new Integer(period))).doubleValue(); + Integer i = (Integer)_averageSendTimes.get(new Integer(period)); + if (i == null) + return -1; + else + return i.doubleValue(); } @@ -102,7 +106,11 @@ class StaticPeerData extends PeerData { * @return milliseconds average, or -1 if we dont track that period */ public double getAverageReceiveTime(int period) { - return ((Integer)_averageReceiveTimes.get(new Integer(period))).doubleValue(); + Integer i = (Integer)_averageReceiveTimes.get(new Integer(period)); + if (i == null) + return -1; + else + return i.doubleValue(); } /** @@ -112,7 +120,11 @@ class StaticPeerData extends PeerData { * @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(); + Integer i = (Integer)_lostMessages.get(new Integer(period)); + if (i == null) + return -1; + else + return i.doubleValue(); } /* (non-Javadoc)