Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Enable the robot to interface with NetworkTables and utilize the transmitted values #37

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 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
41 changes: 41 additions & 0 deletions src/main/java/com/team766/orin/GetApriltagPoseData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.team766.orin;

import edu.wpi.first.apriltag.AprilTag;
import edu.wpi.first.math.geometry.Pose3d;
import edu.wpi.first.math.geometry.Rotation3d;
import edu.wpi.first.math.geometry.Translation3d;
import java.util.ArrayList;

public class GetApriltagPoseData {
maxspier marked this conversation as resolved.
Show resolved Hide resolved

public static ArrayList<AprilTag> getAllTags() {
ArrayList<AprilTag> apriltags = new ArrayList<AprilTag>();
maxspier marked this conversation as resolved.
Show resolved Hide resolved

int[] tagIds;
maxspier marked this conversation as resolved.
Show resolved Hide resolved
double[] tagData;

try {
tagIds = GetOrinRawValue.getIntArray("tag_id");
tagData = GetOrinRawValue.getDoubleArray("raw_pose");
} catch (ValueNotFoundOnTableError e) {
return apriltags; // Can just return an array of zero apriltags here
}

maxspier marked this conversation as resolved.
Show resolved Hide resolved
int counter = 0;
for (int i = 0; i < tagIds.length; i++) {
AprilTag tag =
new AprilTag(
tagIds[i],
new Pose3d(
new Translation3d(
tagData[counter++],
tagData[counter++],
tagData[counter++]),
new Rotation3d()));
maxspier marked this conversation as resolved.
Show resolved Hide resolved
// Remove used values from array

apriltags.add(tag);
}
return apriltags;
}
}
101 changes: 101 additions & 0 deletions src/main/java/com/team766/orin/GetOrinRawValue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.team766.orin;

import edu.wpi.first.networktables.BooleanEntry;
import edu.wpi.first.networktables.BooleanTopic;
import edu.wpi.first.networktables.DoubleArrayEntry;
import edu.wpi.first.networktables.DoubleArrayTopic;
import edu.wpi.first.networktables.DoubleEntry;
import edu.wpi.first.networktables.DoubleTopic;
import edu.wpi.first.networktables.IntegerArrayEntry;
import edu.wpi.first.networktables.IntegerArrayTopic;
import edu.wpi.first.networktables.IntegerEntry;
import edu.wpi.first.networktables.IntegerTopic;
import edu.wpi.first.networktables.NetworkTable;
import edu.wpi.first.networktables.NetworkTableInstance;
import edu.wpi.first.networktables.StringEntry;
import edu.wpi.first.networktables.StringTopic;

/*
* This class is used to get raw values from the NetworkTable. We can send values on the Orin to NetworkTables and get them easily here.
* One important caveat of the NetworkTables code is that it doesn't throw exceptions when a value is not found.
* Instead, it asks for a default value to return. This is a problem because we can't know if the value is not found or if the value is the default value.
* Thus, numbers were chosen as default values that aren't really plausible to be sent by the Orin.
* For example, it is not likley that the Orin will send Integer.MIN_VALUE as a value. In this case, an exception is thrown.
*
* @author Max Spier, 9/28/2024
*/
public class GetOrinRawValue {
maxspier marked this conversation as resolved.
Show resolved Hide resolved
maxspier marked this conversation as resolved.
Show resolved Hide resolved

private static NetworkTableInstance inst = NetworkTableInstance.getDefault();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

something to consider (what's more typical, easier to test, less error prone): instead of having this as a "utility class" (class with only static members and methods), make the members and methods non-static, and create an instance of this in GetAprilTagPoseData.

In a unit test, you could have a "fake" implementation of this that provided data from memory etc.

If this isn't making sense, happy to talk thorugh this on chat or f2f.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would we want to have an instance of this class? The design of this class is to be a utility class for the first layer of getting data from NetworkTables to the robot. IMO I don't think it would be good to make it non-static if we are going to want different parts of code using it at the same time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you typically do not put this kind of state in utility classes; utility classes are typically stateless:

https://medium.com/@salmankhan_27014/best-practices-for-creating-utility-classes-in-software-development-6f9ad1e8d27a

Having all of this state in static members can make the implementation and the usage harder to read and manage, and it makes the code harder to test.

for the use case you're describing, you can use the "Singleton" design pattern:
https://softwareengineering.stackexchange.com/questions/235527/when-to-use-a-singleton-and-when-to-use-a-static-class
https://www.digitalocean.com/community/tutorials/java-singleton-design-pattern-best-practices-examples

In a Singleton, you have all of the relevant state in an instance, and you only allow creation of a single instance (which you could replace through eg package visible methods) for usage throughout your code.

Does that make sense? Happy to explain/chat more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(this is non-blocking ofc :) - more just feedback on what typical industry practices are for coding something like this - and how you can make this code easier to maintain and test.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! I'll take a look tonight at the meeting! Thanks!

private static NetworkTable table = inst.getTable("/SmartDashboard");
rcahoon marked this conversation as resolved.
Show resolved Hide resolved

public static int getInt(String key) throws ValueNotFoundOnTableError {
maxspier marked this conversation as resolved.
Show resolved Hide resolved
IntegerTopic topic = table.getIntegerTopic(key);
IntegerEntry value = topic.getEntry(Integer.MIN_VALUE);
maxspier marked this conversation as resolved.
Show resolved Hide resolved
if (value.get() == Integer.MIN_VALUE) {
throw new ValueNotFoundOnTableError(key);
}
return (int) value.get();
}

public static double getDouble(String key) throws ValueNotFoundOnTableError {
DoubleTopic topic = table.getDoubleTopic(key);
DoubleEntry value = topic.getEntry(Double.NEGATIVE_INFINITY);
if (value.get() == Double.NEGATIVE_INFINITY) {
maxspier marked this conversation as resolved.
Show resolved Hide resolved
throw new ValueNotFoundOnTableError(key);
}
return value.get();
}

/*
* Be careful to know that the boolean value is valid. If the value is not found, the default value is returned and no exception is thrown.
* This is because true/false are the only two possible values for a boolean - if one were to be the default it would be hard to know if the value was not found or if the value was the default.
*/
maxspier marked this conversation as resolved.
Show resolved Hide resolved
public static boolean getBoolean(String key) {
BooleanTopic topic = table.getBooleanTopic(key);
BooleanEntry value = topic.getEntry(false);
return value.get();
}

public static String getString(String key) throws ValueNotFoundOnTableError {
StringTopic topic = table.getStringTopic(key);
StringEntry value =
topic.getEntry(
"Team 766 is such a great team and they will go to worlds every year because they are such a great team!!!");
maxspier marked this conversation as resolved.
Show resolved Hide resolved
if (value.get()
.equals(
"Team 766 is such a great team and they will go to worlds every year because they are such a great team!!!")) {
throw new ValueNotFoundOnTableError(key);
}
return value.get();
}

public static int[] getIntArray(String key) throws ValueNotFoundOnTableError {
maxspier marked this conversation as resolved.
Show resolved Hide resolved
IntegerArrayTopic topic = table.getIntegerArrayTopic(key);
long[] arr = new long[1];
maxspier marked this conversation as resolved.
Show resolved Hide resolved
arr[0] = Integer.MIN_VALUE;
IntegerArrayEntry values = topic.getEntry(arr);
long[] array = values.get();
maxspier marked this conversation as resolved.
Show resolved Hide resolved

if (array.length == 0 || (array[0] == Integer.MIN_VALUE && array.length == 1)) {
throw new ValueNotFoundOnTableError(key);
}

int[] toReturn = new int[array.length];
for (int i = 0; i < array.length; i++) {
toReturn[i] = (int) array[i];
}
return toReturn;
}

public static double[] getDoubleArray(String key) throws ValueNotFoundOnTableError {
DoubleArrayTopic topic = table.getDoubleArrayTopic(key);
double[] arr = new double[] {Double.NEGATIVE_INFINITY};
DoubleArrayEntry values = topic.getEntry(arr);
if (values.get().length == 1 && values.get()[0] == Double.NEGATIVE_INFINITY) {
throw new ValueNotFoundOnTableError(key);
}

return values.get();
}
}
7 changes: 7 additions & 0 deletions src/main/java/com/team766/orin/ValueNotFoundOnTableError.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.team766.orin;

public class ValueNotFoundOnTableError extends Exception {
public ValueNotFoundOnTableError(String key) {
super("Value not found on table: " + key);
}
}
Loading