Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use NT4 metadata #760

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import javafx.beans.property.Property;
import javafx.beans.property.StringProperty;

import java.util.Optional;

/**
* A data source provides some kind of data that widgets can display and manipulate. It can be
* active or inactive; active sources may update at any time, while inactive sources are guaranteed
Expand Down Expand Up @@ -161,4 +163,12 @@ default String getId() {
*/
void removeClient(Sourced client);

/**
* Preferred widget for the source.
*
* @return optionally a string identifier of the preferred widget type.
*/
default Optional<String> preferredWidget() {
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ private boolean previewSource(GridPoint point, Dragboard dragboard) {
}
SourceEntry entry = DeserializationHelper.sourceFromDrag(dragboard.getContent(DataFormats.source));
DataSource source = entry.get();
Optional<String> componentName = Components.getDefault().pickComponentNameFor(source.getDataType());
Optional<String> componentName = source.preferredWidget()
.or(() -> Components.getDefault().pickComponentNameFor(source.getDataType()));
Optional<DataSource<?>> dummySource = DummySource.forTypes(source.getDataType());
if (componentName.isPresent() && dummySource.isPresent()) {
if (tilePreviewSize == null) {
Expand Down Expand Up @@ -290,7 +291,8 @@ private void handleDragDropped(DragEvent event) {
* @param point the point to place the widget for the source
*/
private void dropSource(DataSource<?> source, GridPoint point) {
Components.getDefault().pickComponentNameFor(source.getDataType())
source.preferredWidget()
.or(() -> Components.getDefault().pickComponentNameFor(source.getDataType()))
.flatMap(name -> Components.getDefault().createComponent(name, source))
.filter(widget -> pane.isOpen(point, pane.sizeOfWidget(widget), n -> widget.getView() == n))
.map(pane::addComponentToTile)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package edu.wpi.first.shuffleboard.plugin.networktables;

import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import edu.wpi.first.shuffleboard.api.sources.DataSource;
import edu.wpi.first.shuffleboard.api.sources.SourceTypes;
import edu.wpi.first.shuffleboard.api.tab.model.ComponentModel;
Expand Down Expand Up @@ -337,6 +339,26 @@ private Map<String, Object> properties(String realPath) {
props.put(k, propsTable.getEntry(k).getValue().getValue());
}
}
if (!inst.getTopic(realPath).exists()) {
var propsTable = inst.getTable(realPath);
for (String k : propsTable.getKeys()) {
props.put(k, propsTable.getEntry(k).getValue().getValue());
}
} else {
String metadata = inst.getTopic(realPath).getProperties();
JsonParser parser = new JsonParser();
var obj = parser.parse(metadata).getAsJsonObject();
obj.entrySet().forEach(entry -> {
JsonPrimitive primitive = entry.getValue().getAsJsonPrimitive();
if (primitive.isBoolean()) {
props.put(entry.getKey(), primitive.getAsBoolean());
} else if (primitive.isNumber()) {
props.put(entry.getKey(), primitive.getAsNumber());
} else {
props.put(entry.getKey(), primitive.getAsString());
}
});
}
return props;
}

Expand All @@ -354,8 +376,9 @@ private void updateWidget(ParentModel parent, String path) {
preferredComponent(
path,
sourceSupplier
.map(DataSource::getDataType)
.map(componentRegistry::defaultComponentNameFor)
.map(source -> source.preferredWidget()
.or(() -> componentRegistry.defaultComponentNameFor(source.getDataType()))
)
.map(Optional::orElseThrow)
),
properties(path));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package edu.wpi.first.shuffleboard.plugin.networktables.sources;

import edu.wpi.first.networktables.NetworkTableValue;
import edu.wpi.first.shuffleboard.api.data.ComplexData;
import edu.wpi.first.shuffleboard.api.data.ComplexDataType;
import edu.wpi.first.shuffleboard.api.data.IncompleteDataException;
Expand All @@ -14,6 +15,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;

Expand All @@ -28,6 +30,8 @@ public class CompositeNetworkTableSource<D extends ComplexData<D>> extends Netwo

private static final Logger log = Logger.getLogger(CompositeNetworkTableSource.class.getName());

private Optional<String> preferredWidget;

private final Map<String, Object> backingMap = new HashMap<>();
private final ComplexDataType<D> dataType;

Expand All @@ -44,6 +48,7 @@ public CompositeNetworkTableSource(String tableName, ComplexDataType<D> dataType
this.dataType = dataType;
String path = NetworkTable.normalizeKey(tableName, false);
NetworkTable table = NetworkTableInstance.getDefault().getTable(path);
preferredWidget = loadWidgetFromWidgetEntry(table);
setData(dataType.getDefaultValue());

setTableListener((key, event) -> {
Expand Down Expand Up @@ -91,4 +96,15 @@ public ComplexDataType<D> getDataType() {
protected boolean isSingular() {
return false;
}

@Override
public Optional<String> preferredWidget() {
return preferredWidget;
}

private static Optional<String> loadWidgetFromWidgetEntry(NetworkTable table) {
return Optional.ofNullable(table.getValue(".widget"))
.filter(NetworkTableValue::isString)
.map(NetworkTableValue::getString);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package edu.wpi.first.shuffleboard.plugin.networktables.sources;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import edu.wpi.first.networktables.NetworkTableEvent.Kind;
import edu.wpi.first.networktables.Topic;
import edu.wpi.first.shuffleboard.api.data.DataType;
import edu.wpi.first.shuffleboard.api.data.DataTypes;
import edu.wpi.first.shuffleboard.api.sources.Sources;
Expand All @@ -9,6 +14,8 @@
import edu.wpi.first.networktables.NetworkTableEntry;
import edu.wpi.first.networktables.NetworkTableEvent;

import java.util.Optional;

/**
* A data source backed by a single key-value pair in a network table.
*/
Expand All @@ -21,6 +28,8 @@ public class SingleKeyNetworkTableSource<T> extends NetworkTableSource<T> {
*/
private volatile boolean initialUpdate = true;

private Optional<String> preferredWidget;

/**
* Creates a single-key network table source backed by the value in the given table
* associated with the given key.
Expand All @@ -33,6 +42,7 @@ public class SingleKeyNetworkTableSource<T> extends NetworkTableSource<T> {
public SingleKeyNetworkTableSource(NetworkTable table, String key, DataType dataType) {
super(key, dataType);
setName(key);
preferredWidget = loadWidgetFromTopicProps(table.getTopic(key));
setTableListener((__, event) -> {
if (event.is(NetworkTableEvent.Kind.kUnpublish)) {
setActive(false);
Expand All @@ -49,6 +59,8 @@ public SingleKeyNetworkTableSource(NetworkTable table, String key, DataType data
if (isActive()) {
setData((T) value);
}
} else if (event.is(Kind.kProperties)) {
preferredWidget = loadWidgetFromTopicProps(table.getTopic(key));
}
});

Expand Down Expand Up @@ -83,4 +95,27 @@ public SingleKeyNetworkTableSource(NetworkTable table, String key, DataType data
protected boolean isSingular() {
return true;
}

@Override
public Optional<String> preferredWidget() {
return preferredWidget;
}

private static Optional<String> loadWidgetFromTopicProps(Topic topic) {
String metadata = topic.getProperties();
JsonParser parser = new JsonParser();
try {
JsonObject obj = parser.parse(metadata).getAsJsonObject();
JsonElement widgetProp = obj.get("widget");
if (widgetProp == null || !widgetProp.isJsonPrimitive()) {
System.err.println("Metadata widget field for topic `" + topic.getName()
+ "` doesn't exist or isn't primitive!");
return Optional.empty();
}
return Optional.of(widgetProp.getAsString());
} catch (Exception e) {
e.printStackTrace();
return Optional.empty();
}
}
}