Skip to content

Commit

Permalink
feat(gui): lazy loading TrafficCounterLineChart
Browse files Browse the repository at this point in the history
  • Loading branch information
Zmax0 committed Aug 15, 2024
1 parent a6a9b8e commit 1e3480b
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 121 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import com.urbanspork.client.gui.Resource;
import com.urbanspork.client.gui.console.widget.*;
import com.urbanspork.client.gui.i18n.I18N;
import com.urbanspork.client.gui.traffic.TrafficCounterLineChartBuilder;
import com.urbanspork.client.gui.traffic.TrafficCounterLineChartBackstage;
import com.urbanspork.client.gui.tray.Tray;
import com.urbanspork.common.codec.CipherKind;
import com.urbanspork.common.config.ClientConfig;
Expand All @@ -24,6 +24,7 @@
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableObjectValue;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
Expand All @@ -46,6 +47,7 @@
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class Console extends Application {
private static final Logger logger = LoggerFactory.getLogger(Console.class);
Expand All @@ -55,9 +57,11 @@ public class Console extends Application {
Tray tray;
Proxy proxy;
final ObjectProperty<TrafficCounter> trafficCounter = new SimpleObjectProperty<>();
final TrafficCounterLineChartBackstage trafficCounterLineChartBackstage = new TrafficCounterLineChartBackstage(trafficCounter);

private Stage primaryStage;
private JFXTabPane root;
private Tab tab2;
private TextArea logTextArea;
private JFXListView<ServerConfig> serverConfigJFXListView;
private Button newServerConfigButton;
Expand Down Expand Up @@ -97,6 +101,7 @@ public void start(Stage primaryStage) {
primaryStage.setTitle(I18N.getString(I18N.PROGRAM_TITLE));
primaryStage.setOnCloseRequest(event -> primaryStage.hide());
primaryStage.hide();
initTrafficCounterLineChart();
launchProxy();
}

Expand Down Expand Up @@ -304,7 +309,7 @@ private JFXTabPane initTabPane() {
// tab1
Tab tab1 = newSingleNodeTab(logTextArea, I18N.getString(I18N.CONSOLE_TAB1_TEXT));
// tab2
Tab tab2 = initTrafficTab();
tab2 = initTrafficTab();
// ====================
// main tab pane
// ====================
Expand Down Expand Up @@ -349,7 +354,6 @@ private Tab newSingleNodeTab(Node node, String tabTitle) {
private Tab initTrafficTab() {
StackPane stackPane = new StackPane();
stackPane.setAlignment(Pos.TOP_CENTER);
stackPane.getChildren().add(new TrafficCounterLineChartBuilder(trafficCounter).build());
Tab tab = new Tab(I18N.getString(I18N.CONSOLE_TAB2_TEXT));
tab.setContent(stackPane);
tab.setClosable(false);
Expand Down Expand Up @@ -546,6 +550,20 @@ private void importServerConfig() {
dialog.showAndWait().map(URI::create).flatMap(ShareableServerConfig::fromUri).ifPresent(serverConfigObservableList::add);
}

private void initTrafficCounterLineChart() {
ObservableList<Node> children = ((StackPane) tab2.getContent()).getChildren();
primaryStage.setOnHidden(event -> {
children.clear();
trafficCounterLineChartBackstage.stop();
});
primaryStage.setOnShown(event -> {
if (children.isEmpty()) {
children.add(trafficCounterLineChartBackstage.newLineChart());
}
Optional.of(trafficCounter).map(ObservableObjectValue::get).ifPresent(trafficCounterLineChartBackstage::refresh);
});
}

private boolean validate() {
boolean result = currentConfigHostTextField.validate();
result |= currentConfigPortTextField.validate();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package com.urbanspork.client.gui.traffic;

import com.urbanspork.client.gui.spine.CatmullRom;
import io.netty.handler.traffic.TrafficCounter;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.property.ObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Point2D;
import javafx.scene.chart.Axis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathElement;
import javafx.util.Duration;

import java.util.ArrayList;

public class TrafficCounterLineChartBackstage {
private static final int WINDOW = 60;
private final Timeline timeline = new Timeline();
private final ObservableList<XYChart.Data<Number, Number>> write = FXCollections.observableArrayList(new ArrayList<>());
private final ObservableList<XYChart.Data<Number, Number>> read = FXCollections.observableArrayList(new ArrayList<>());
private XYChart.Series<Number, Number> writeSeries;
private XYChart.Series<Number, Number> readSeries;

public TrafficCounterLineChartBackstage(ObjectProperty<TrafficCounter> trafficCounter) {
timeline.setCycleCount(Timeline.INDEFINITE);
trafficCounter.addListener((observable, oldValue, newValue) -> {
if (newValue != null && timeline.getStatus() == Animation.Status.RUNNING) {
refresh(newValue);
}
});
}

public LineChart<Number, Number> newLineChart() {
NumberAxis xAxis = new NumberAxis(0, WINDOW, WINDOW);
NumberAxis yAxis = new NumberAxis();
yAxis.setTickLabelsVisible(false);
yAxis.setTickMarkVisible(false);
yAxis.lookup(".axis-minor-tick-mark").setVisible(false);
LineChart<Number, Number> lineChart = new TrafficCounterLineChart(xAxis, yAxis);
lineChart.setAnimated(false);
lineChart.setCreateSymbols(false);
ObservableList<XYChart.Series<Number, Number>> list = lineChart.getData();
write.clear();
read.clear();
for (int i = 0; i <= WINDOW; i++) {
write.add(new XYChart.Data<>(i, 0));
read.add(new XYChart.Data<>(i, 0));
}
writeSeries = new XYChart.Series<>(write);
readSeries = new XYChart.Series<>(read);
writeSeries.setName("0 KB/s");
readSeries.setName("0 KB/s");
list.add(writeSeries);
list.add(readSeries);
return lineChart;
}

public void refresh(TrafficCounter counter) {
ObservableList<KeyFrame> keyFrames = timeline.getKeyFrames();
keyFrames.clear();
keyFrames.add(new KeyFrame(Duration.millis(counter.checkInterval()), event -> {
long writeBytes = counter.lastWrittenBytes() / 1024;
long readBytes = counter.lastReadBytes() / 1024;
writeSeries.setName(writeBytes + " KB/s");
readSeries.setName(readBytes + " KB/s");
scroll(write, writeBytes);
scroll(read, readBytes);
}));
timeline.playFromStart();
}

public void stop() {
timeline.stop();
}

private void scroll(ObservableList<XYChart.Data<Number, Number>> dataList, Number y) {
for (int i = 0; i < dataList.size() - 1; i++) {
dataList.get(i).setYValue(dataList.get(i + 1).getYValue());
}
dataList.getLast().setYValue(y);
}

private static class TrafficCounterLineChart extends LineChart<Number, Number> {
public TrafficCounterLineChart(Axis<Number> xAxis, Axis<Number> yAxis) {
super(xAxis, yAxis);
}

@Override
protected void layoutPlotChildren() {
super.layoutPlotChildren();
ObservableList<Series<Number, Number>> data = getData();
data.stream().map(Series::getNode).<Path>mapMulti((node, consumer) -> {
if (node instanceof Path path) {
consumer.accept(path);
}
}).forEach(this::curve);
}

private void curve(Path path) {
ObservableList<PathElement> elements = path.getElements();
if (elements.size() > 3) {
curve(elements);
}
}

private void curve(ObservableList<PathElement> elements) {
int size = elements.size();
Point2D[] points = new Point2D[size - 1];
for (int i = 0; i < points.length; i++) {
PathElement e = elements.get(i + 1);
if (e instanceof LineTo lineTo) {
points[i] = new Point2D(lineTo.getX(), lineTo.getY());
}
}
Point2D[] interpolate = new CatmullRom(0.5).interpolate(points, 32);
// update
for (int i = 1; i < size; i++) {
PathElement e = elements.get(i);
if (e instanceof LineTo lineTo) {
lineTo.setX(interpolate[i - 1].getX());
lineTo.setY(interpolate[i - 1].getY());
}
}
// add
for (int i = 0; i < interpolate.length - size; i++) {
elements.add(new LineTo(interpolate[i + size].getX(), interpolate[i + size].getY()));
}
}
}
}

This file was deleted.

0 comments on commit 1e3480b

Please sign in to comment.