Remove angle offset from shooter table

Fix logging using format strings
Add autonomous chooser to the dashboard
Update path recorder
Move path commands to a class
This commit is contained in:
nathanrsxtn
2022-04-19 20:36:59 -06:00
parent 0196174180
commit b069720145
14 changed files with 710 additions and 249 deletions
+1
View File
@@ -18,6 +18,7 @@
- [ ] Fix all XXX comments.
- [ ] Visit TODO comments.
- [ ] Forward the limelight port so it can be accessed over a USB tether.
- [x] Remove turret offset from shooter tables.
## Planned
- [ ] Shuffleboard replacements options:
- [ ] Run a web server on the robot and bypass network tables.
+255 -93
View File
@@ -18,12 +18,178 @@
"autoPopulate": true,
"autoPopulatePrefix": "LiveWindow/",
"widgetPane": {
"gridSize": 128.0,
"gridSize": 60.0,
"showGrid": true,
"hgap": 16.0,
"vgap": 16.0,
"hgap": 10.0,
"vgap": 10.0,
"titleType": 0,
"tiles": {}
"tiles": {
"4,5": {
"size": [
4,
6
],
"content": {
"_type": "Subsystem Layout",
"_source0": "network_table:///LiveWindow/Climber",
"_title": "LiveWindow/Climber",
"Layout/Label position": "BOTTOM",
"_children": [
{
"_type": "Text View",
"_source0": "network_table:///LiveWindow/Climber/Left Claw/Value",
"_title": "Left Claw/Value"
},
{
"_type": "Text View",
"_source0": "network_table:///LiveWindow/Climber/Right Claw/Value",
"_title": "Right Claw/Value"
},
{
"_type": "Text View",
"_source0": "network_table:///LiveWindow/Climber/Elbow/Value",
"_title": "Elbow/Value"
}
]
}
},
"0,0": {
"size": [
4,
13
],
"content": {
"_type": "Subsystem Layout",
"_source0": "network_table:///LiveWindow/Drive",
"_title": "LiveWindow/Drive",
"Layout/Label position": "BOTTOM",
"_children": [
{
"_type": "Text View",
"_source0": "network_table:///LiveWindow/Drive/Left Front Drive Motor/Value",
"_title": "Left Front Drive Motor/Value"
},
{
"_type": "Text View",
"_source0": "network_table:///LiveWindow/Drive/Right Front Steer Motor/Value",
"_title": "Right Front Steer Motor/Value"
},
{
"_type": "Text View",
"_source0": "network_table:///LiveWindow/Drive/Right Back Steer Motor/Value",
"_title": "Right Back Steer Motor/Value"
},
{
"_type": "Text View",
"_source0": "network_table:///LiveWindow/Drive/Right Back Drive Motor/Value",
"_title": "Right Back Drive Motor/Value"
},
{
"_type": "Text View",
"_source0": "network_table:///LiveWindow/Drive/Left Back Steer Motor/Value",
"_title": "Left Back Steer Motor/Value"
},
{
"_type": "Text View",
"_source0": "network_table:///LiveWindow/Drive/Left Front Steer Motor/Value",
"_title": "Left Front Steer Motor/Value"
},
{
"_type": "Text View",
"_source0": "network_table:///LiveWindow/Drive/Left Back Drive Motor/Value",
"_title": "Left Back Drive Motor/Value"
},
{
"_type": "Text View",
"_source0": "network_table:///LiveWindow/Drive/Right Front Drive Motor/Value",
"_title": "Right Front Drive Motor/Value"
}
]
}
},
"4,11": {
"size": [
4,
2
],
"content": {
"_type": "Subsystem Layout",
"_source0": "network_table:///LiveWindow/Intake",
"_title": "LiveWindow/Intake",
"Layout/Label position": "BOTTOM",
"_children": [
{
"_type": "Text View",
"_source0": "network_table:///LiveWindow/Intake/Intake Motor/Value",
"_title": "Intake Motor/Value"
}
]
}
},
"4,0": {
"size": [
4,
5
],
"content": {
"_type": "Subsystem Layout",
"_source0": "network_table:///LiveWindow/Shooter",
"_title": "LiveWindow/Shooter",
"Layout/Label position": "BOTTOM",
"_children": [
{
"_type": "Text View",
"_source0": "network_table:///LiveWindow/Shooter/BoomBoom Left Motor/Value",
"_title": "BoomBoom Left Motor/Value"
},
{
"_type": "Text View",
"_source0": "network_table:///LiveWindow/Shooter/BoomBoom Right Motor/Value",
"_title": "BoomBoom Right Motor/Value"
}
]
}
},
"8,0": {
"size": [
5,
13
],
"content": {
"_type": "Subsystem Layout",
"_source0": "network_table:///LiveWindow/Ungrouped",
"_title": "LiveWindow/Ungrouped",
"Layout/Label position": "BOTTOM",
"_children": [
{
"_type": "Text View",
"_source0": "network_table:///LiveWindow/Ungrouped/Spark[4]/Value",
"_title": "Spark[4]/Value"
},
{
"_type": "PID Controller",
"_source0": "network_table:///LiveWindow/Ungrouped/PIDController[1]",
"_title": "PIDController[1]"
},
{
"_type": "PID Controller",
"_source0": "network_table:///LiveWindow/Ungrouped/PIDController[3]",
"_title": "PIDController[3]"
},
{
"_type": "Text View",
"_source0": "network_table:///LiveWindow/Ungrouped/Pigeon 2 [14]/Heading",
"_title": "Pigeon 2 [14]/Heading"
},
{
"_type": "PID Controller",
"_source0": "network_table:///LiveWindow/Ungrouped/PIDController[2]",
"_title": "PIDController[2]"
}
]
}
}
}
}
},
{
@@ -37,68 +203,6 @@
"vgap": 10.0,
"titleType": 1,
"tiles": {
"0,0": {
"size": [
6,
8
],
"content": {
"_type": "Grid Layout",
"_title": "Match",
"Layout/Number of columns": 1,
"Layout/Number of rows": 3,
"Layout/Label position": "HIDDEN",
"_children": {
"0,0": {
"_type": "Basic FMS Info",
"_title": "Basic FMS Info"
},
"0,1": {
"_type": "Field",
"_title": "Field",
"Game/Game": "A2021_Infinite_Recharge",
"Visuals/Robot Icon Size": 50.0,
"Visuals/Show Outside Circles": false
},
"0,2": {
"_type": "Grid Layout",
"_title": "Match Time",
"Layout/Number of columns": 3,
"Layout/Number of rows": 1,
"Layout/Label position": "BOTTOM",
"_children": {
"0,0": {
"_type": "Number Bar",
"_title": "Autonomous",
"Range/Min": 0.0,
"Range/Max": 15.0,
"Range/Center": 0.0,
"Visuals/Num tick marks": 4,
"Visuals/Show text": true
},
"1,0": {
"_type": "Number Bar",
"_title": "Teleoperated",
"Range/Min": 0.0,
"Range/Max": 90.0,
"Range/Center": 0.0,
"Visuals/Num tick marks": 5,
"Visuals/Show text": true
},
"2,0": {
"_type": "Number Bar",
"_title": "Climb",
"Range/Min": 0.0,
"Range/Max": 30.0,
"Range/Center": 0.0,
"Visuals/Num tick marks": 5,
"Visuals/Show text": true
}
}
}
}
}
},
"0,8": {
"size": [
6,
@@ -111,16 +215,18 @@
"_children": [
{
"_type": "Graph",
"_source0": "network_table:///Robot/Values/Voltage",
"_title": "Graph",
"Graph/Visible time": 30.0,
"Graph/Visible time": 15.0,
"Graph/X-axis auto scrolling": true,
"Y-axis/Automatic bounds": true,
"Y-axis/Upper bound": 1.0,
"Y-axis/Lower bound": -1.0,
"Y-axis/Unit": "ul"
"Y-axis/Upper bound": 14.0,
"Y-axis/Lower bound": 0.0,
"Y-axis/Unit": "V"
},
{
"_type": "PDP",
"_source0": "network_table:///Robot/PDP",
"_title": "PDP",
"Visuals/Show voltage and current values": true
}
@@ -134,6 +240,7 @@
],
"content": {
"_type": "Robot Preferences",
"_source0": "network_table:///Preferences",
"_title": "Robot Preferences",
"Miscellaneous/Show search box": false
}
@@ -150,7 +257,8 @@
"_children": [
{
"_type": "Swerve Drivebase",
"_title": "",
"_source0": "network_table:///Robot/Drivebase",
"_title": "/Robot/Drive/Drivebase",
"Visuals/Show velocity vectors": true
},
{
@@ -169,10 +277,12 @@
"_children": {
"0,1": {
"_type": "Split Button Chooser",
"_source0": "network_table:///Robot/Drive Speed",
"_title": "Speed"
},
"0,0": {
"_type": "3-Axis Accelerometer",
"_source0": "network_table:///Robot/Accelerometer",
"_title": "Accelerometer",
"Accelerometer/Range": "k16G",
"Visuals/Show value": true,
@@ -183,6 +293,7 @@
},
"0,0": {
"_type": "Gyro",
"_source0": "network_table:///Robot/Gyro",
"_title": "Gyro",
"Visuals/Major tick spacing": 45.0,
"Visuals/Starting angle": 180.0,
@@ -201,6 +312,7 @@
],
"content": {
"_type": "Camera Stream",
"_source0": "network_table:///Robot/Driver Camera",
"_title": "Camera",
"Crosshair/Show crosshair": false,
"Crosshair/Crosshair color": "#FFFFFFFF",
@@ -226,6 +338,7 @@
"_children": {
"1,0": {
"_type": "Boolean Box",
"_source0": "network_table:///Robot/Values/Shooter Safety",
"_title": "Shooter Safety",
"Colors/Color when true": "#7CFC00FF",
"Colors/Color when false": "#8B0000FF"
@@ -237,30 +350,31 @@
"_children": [
{
"_type": "Number Bar",
"_source0": "network_table:///Robot/Values/Drum",
"_title": "Speed",
"Range/Min": 0.0,
"Range/Max": 100.0,
"Range/Center": 0.0,
"Visuals/Num tick marks": 0,
"Visuals/Num tick marks": 6,
"Visuals/Show text": true
},
{
"_type": "Number Bar",
"_type": "Number Slider",
"_source0": "network_table:///Robot/Values/Angle",
"_title": "Angle",
"Range/Min": 0.0,
"Range/Max": 100.0,
"Range/Center": 0.0,
"Visuals/Num tick marks": 0,
"Visuals/Show text": true
"Slider Settings/Min": -180.0,
"Slider Settings/Max": 180.0,
"Slider Settings/Block increment": 1.0,
"Visuals/Display value": true
},
{
"_type": "Number Bar",
"_type": "Number Slider",
"_source0": "network_table:///Robot/Values/Hood",
"_title": "Hood",
"Range/Min": 0.0,
"Range/Max": 100.0,
"Range/Center": 0.0,
"Visuals/Num tick marks": 0,
"Visuals/Show text": true
"Slider Settings/Min": 0.0,
"Slider Settings/Max": 120.0,
"Slider Settings/Block increment": 1.0,
"Visuals/Display value": true
}
]
}
@@ -288,29 +402,32 @@
"_children": {
"0,0": {
"_type": "Number Bar",
"_source0": "network_table:///Robot/Values/Intake",
"_title": "Intake",
"Range/Min": -1.0,
"Range/Max": 1.0,
"Range/Center": 0.0,
"Visuals/Num tick marks": 5,
"Visuals/Num tick marks": 3,
"Visuals/Show text": true
},
"1,0": {
"_type": "Number Bar",
"_source0": "network_table:///Robot/Values/Serializer",
"_title": "Serializer",
"Range/Min": -1.0,
"Range/Max": 1.0,
"Range/Center": 0.0,
"Visuals/Num tick marks": 5,
"Visuals/Num tick marks": 3,
"Visuals/Show text": true
},
"2,0": {
"_type": "Number Bar",
"_source0": "network_table:///Robot/Values/Storage",
"_title": "Storage",
"Range/Min": -1.0,
"Range/Max": 1.0,
"Range/Center": 0.0,
"Visuals/Num tick marks": 5,
"Visuals/Num tick marks": 3,
"Visuals/Show text": true
}
}
@@ -324,6 +441,7 @@
"_children": {
"0,0": {
"_type": "Split Button Chooser",
"_source0": "network_table:///Robot/Extender",
"_title": "Extender"
}
}
@@ -342,16 +460,17 @@
"Layout/Label position": "BOTTOM",
"_children": [
{
"_type": "Number Bar",
"_type": "Number Slider",
"_source0": "network_table:///Robot/Values/Claws",
"_title": "Claws",
"Range/Min": -1.0,
"Range/Max": 1.0,
"Range/Center": 0.0,
"Visuals/Num tick marks": 5,
"Visuals/Show text": true
"Slider Settings/Min": 0.0,
"Slider Settings/Max": 1.0,
"Slider Settings/Block increment": 0.0625,
"Visuals/Display value": true
},
{
"_type": "Number Bar",
"_source0": "network_table:///Robot/Values/Arm",
"_title": "Arm",
"Range/Min": -1.0,
"Range/Max": 1.0,
@@ -369,6 +488,7 @@
],
"content": {
"_type": "Camera Stream",
"_source0": "network_table:///photonvision/photoncamera",
"_title": "Limelight",
"Crosshair/Show crosshair": true,
"Crosshair/Crosshair color": "#FFFFFFFF",
@@ -379,6 +499,48 @@
"imageWidth": -1,
"imageHeight": -1
}
},
"0,0": {
"size": [
6,
4
],
"content": {
"_type": "List Layout",
"_title": "Match",
"Layout/Label position": "BOTTOM",
"_children": [
{
"_type": "Basic FMS Info",
"_source0": "network_table:///FMSInfo",
"_title": "Basic FMS Info"
},
{
"_type": "Number Bar",
"_source0": "network_table:///Robot/Values/Match Time",
"_title": "Match Time",
"Range/Min": 0.0,
"Range/Max": 30.0,
"Range/Center": 0.0,
"Visuals/Num tick marks": 4,
"Visuals/Show text": true
}
]
}
},
"0,4": {
"size": [
6,
4
],
"content": {
"_type": "Field",
"_source0": "network_table:///Robot/Field",
"_title": "Field",
"Game/Game": "A2021_Infinite_Recharge",
"Visuals/Robot Icon Size": 50.0,
"Visuals/Show Outside Circles": false
}
}
}
}
+17 -17
View File
@@ -1,17 +1,17 @@
Distance (in) ,Hood Ext. (u) ,Drum Velocity (u/ds) ,Turret Offset (deg)
0 ,-29.5 ,8000 ,10
78.5 ,-29.5 ,8500 ,8.5
88 ,-34.2 ,8600 ,8.5
90 ,-35.47 ,9500 ,8.5
99 ,-39.62 ,9500 ,8.0
111 ,-42 ,9500 ,8.0
127.25 ,-49.12 ,9500 ,8.0
141 ,-59.4 ,9900 ,7.5
150 ,-66.22 ,10000 ,7.5
164.5 ,-75.52 ,10000 ,6.5
189.9 ,-81.39 ,11000 ,6.0
207 ,-104.07 ,11000 ,5.5
227 ,-105.32 ,11500 ,5.0
239 ,-105.5 ,12380 ,4.5
255.5 ,-105.8 ,13500 ,4.0
999 ,-105.8 ,13500 ,3.0
Distance (in) ,Hood Ext. (u) ,Drum Velocity (u/ds)
0 ,-29.5 ,8000
78.5 ,-29.5 ,8500
88 ,-34.2 ,8600
90 ,-35.47 ,9500
99 ,-39.62 ,9500
111 ,-42 ,9500
127.25 ,-49.12 ,9500
141 ,-59.4 ,9900
150 ,-66.22 ,10000
164.5 ,-75.52 ,10000
189.9 ,-81.39 ,11000
207 ,-104.07 ,11000
227 ,-105.32 ,11500
239 ,-105.5 ,12380
255.5 ,-105.8 ,13500
999 ,-105.8 ,13500
1 Distance (in) Hood Ext. (u) Drum Velocity (u/ds) Drum Velocity (u/ds) Turret Offset (deg)
2 0 -29.5 8000 8000 10
3 78.5 -29.5 8500 8500 8.5
4 88 -34.2 8600 8600 8.5
5 90 -35.47 9500 9500 8.5
6 99 -39.62 9500 9500 8.0
7 111 -42 9500 9500 8.0
8 127.25 -49.12 9500 9500 8.0
9 141 -59.4 9900 9900 7.5
10 150 -66.22 10000 10000 7.5
11 164.5 -75.52 10000 10000 6.5
12 189.9 -81.39 11000 11000 6.0
13 207 -104.07 11000 11000 5.5
14 227 -105.32 11500 11500 5.0
15 239 -105.5 12380 12380 4.5
16 255.5 -105.8 13500 13500 4.0
17 999 -105.8 13500 13500 3.0
+1
View File
@@ -67,6 +67,7 @@ public class Robot extends TimedRobot {
// running already-scheduled commands, removing finished or interrupted commands, and running
// subsystem periodic() methods. This must be called from the robot's periodic block in order for
// anything in the Command-based framework to work.
m_robotContainer.updateValues();
CommandScheduler.getInstance().run();
}
+177 -63
View File
@@ -4,37 +4,42 @@
package frc4388.robot;
import java.util.Objects;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import com.pathplanner.lib.PathPlanner;
import com.pathplanner.lib.PathPlannerTrajectory;
import com.pathplanner.lib.PathPlannerTrajectory.PathPlannerState;
import com.pathplanner.lib.commands.PPSwerveControllerCommand;
import edu.wpi.first.math.controller.PIDController;
import edu.wpi.first.math.controller.ProfiledPIDController;
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.networktables.NTSendable;
import edu.wpi.first.networktables.NTSendableBuilder;
import edu.wpi.first.networktables.NetworkTable;
import edu.wpi.first.networktables.NetworkTableEntry;
import edu.wpi.first.networktables.NetworkTableInstance;
import edu.wpi.first.networktables.NetworkTableValue;
import edu.wpi.first.util.sendable.Sendable;
import edu.wpi.first.util.sendable.SendableBuilder;
import edu.wpi.first.util.sendable.SendableRegistry;
import edu.wpi.first.wpilibj.DriverStation;
import edu.wpi.first.wpilibj.GenericHID;
import edu.wpi.first.wpilibj.Preferences;
import edu.wpi.first.wpilibj.PowerDistribution;
import edu.wpi.first.wpilibj.XboxController;
import edu.wpi.first.wpilibj.smartdashboard.SendableChooser;
import edu.wpi.first.wpilibj.smartdashboard.SendableBuilderImpl;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.Command;
import edu.wpi.first.wpilibj2.command.CommandGroupBase;
import edu.wpi.first.wpilibj2.command.InstantCommand;
import edu.wpi.first.wpilibj2.command.ParallelDeadlineGroup;
import edu.wpi.first.wpilibj2.command.RunCommand;
import edu.wpi.first.wpilibj2.command.SequentialCommandGroup;
import edu.wpi.first.wpilibj2.command.WaitCommand;
import edu.wpi.first.wpilibj2.command.button.JoystickButton;
import frc4388.robot.Constants.AutoConstants;
import frc4388.robot.Constants.OIConstants;
import frc4388.robot.Constants.StorageConstants;
import frc4388.robot.Constants.SwerveDriveConstants;
import frc4388.robot.commands.PathPlannerCommand;
import frc4388.robot.commands.PathRecorder;
import frc4388.robot.commands.ExtenderIntakeCommands.ExtenderIntakeGroup;
import frc4388.robot.commands.ExtenderIntakeCommands.RunExtender;
import frc4388.robot.commands.shooter.TimedWaitUntilCommand;
import frc4388.robot.commands.shooter.TrackTarget;
import frc4388.robot.commands.shuffleboard.CommandSchedule;
@@ -54,6 +59,7 @@ import frc4388.robot.subsystems.Turret;
import frc4388.robot.subsystems.VisionOdometry;
import frc4388.utility.controller.ButtonBox;
import frc4388.utility.controller.DeadbandedXboxController;
import frc4388.utility.shuffleboard.CachingSendableChooser;
//TODO: Try using ConditionalCommand for subsystem default commands.
//TODO: Replace Path Recorder with Auto Chooser.
@@ -88,8 +94,8 @@ public class RobotContainer {
public final VisionOdometry m_robotVisionOdometry = new VisionOdometry(m_robotSwerveDrive, m_robotTurret);
/* Dashboard Tools */
private final PathRecorder m_pathChooser = new PathRecorder(m_robotSwerveDrive);
private final SendableChooser<Command> autoChooser = new SendableChooser<Command>();
private final CachingSendableChooser<Command> m_autoChooser = new CachingSendableChooser<>();
private final PathRecorder m_pathRecorder = new PathRecorder(m_robotSwerveDrive, m_autoChooser);
private final ShooterTuner m_shooterTuner = new ShooterTuner(m_robotBoomBoom);
private final CommandSchedule m_commandSchedule = new CommandSchedule(10, 5, false);
@@ -114,16 +120,115 @@ public class RobotContainer {
private DriveMode currentDriveMode = DriveMode.ON;
private final Map<String, Sendable> tablesToData = new HashMap<>();
private final NetworkTable networkTable = NetworkTableInstance.getDefault().getTable("Robot");
private synchronized void putData(String key, Sendable data) {
Sendable sddata = tablesToData.get(key);
if (sddata == null || sddata != data) {
tablesToData.put(key, data);
NetworkTable dataTable = networkTable.getSubTable(key);
SendableBuilderImpl builder = new SendableBuilderImpl();
builder.setTable(dataTable);
SendableRegistry.publish(data, builder);
builder.startListeners();
dataTable.getEntry(".name").setString(key);
}
}
public synchronized void updateValues() {
for (Sendable data : tablesToData.values()) {
SendableRegistry.update(data);
}
}
/**
* The container for the robot. Contains subsystems, OI devices, and commands.
*/
public RobotContainer() {
autoChooser.addOption("threeBallPlus", threeBallPlus);
m_autoChooser.addOption("OneBall", this::makeOneBallCommand);
m_autoChooser.addOption("TwoBall", this::makeTwoBallCommand);
m_autoChooser.setDefaultOption("ThreeBall", this::makeThreeBallCommand);
SmartDashboard.putData("AutoChooser", autoChooser);
SmartDashboard.putData("Autonomous", m_autoChooser);
SmartDashboard.putData("Path Recorder", m_pathRecorder);
Preferences.initString("Autonomous", "Three Ball");
// Preferences.initString("Autonomous", "Three Ball");
var pdp = new PowerDistribution() {
@Override
public void initSendable(SendableBuilder builder) {
super.initSendable(builder);
builder.setSmartDashboardType("PowerDistributionPanel");
}
};
putData("Values", builder -> {
builder.addDoubleProperty("Match Time", DriverStation::getMatchTime, null);
builder.addDoubleProperty("Voltage", pdp::getVoltage, null);
builder.addDoubleProperty("Claws", m_robotClaws.m_rightClaw::get, null);
builder.addDoubleProperty("Arm", m_robotMap.elbow::get, null);
builder.addDoubleProperty("Intake", m_robotMap.intakeMotor::get, null);
builder.addDoubleProperty("Serializer", m_robotMap.serializerBelt::get, null);
builder.addDoubleProperty("Storage", m_robotMap.storageMotor::get, null);
builder.addDoubleProperty("Drum", m_robotMap.shooterFalconRight::get, null);
builder.addDoubleProperty("Angle", m_robotMap.shooterTurret::get, null);
builder.addDoubleProperty("Hood", m_robotMap.angleAdjusterMotor::get, null);
builder.addBooleanProperty("Shooter Safety", this::isLockedOn, null);
});
putData("Field", m_robotSwerveDrive.m_field);
putData("PDP", pdp);
putData("Extender", new NTSendable() {
@Override
public void initSendable(NTSendableBuilder builder) {
builder.setSmartDashboardType("String Chooser");
builder.getEntry(".instance").setDouble(0);
builder.addStringProperty("default", () -> "Retracted", null);
builder.addStringArrayProperty("options", () -> new String[] {"Retracted", "Extended"}, null);
builder.addStringProperty("active", () -> m_robotExtender.getPosition() <= 0 ? "Retracted" : "Extended", null);
builder.addStringProperty("selected", null, null);
}
});
putData("Drivebase", m_robotSwerveDrive);
putData("Gyro", m_robotSwerveDrive.m_gyro);
putData("Drive Speed", new NTSendable() {
@Override
public void initSendable(NTSendableBuilder builder) {
builder.setSmartDashboardType("String Chooser");
builder.getEntry(".instance").setDouble(0);
builder.addStringProperty("default", () -> "Low", null);
builder.addStringArrayProperty("options", () -> new String[] {"Low", "High"}, null);
builder.addStringProperty("active", () -> m_robotSwerveDrive.speedAdjust == SwerveDriveConstants.JOYSTICK_TO_METERS_PER_SECOND_SLOW ? "Low" : "High", null);
builder.addStringProperty("selected", null, val -> m_robotSwerveDrive.speedAdjust = val.equals("Low") ? SwerveDriveConstants.JOYSTICK_TO_METERS_PER_SECOND_SLOW : SwerveDriveConstants.JOYSTICK_TO_METERS_PER_SECOND_FAST);
}
});
putData("Accelerometer", new NTSendable() {
@Override
public void initSendable(NTSendableBuilder builder) {
builder.setSmartDashboardType("3AxisAccelerometer");
NetworkTableEntry entryX = builder.getEntry("X");
NetworkTableEntry entryY = builder.getEntry("Y");
NetworkTableEntry entryZ = builder.getEntry("Z");
builder.setUpdateTable(() -> {
short[] data = new short[3];
m_robotSwerveDrive.m_gyro.getBiasedAccelerometer(data);
entryX.setDouble(data[0]);
entryY.setDouble(data[1]);
entryZ.setDouble(data[2]);
});
}
});
putData("Drive Camera", m_robotCamera);
configureButtonBindings();
/* Default Commands */
// Swerve Drive with Input
@@ -328,7 +433,7 @@ public class RobotContainer {
new JoystickButton(getDriverController(), XboxController.Button.kY.value).whileHeld(new RunCommand(() -> m_robotExtender.runExtender(-1.0), m_robotExtender)).whenReleased(new RunCommand(() -> m_robotExtender.runExtender(0.0), m_robotExtender));
}
private boolean isLockedOn() {
public boolean isLockedOn() {
return m_robotTurret.isLockedOn() || m_robotHood.isLockedOn() || m_robotBoomBoom.isLockedOn();
}
@@ -338,30 +443,63 @@ public class RobotContainer {
* @return the command to run in autonomous
*/
public Command getAutonomousCommand() {
return autoChooser.getSelected();
return m_autoChooser.getSelected();
}
private final CommandGroupBase threeBallPlus = CommandGroupBase.sequence(
new InstantCommand(() -> m_robotBoomBoom.runDrumShooterVelocityPID(8000), m_robotBoomBoom).withName("StartIdlingShooter"),
new InstantCommand(() -> m_robotIntake.runAtOutput(-1), m_robotIntake).withName("StartRunningIntake"),
// new RunExtender(m_robotExtender).withName("DeployExtender"),
new InstantCommand(() -> m_robotSerializer.setSerializer(0.8), m_robotSerializer).withName("StartRunningSerializer"),
// Get Second Ball
new InstantCommand(() -> m_robotTurret.runShooterRotatePID(-180), m_robotTurret).withName("StartTurningShooter"),
makePathingGroup(AutoConstants.PATH_MAX_VEL, AutoConstants.PATH_MAX_ACCEL, "JMove1").withName("JMove1"),
// Shoot Preloaded and Second Ball
makeTimeoutTrackShotGroup(AutoConstants.LOCK_ON_DURATION, AutoConstants.LOCK_ON_TIME_ALLOWANCE, AutoConstants.STORAGE_TIME_TWO_BALLS, "FirstSecondBall"),
// Get Third Ball
new InstantCommand(() -> m_robotBoomBoom.runDrumShooterVelocityPID(8000), m_robotBoomBoom).withName("StartIdlingShooter"),
new InstantCommand(() -> m_robotTurret.runShooterRotatePID(-120), m_robotTurret).withName("StartTurningShooter"),
makePathingGroup(AutoConstants.PATH_MAX_VEL, AutoConstants.PATH_MAX_ACCEL, "JMove2").withName("JMove2"),
// Shoot Third Ball
makeTimeoutTrackShotGroup(AutoConstants.LOCK_ON_DURATION, AutoConstants.LOCK_ON_TIME_ALLOWANCE, AutoConstants.STORAGE_TIME_ONE_BALL, "ThirdBall"),
// Stop
new InstantCommand(() -> m_robotIntake.runAtOutput(0), m_robotIntake).withName("StopRunningIntake"),
new InstantCommand(() -> m_robotSerializer.setSerializer(0.0), m_robotSerializer).withName("StopRunningSerializer"),
new InstantCommand(() -> m_robotStorage.runStorage(0.0)).withName("ThirdBallStopFeed")
);
private Command makeOneBallCommand() {
return CommandGroupBase.sequence(
makeStartupCommandPart(),
// Shoot Preloaded Ball
makeTimeoutTrackShotGroup(AutoConstants.LOCK_ON_DURATION, AutoConstants.LOCK_ON_TIME_ALLOWANCE, AutoConstants.STORAGE_TIME_ONE_BALL, "FirstBall"),
makeStopCommandPart()
).withName("OneBall");
}
private Command makeTwoBallCommand() {
return CommandGroupBase.sequence(
makeStartupCommandPart(),
// Get Second Ball
new InstantCommand(() -> m_robotTurret.runShooterRotatePID(-180), m_robotTurret).withName("StartTurningShooter"),
new PathPlannerCommand("JMove1", AutoConstants.PATH_MAX_VEL, AutoConstants.PATH_MAX_ACCEL, m_robotSwerveDrive).withName("JMove1"),
// Shoot Preloaded and Second Ball
makeTimeoutTrackShotGroup(AutoConstants.LOCK_ON_DURATION, AutoConstants.LOCK_ON_TIME_ALLOWANCE, AutoConstants.STORAGE_TIME_TWO_BALLS, "FirstSecondBall"),
makeStopCommandPart()
).withName("TwoBall");
}
private Command makeThreeBallCommand() {
return CommandGroupBase.sequence(
makeStartupCommandPart(),
// Get Second Ball
new InstantCommand(() -> m_robotTurret.runShooterRotatePID(-180), m_robotTurret).withName("StartTurningShooter"),
new PathPlannerCommand("JMove1", AutoConstants.PATH_MAX_VEL, AutoConstants.PATH_MAX_ACCEL, m_robotSwerveDrive).withName("JMove1"),
// Shoot Preloaded and Second Ball
makeTimeoutTrackShotGroup(AutoConstants.LOCK_ON_DURATION, AutoConstants.LOCK_ON_TIME_ALLOWANCE, AutoConstants.STORAGE_TIME_TWO_BALLS, "FirstSecondBall"),
// Get Third Ball
new InstantCommand(() -> m_robotBoomBoom.runDrumShooterVelocityPID(8000), m_robotBoomBoom).withName("StartIdlingShooter"),
new InstantCommand(() -> m_robotTurret.runShooterRotatePID(-120), m_robotTurret).withName("StartTurningShooter"),
new PathPlannerCommand("JMove2", AutoConstants.PATH_MAX_VEL, AutoConstants.PATH_MAX_ACCEL, m_robotSwerveDrive).withName("JMove2"),
// Shoot Third Ball
makeTimeoutTrackShotGroup(AutoConstants.LOCK_ON_DURATION, AutoConstants.LOCK_ON_TIME_ALLOWANCE, AutoConstants.STORAGE_TIME_ONE_BALL, "ThirdBall"),
makeStopCommandPart()
).withName("ThreeBall");
}
private Command makeStartupCommandPart() {
return CommandGroupBase.sequence(
new InstantCommand(() -> m_robotBoomBoom.runDrumShooterVelocityPID(8000), m_robotBoomBoom).withName("StartIdlingShooter"),
new InstantCommand(() -> m_robotIntake.runAtOutput(-1), m_robotIntake).withName("StartRunningIntake")
// new RunExtender(m_robotExtender).withName("DeployExtender")
);
}
private Command makeStopCommandPart() {
return CommandGroupBase.sequence(
new InstantCommand(() -> m_robotIntake.runAtOutput(0), m_robotIntake).withName("StopRunningIntake"),
new InstantCommand(() -> m_robotSerializer.setSerializer(0.0), m_robotSerializer).withName("StopRunningSerializer"),
new InstantCommand(() -> m_robotStorage.runStorage(0.0)).withName("StopRunningStorage")
);
}
private ParallelDeadlineGroup makeTimeoutTrackShotGroup(double lockOnDuration, double lockOnTimeAllowance, double storageRunTime, String name) {
return CommandGroupBase.sequence(
@@ -372,30 +510,6 @@ public class RobotContainer {
);
}
/**
* Generate autonomous
* @param maxVel max velocity for the path (null to override default value of 5.0)
* @param maxAccel max acceleration for the path (null to override default value of 5.0)
* @param inputs strings (paths) or commands you want to run (in order)
* @return array of commands
*/
private SequentialCommandGroup makePathingGroup(Double maxVel, Double maxAccel, String input) {
PIDController xController = SwerveDriveConstants.X_CONTROLLER;
PIDController yController = SwerveDriveConstants.Y_CONTROLLER;
ProfiledPIDController thetaController = SwerveDriveConstants.THETA_CONTROLLER;
thetaController.enableContinuousInput(-Math.PI, Math.PI);
PathPlannerTrajectory pathTrajectory = PathPlanner.loadPath(input, Objects.requireNonNullElse(maxVel, SwerveDriveConstants.PATH_MAX_VELOCITY), Objects.requireNonNullElse(maxAccel, SwerveDriveConstants.PATH_MAX_ACCELERATION));
PathPlannerState initialState = pathTrajectory.getInitialState();
Pose2d initialPosition = new Pose2d(initialState.poseMeters.getTranslation(), initialState.holonomicRotation);
return new SequentialCommandGroup(
new InstantCommand(() -> m_robotSwerveDrive.resetOdometry(initialPosition), m_robotSwerveDrive),
new PPSwerveControllerCommand(pathTrajectory, m_robotSwerveDrive::getOdometry, m_robotSwerveDrive.m_kinematics, xController, yController, thetaController, m_robotSwerveDrive::setModuleStates, m_robotSwerveDrive),
new InstantCommand(m_robotSwerveDrive::stopModules, m_robotSwerveDrive)
);
}
public void switchControlMode() {
currentControlMode = currentControlMode == ControlMode.SHOOTER ? ControlMode.CLIMBER : ControlMode.SHOOTER;
}
+8 -8
View File
@@ -255,14 +255,14 @@ public class RobotMap {
}
private void registerDevices() {
SendableRegistry.setName(frontLeftSteerMotor, "SwerveDrive", "Left Front Steer Motor");
SendableRegistry.setName(frontLeftDriveMotor, "SwerveDrive", "Left Front Drive Motor");
SendableRegistry.setName(frontRightSteerMotor, "SwerveDrive", "Right Front Steer Motor");
SendableRegistry.setName(frontRightDriveMotor, "SwerveDrive", "Right Front Drive Motor");
SendableRegistry.setName(backLeftSteerMotor, "SwerveDrive", "Left Back Steer Motor");
SendableRegistry.setName(backLeftDriveMotor, "SwerveDrive", "Left Back Drive Motor");
SendableRegistry.setName(backRightSteerMotor, "SwerveDrive", "Right Back Steer Motor");
SendableRegistry.setName(backRightDriveMotor, "SwerveDrive", "Right Back Drive Motor");
SendableRegistry.setName(frontLeftSteerMotor, "Drive", "Left Front Steer Motor");
SendableRegistry.setName(frontLeftDriveMotor, "Drive", "Left Front Drive Motor");
SendableRegistry.setName(frontRightSteerMotor, "Drive", "Right Front Steer Motor");
SendableRegistry.setName(frontRightDriveMotor, "Drive", "Right Front Drive Motor");
SendableRegistry.setName(backLeftSteerMotor, "Drive", "Left Back Steer Motor");
SendableRegistry.setName(backLeftDriveMotor, "Drive", "Left Back Drive Motor");
SendableRegistry.setName(backRightSteerMotor, "Drive", "Right Back Steer Motor");
SendableRegistry.setName(backRightDriveMotor, "Drive", "Right Back Drive Motor");
SendableRegistry.setName(intakeMotor, "Intake", "Intake Motor");
SendableRegistry.setName((SendableCANSparkMax) extenderMotor, "Intake", "Extender Motor");
@@ -0,0 +1,60 @@
package frc4388.robot.commands;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.pathplanner.lib.PathPlanner;
import com.pathplanner.lib.PathPlannerTrajectory;
import com.pathplanner.lib.PathPlannerTrajectory.PathPlannerState;
import com.pathplanner.lib.commands.PPSwerveControllerCommand;
import edu.wpi.first.math.controller.PIDController;
import edu.wpi.first.math.controller.ProfiledPIDController;
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.wpilibj2.command.InstantCommand;
import edu.wpi.first.wpilibj2.command.PrintCommand;
import edu.wpi.first.wpilibj2.command.SequentialCommandGroup;
import frc4388.robot.Constants.SwerveDriveConstants;
import frc4388.robot.subsystems.SwerveDrive;
public class PathPlannerCommand extends SequentialCommandGroup {
private static final Logger LOGGER = Logger.getLogger(PathPlannerCommand.class.getSimpleName());
/** Function that removes the ".path" from the end of a string. */
private static final Function<CharSequence, String> PATH_EXTENSION_REMOVER = ((Function<CharSequence, Matcher>) Pattern.compile(".path")::matcher).andThen(m -> m.replaceFirst(""));
private final SwerveDrive m_swerveDrive;
public PathPlannerCommand(String pathFileName, SwerveDrive swerveDrive) {
this(pathFileName, SwerveDriveConstants.PATH_MAX_VELOCITY, SwerveDriveConstants.PATH_MAX_ACCELERATION, swerveDrive);
}
public PathPlannerCommand(String pathName, double maxVel, double maxAccel, SwerveDrive swerveDrive) {
m_swerveDrive = swerveDrive;
addRequirements(swerveDrive);
String pathFileName = PATH_EXTENSION_REMOVER.apply(pathName);
setName(pathFileName);
PIDController xController = SwerveDriveConstants.X_CONTROLLER;
PIDController yController = SwerveDriveConstants.Y_CONTROLLER;
ProfiledPIDController thetaController = SwerveDriveConstants.THETA_CONTROLLER;
thetaController.enableContinuousInput(-Math.PI, Math.PI);
LOGGER.log(Level.WARNING, "Loading path {0}.", pathFileName);
PathPlannerTrajectory pathTrajectory = PathPlanner.loadPath(pathFileName, maxVel, maxAccel);
if (pathTrajectory != null) {
LOGGER.info("Done loading.");
m_swerveDrive.m_field.getObject("traj").setTrajectory(pathTrajectory);
PathPlannerState initialState = pathTrajectory.getInitialState();
Pose2d initialPosition = new Pose2d(initialState.poseMeters.getTranslation(), initialState.holonomicRotation);
addCommands(
new InstantCommand(() -> m_swerveDrive.resetOdometry(initialPosition), m_swerveDrive),
new PPSwerveControllerCommand(pathTrajectory, m_swerveDrive::getOdometry, m_swerveDrive.m_kinematics, xController, yController, thetaController, m_swerveDrive::setModuleStates, m_swerveDrive),
new InstantCommand(m_swerveDrive::stopModules, m_swerveDrive)
);
} else {
LOGGER.log(Level.SEVERE, "Failed loading path {0}.", pathFileName);
addCommands(new PrintCommand("Path failed to load."));
}
}
}
@@ -18,16 +18,11 @@ import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import com.diffplug.common.base.Errors;
import com.pathplanner.lib.PathPlanner;
import com.pathplanner.lib.PathPlannerTrajectory;
import edu.wpi.first.math.Pair;
import edu.wpi.first.math.geometry.Rotation2d;
@@ -36,7 +31,7 @@ import edu.wpi.first.networktables.NetworkTable;
import edu.wpi.first.networktables.NetworkTableInstance;
import edu.wpi.first.wpilibj.Filesystem;
import edu.wpi.first.wpilibj.RobotBase;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.Command;
import edu.wpi.first.wpilibj2.command.CommandBase;
import edu.wpi.first.wpilibj2.command.InstantCommand;
import edu.wpi.first.wpilibj2.command.NotifierCommand;
@@ -44,24 +39,22 @@ import frc4388.robot.Constants.SwerveDriveConstants;
import frc4388.robot.subsystems.SwerveDrive;
import frc4388.utility.PathPlannerUtil;
import frc4388.utility.PathPlannerUtil.Path.Waypoint;
import frc4388.utility.shuffleboard.ListeningSendableChooser;
import frc4388.utility.shuffleboard.CachingSendableChooser;
// TODO: Fix naming inconsistency with fields.
public class PathRecorder extends CommandBase {
private static final Logger LOGGER = Logger.getLogger(PathRecorder.class.getSimpleName());
private static double PATH_POLLING_PERIOD = 0.5;
private static final double PATH_POLLING_PERIOD = 0.5;
private static final Clock SYSTEM_CLOCK = Clock.system(ZoneId.systemDefault());
private static final Path PATHPLANNER_DIRECTORY = Filesystem.getDeployDirectory().toPath().resolve("pathplanner");
private static final String PATHPLANNER_EXTENSION = ".path";
private static final DateTimeFormatter RECORDING_FILE_NAME_FORMATTER = DateTimeFormatter.ofPattern("'Recording' yyyy-MM-dd HH-mm-ss.SSS'.path'");
// Function that removes the ".path" from the end of a string.
private static final Function<CharSequence, String> PATH_EXTENSION_REMOVER = ((Function<CharSequence, Matcher>) Pattern.compile(PATHPLANNER_EXTENSION)::matcher).andThen(m -> m.replaceFirst(""));
private static final String RECORDING_PREFIX = "Recording";
private static final DateTimeFormatter RECORDING_FILE_NAME_FORMATTER = DateTimeFormatter.ofPattern("'" + RECORDING_PREFIX + "' yyyy-MM-dd HH-mm-ss.SSS'.path'");
private PathPlannerTrajectory loadedPathTrajectory = null;
private WatchKey m_watchKey;
private final ListeningSendableChooser<File> pathChooser = new ListeningSendableChooser<>(this::loadPath);
private final CachingSendableChooser<Command> m_autoChooser;
private final List<Waypoint> pathPoints = new ArrayList<>();
private final NetworkTableInstance networkTableInstance = NetworkTableInstance.getDefault();
private final NetworkTable recordingNetworkTable = networkTableInstance.getTable("Recording");
private final NetworkTable recordingNetworkTable = networkTableInstance.getTable(RECORDING_PREFIX);
private final SwerveDrive m_swerveDrive;
private final CommandBase m_pathWatcher = new NotifierCommand(this::updatePathChooser, PATH_POLLING_PERIOD) {
@Override
@@ -76,8 +69,9 @@ public class PathRecorder extends CommandBase {
}
}.withName("Save Recording");
public PathRecorder(SwerveDrive swerveDrive) {
public PathRecorder(SwerveDrive swerveDrive, CachingSendableChooser<Command> autoChooser) {
m_swerveDrive = swerveDrive;
m_autoChooser = autoChooser;
addRequirements(swerveDrive);
setName("Record Path (Cancel to Save)");
}
@@ -85,7 +79,7 @@ public class PathRecorder extends CommandBase {
/**
* Creates a WatchKey for the path planner directory and registers it with the WatchService. Then
* creates a NotifierCommand that will update the path chooser with the latest path files. Finally,
* adds the existing path files to the path chooser
* adds the existing recorded path files to the path chooser
*/
@Override
public void initialize() {
@@ -95,8 +89,7 @@ public class PathRecorder extends CommandBase {
} catch (IOException exception) {
LOGGER.log(Level.SEVERE, "Exception with path file watcher.", exception);
}
Arrays.stream(PATHPLANNER_DIRECTORY.toFile().listFiles()).filter(file -> file.getName().endsWith(PATHPLANNER_EXTENSION)).sorted(Comparator.comparingLong(File::lastModified)).forEachOrdered(file -> pathChooser.addOption(file.getName(), file));
SmartDashboard.putData("Path Chooser", pathChooser);
Arrays.stream(PATHPLANNER_DIRECTORY.toFile().listFiles()).filter(file -> file.getName().endsWith(PATHPLANNER_EXTENSION) && file.getName().startsWith(RECORDING_PREFIX)).sorted(Comparator.comparingLong(File::lastModified)).forEachOrdered(file -> m_autoChooser.addOption(file.getName(), () -> new PathPlannerCommand(file.getName(), m_swerveDrive)));
}
/**
@@ -107,7 +100,7 @@ public class PathRecorder extends CommandBase {
public void execute() {
Translation2d position = m_swerveDrive.m_odometry.getPoseMeters().getTranslation();
Rotation2d rotation = m_swerveDrive.m_gyro.getRotation2d();
// FIXME: Chassis speeds are created from joystick inputs and do not reflect actual robot velocity.
// FIXME: Chassis speeds are created from joystick inputs and may not reflect actual robot velocity.
Translation2d velocity = new Translation2d(m_swerveDrive.getChassisSpeeds().vxMetersPerSecond, m_swerveDrive.getChassisSpeeds().vyMetersPerSecond);
Waypoint waypoint = new Waypoint(position, position, position, rotation.getDegrees(), false, SwerveDriveConstants.PATH_RECORD_VELOCITY ? velocity.getNorm() : null, false);
pathPoints.add(waypoint);
@@ -125,10 +118,6 @@ public class PathRecorder extends CommandBase {
m_pathSaver.schedule();
}
public PathPlannerTrajectory getPath() {
return loadedPathTrajectory;
}
/**
* Called when a file is created, modified, or deleted. Adds newly created .path files to the
* SendableChooser. Reloads the path if the currently selected file is modified.
@@ -145,12 +134,12 @@ public class PathRecorder extends CommandBase {
if (watchEventFileName.endsWith(PATHPLANNER_EXTENSION)) {
if (pathWatchEvent.kind().equals(StandardWatchEventKinds.ENTRY_CREATE)) {
LOGGER.log(Level.WARNING, "PathPlanner file {0} created. Options added to SendableChooser.", watchEventFileName);
pathChooser.addOption(watchEventFile.getName(), watchEventFile);
m_autoChooser.addOption(watchEventFile.getName(), () -> new PathPlannerCommand(watchEventFile.getName(), m_swerveDrive));
} else if (pathWatchEvent.kind().equals(StandardWatchEventKinds.ENTRY_MODIFY)) {
LOGGER.log(Level.WARNING, "PathPlanner file {0} modified.", watchEventFileName);
if (watchEventFileName.equals(pathChooser.getSelected().getName())) {
if (watchEventFileName.equals(m_autoChooser.getSelected().getName())) {
LOGGER.log(Level.SEVERE, "PathPlanner file {0} already selected. Reloading path.", watchEventFileName);
loadPath(watchEventFileName);
m_autoChooser.reloadSelected();
}
} else if (pathWatchEvent.kind().equals(StandardWatchEventKinds.ENTRY_DELETE)) {
LOGGER.log(Level.SEVERE, "PathPlanner file {0} deleted. Removing options from SendableChooser not yet implemented.", watchEventFileName);
@@ -162,20 +151,6 @@ public class PathRecorder extends CommandBase {
}
}
private void loadPath(String pathName) {
LOGGER.warning("Unloading path.");
loadedPathTrajectory = null;
LOGGER.info("Done unloading.");
if (pathName != null) {
LOGGER.log(Level.WARNING, "Loading path {0}.", pathName);
loadedPathTrajectory = PathPlanner.loadPath(PATH_EXTENSION_REMOVER.apply(pathName), SwerveDriveConstants.PATH_MAX_VELOCITY, SwerveDriveConstants.PATH_MAX_ACCELERATION);
m_swerveDrive.m_field.getObject("traj").setTrajectory(loadedPathTrajectory);
LOGGER.info("Done loading.");
} else {
LOGGER.severe("No path to load.");
}
}
private void savePath() {
// INFO: Had to chown the pathplanner folder in order to save paths.
File outputFile = PATHPLANNER_DIRECTORY.resolve(ZonedDateTime.now(SYSTEM_CLOCK).format(RECORDING_FILE_NAME_FORMATTER)).toFile();
@@ -217,7 +192,6 @@ public class PathRecorder extends CommandBase {
StringWriter writer = new StringWriter();
path.write(writer);
recordingNetworkTable.getEntry(outputFile.getName()).setString(writer.toString());
pathChooser.setDefaultOption(outputFile.getName(), outputFile);
LOGGER.log(Level.INFO, "Recorded path to {0}.", outputFile.getPath());
} else LOGGER.log(Level.SEVERE, "Unable to record path to {0}", outputFile.getPath());
}
@@ -61,7 +61,6 @@ public class ShooterTuner extends CommandBase {
sbi.setTable(NetworkTableInstance.getDefault().getTable("ShooterTuner/Manual Shooter Data"));
sbi.addDoubleProperty("Drum Velocity", () -> tableOverrideEntry.drumVelocity, d -> tableOverrideEntry.drumVelocity = d);
sbi.addDoubleProperty("Hood Extension", () -> tableOverrideEntry.hoodExt, d -> tableOverrideEntry.hoodExt = d);
sbi.addDoubleProperty("Turret Offset", () -> tableOverrideEntry.turretOffset, d -> tableOverrideEntry.turretOffset = d);
sbi.addDoubleProperty("Distance", () -> tableOverrideEntry.distance, d -> tableOverrideEntry.distance = d);
sbi.addBooleanProperty("Measure Distance", () -> measureDistance, b -> measureDistance = b);
// manual.add("Manual Shooter Data", m_shotEditor);
@@ -79,7 +78,6 @@ public class ShooterTuner extends CommandBase {
tableOverrideEntry.distance = 0.0;
tableOverrideEntry.hoodExt = 0.0;
tableOverrideEntry.drumVelocity = 0.0;
tableOverrideEntry.turretOffset = 0.0;
m_boomBoom.setShooterTable(new ShooterTableEntry[] { tableOverrideEntry });
Shuffleboard.selectTab("Shooter Tuner");
SmartDashboard.putData("Shooter Table", m_tableEditor);
@@ -116,7 +114,6 @@ public class ShooterTuner extends CommandBase {
builder.setSmartDashboardType("RobotPreferences");
builder.addDoubleProperty("Drum Velocity", () -> tableOverrideEntry.drumVelocity, d -> tableOverrideEntry.drumVelocity = d);
builder.addDoubleProperty("Hood Extension", () -> tableOverrideEntry.hoodExt, d -> tableOverrideEntry.hoodExt = d);
builder.addDoubleProperty("Turret Offset", () -> tableOverrideEntry.turretOffset, d -> tableOverrideEntry.turretOffset = d);
builder.addDoubleProperty("Distance", () -> tableOverrideEntry.distance, d -> tableOverrideEntry.distance = d);
builder.addBooleanProperty("Measure Distance", () -> measureDistance, b -> measureDistance = b);
}
@@ -55,7 +55,7 @@ public class BoomBoom extends SubsystemBase {
// SimpleMotorFeedforward feedforward = new SimpleMotorFeedforward(69, 42, 0); //get real values later
public static class ShooterTableEntry {
public Double distance, hoodExt, drumVelocity, turretOffset;
public Double distance, hoodExt, drumVelocity;
}
private ShooterTableEntry[] m_shooterTable;
@@ -101,17 +101,6 @@ public class BoomBoom extends SubsystemBase {
return NumericData.linearInterpolate(m_shooterTable, distance, e -> e.distance, e -> e.hoodExt).doubleValue();
}
/**
* This is a function that takes a value (distance) and returns a value (turretOffset) that is a linear
* interpolation of the two values (turretOffset) at the two closest points in the table (m_shooterTable)
* to the given value (distance).
* @param distance Distance in shooter table
* @return Turret offset in degrees
*/
public Double getOffset(final Double distance) {
return NumericData.linearInterpolate(m_shooterTable, distance, e -> e.distance, e -> e.turretOffset).doubleValue();
}
@Override
public void periodic() {
// This method will be called once per scheduler run
@@ -191,7 +180,6 @@ public class BoomBoom extends SubsystemBase {
dummyEntry.distance = 0.0;
dummyEntry.hoodExt = 0.0;
dummyEntry.drumVelocity = 0.0;
dummyEntry.turretOffset = 0.0;
LOGGER.log(Level.SEVERE, "Exception while reading shooter CSV table.", exception);
m_shooterTable = new ShooterTableEntry[] { dummyEntry };
}
@@ -57,10 +57,6 @@ public class SwerveDrive extends SubsystemBase {
public final Field2d m_field = new Field2d();
public SwerveDrive(SwerveModule frontLeft, SwerveModule frontRight, SwerveModule backLeft, SwerveModule backRight, WPI_Pigeon2 gyro) {
//XXX:
setName("Swerve Drive");
addChild("Gyro", m_gyro);
m_frontLeft = frontLeft;
m_frontRight = frontRight;
m_backLeft = backLeft;
@@ -99,7 +99,7 @@ public class AnsiLogging {
ZonedDateTime time = ZonedDateTime.ofInstant(logRecord.getInstant(), ZONE_ID);
// Get the logger name, source class name, and/or source method name.
String source = Optional.ofNullable(logRecord.getLoggerName()).or(() -> Optional.ofNullable(logRecord.getSourceClassName())).map(s -> s + " ").orElse("") + Optional.ofNullable(logRecord.getSourceMethodName()).orElse("");
String message = logRecord.getMessage();
String message = formatMessage(logRecord);
// Get the stack trace of the exception if it was thrown.
String throwable = Optional.ofNullable(logRecord.getThrown()).map(LoggingAnsiFormatter::makeStackTraceString).orElse("");
// Select the appropriate format string for the log level.
@@ -0,0 +1,170 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
// package edu.wpi.first.wpilibj.smartdashboard;
package frc4388.utility.shuffleboard;
import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam;
import edu.wpi.first.networktables.NTSendable;
import edu.wpi.first.networktables.NTSendableBuilder;
import edu.wpi.first.networktables.NetworkTableEntry;
import edu.wpi.first.util.sendable.SendableRegistry;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
/**
* The {@link CachingSendableChooser} class is a useful tool for presenting a selection of options to the
* {@link SmartDashboard}.
*
* <p>For instance, you may wish to be able to select between multiple autonomous modes. You can do
* this by putting every possible Command you want to run as an autonomous into a {@link
* CachingSendableChooser} and then put it into the {@link SmartDashboard} to have a list of options appear
* on the laptop. Once autonomous starts, simply ask the {@link CachingSendableChooser} what the selected
* value is.
*
* @param <V> The type of the values to be stored
*/
public class CachingSendableChooser<V> implements NTSendable, AutoCloseable {
/** The key for the default value. */
private static final String DEFAULT = "default";
/** The key for the selected option. */
private static final String SELECTED = "selected";
/** The key for the active option. */
private static final String ACTIVE = "active";
/** The key for the option array. */
private static final String OPTIONS = "options";
/** The key for the instance number. */
private static final String INSTANCE = ".instance";
/** A map linking strings to the objects the represent. */
private final Map<String, Supplier<V>> m_map = new LinkedHashMap<>();
private String m_defaultChoice = "";
private final int m_instance;
private static final AtomicInteger s_instances = new AtomicInteger();
/** Instantiates a {@link ListeningSendableChooser}. */
public CachingSendableChooser() {
m_instance = s_instances.getAndIncrement();
SendableRegistry.add(this, "SendableChooser", m_instance);
}
@Override
public void close() {
SendableRegistry.remove(this);
}
/**
* Adds the given object to the list of options. On the {@link SmartDashboard} on the desktop, the
* object will appear as the given name.
*
* @param name the name of the option
* @param object the option
*/
public void addOption(String name, Supplier<V> object) {
m_map.put(name, object);
}
/**
* Adds the given object to the list of options and marks it as the default. Functionally, this is
* very close to {@link #addOption(String, Object)} except that it will use this as the default
* option if none other is explicitly selected.
*
* @param name the name of the option
* @param object the option
*/
public void setDefaultOption(String name, Supplier<V> object) {
requireNonNullParam(name, "name", "setDefaultOption");
m_defaultChoice = name;
addOption(name, object);
m_mutex.lock();
try {
m_selectedValue = object.get();
} finally {
m_mutex.unlock();
}
}
/**
* Returns the selected option. If there is none selected, it will return the default. If there is
* none selected and no default, then it will return {@code null}.
*
* @return the option selected
*/
public V getSelected() {
m_mutex.lock();
try {
return m_selectedValue;
} finally {
m_mutex.unlock();
}
}
/**
* Re-runs the value supplier for the current selection.
*/
public void reloadSelected() {
m_mutex.lock();
try {
m_selectedValue = m_map.get(m_selected).get();
} finally {
m_mutex.unlock();
}
}
private String m_selected;
private V m_selectedValue;
private final List<NetworkTableEntry> m_activeEntries = new ArrayList<>();
private final ReentrantLock m_mutex = new ReentrantLock();
@Override
public void initSendable(NTSendableBuilder builder) {
builder.setSmartDashboardType("String Chooser");
builder.getEntry(INSTANCE).setDouble(m_instance);
builder.addStringProperty(DEFAULT, () -> m_defaultChoice, null);
builder.addStringArrayProperty(OPTIONS, () -> m_map.keySet().toArray(new String[0]), null);
builder.addStringProperty(
ACTIVE,
() -> {
m_mutex.lock();
try {
if (m_selected != null) {
return m_selected;
} else {
return m_defaultChoice;
}
} finally {
m_mutex.unlock();
}
},
null);
m_mutex.lock();
try {
m_activeEntries.add(builder.getEntry(ACTIVE));
} finally {
m_mutex.unlock();
}
builder.addStringProperty(
SELECTED,
null,
val -> {
m_mutex.lock();
try {
m_selected = val;
m_selectedValue = m_map.get(val).get();
for (NetworkTableEntry entry : m_activeEntries) {
entry.setString(val);
}
} finally {
m_mutex.unlock();
}
});
}
}
@@ -24,18 +24,17 @@ public class SendableTable implements Sendable {
public void initSendable(SendableBuilder builder) {
builder.setSmartDashboardType("Table");
builder.addRawProperty("table", this::getTableAsBytes, this::setTableFromBytes);
builder.addStringArrayProperty("header", () -> new String[] { "distance", "hoodExt", "drumVelocity", "turretOffset" }, null);
builder.addStringArrayProperty("header", () -> new String[] { "distance", "hoodExt", "drumVelocity"}, null);
}
private byte[] getTableAsBytes() {
ShooterTableEntry[] table = m_tableGetter.get();
if (!Arrays.equals(tableCache, table)) {
ByteBuffer byteBuffer = ByteBuffer.allocate(Double.BYTES * 4 * table.length);
ByteBuffer byteBuffer = ByteBuffer.allocate(Double.BYTES * 3 * table.length);
Arrays.stream(table).forEach(e -> {
byteBuffer.putDouble(e.distance);
byteBuffer.putDouble(e.hoodExt);
byteBuffer.putDouble(e.drumVelocity);
byteBuffer.putDouble(e.turretOffset);
});
tableCache = table;
bytesCache = byteBuffer.array();
@@ -46,12 +45,11 @@ public class SendableTable implements Sendable {
private void setTableFromBytes(byte[] bytes) {
if (bytes.length > 0 && !Arrays.equals(bytesCache, bytes)) {
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
ShooterTableEntry[] table = new ShooterTableEntry[bytes.length / (4 * Double.BYTES)];
ShooterTableEntry[] table = new ShooterTableEntry[bytes.length / (3 * Double.BYTES)];
for (int i = 0; i < table.length; i++) {
table[i].distance = byteBuffer.getDouble();
table[i].hoodExt = byteBuffer.getDouble();
table[i].drumVelocity = byteBuffer.getDouble();
table[i].turretOffset = byteBuffer.getDouble();
}
m_tableSetter.accept(table);
}