Slightly overhaul status system

This commit is contained in:
Michael Mikovsky
2025-07-11 17:58:22 -06:00
parent bf4da44338
commit c183c08a3d
19 changed files with 698 additions and 194 deletions
+4 -95
View File
@@ -7,10 +7,6 @@
package frc4388.robot; package frc4388.robot;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
// Advantagekit // Advantagekit
import org.littletonrobotics.junction.LogFileUtil; import org.littletonrobotics.junction.LogFileUtil;
import org.littletonrobotics.junction.LoggedRobot; import org.littletonrobotics.junction.LoggedRobot;
@@ -19,23 +15,16 @@ import org.littletonrobotics.junction.networktables.NT4Publisher;
import org.littletonrobotics.junction.wpilog.WPILOGReader; import org.littletonrobotics.junction.wpilog.WPILOGReader;
import org.littletonrobotics.junction.wpilog.WPILOGWriter; import org.littletonrobotics.junction.wpilog.WPILOGWriter;
import com.ctre.phoenix6.CANBus;
import com.ctre.phoenix6.CANBus.CANBusStatus;
import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.Command;
import edu.wpi.first.wpilibj2.command.CommandScheduler; import edu.wpi.first.wpilibj2.command.CommandScheduler;
import frc4388.robot.constants.BuildConstants; import frc4388.robot.constants.BuildConstants;
import frc4388.robot.constants.Constants;
import frc4388.robot.constants.Constants.SimConstants; import frc4388.robot.constants.Constants.SimConstants;
import frc4388.utility.DeferredBlock; import frc4388.utility.DeferredBlock;
import frc4388.utility.DeferredBlockMulti; import frc4388.utility.DeferredBlockMulti;
import frc4388.utility.Trim; import frc4388.utility.Trim;
import frc4388.utility.compute.RobotTime; import frc4388.utility.compute.RobotTime;
import frc4388.utility.status.CanDevice; import frc4388.utility.status.FaultReporter;
import frc4388.utility.status.Status;
import frc4388.utility.status.Subsystem;
import frc4388.utility.status.Status.Report;
import frc4388.utility.status.Status.ReportLevel;
//import frc4388.robot.subsystems.LED; //import frc4388.robot.subsystems.LED;
/** /**
* The VM is configured to automatically run this class, and to call the * The VM is configured to automatically run this class, and to call the
@@ -63,23 +52,7 @@ public class Robot extends LoggedRobot {
// Create a shuffleboard update thread, that will periodically update the values on shuffleboard // Create a shuffleboard update thread, that will periodically update the values on shuffleboard
new Thread() { FaultReporter.startThread();
public void run() {
try{
while(!this.isInterrupted() && this.isAlive()){
Thread.sleep(500);
for(int i=0;i<Subsystem.subsystems.size(); i++){
Subsystem.subsystems.get(i).queryStatus();
}
// System.out.println("Updated statuses!");
}
}catch(Exception e){
e.printStackTrace();
}
}
}.start();
} }
/** /**
@@ -189,71 +162,7 @@ public class Robot extends LoggedRobot {
@Override @Override
public void testInit() { public void testInit() {
FaultReporter.printReport();
List<String> errors = new ArrayList<>();
// Subsystems header
System.out.println(new String(Base64.getDecoder().decode("IOKWl+KWhOKWhOKWluKWl+KWliDilpfilpbilpfiloTiloTilpYgIOKWl+KWhOKWhOKWluKWl+KWliAg4paX4paW4paX4paE4paE4paW4paX4paE4paE4paE4paW4paX4paE4paE4paE4paW4paX4paWICDilpfilpYg4paX4paE4paE4paWCuKWkOKWjCAgIOKWkOKWjCDilpDilozilpDilowg4paQ4paM4paQ4paMICAgIOKWneKWmuKWnuKWmOKWkOKWjCAgICAg4paIICDilpDilowgICDilpDilpvilprilp7ilpzilozilpDilowgICAKIOKWneKWgOKWmuKWluKWkOKWjCDilpDilozilpDilpviloDilprilpYg4pad4paA4paa4paWICDilpDilowgIOKWneKWgOKWmuKWliAg4paIICDilpDilpviloDiloDilpjilpDilowgIOKWkOKWjCDilp3iloDilprilpYK4paX4paE4paE4pae4paY4pad4paa4paE4pae4paY4paQ4paZ4paE4pae4paY4paX4paE4paE4pae4paYICDilpDilowg4paX4paE4paE4pae4paYICDiloggIOKWkOKWmeKWhOKWhOKWluKWkOKWjCAg4paQ4paM4paX4paE4paE4pae4paY")));
for(int i=0;i< Subsystem.subsystems.size();i++){
Subsystem subsystem = Subsystem.subsystems.get(i);
System.out.println("** Subsystem diagnostic report for " + subsystem.getName() + ":");
Status status = subsystem.diagnosticStatus();
for(int a=0;a<status.reports.size();a++){
Report r = status.reports.get(a);
if(r.reportLevel == ReportLevel.ERROR)
errors.add(subsystem.getName() + " - " + r.toString());
subsystem.Log(r.toString());
}
}
// CAN header
System.out.println(new String(Base64.getDecoder().decode("IOKWl+KWhOKWhOKWliDilpfiloTilpYg4paX4paWICDilpfilpYK4paQ4paMICAg4paQ4paMIOKWkOKWjOKWkOKWm+KWmuKWluKWkOKWjArilpDilowgICDilpDilpviloDilpzilozilpDilowg4pad4pac4paMCuKWneKWmuKWhOKWhOKWluKWkOKWjCDilpDilozilpDilowgIOKWkOKWjCh0KQ==")));
CANBus canBus = new CANBus(Constants.CANBUS_NAME);
CANBusStatus canInfo = canBus.getStatus();
System.out.println("CANInfo BusOffCount - " + canInfo.BusOffCount);
System.out.println("CANInfo BusUtilization - " + canInfo.BusUtilization);
System.out.println("CANInfo RX Errors count - " + canInfo.REC);
System.out.println("CANInfo TX Errors count - " + canInfo.TEC);
System.out.println("CANInfo Transmit buffer full count - " + canInfo.TxFullCount);
// Broken turniary operator
ReportLevel canReportLevel = canInfo.Status.isOK() ? (canInfo.Status.isWarning() ? ReportLevel.WARNING : ReportLevel.ERROR) : ReportLevel.INFO;
String canStatus = "CAN " + canReportLevel.name() + " - " + canInfo.Status.getName() + " (" + canInfo.Status.getDescription() + ")";
if(canReportLevel == ReportLevel.ERROR) {
errors.add(canStatus);
}
System.out.println(canStatus);
for(int i=0;i<CanDevice.devices.size();i++){
CanDevice device = CanDevice.devices.get(i);
System.out.println("** CAN diagnostic report for " + device.name + ":");
Status status = device.diagnosticStatus();
for(int a=0;a<status.reports.size();a++){
Report r = status.reports.get(a);
if(r.reportLevel == ReportLevel.ERROR)
errors.add(device.getName() + " - " + r.toString());
device.Log(r.toString());
}
}
// System.out.println("Found CAN devices: " + new DeviceFinder().Find());
if(errors.size() > 0) {
// Errors header
System.out.println(new String(Base64.getDecoder().decode("4paX4paE4paE4paE4paW4paX4paE4paE4paWIOKWl+KWhOKWhOKWliAg4paX4paE4paWIOKWl+KWhOKWhOKWliAg4paX4paE4paE4paWCuKWkOKWjCAgIOKWkOKWjCDilpDilozilpDilowg4paQ4paM4paQ4paMIOKWkOKWjOKWkOKWjCDilpDilozilpDilowgICAK4paQ4pab4paA4paA4paY4paQ4pab4paA4paa4paW4paQ4pab4paA4paa4paW4paQ4paMIOKWkOKWjOKWkOKWm+KWgOKWmuKWliDilp3iloDilprilpYK4paQ4paZ4paE4paE4paW4paQ4paMIOKWkOKWjOKWkOKWjCDilpDilozilp3ilpriloTilp7ilpjilpDilowg4paQ4paM4paX4paE4paE4pae4paY")));
for(int i=0;i<errors.size(); i++){
System.out.println(errors.get(i));
}
}
} }
+29 -4
View File
@@ -22,6 +22,10 @@ import frc4388.robot.constants.Constants.LiDARConstants;
import frc4388.robot.constants.Constants.VisionConstants; import frc4388.robot.constants.Constants.VisionConstants;
import frc4388.robot.constants.DriveConstants; import frc4388.robot.constants.DriveConstants;
import frc4388.robot.subsystems.Lidar; import frc4388.robot.subsystems.Lidar;
import frc4388.utility.status.FaultCANCoder;
import frc4388.utility.status.FaultPhotonCamera;
import frc4388.utility.status.FaultPidgeon2;
import frc4388.utility.status.FaultTalonFX;
/** /**
* Defines and holds all I/O objects on the Roborio. This is useful for unit * Defines and holds all I/O objects on the Roborio. This is useful for unit
@@ -38,10 +42,6 @@ public class RobotMap {
public final Lidar reverseLidar = new Lidar(LiDARConstants.REVERSE_LIDAR_DIO_CHANNEL, "Reverse"); public final Lidar reverseLidar = new Lidar(LiDARConstants.REVERSE_LIDAR_DIO_CHANNEL, "Reverse");
public RobotMap() {
configureDriveMotorControllers();
}
/* LED Subsystem */ /* LED Subsystem */
// public final Spark LEDController = new Spark(LEDConstants.LED_SPARK_ID); // public final Spark LEDController = new Spark(LEDConstants.LED_SPARK_ID);
@@ -66,6 +66,31 @@ public class RobotMap {
} }
public RobotMap() {
configureDriveMotorControllers();
FaultPhotonCamera.addDevice(leftCamera, "Left Camera");
FaultPhotonCamera.addDevice(rightCamera, "Right Camera");
FaultPidgeon2.addDevice(swerveDrivetrain.getPigeon2(), "Gyro");
FaultTalonFX.addDevice(elevator, "Elevator");
FaultTalonFX.addDevice(endeffector, "Endeffector");
FaultTalonFX.addDevice(swerveDrivetrain.getModule(0).getDriveMotor(), "Module 0 Drive");
FaultTalonFX.addDevice(swerveDrivetrain.getModule(0).getSteerMotor(), "Module 0 Steer");
FaultCANCoder.addDevice(swerveDrivetrain.getModule(0).getEncoder(), "Module 0 CANCoder");
FaultTalonFX.addDevice(swerveDrivetrain.getModule(1).getDriveMotor(), "Module 1 Drive");
FaultTalonFX.addDevice(swerveDrivetrain.getModule(1).getSteerMotor(), "Module 1 Steer");
FaultCANCoder.addDevice(swerveDrivetrain.getModule(1).getEncoder(), "Module 1 CANCoder");
FaultTalonFX.addDevice(swerveDrivetrain.getModule(2).getDriveMotor(), "Module 2 Drive");
FaultTalonFX.addDevice(swerveDrivetrain.getModule(2).getSteerMotor(), "Module 2 Steer");
FaultCANCoder.addDevice(swerveDrivetrain.getModule(2).getEncoder(), "Module 2 CANCoder");
FaultTalonFX.addDevice(swerveDrivetrain.getModule(3).getDriveMotor(), "Module 3 Drive");
FaultTalonFX.addDevice(swerveDrivetrain.getModule(3).getSteerMotor(), "Module 3 Steer");
FaultCANCoder.addDevice(swerveDrivetrain.getModule(3).getEncoder(), "Module 3 CANCoder");
}
public class RobotMapSim { public class RobotMapSim {
public PhotonCameraSim leftCamera; public PhotonCameraSim leftCamera;
public PhotonCameraSim rightCamera; public PhotonCameraSim rightCamera;
@@ -7,12 +7,12 @@ public final class BuildConstants {
public static final String MAVEN_GROUP = ""; public static final String MAVEN_GROUP = "";
public static final String MAVEN_NAME = "2025RidgeScape"; public static final String MAVEN_NAME = "2025RidgeScape";
public static final String VERSION = "unspecified"; public static final String VERSION = "unspecified";
public static final int GIT_REVISION = 164; public static final int GIT_REVISION = 165;
public static final String GIT_SHA = "06d525ade1118ae193383bc86066ea5563d86a1f"; public static final String GIT_SHA = "bf4da44338d8e91c50e34c3ac68dc12f9d335b08";
public static final String GIT_DATE = "2025-04-22 18:21:00 EDT"; public static final String GIT_DATE = "2025-07-11 14:07:53 MDT";
public static final String GIT_BRANCH = "advantagekit"; public static final String GIT_BRANCH = "advantagekit";
public static final String BUILD_DATE = "2025-07-11 15:07:31 EDT"; public static final String BUILD_DATE = "2025-07-11 17:46:28 MDT";
public static final long BUILD_UNIX_TIME = 1752260851324L; public static final long BUILD_UNIX_TIME = 1752277588371L;
public static final int DIRTY = 1; public static final int DIRTY = 1;
private BuildConstants(){} private BuildConstants(){}
@@ -243,5 +243,4 @@ public final class DriveConstants {
// misc // misc
public static final int TIMEOUT_MS = 30; public static final int TIMEOUT_MS = 30;
public static final int SMARTDASHBOARD_UPDATE_FRAME = 2; public static final int SMARTDASHBOARD_UPDATE_FRAME = 2;
} }
@@ -12,16 +12,18 @@ import com.ctre.phoenix6.hardware.TalonFX;
import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.drive.DifferentialDrive;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import frc4388.robot.constants.DriveConstants; import frc4388.robot.constants.DriveConstants;
import frc4388.utility.RobotGyro; import frc4388.utility.RobotGyro;
import frc4388.utility.compute.RobotTime; import frc4388.utility.compute.RobotTime;
import frc4388.utility.status.Status; import frc4388.utility.status.Status;
import frc4388.utility.status.Subsystem; import frc4388.utility.status.FaultReporter;
import frc4388.utility.status.Queryable;
/** /**
* Add your docs here. * Add your docs here.
*/ */
public class DiffDrive extends Subsystem { public class DiffDrive extends SubsystemBase implements Queryable {
// Put methods for controlling this subsystem // Put methods for controlling this subsystem
// here. Call these from Commands. // here. Call these from Commands.
@@ -40,7 +42,7 @@ public class DiffDrive extends Subsystem {
public DiffDrive(TalonFX leftFrontMotor, TalonFX rightFrontMotor, TalonFX leftBackMotor, public DiffDrive(TalonFX leftFrontMotor, TalonFX rightFrontMotor, TalonFX leftBackMotor,
TalonFX rightBackMotor, DifferentialDrive driveTrain, RobotGyro gyro) { TalonFX rightBackMotor, DifferentialDrive driveTrain, RobotGyro gyro) {
super(); FaultReporter.register(this);
m_leftFrontMotor = leftFrontMotor; m_leftFrontMotor = leftFrontMotor;
m_rightFrontMotor = rightFrontMotor; m_rightFrontMotor = rightFrontMotor;
@@ -90,7 +92,7 @@ public class DiffDrive extends Subsystem {
@Override @Override
public String getSubsystemName() { public String getName() {
return "Diff Drive"; return "Diff Drive";
} }
@@ -101,7 +103,7 @@ public class DiffDrive extends Subsystem {
@Override @Override
public Status diagnosticStatus() { public Status diagnosticStatus() {
Log("Diagnostic info for this has not been inplemented!"); //TODO // Log("Diagnostic info for this has not been inplemented!"); //TODO
return new Status(); return new Status();
} }
} }
@@ -10,14 +10,16 @@ import com.ctre.phoenix6.signals.NeutralModeValue;
import edu.wpi.first.wpilibj.DigitalInput; import edu.wpi.first.wpilibj.DigitalInput;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import frc4388.robot.constants.Constants.AutoConstants; import frc4388.robot.constants.Constants.AutoConstants;
import frc4388.robot.constants.Constants.ElevatorConstants; import frc4388.robot.constants.Constants.ElevatorConstants;
import frc4388.robot.constants.Constants.LEDConstants; import frc4388.robot.constants.Constants.LEDConstants;
import frc4388.utility.status.Status; import frc4388.utility.status.Status;
import frc4388.utility.status.Subsystem; import frc4388.utility.status.FaultReporter;
import frc4388.utility.status.Queryable;
import frc4388.utility.status.Status.ReportLevel; import frc4388.utility.status.Status.ReportLevel;
public class Elevator extends Subsystem { public class Elevator extends SubsystemBase implements Queryable {
/** Creates a new Elevator. */ /** Creates a new Elevator. */
private TalonFX elevatorMotor; private TalonFX elevatorMotor;
private TalonFX endeffectorMotor; private TalonFX endeffectorMotor;
@@ -77,6 +79,8 @@ public class Elevator extends Subsystem {
elevatorMotor.getConfigurator().apply(ElevatorConstants.ELEVATOR_PID); elevatorMotor.getConfigurator().apply(ElevatorConstants.ELEVATOR_PID);
endeffectorMotor.getConfigurator().apply(ElevatorConstants.ENDEFFECTOR_PID); endeffectorMotor.getConfigurator().apply(ElevatorConstants.ENDEFFECTOR_PID);
currentState = CoordinationState.Ready; currentState = CoordinationState.Ready;
FaultReporter.register(this);
} }
//PID methods //PID methods
@@ -370,7 +374,7 @@ public class Elevator extends Subsystem {
} }
@Override @Override
public String getSubsystemName() { public String getName() {
return "Elevator"; return "Elevator";
} }
@@ -382,8 +386,6 @@ public class Elevator extends Subsystem {
Status status = new Status(); Status status = new Status();
status.addReport(ReportLevel.INFO, "Elevator Mode: " + currentState.name()); status.addReport(ReportLevel.INFO, "Elevator Mode: " + currentState.name());
status.diagnoseHardwareCTRE("Elevator Motor", elevatorMotor);
status.diagnoseHardwareCTRE("Endeffector Motor", endeffectorMotor);
return status; return status;
} }
@@ -10,9 +10,11 @@ package frc4388.robot.subsystems;
import edu.wpi.first.wpilibj.DriverStation; import edu.wpi.first.wpilibj.DriverStation;
import edu.wpi.first.wpilibj.motorcontrol.Spark; import edu.wpi.first.wpilibj.motorcontrol.Spark;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import frc4388.robot.constants.Constants.LEDConstants; import frc4388.robot.constants.Constants.LEDConstants;
import frc4388.utility.status.Status; import frc4388.utility.status.Status;
import frc4388.utility.status.Subsystem; import frc4388.utility.status.FaultReporter;
import frc4388.utility.status.Queryable;
import frc4388.utility.status.Status.ReportLevel; import frc4388.utility.status.Status.ReportLevel;
import frc4388.utility.structs.LEDPatterns; import frc4388.utility.structs.LEDPatterns;
@@ -20,7 +22,10 @@ import frc4388.utility.structs.LEDPatterns;
* Allows for the control of a 5v LED Strip using a Rev Robotics Blinkin LED * Allows for the control of a 5v LED Strip using a Rev Robotics Blinkin LED
* Driver * Driver
*/ */
public class LED extends Subsystem { public class LED extends SubsystemBase implements Queryable {
public LED() {
FaultReporter.register(this);
}
private static Spark LEDController = new Spark(LEDConstants.LED_SPARK_ID); private static Spark LEDController = new Spark(LEDConstants.LED_SPARK_ID);
private LEDPatterns mode = LEDConstants.DEFAULT_PATTERN; private LEDPatterns mode = LEDConstants.DEFAULT_PATTERN;
@@ -44,7 +49,7 @@ public class LED extends Subsystem {
} }
@Override @Override
public String getSubsystemName() { public String getName() {
return "LEDs"; return "LEDs";
} }
@@ -57,9 +62,7 @@ public class LED extends Subsystem {
public Status diagnosticStatus() { public Status diagnosticStatus() {
Status status = new Status(); Status status = new Status();
if(LEDController.isAlive()) if(!LEDController.isAlive())
status.addReport(ReportLevel.INFO, "LED is CONNECTED");
else
status.addReport(ReportLevel.ERROR, "LED is DISCONNECTED"); status.addReport(ReportLevel.ERROR, "LED is DISCONNECTED");
status.addReport(ReportLevel.INFO, "LED Mode: " + mode.name()); status.addReport(ReportLevel.INFO, "LED Mode: " + mode.name());
@@ -6,19 +6,23 @@ import edu.wpi.first.wpilibj.shuffleboard.BuiltInLayouts;
import edu.wpi.first.wpilibj.shuffleboard.BuiltInWidgets; import edu.wpi.first.wpilibj.shuffleboard.BuiltInWidgets;
import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard; import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardLayout; import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardLayout;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import frc4388.robot.constants.Constants.LiDARConstants; import frc4388.robot.constants.Constants.LiDARConstants;
import frc4388.utility.status.Status; import frc4388.utility.status.Status;
import frc4388.utility.status.Subsystem; import frc4388.utility.status.FaultReporter;
import frc4388.utility.status.Queryable;
import frc4388.utility.status.Status.ReportLevel; import frc4388.utility.status.Status.ReportLevel;
// https://girlsofsteeldocs.readthedocs.io/en/latest/technical-resources/sensors/LIDAR-Lite-Distance-Sensor.html#minimal-roborio-interface // https://girlsofsteeldocs.readthedocs.io/en/latest/technical-resources/sensors/LIDAR-Lite-Distance-Sensor.html#minimal-roborio-interface
public class Lidar extends Subsystem { public class Lidar extends SubsystemBase implements Queryable {
private Counter LidarPWM; private Counter LidarPWM;
private String name = "Lidar"; private String name = "Lidar";
private double distance = -1; private double distance = -1;
public Lidar(int port, String name) { public Lidar(int port, String name) {
FaultReporter.register(this);
this.name = name; this.name = name;
LidarPWM = new Counter(port); LidarPWM = new Counter(port);
LidarPWM.setMaxPeriod(1.00); //set the max period that can be measured LidarPWM.setMaxPeriod(1.00); //set the max period that can be measured
@@ -27,7 +31,7 @@ public class Lidar extends Subsystem {
subsystemLayout = Shuffleboard.getTab("Subsystems") subsystemLayout = Shuffleboard.getTab("Subsystems")
.getLayout(getSubsystemName(), BuiltInLayouts.kList) .getLayout(getName(), BuiltInLayouts.kList)
.withSize(2, 2); .withSize(2, 2);
sbDistance = subsystemLayout sbDistance = subsystemLayout
@@ -63,7 +67,7 @@ public class Lidar extends Subsystem {
GenericEntry sbWithinDistance; GenericEntry sbWithinDistance;
@Override @Override
public String getSubsystemName() { public String getName() {
return "Lidar " + name; return "Lidar " + name;
} }
@@ -77,11 +81,8 @@ public class Lidar extends Subsystem {
public Status diagnosticStatus() { public Status diagnosticStatus() {
Status s = new Status(); Status s = new Status();
if(distance == -1){ if(distance == -1)
s.addReport(ReportLevel.ERROR, "LiDAR DISCONNECTED"); s.addReport(ReportLevel.ERROR, "LiDAR DISCONNECTED");
}else{
s.addReport(ReportLevel.INFO, "LiDAR Connected");
}
s.addReport(ReportLevel.INFO, "LiDAR Distance: " + distance); s.addReport(ReportLevel.INFO, "LiDAR Distance: " + distance);
@@ -25,11 +25,12 @@ import edu.wpi.first.wpilibj.shuffleboard.BuiltInWidgets;
import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard; import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardLayout; import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardLayout;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import frc4388.robot.constants.DriveConstants; import frc4388.robot.constants.DriveConstants;
import frc4388.utility.compute.TimesNegativeOne; import frc4388.utility.compute.TimesNegativeOne;
import frc4388.utility.status.Status; import frc4388.utility.status.Status;
import frc4388.utility.status.Subsystem; import frc4388.utility.status.FaultReporter;
import frc4388.utility.status.Status.ReportLevel; import frc4388.utility.status.Queryable;
import com.pathplanner.lib.controllers.PPHolonomicDriveController; import com.pathplanner.lib.controllers.PPHolonomicDriveController;
import com.pathplanner.lib.util.PathPlannerLogging; import com.pathplanner.lib.util.PathPlannerLogging;
@@ -37,7 +38,7 @@ import com.pathplanner.lib.config.PIDConstants;
import com.pathplanner.lib.auto.AutoBuilder; import com.pathplanner.lib.auto.AutoBuilder;
import com.pathplanner.lib.config.RobotConfig; import com.pathplanner.lib.config.RobotConfig;
public class SwerveDrive extends Subsystem { public class SwerveDrive extends SubsystemBase implements Queryable {
private SwerveDrivetrain<TalonFX, TalonFX, CANcoder> swerveDriveTrain; private SwerveDrivetrain<TalonFX, TalonFX, CANcoder> swerveDriveTrain;
private Vision vision; private Vision vision;
@@ -65,7 +66,7 @@ public class SwerveDrive extends Subsystem {
public SwerveDrive(SwerveDrivetrain<TalonFX, TalonFX, CANcoder> swerveDriveTrain, Vision vision) { public SwerveDrive(SwerveDrivetrain<TalonFX, TalonFX, CANcoder> swerveDriveTrain, Vision vision) {
// public SwerveDrive(SwerveDrivetrain<TalonFX, TalonFX, CANcoder> // public SwerveDrive(SwerveDrivetrain<TalonFX, TalonFX, CANcoder>
// swerveDriveTrain) { // swerveDriveTrain) {
super(); FaultReporter.register(this);
this.swerveDriveTrain = swerveDriveTrain; this.swerveDriveTrain = swerveDriveTrain;
this.vision = vision; this.vision = vision;
@@ -448,12 +449,12 @@ public class SwerveDrive extends Subsystem {
@Override @Override
public String getSubsystemName() { public String getName() {
return "Swerve Drive Controller"; return "Swerve Drive Controller";
} }
ShuffleboardLayout subsystemLayout = Shuffleboard.getTab("Subsystems") ShuffleboardLayout subsystemLayout = Shuffleboard.getTab("Subsystems")
.getLayout(getSubsystemName(), BuiltInLayouts.kList) .getLayout(getName(), BuiltInLayouts.kList)
.withSize(2, 2); .withSize(2, 2);
GenericEntry sbGyro = subsystemLayout GenericEntry sbGyro = subsystemLayout
@@ -478,8 +479,8 @@ public class SwerveDrive extends Subsystem {
public Status diagnosticStatus() { public Status diagnosticStatus() {
Status status = new Status(); Status status = new Status();
status.addReport(ReportLevel.INFO, // status.addReport(ReportLevel.INFO,
"Don't know how to diganose new CTRE swerve systems. please check under the CAN(t) section for more detailed information about the swerves there."); // "Don't know how to diganose new CTRE swerve systems. please check under the CAN(t) section for more detailed information about the swerves there.");
return status; return status;
} }
@@ -27,13 +27,14 @@ import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardLayout; import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardLayout;
import edu.wpi.first.wpilibj.smartdashboard.Field2d; import edu.wpi.first.wpilibj.smartdashboard.Field2d;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import frc4388.robot.constants.Constants.FieldConstants; import frc4388.robot.constants.Constants.FieldConstants;
import frc4388.robot.constants.Constants.VisionConstants; import frc4388.robot.constants.Constants.VisionConstants;
import frc4388.utility.status.Status; import frc4388.utility.status.Status;
import frc4388.utility.status.Subsystem; import frc4388.utility.status.FaultReporter;
import frc4388.utility.status.Status.ReportLevel; import frc4388.utility.status.Queryable;
public class Vision extends Subsystem { public class Vision extends SubsystemBase implements Queryable {
// private PhotonCamera leftCamera; // private PhotonCamera leftCamera;
// private PhotonCamera rightCamera; // private PhotonCamera rightCamera;
@@ -59,7 +60,7 @@ public class Vision extends Subsystem {
private Field2d field = new Field2d(); private Field2d field = new Field2d();
ShuffleboardLayout subsystemLayout = Shuffleboard.getTab("Subsystems") ShuffleboardLayout subsystemLayout = Shuffleboard.getTab("Subsystems")
.getLayout(getSubsystemName(), BuiltInLayouts.kList) .getLayout(getName(), BuiltInLayouts.kList)
.withSize(2, 2); .withSize(2, 2);
GenericEntry sbTagDetected = subsystemLayout GenericEntry sbTagDetected = subsystemLayout
@@ -72,17 +73,8 @@ public class Vision extends Subsystem {
.withWidget(BuiltInWidgets.kBooleanBox) .withWidget(BuiltInWidgets.kBooleanBox)
.getEntry(); .getEntry();
GenericEntry sbLeftCamConnected = subsystemLayout
.add("Left Camera Connnected", false)
.withWidget(BuiltInWidgets.kBooleanBox)
.getEntry();
GenericEntry sbRightCamConnected = subsystemLayout
.add("Right Camera Connnected", false)
.withWidget(BuiltInWidgets.kBooleanBox)
.getEntry();
public Vision(PhotonCamera leftCamera, PhotonCamera rightCamera){ public Vision(PhotonCamera leftCamera, PhotonCamera rightCamera){
FaultReporter.register(this);
SmartDashboard.putData(field); SmartDashboard.putData(field);
this.cameras = new PhotonCamera[]{leftCamera, rightCamera}; this.cameras = new PhotonCamera[]{leftCamera, rightCamera};
@@ -307,7 +299,7 @@ public class Vision extends Subsystem {
@Override @Override
public String getSubsystemName() { public String getName() {
return "Vision"; return "Vision";
} }
@@ -321,27 +313,12 @@ public class Vision extends Subsystem {
public void queryStatus() { public void queryStatus() {
sbTagDetected.setBoolean(isTagDetected); sbTagDetected.setBoolean(isTagDetected);
sbTagProcessed.setBoolean(isTagProcessed); sbTagProcessed.setBoolean(isTagProcessed);
sbLeftCamConnected.setBoolean(cameras[0].isConnected());
sbRightCamConnected.setBoolean(cameras[1].isConnected());
// field.setRobotPose(getPose2d()); // field.setRobotPose(getPose2d());
} }
@Override @Override
public Status diagnosticStatus() { public Status diagnosticStatus() {
Status status = new Status(); return new Status();
if(cameras[0].isConnected())
status.addReport(ReportLevel.INFO, "Left Camera Connected");
else
status.addReport(ReportLevel.ERROR, "Left Camera DISCONNECTED");
if(cameras[1].isConnected())
status.addReport(ReportLevel.INFO, "Right Camera Connected");
else
status.addReport(ReportLevel.ERROR, "Right Camera DISCONNECTED");
return status;
} }
} }
@@ -0,0 +1,82 @@
package frc4388.utility.status;
import com.ctre.phoenix6.controls.EmptyControl;
import com.ctre.phoenix6.hardware.CANcoder;
import frc4388.utility.status.Status.ReportLevel;
public class FaultCANCoder implements Queryable {
private String name;
private CANcoder cancoder;
public static void addDevice(CANcoder cancoder, String name) {
FaultCANCoder p = new FaultCANCoder();
p.name = name;
p.cancoder = cancoder;
FaultReporter.register(p);
}
@Override
public String getName() {
return name;
}
@Override
public void queryStatus() {
// TODO Auto-generated method stub
// throw new UnsupportedOperationException("Unimplemented method 'queryStatus'");
}
@Override
public Status diagnosticStatus() {
Status s = new Status();
boolean debounceBad = !QueryUtils.isDebounceOk(cancoder.getSupplyVoltage());
boolean emptyControlBad = cancoder.setControl(new EmptyControl()).value != 0;
if(debounceBad || emptyControlBad) {
s.addReport(ReportLevel.ERROR, "device is unreachable - Failed" +
(debounceBad ? " Failed debounce test" : "") +
(emptyControlBad ? " Failed empty control test" : "")
);
}
// faults
if (cancoder.getFault_Hardware().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Hardware fault detected");
}
if (cancoder.getFault_BootDuringEnable().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Device booted while enabled");
}
if (cancoder.getFault_BadMagnet().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Bad magnet");
}
if (cancoder.getFault_Undervoltage().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Device supply voltage near brownout");
}
if (cancoder.getFault_UnlicensedFeatureInUse().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Unlicensed feature in use");
}
// sticky faults
if (cancoder.getStickyFault_Hardware().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] Hardware fault detected");
}
if (cancoder.getStickyFault_BootDuringEnable().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] Device booted while enabled");
}
if (cancoder.getStickyFault_BadMagnet().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] Bad magnet");
}
if (cancoder.getStickyFault_Undervoltage().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] Device supply voltage near brownout");
}
if (cancoder.getStickyFault_UnlicensedFeatureInUse().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] Unlicensed feature in use");
}
return s;
}
}
@@ -0,0 +1,38 @@
package frc4388.utility.status;
import org.photonvision.PhotonCamera;
import frc4388.utility.status.Status.ReportLevel;
public class FaultPhotonCamera implements Queryable {
private String name;
private PhotonCamera cam;
public static void addDevice(PhotonCamera cam, String name) {
FaultPhotonCamera p = new FaultPhotonCamera();
p.name = name;
p.cam = cam;
FaultReporter.register(p);
}
@Override
public String getName() {
return name;
}
@Override
public void queryStatus() {}
@Override
public Status diagnosticStatus() {
Status s = new Status();
if(!cam.isConnected())
s.addReport(ReportLevel.ERROR, "Not Connected!");
return s;
}
}
@@ -0,0 +1,174 @@
package frc4388.utility.status;
import com.ctre.phoenix6.controls.EmptyControl;
import com.ctre.phoenix6.hardware.Pigeon2;
import frc4388.utility.status.Status.ReportLevel;
public class FaultPidgeon2 implements Queryable {
private String name;
private Pigeon2 pigeon2;
public static void addDevice(Pigeon2 pigeon2, String name) {
FaultPidgeon2 p = new FaultPidgeon2();
p.name = name;
p.pigeon2 = pigeon2;
FaultReporter.register(p);
}
@Override
public String getName() {
return name;
}
@Override
public void queryStatus() {
// TODO Auto-generated method stub
// throw new UnsupportedOperationException("Unimplemented method 'queryStatus'");
}
@Override
public Status diagnosticStatus() {
Status s = new Status();
boolean debounceBad = !QueryUtils.isDebounceOk(pigeon2.getSupplyVoltage());
boolean emptyControlBad = pigeon2.setControl(new EmptyControl()).value != 0;
if(debounceBad || emptyControlBad) {
s.addReport(ReportLevel.ERROR, "device is unreachable - Failed" +
(debounceBad ? " Failed debounce test" : "") +
(emptyControlBad ? " Failed empty control test" : "")
);
}
s.addReport(ReportLevel.INFO, "Voltage: " + pigeon2.getSupplyVoltage());
s.addReport(ReportLevel.INFO, "Pitch: " + pigeon2.getPitch());
s.addReport(ReportLevel.INFO, "Yaw: " + pigeon2.getYaw());
s.addReport(ReportLevel.INFO, "Roll: " + pigeon2.getRoll());
s.addReport(ReportLevel.INFO, "Acceleration X: " + pigeon2.getAccelerationX());
s.addReport(ReportLevel.INFO, "Acceleration Y: " + pigeon2.getAccelerationY());
s.addReport(ReportLevel.INFO, "Acceleration Z: " + pigeon2.getAccelerationZ());
s.addReport(ReportLevel.INFO, "Magnomiter X: " + pigeon2.getMagneticFieldX());
s.addReport(ReportLevel.INFO, "Magnomiter Y: " + pigeon2.getMagneticFieldY());
s.addReport(ReportLevel.INFO, "Magnomiter Z: " + pigeon2.getMagneticFieldZ());
// faults
if (pigeon2.getFault_BootDuringEnable().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Device booted while enabled");
}
if (pigeon2.getFault_BootIntoMotion().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Device booted while in motion");
}
if (pigeon2.getFault_BootupAccelerometer().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Accelerometer fault detected");
}
if (pigeon2.getFault_BootupGyroscope().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Gyro fault detected");
}
if (pigeon2.getFault_BootupMagnetometer().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Magnetometer fault detected");
}
if (pigeon2.getFault_DataAcquiredLate().getValue() == Boolean.TRUE) {
s.addReport(
ReportLevel.ERROR,
"Motion stack data acquisition slower than expected");
}
if (pigeon2.getFault_Hardware().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Hardware fault detected");
}
if (pigeon2.getFault_LoopTimeSlow().getValue() == Boolean.TRUE) {
s.addReport(
ReportLevel.ERROR,
"Motion stack loop time was slower than expected");
}
if (pigeon2.getFault_SaturatedAccelerometer().getValue() == Boolean.TRUE) {
s.addReport(
ReportLevel.ERROR, "Accelerometer values are saturated");
}
if (pigeon2.getFault_SaturatedGyroscope().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Gyro values are saturated");
}
if (pigeon2.getFault_SaturatedMagnetometer().getValue() == Boolean.TRUE) {
s.addReport(
ReportLevel.ERROR, "Magnetometer values are saturated");
}
if (pigeon2.getFault_Undervoltage().getValue() == Boolean.TRUE) {
s.addReport(
ReportLevel.ERROR, "Device supply voltage near brownout");
}
if (pigeon2.getFault_UnlicensedFeatureInUse().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Unlicensed feature in use");
}
// sticky faults
if (pigeon2.getStickyFault_BootDuringEnable().getValue() == Boolean.TRUE) {
s.addReport(
ReportLevel.ERROR, "[STICKY] Device booted while enabled");
}
if (pigeon2.getStickyFault_BootIntoMotion().getValue() == Boolean.TRUE) {
s.addReport(
ReportLevel.ERROR, "[STICKY] Device booted while in motion");
}
if (pigeon2.getStickyFault_BootupAccelerometer().getValue() == Boolean.TRUE) {
s.addReport(
ReportLevel.ERROR, "[STICKY] Accelerometer fault detected");
}
if (pigeon2.getStickyFault_BootupGyroscope().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] Gyro fault detected");
}
if (pigeon2.getStickyFault_BootupMagnetometer().getValue() == Boolean.TRUE) {
s.addReport(
ReportLevel.ERROR, "[STICKY] Magnetometer fault detected");
}
if (pigeon2.getStickyFault_DataAcquiredLate().getValue() == Boolean.TRUE) {
s.addReport(
ReportLevel.ERROR,
String.format(
"[STICKY] Motion stack data acquisition slower than expected"));
}
if (pigeon2.getStickyFault_Hardware().getValue() == Boolean.TRUE) {
s.addReport(
ReportLevel.ERROR, "[STICKY] Hardware fault detected");
}
if (pigeon2.getStickyFault_LoopTimeSlow().getValue() == Boolean.TRUE) {
s.addReport(
ReportLevel.ERROR,
String.format(
"[STICKY] Motion stack loop time was slower than expected"));
}
if (pigeon2.getStickyFault_SaturatedAccelerometer().getValue() == Boolean.TRUE) {
s.addReport(
ReportLevel.ERROR,
"[STICKY] Accelerometer values are saturated");
}
if (pigeon2.getStickyFault_SaturatedGyroscope().getValue() == Boolean.TRUE) {
s.addReport(
ReportLevel.ERROR, "[STICKY] Gyro values are saturated");
}
if (pigeon2.getStickyFault_SaturatedMagnetometer().getValue() == Boolean.TRUE) {
s.addReport(
ReportLevel.ERROR,
"[STICKY] Magnetometer values are saturated");
}
if (pigeon2.getStickyFault_Undervoltage().getValue() == Boolean.TRUE) {
s.addReport(
ReportLevel.ERROR,
"[STICKY] Device supply voltage near brownout");
}
if (pigeon2.getStickyFault_UnlicensedFeatureInUse().getValue() == Boolean.TRUE) {
s.addReport(
ReportLevel.ERROR, "[STICKY] Unlicensed feature in use");
}
return s;
}
}
@@ -0,0 +1,129 @@
package frc4388.utility.status;
import java.util.ArrayList;
import java.util.List;
import com.ctre.phoenix6.CANBus;
import com.ctre.phoenix6.CANBus.CANBusStatus;
import frc4388.robot.constants.Constants;
import frc4388.utility.status.Status.Report;
import frc4388.utility.status.Status.ReportLevel;
public class FaultReporter {
private static final String REPORTS_HEADER =
"▄▖ ▗ \n" + //
"▙▘█▌▛▌▛▌▛▘▜▘▛▘\n" + //
"▌▌▙▖▙▌▙▌▌ ▐▖▄▌\n" + //
"";
private static final String CAN_HEADER =
"▄▖▄▖▖ ▖ \n" + //
"▌ ▌▌▛▖▌ \n" + //
"▙▖▛▌▌▝▌(t)\n" + //
" ";
private static final String ERROR_HEADER =
"▄▖▄▖▄▖▄▖▄▖▄▖\n" + //
"▙▖▙▘▙▘▌▌▙▘▚ \n" + //
"▙▖▌▌▌▌▙▌▌▌▄▌\n" + //
" ";
private static List<Queryable> queryables = new ArrayList<>();
public static void startThread() {
new Thread() {
public void run() {
try{
while(!this.isInterrupted() && this.isAlive()){
Thread.sleep(500);
for(int i=0;i<queryables.size(); i++){
queryables.get(i).queryStatus();
}
// System.out.println("Updated statuses!");
}
}catch(Exception e){
e.printStackTrace();
}
}
}.start();
}
public static void register(Queryable q) {
queryables.add(q);
}
private static void Log(Queryable q, String s){
System.out.println(q.getName() + " - " + s);
}
public static void printReport() {
List<String> errors = new ArrayList<>();
// Subsystems header
System.out.println(REPORTS_HEADER);
for(int i=0;i< queryables.size();i++){
Queryable q = queryables.get(i);
System.out.println("** Subsystem diagnostic report for " + q.getName() + ":");
Status status = q.diagnosticStatus();
for(int a=0;a<status.reports.size();a++){
Report r = status.reports.get(a);
if(r.reportLevel == ReportLevel.ERROR)
errors.add(q.getName() + " - " + r.toString());
Log(q, r.toString());
}
}
// CAN header
System.out.println(CAN_HEADER);
CANBus canBus = new CANBus(Constants.CANBUS_NAME);
CANBusStatus canInfo = canBus.getStatus();
System.out.println("CANInfo BusOffCount - " + canInfo.BusOffCount);
System.out.println("CANInfo BusUtilization - " + canInfo.BusUtilization);
System.out.println("CANInfo RX Errors count - " + canInfo.REC);
System.out.println("CANInfo TX Errors count - " + canInfo.TEC);
System.out.println("CANInfo Transmit buffer full count - " + canInfo.TxFullCount);
// Broken turniary operator
ReportLevel canReportLevel = canInfo.Status.isOK() ? (canInfo.Status.isWarning() ? ReportLevel.WARNING : ReportLevel.ERROR) : ReportLevel.INFO;
String canStatus = "CAN " + canReportLevel.name() + " - " + canInfo.Status.getName() + " (" + canInfo.Status.getDescription() + ")";
if(canReportLevel == ReportLevel.ERROR) {
errors.add(canStatus);
}
System.out.println(canStatus);
// for(int i=0;i<CanDevice.devices.size();i++){
// CanDevice device = CanDevice.devices.get(i);
// System.out.println("** CAN diagnostic report for " + device.name + ":");
// Status status = device.diagnosticStatus();
// for(int a=0;a<status.reports.size();a++){
// Report r = status.reports.get(a);
// if(r.reportLevel == ReportLevel.ERROR)
// errors.add(device.getName() + " - " + r.toString());
// device.Log(r.toString());
// }
// }
// System.out.println("Found CAN devices: " + new DeviceFinder().Find());
if(errors.size() > 0) {
// Errors header
System.out.println(ERROR_HEADER);
for(int i=0;i<errors.size(); i++){
System.out.println(errors.get(i));
}
}
}
}
@@ -0,0 +1,160 @@
package frc4388.utility.status;
import com.ctre.phoenix6.controls.EmptyControl;
import com.ctre.phoenix6.hardware.TalonFX;
import frc4388.utility.status.Status.ReportLevel;
public class FaultTalonFX implements Queryable {
private String name;
private TalonFX motor;
public static void addDevice(TalonFX motor, String name) {
FaultTalonFX p = new FaultTalonFX();
p.name = name;
p.motor = motor;
FaultReporter.register(p);
}
@Override
public String getName() {
return name;
}
@Override
public void queryStatus() {
// TODO Auto-generated method stub
// throw new UnsupportedOperationException("Unimplemented method 'queryStatus'");
}
@Override
public Status diagnosticStatus() {
Status s = new Status();
boolean debounceBad = !QueryUtils.isDebounceOk(motor.getSupplyVoltage());
boolean emptyControlBad = motor.setControl(new EmptyControl()).value != 0;
if(debounceBad || emptyControlBad) {
s.addReport(ReportLevel.ERROR, "device is unreachable - Failed" +
(debounceBad ? " Failed debounce test" : "") +
(emptyControlBad ? " Failed empty control test" : "")
);
}
s.addReport(ReportLevel.INFO, "Voltage: " + motor.getSupplyVoltage());
s.addReport(ReportLevel.INFO, "Current: " + motor.getSupplyCurrent());
s.addReport(ReportLevel.INFO, "Device temp: " + motor.getDeviceTemp());
s.addReport(ReportLevel.INFO, "Processor temp: " + motor.getProcessorTemp());
s.addReport(ReportLevel.INFO, "Position: " + motor.getPosition());
s.addReport(ReportLevel.INFO, "Velocity: " + motor.getVelocity());
s.addReport(ReportLevel.INFO, "Acceleration: " + motor.getAcceleration());
// faults<
if (motor.getFault_BootDuringEnable().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Device booted while enabled");
}
if (motor.getFault_BridgeBrownout().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Bridge was disabled most likely due to supply voltage dropping too low");
}
if (motor.getFault_DeviceTemp().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Device temperature exceeded limit");
}
if (motor.getFault_FusedSensorOutOfSync().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Remote sensor is out of sync");
}
if (motor.getFault_Hardware().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Hardware failure detected");
}
if (motor.getFault_MissingDifferentialFX().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "The remote Talon used for differential control is not present");
}
if (motor.getFault_MissingHardLimitRemote().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR,"The remote limit switch device is not present");
}
if (motor.getFault_MissingSoftLimitRemote().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "The remote soft limit device is not present");
}
if (motor.getFault_OverSupplyV().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Supply voltage exceeded limit");
}
if (motor.getFault_ProcTemp().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Processor temperature exceeded limit");
}
if (motor.getFault_RemoteSensorDataInvalid().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "The remote sensor's data is no longer trusted");
}
if (motor.getFault_RemoteSensorPosOverflow().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "remote sensor position has overflowed");
}
if (motor.getFault_RemoteSensorReset().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "The remote sensor has reset");
}
if (motor.getFault_Undervoltage().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, " Device supply voltage near brownout");
}
if (motor.getFault_UnlicensedFeatureInUse().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Unlicensed feature in use");
}
if (motor.getFault_UnstableSupplyV().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "Supply voltage is unstable");
}
// sticky faults
if (motor.getStickyFault_BootDuringEnable().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] Device booted while enabled");
}
if (motor.getStickyFault_BridgeBrownout().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] Bridge was disabled most likely due to supply voltage dropping too low");
}
if (motor.getStickyFault_DeviceTemp().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] Device temperature exceeded limit");
}
if (motor.getStickyFault_FusedSensorOutOfSync().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] Remote sensor is out of sync");
}
if (motor.getStickyFault_Hardware().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] Hardware failure detected");
}
if (motor.getStickyFault_MissingDifferentialFX().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] The remote Talon used for differential control is not present");
}
if (motor.getStickyFault_MissingHardLimitRemote().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] The remote limit switch device is not present");
}
if (motor.getStickyFault_MissingSoftLimitRemote().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] The remote soft limit device is not present");
}
if (motor.getStickyFault_OverSupplyV().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] Supply voltage exceeded limit");
}
if (motor.getStickyFault_ProcTemp().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] Processor temperature exceeded limit");
}
if (motor.getStickyFault_RemoteSensorDataInvalid().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] The remote sensor's data is no longer trusted");
}
if (motor.getStickyFault_RemoteSensorPosOverflow().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] The remote sensor position has overflowed");
}
if (motor.getStickyFault_RemoteSensorReset().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] The remote sensor has reset");
}
if (motor.getStickyFault_Undervoltage().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] Device supply voltage near brownout");
}
if (motor.getStickyFault_UnlicensedFeatureInUse().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] Unlicensed feature in use");
}
if (motor.getStickyFault_UnstableSupplyV().getValue() == Boolean.TRUE) {
s.addReport(ReportLevel.ERROR, "[STICKY] Supply voltage is unstable");
}
return s;
}
}
@@ -0,0 +1,13 @@
package frc4388.utility.status;
import com.ctre.phoenix6.StatusSignal;
import edu.wpi.first.math.filter.Debouncer;
public class QueryUtils {
public static boolean isDebounceOk(@SuppressWarnings("rawtypes") StatusSignal status) {
Debouncer connectedDebounce = new Debouncer(0.5);
status.refresh();
return connectedDebounce.calculate(status.getStatus().isOK());
};
}
@@ -0,0 +1,10 @@
package frc4388.utility.status;
public interface Queryable {
// Get name of subsystem, for use in log.
String getName();
// Get what the subystem is currently doing, such as "Shooter spun up". This should post to SmartDashboard
void queryStatus();
// Proactivly search for any errors in each subsystem
Status diagnosticStatus();
}
@@ -44,6 +44,10 @@ public class Status {
} }
public void diagnoseHardwareCTRE(String deviceName, TalonFX motor) { public void diagnoseHardwareCTRE(String deviceName, TalonFX motor) {
addReport(ReportLevel.ERROR, deviceName + " Motor (TalonFX) Alive?: " + (motor.isAlive() ? "Alive." : "Dead!"));
if (motor.isAlive()) addReport(ReportLevel.INFO, deviceName + " Motor (TalonFX) Alive?: Alive."); if (motor.isAlive()) addReport(ReportLevel.INFO, deviceName + " Motor (TalonFX) Alive?: Alive.");
else addReport(ReportLevel.ERROR, deviceName + " Motor (TalonFX) Alive?: Dead!"); else addReport(ReportLevel.ERROR, deviceName + " Motor (TalonFX) Alive?: Dead!");
} }
@@ -1,25 +0,0 @@
package frc4388.utility.status;
import java.util.ArrayList;
import java.util.List;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
public abstract class Subsystem extends SubsystemBase {
public static List<Subsystem> subsystems = new ArrayList<>();
public Subsystem(){
subsystems.add(this);
}
public void Log(String str) {
System.out.println(getSubsystemName() + " - " + str);
}
// Get name of subsystem, for use in log.
public abstract String getSubsystemName();
// Get what the subystem is currently doing, such as "Shooter spun up". This should post to SmartDashboard
public abstract void queryStatus();
// Proactivly search for any errors in each subsystem
public abstract Status diagnosticStatus();
}