Skip to content

Commit cdd5e7a

Browse files
authored
Puncture the bloat balloon (#807)
Removes various sources of high CPU usage: - The live network table preview has been removed due to the high frequency of node redraws. Spot checking data will now need to be done by dragging a data widget into a tab. Generic table data should still be displayable with the generic tree table widget. The NT source preview now displays the data type of a topic instead of its current value. - The subscribe-to-all-NT-data behavior has been changed to only subscribe to topic information. This should cut down on bandwidth and some CPU usage - The widget gallery has been removed with no replacement. Drawing the large number of dummy widgets took a surprising CPU load and occasionally caused problems at startup when third party widgets from plugins would crash And some bug fixes: - Fixes widgets getting permanently disabled after network disconnects - This was caused by the placeholder destroyed sources not always being removed when restored, causing the widget to think it still had a disconnected data source
1 parent 45eb356 commit cdd5e7a

File tree

23 files changed

+246
-369
lines changed

23 files changed

+246
-369
lines changed

api/src/main/java/edu/wpi/first/shuffleboard/api/components/SourceTreeTable.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,23 +44,23 @@ public class SourceTreeTable<S extends SourceEntry, V> extends TreeTableView<S>
4444
private final ObjectProperty<SourceType> sourceType = new SimpleObjectProperty<>(this, "sourceType", null);
4545

4646
private final TreeTableColumn<S, String> keyColumn = new TreeTableColumn<>("Name");
47-
private final TreeTableColumn<S, V> valueColumn = new TreeTableColumn<>("Value");
47+
private final TreeTableColumn<S, V> infoColumn = new TreeTableColumn<>("Info");
4848

4949
/**
5050
* Creates a new source tree table. It comes pre-populated with a key and a value column.
5151
*/
5252
public SourceTreeTable() {
5353
keyColumn.prefWidthProperty().bind(widthProperty().divide(2).subtract(2));
54-
valueColumn.prefWidthProperty().bind(widthProperty().divide(2).subtract(2));
54+
infoColumn.prefWidthProperty().bind(widthProperty().divide(2).subtract(2));
5555

5656
keyColumn.setCellValueFactory(
5757
f -> new ReadOnlyStringWrapper(getEntryForCellData(f).getViewName()));
58-
valueColumn.setCellValueFactory(
59-
f -> new ReadOnlyObjectWrapper(getEntryForCellData(f).getValueView()));
58+
infoColumn.setCellValueFactory(
59+
f -> new ReadOnlyObjectWrapper(getEntryForCellData(f).getInfo()));
6060
Label placeholder = new Label("No data available");
6161
setPlaceholder(placeholder);
6262

63-
getColumns().addAll(keyColumn, valueColumn);
63+
getColumns().addAll(keyColumn, infoColumn);
6464
}
6565

6666
/**

api/src/main/java/edu/wpi/first/shuffleboard/api/dnd/DataFormats.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,6 @@ public final class DataFormats {
3333
*/
3434
public static final DataFormat source = new DataFormat(APP_PREFIX + "/data-source");
3535

36-
/**
37-
* The data format for widget type names (string).
38-
*/
39-
public static final DataFormat widgetType = new DataFormat(APP_PREFIX + "/widget-type");
40-
4136
/**
4237
* The data format for components that do not exist inside a tile.
4338
*/

api/src/main/java/edu/wpi/first/shuffleboard/api/sources/SourceEntry.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ default String getViewName() {
3030
Object getValue();
3131

3232
/**
33-
* Gets an object used to display the value of the source this entry represents. Implementers are encouraged to
33+
* Gets an object used to display information about the source. Implementers are encouraged to
3434
* sharpen the return type
3535
*/
36-
Object getValueView();
36+
Object getInfo();
3737

3838
}

api/src/main/java/edu/wpi/first/shuffleboard/api/sources/SourceTypes.java

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@
33
import edu.wpi.first.shuffleboard.api.data.DataType;
44
import edu.wpi.first.shuffleboard.api.data.DataTypes;
55
import edu.wpi.first.shuffleboard.api.sources.recording.TimestampedData;
6-
import edu.wpi.first.shuffleboard.api.util.PropertyUtils;
76
import edu.wpi.first.shuffleboard.api.util.Registry;
87

9-
import org.fxmisc.easybind.EasyBind;
8+
import javafx.collections.ListChangeListener;
109

1110
import java.util.HashMap;
1211
import java.util.Map;
1312
import java.util.Objects;
14-
import java.util.Optional;
1513

1614
import javafx.beans.InvalidationListener;
1715
import javafx.collections.FXCollections;
@@ -49,11 +47,25 @@ public SourceTypes() {
4947
register(Static);
5048

5149
typeNames.addListener((InvalidationListener) __ -> {
52-
Optional<ObservableList<String>> names = typeNames.stream()
50+
typeNames
51+
.stream()
5352
.map(this::forName)
5453
.map(SourceType::getAvailableSourceUris)
55-
.reduce(PropertyUtils::combineLists);
56-
names.ifPresent(l -> EasyBind.listBind(allUris, l));
54+
.forEach(observableUriList -> {
55+
observableUriList.addListener((ListChangeListener<? super String>) c -> {
56+
while (c.next()) {
57+
if (c.wasAdded()) {
58+
for (String uri : c.getAddedSubList()) {
59+
if (!allUris.contains(uri)) {
60+
allUris.add(uri);
61+
}
62+
}
63+
} else if (c.wasRemoved()) {
64+
allUris.removeAll(c.getRemoved());
65+
}
66+
}
67+
});
68+
});
5769
});
5870
}
5971

api/src/main/java/edu/wpi/first/shuffleboard/api/sources/recording/Recorder.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,8 @@
22

33
import edu.wpi.first.shuffleboard.api.DashboardMode;
44
import edu.wpi.first.shuffleboard.api.data.DataType;
5-
import edu.wpi.first.shuffleboard.api.data.DataTypes;
65
import edu.wpi.first.shuffleboard.api.properties.AtomicBooleanProperty;
76
import edu.wpi.first.shuffleboard.api.sources.DataSource;
8-
import edu.wpi.first.shuffleboard.api.sources.SourceType;
9-
import edu.wpi.first.shuffleboard.api.sources.SourceTypes;
107
import edu.wpi.first.shuffleboard.api.sources.recording.serialization.Serializer;
118
import edu.wpi.first.shuffleboard.api.sources.recording.serialization.Serializers;
129
import edu.wpi.first.shuffleboard.api.util.ShutdownHooks;
@@ -135,14 +132,6 @@ public void start() {
135132
startTime = Instant.now();
136133
firstSave = true;
137134
recording = new Recording();
138-
// Record initial conditions
139-
SourceTypes.getDefault().getItems().stream()
140-
.map(SourceType::getAvailableSources)
141-
.forEach(sources -> sources.forEach((id, value) -> {
142-
DataTypes.getDefault().forJavaType(value.getClass())
143-
.map(t -> new TimestampedData(id, t, value, 0L))
144-
.ifPresent(recording::append);
145-
}));
146135
}
147136
setRunning(true);
148137
}

api/src/main/java/edu/wpi/first/shuffleboard/api/widget/SingleSourceWidget.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
import edu.wpi.first.shuffleboard.api.data.IncompatibleSourceException;
44
import edu.wpi.first.shuffleboard.api.sources.DataSource;
5-
65
import javafx.beans.property.ObjectProperty;
76
import javafx.beans.property.Property;
87
import javafx.beans.property.SimpleObjectProperty;
8+
import javafx.collections.ListChangeListener;
99

1010
/**
1111
* A partial implementation of {@code Widget} that only has a single source.
@@ -14,6 +14,36 @@ public abstract class SingleSourceWidget extends AbstractWidget {
1414

1515
protected final ObjectProperty<DataSource> source = new SimpleObjectProperty<>(this, "source", DataSource.none());
1616

17+
/**
18+
* Instantiates a new single source widget. This automatically registers listeners to make the
19+
* {@link #source} and {@link #sources} properties stay in sync such that both properties will
20+
* have the same, single data source object.
21+
*/
22+
public SingleSourceWidget() {
23+
// Bidirectional binding to make the sources list act like a single-element wrapper around
24+
// the source property
25+
source.addListener((__, oldSource, newSource) -> sources.setAll(newSource));
26+
27+
sources.addListener(new ListChangeListener<DataSource>() {
28+
@Override
29+
public void onChanged(Change<? extends DataSource> c) {
30+
while (c.next()) {
31+
if (c.wasAdded()) {
32+
var added = c.getAddedSubList();
33+
if (!added.isEmpty()) {
34+
var addedSource = added.get(0);
35+
if (addedSource != source.get()) {
36+
source.set(addedSource);
37+
}
38+
}
39+
} else if (c.wasRemoved()) {
40+
source.set(DataSource.none());
41+
}
42+
}
43+
}
44+
});
45+
}
46+
1747
@Override
1848
public final void addSource(DataSource source) throws IncompatibleSourceException {
1949
if (getDataTypes().contains(source.getDataType())) {

api/src/main/resources/edu/wpi/first/shuffleboard/api/base.css

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -261,20 +261,6 @@
261261
-fx-stroke-width: 0.25em;
262262
}
263263

264-
/*******************************************************************************
265-
* *
266-
* Widget Gallery *
267-
* *
268-
******************************************************************************/
269-
.widget-gallery .item {
270-
-fx-border-width: 2;
271-
-fx-border-color: -swatch-200;
272-
-fx-border-insets: 5;
273-
-fx-background-insets: 5;
274-
-fx-alignment: center;
275-
-fx-cursor: hand;
276-
}
277-
278264
/*******************************************************************************
279265
* *
280266
* Property sheet *

api/src/test/java/edu/wpi/first/shuffleboard/api/components/SourceTreeTableTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public Object getValue() {
8686
}
8787

8888
@Override
89-
public Object getValueView() {
89+
public Object getInfo() {
9090
return uri;
9191
}
9292

app/src/main/java/edu/wpi/first/shuffleboard/app/LeftDrawerController.java

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@
66
import edu.wpi.first.shuffleboard.api.util.FxUtils;
77
import edu.wpi.first.shuffleboard.api.util.StringUtils;
88
import edu.wpi.first.shuffleboard.api.widget.Component;
9-
import edu.wpi.first.shuffleboard.api.widget.Components;
109
import edu.wpi.first.shuffleboard.app.components.InteractiveSourceTree;
11-
import edu.wpi.first.shuffleboard.app.components.WidgetGallery;
1210
import edu.wpi.first.shuffleboard.app.plugin.PluginLoader;
1311

1412
import com.google.common.collect.ArrayListMultimap;
1513
import com.google.common.collect.Multimap;
1614

15+
import javafx.scene.control.ScrollPane;
1716
import org.controlsfx.glyphfont.FontAwesome;
1817
import org.controlsfx.glyphfont.Glyph;
1918
import org.controlsfx.glyphfont.GlyphFont;
@@ -22,7 +21,6 @@
2221

2322
import java.util.Comparator;
2423
import java.util.function.Consumer;
25-
import java.util.stream.Collectors;
2624

2725
import javafx.animation.KeyFrame;
2826
import javafx.animation.KeyValue;
@@ -36,7 +34,6 @@
3634
import javafx.geometry.Pos;
3735
import javafx.scene.control.Accordion;
3836
import javafx.scene.control.Labeled;
39-
import javafx.scene.control.TabPane;
4037
import javafx.scene.control.TextField;
4138
import javafx.scene.control.TitledPane;
4239
import javafx.scene.control.Tooltip;
@@ -58,12 +55,10 @@ public final class LeftDrawerController {
5855
@FXML
5956
private Pane root;
6057
@FXML
61-
private TabPane tabs;
58+
private ScrollPane sourceContainer;
6259
@FXML
6360
private Accordion sourcesAccordion;
6461
@FXML
65-
private WidgetGallery widgetGallery;
66-
@FXML
6762
private Pane handle;
6863
@FXML
6964
private Labeled expandContractButton;
@@ -81,7 +76,8 @@ private void initialize() {
8176
listenToPluginChanges(plugin);
8277
setup(plugin);
8378
});
84-
tabs.maxWidthProperty().bind(root.widthProperty().subtract(handle.widthProperty()));
79+
sourceContainer.maxWidthProperty().bind(root.widthProperty().subtract(handle.widthProperty()));
80+
sourceContainer.minWidthProperty().bind(root.widthProperty().subtract(handle.widthProperty()));
8581
sourcesAccordion.getPanes().sort(Comparator.comparing(TitledPane::getText));
8682
PluginLoader.getDefault().getKnownPlugins().addListener((ListChangeListener<Plugin>) c -> {
8783
while (c.next()) {
@@ -173,23 +169,16 @@ private void setup(Plugin plugin) {
173169
sourcesAccordion.setExpandedPane(titledPane);
174170
}
175171
});
176-
177-
// Add widgets to the gallery as well
178-
widgetGallery.setWidgets(Components.getDefault().allWidgets().collect(Collectors.toList()));
179172
});
180173
}
181174

182175
/**
183-
* Removes all traces from a plugin from the left drawer. Source trees will be removed and all widgets
184-
* defined by the plugin will be removed from the gallery.
176+
* Removes all traces from a plugin from the left drawer.
185177
*/
186178
private void tearDown(Plugin plugin) {
187179
// Remove the source panes
188180
sourcesAccordion.getPanes().removeAll(sourcePanes.removeAll(plugin));
189181
FXCollections.sort(sourcesAccordion.getPanes(), Comparator.comparing(TitledPane::getText));
190-
191-
// Remove widgets from the gallery
192-
widgetGallery.setWidgets(Components.getDefault().allWidgets().collect(Collectors.toList()));
193182
}
194183

195184
@FXML

app/src/main/java/edu/wpi/first/shuffleboard/app/MainWindowController.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package edu.wpi.first.shuffleboard.app;
22

3+
import com.google.common.base.Stopwatch;
34
import edu.wpi.first.shuffleboard.api.plugin.Plugin;
45
import edu.wpi.first.shuffleboard.api.prefs.Category;
56
import edu.wpi.first.shuffleboard.api.sources.recording.Recorder;
@@ -21,6 +22,7 @@
2122
import edu.wpi.first.shuffleboard.app.sources.recording.Playback;
2223
import edu.wpi.first.shuffleboard.app.tab.TabInfoRegistry;
2324

25+
import java.util.concurrent.TimeUnit;
2426
import org.fxmisc.easybind.EasyBind;
2527

2628
import java.awt.Desktop;
@@ -251,7 +253,15 @@ public void load() throws IOException {
251253
* @throws IOException if the file could not be read from
252254
*/
253255
public void load(File saveFile) throws IOException {
256+
var timer = Stopwatch.createStarted();
254257
setDashboard(saveFileHandler.load(saveFile));
258+
log.info(
259+
"Loaded save file "
260+
+ saveFile.getAbsolutePath()
261+
+ " in "
262+
+ timer.elapsed(TimeUnit.MILLISECONDS)
263+
+ " milliseconds"
264+
);
255265
}
256266

257267
@FXML

0 commit comments

Comments
 (0)