mirror of
https://github.com/Team4388/RiseOfRidgebotics2020.git
synced 2026-06-09 08:48:01 -06:00
Cleanup mode switching
- Simplify control declarations - Format files - Disable CSV reflection table colors when deployed
This commit is contained in:
@@ -8,8 +8,6 @@
|
||||
package frc4388.robot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.Vector;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.ctre.phoenix.motorcontrol.SupplyCurrentLimitConfiguration;
|
||||
@@ -29,260 +27,261 @@ import frc4388.utility.LEDPatterns;
|
||||
* constants are needed, to reduce verbosity.
|
||||
*/
|
||||
public final class Constants {
|
||||
public enum Mode {
|
||||
COMPETITIVE, CASUAL;
|
||||
public enum Mode {
|
||||
COMPETITIVE, CASUAL;
|
||||
|
||||
private static Mode mode = Mode.COMPETITIVE;
|
||||
private static Vector<Consumer<Mode>> changeHandlers = new Vector<>();
|
||||
private static Mode mode = Mode.COMPETITIVE;
|
||||
private static ArrayList<Consumer<Mode>> changeHandlers = new ArrayList<>();
|
||||
|
||||
public static void register(Consumer<Mode> changeHandler) {
|
||||
changeHandlers.add(changeHandler);
|
||||
}
|
||||
|
||||
public static Mode get() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
public static void set(Mode mode) {
|
||||
System.out.println(mode);
|
||||
int i = mode.ordinal();
|
||||
Mode.mode = mode;
|
||||
CommandScheduler.getInstance().disable();
|
||||
changeHandlers.forEach(c -> c.accept(mode));
|
||||
CommandScheduler.getInstance().enable();
|
||||
DriveConstants.DRIVE_WITH_JOYSTICK_FACTOR = DriveConstants.DRIVE_WITH_JOYSTICK_FACTOR_MODES[i];
|
||||
IntakeConstants.INTAKE_SPEED = IntakeConstants.INTAKE_SPEED_MODES[i];
|
||||
StorageConstants.STORAGE_SPEED = StorageConstants.STORAGE_SPEED_MODES[i];
|
||||
}
|
||||
public static void toggle() {
|
||||
int i = mode.ordinal() + 1;
|
||||
Mode[] values = values();
|
||||
i = i >= values.length ? 0 : i;
|
||||
set(values[i]);
|
||||
}
|
||||
public static void register(Consumer<Mode> changeHandler) {
|
||||
changeHandlers.add(changeHandler);
|
||||
}
|
||||
|
||||
public static final int SELECTED_AUTO = 0;
|
||||
|
||||
public static final class DriveConstants {
|
||||
/* Drive Train IDs */
|
||||
public static final int DRIVE_LEFT_FRONT_CAN_ID = 2;
|
||||
public static final int DRIVE_RIGHT_FRONT_CAN_ID = 4;
|
||||
public static final int DRIVE_LEFT_BACK_CAN_ID = 3;
|
||||
public static final int DRIVE_RIGHT_BACK_CAN_ID = 5;
|
||||
public static final int PIGEON_ID = 6;
|
||||
|
||||
/* Drive Inversions */
|
||||
public static final boolean isRightMotorInverted = true;
|
||||
public static final boolean isLeftMotorInverted = false;
|
||||
public static final boolean isRightArcadeInverted = false;
|
||||
public static final boolean isAuxPIDInverted = false;
|
||||
|
||||
/* Drive Configuration */
|
||||
public static final int DRIVE_TIMEOUT_MS = 30; // Use for all motor config
|
||||
public static final double OPEN_LOOP_RAMP_RATE = 0.2; // Seconds from 0 to full power on motor
|
||||
public static final double NEUTRAL_DEADBAND = 0.04;
|
||||
public static final SupplyCurrentLimitConfiguration SUPPLY_CURRENT_LIMIT_CONFIG = new SupplyCurrentLimitConfiguration(false, 60, 40, 2);
|
||||
public static final int CLOSED_LOOP_TIME_MS = 1; // Higher numbers mean slower control loops
|
||||
public static final double COS_MULTIPLIER_LOW = 1.0;
|
||||
public static final double COS_MULTIPLIER_HIGH = 0.8;
|
||||
|
||||
/* Drive Train Characteristics */
|
||||
public static final double MOTOR_ROT_PER_WHEEL_ROT_HIGH = 7.29;
|
||||
public static final double MOTOR_ROT_PER_WHEEL_ROT_LOW = 15;
|
||||
public static final double WHEEL_DIAMETER_INCHES = 6;
|
||||
public static final double TICKS_PER_GYRO_REV = 8192;
|
||||
public static final double TICKS_PER_MOTOR_REV = 2048;
|
||||
|
||||
/* PID Constants Drive*/
|
||||
public static final Gains DRIVE_DISTANCE_GAINS_LOW = new Gains(0.1, 0.0, 1.0, 0.0, 0, 0.5);
|
||||
public static final Gains DRIVE_VELOCITY_GAINS_LOW = new Gains(0.05, 0.0, 1.0, 0.025, 0, 1.0);
|
||||
public static final Gains DRIVE_TURNING_GAINS_LOW = new Gains(0.5, 0.0, 2.0, 0.0, 0, 0.55);
|
||||
public static final Gains DRIVE_MOTION_MAGIC_GAINS_LOW = new Gains(0.1, 0.0, 0, 0.025, 0, 1.0);
|
||||
public static final int DRIVE_CRUISE_VELOCITY = 30000;
|
||||
public static final int DRIVE_ACCELERATION = 23000;
|
||||
|
||||
private static final double[] DRIVE_WITH_JOYSTICK_FACTOR_MODES = { 1.0, 0.8 };
|
||||
public static double DRIVE_WITH_JOYSTICK_FACTOR;
|
||||
|
||||
public static final Gains DRIVE_DISTANCE_GAINS_HIGH = new Gains(0.1, 0.0, 0.0, 0.0, 0, 0.5);
|
||||
public static final Gains DRIVE_VELOCITY_GAINS_HIGH = new Gains(0.1, 0.0, 0.0, 0.0, 0, 1.0);
|
||||
public static final Gains DRIVE_TURNING_GAINS_HIGH = new Gains(0.2, 0.0, 0.0, 0.0, 0, 0.55);
|
||||
public static final Gains DRIVE_MOTION_MAGIC_GAINS_HIGH = new Gains(0.1, 0.0, 0.0, 0.0, 0, 1.0);
|
||||
public static final int DRIVE_CRUISE_VELOCITY_HIGH = 20000;
|
||||
public static final int DRIVE_ACCELERATION_HIGH = 7000;
|
||||
|
||||
public static final Gains DRIVE_VELOCITY_GAINS_BACK = new Gains(0.0, 0.0, 0.0, 0.05, 0, 1.0);
|
||||
|
||||
/* Trajectory Constants */
|
||||
public static final double MAX_SPEED_METERS_PER_SECOND = 1.0;
|
||||
public static final double MAX_ACCELERATION_METERS_PER_SECOND_SQUARED = 1.0;
|
||||
public static final double TRACK_WIDTH_METERS = 0.648;
|
||||
public static final DifferentialDriveKinematics kDriveKinematics = new DifferentialDriveKinematics(TRACK_WIDTH_METERS);
|
||||
|
||||
/* Remote Sensors */
|
||||
public static final int REMOTE_0 = 0;
|
||||
public static final int REMOTE_1 = 1;
|
||||
|
||||
/* PID Indexes */
|
||||
public static final int PID_PRIMARY = 0;
|
||||
public static final int PID_TURN = 1;
|
||||
|
||||
/* PID SLOTS */
|
||||
public static final int SLOT_DISTANCE = 0;
|
||||
public static final int SLOT_VELOCITY = 1;
|
||||
public static final int SLOT_TURNING = 2;
|
||||
public static final int SLOT_MOTION_MAGIC = 3;
|
||||
|
||||
/* Ratio Calculation */
|
||||
public static final double INCHES_PER_WHEEL_REV = WHEEL_DIAMETER_INCHES * Math.PI;
|
||||
public static final double TICK_TIME_TO_SECONDS = 0.1;
|
||||
public static final double SECONDS_TO_TICK_TIME = 1 / TICK_TIME_TO_SECONDS;
|
||||
public static final double INCHES_PER_METER = 39.370;
|
||||
public static final double METERS_PER_INCH = 1 / INCHES_PER_METER;
|
||||
|
||||
public static final double WHEEL_ROT_PER_MOTOR_ROT_HIGH = 1 / MOTOR_ROT_PER_WHEEL_ROT_HIGH;
|
||||
public static final double TICKS_PER_WHEEL_REV_HIGH = TICKS_PER_MOTOR_REV * MOTOR_ROT_PER_WHEEL_ROT_HIGH;
|
||||
public static final double TICKS_PER_INCH_HIGH = TICKS_PER_WHEEL_REV_HIGH / INCHES_PER_WHEEL_REV;
|
||||
public static final double INCHES_PER_TICK_HIGH = 1 / TICKS_PER_INCH_HIGH;
|
||||
|
||||
public static final double WHEEL_ROT_PER_MOTOR_ROT_LOW = 1 / MOTOR_ROT_PER_WHEEL_ROT_LOW;
|
||||
public static final double TICKS_PER_WHEEL_REV_LOW = TICKS_PER_MOTOR_REV * MOTOR_ROT_PER_WHEEL_ROT_LOW;
|
||||
public static final double TICKS_PER_INCH_LOW = TICKS_PER_WHEEL_REV_LOW / INCHES_PER_WHEEL_REV;
|
||||
public static final double INCHES_PER_TICK_LOW = 1 / TICKS_PER_INCH_LOW;
|
||||
public static Mode get() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
public static final class ShooterConstants {
|
||||
/* Motor IDs */
|
||||
public static final int SHOOTER_FALCON_BALLER_ID = 8;
|
||||
public static final int SHOOTER_FALCON_BALLER_FOLLOWER_ID = 15;
|
||||
public static final int SHOOTER_ANGLE_ADJUST_ID = 10;
|
||||
public static final int SHOOTER_ROTATE_ID = 9;
|
||||
|
||||
/* PID Constants Shooter */
|
||||
public static final int SHOOTER_SLOT_IDX = 0;
|
||||
public static final int SHOOTER_PID_LOOP_IDX = 1;
|
||||
public static final int SHOOTER_TIMEOUT_MS = 30;
|
||||
public static final Gains DRUM_SHOOTER_GAINS = new Gains(0.34, 0.0, 0.0, 0.055, 0, 1.0); // Ff was 0.055
|
||||
// public static final Gains DRUM_SHOOTER_GAINS = new Gains(0.55, 0.0, 100, 0.0, 0, 1.0);
|
||||
public static final Gains SHOOTER_TURRET_GAINS = new Gains(0.6, 0.0, 0.0, 0.0, 0, 1.0);
|
||||
public static final Gains SHOOTER_ANGLE_GAINS = new Gains(0.05, 0.0, 0.0, 0.0, 0, 0.3);
|
||||
public static final double SHOOTER_TURRET_MIN = -1.0;
|
||||
public static final double ENCODER_TICKS_PER_REV = 2048;
|
||||
public static final double NEO_UNITS_PER_REV = 42;
|
||||
public static final double DEGREES_PER_ROT = 360;
|
||||
|
||||
public static final SupplyCurrentLimitConfiguration SUPPLY_CURRENT_LIMIT_CONFIG = new SupplyCurrentLimitConfiguration(true, 60, 40, 0.5);
|
||||
|
||||
public static final int TURRET_RIGHT_SOFT_LIMIT = -2;
|
||||
public static final int TURRET_LEFT_SOFT_LIMIT = -55;
|
||||
public static final double TURRET_SPEED_MULTIPLIER = 0.3;
|
||||
public static final double TURRET_CALIBRATE_SPEED = 0.075;
|
||||
public static final double TURRET_MOTOR_ROTS_PER_ROT = 101.04972; // 89.56696;
|
||||
public static final double TURRET_MOTOR_POS_AT_ZERO_ROT = -28.452166;
|
||||
|
||||
public static final int HOOD_UP_SOFT_LIMIT = 33;
|
||||
public static final int HOOD_DOWN_SOFT_LIMIT = 3;
|
||||
public static final double HOOD_CONVERT_SLOPE = 0.47;
|
||||
public static final double HOOD_CONVERT_B = 40.5;
|
||||
public static final double HOOD_CALIBRATE_SPEED = 0.2;
|
||||
public static final double HOOD_MOTOR_ROTS_PER_ROT = 1; // TODO: Find
|
||||
public static final double HOOD_MOTOR_POS_AT_ZERO_ROT = 0; // TODO: Find
|
||||
|
||||
public static final double DRUM_RAMP_LIMIT = 1000;
|
||||
public static final double DRUM_VELOCITY_BOUND = 300;
|
||||
public static void set(Mode mode) {
|
||||
System.out.println(mode);
|
||||
int i = mode.ordinal();
|
||||
Mode.mode = mode;
|
||||
CommandScheduler.getInstance().disable();
|
||||
changeHandlers.forEach(c -> c.accept(mode));
|
||||
CommandScheduler.getInstance().enable();
|
||||
DriveConstants.DRIVE_WITH_JOYSTICK_FACTOR = DriveConstants.DRIVE_WITH_JOYSTICK_FACTOR_MODES[i];
|
||||
IntakeConstants.INTAKE_SPEED = IntakeConstants.INTAKE_SPEED_MODES[i];
|
||||
StorageConstants.STORAGE_SPEED = StorageConstants.STORAGE_SPEED_MODES[i];
|
||||
}
|
||||
|
||||
public static final class ClimberConstants {
|
||||
public static final int CLIMBER_SPARK_ID = 14;
|
||||
public static void toggle() {
|
||||
int i = mode.ordinal() + 1;
|
||||
Mode[] values = values();
|
||||
i = i >= values.length ? 0 : i;
|
||||
set(values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class LevelerConstants {
|
||||
public static final int LEVELER_CAN_ID = 30;
|
||||
}
|
||||
public static final int SELECTED_AUTO = 0;
|
||||
|
||||
public static final class IntakeConstants {
|
||||
public static final double EXTENDER_SPEED = 0.3;
|
||||
private static final double[] INTAKE_SPEED_MODES = { 1.0, 0.5 };
|
||||
public static double INTAKE_SPEED;
|
||||
public static final class DriveConstants {
|
||||
/* Drive Train IDs */
|
||||
public static final int DRIVE_LEFT_FRONT_CAN_ID = 2;
|
||||
public static final int DRIVE_RIGHT_FRONT_CAN_ID = 4;
|
||||
public static final int DRIVE_LEFT_BACK_CAN_ID = 3;
|
||||
public static final int DRIVE_RIGHT_BACK_CAN_ID = 5;
|
||||
public static final int PIGEON_ID = 6;
|
||||
|
||||
public static final int INTAKE_SPARK_ID = 12;
|
||||
public static final int EXTENDER_SPARK_ID = 13;
|
||||
}
|
||||
/* Drive Inversions */
|
||||
public static final boolean isRightMotorInverted = true;
|
||||
public static final boolean isLeftMotorInverted = false;
|
||||
public static final boolean isRightArcadeInverted = false;
|
||||
public static final boolean isAuxPIDInverted = false;
|
||||
|
||||
public static final class StorageConstants {
|
||||
public static final int STORAGE_CAN_ID = 11;
|
||||
public static final double STORAGE_PARTIAL_BALL = 2;
|
||||
public static final double STORAGE_FULL_BALL = 7;
|
||||
private static final double[] STORAGE_SPEED_MODES = { 0.75, 0.50 };
|
||||
public static double STORAGE_SPEED;
|
||||
public static final double STORAGE_TIMEOUT = 3000;
|
||||
/* Drive Configuration */
|
||||
public static final int DRIVE_TIMEOUT_MS = 30; // Use for all motor config
|
||||
public static final double OPEN_LOOP_RAMP_RATE = 0.2; // Seconds from 0 to full power on motor
|
||||
public static final double NEUTRAL_DEADBAND = 0.04;
|
||||
public static final SupplyCurrentLimitConfiguration SUPPLY_CURRENT_LIMIT_CONFIG = new SupplyCurrentLimitConfiguration(false, 60, 40, 2);
|
||||
public static final int CLOSED_LOOP_TIME_MS = 1; // Higher numbers mean slower control loops
|
||||
public static final double COS_MULTIPLIER_LOW = 1.0;
|
||||
public static final double COS_MULTIPLIER_HIGH = 0.8;
|
||||
|
||||
/* Storage Characteristics */
|
||||
public static final double MOTOR_ROTS_PER_STORAGE_ROT = 1; // For the first storage belt
|
||||
public static final double INCHES_PER_STORAGE_ROT = 1; // Circumference of the first storage belt
|
||||
/* Drive Train Characteristics */
|
||||
public static final double MOTOR_ROT_PER_WHEEL_ROT_HIGH = 7.29;
|
||||
public static final double MOTOR_ROT_PER_WHEEL_ROT_LOW = 15;
|
||||
public static final double WHEEL_DIAMETER_INCHES = 6;
|
||||
public static final double TICKS_PER_GYRO_REV = 8192;
|
||||
public static final double TICKS_PER_MOTOR_REV = 2048;
|
||||
|
||||
/* Ball Indexes */
|
||||
public static final int BEAM_SENSOR_SHOOTER = 11;
|
||||
public static final int BEAM_SENSOR_USELESS = 12;
|
||||
public static final int BEAM_SENSOR_STORAGE = 13;
|
||||
public static final int BEAM_SENSOR_INTAKE = 14;
|
||||
/* PID Constants Drive*/
|
||||
public static final Gains DRIVE_DISTANCE_GAINS_LOW = new Gains(0.1, 0.0, 1.0, 0.0, 0, 0.5);
|
||||
public static final Gains DRIVE_VELOCITY_GAINS_LOW = new Gains(0.05, 0.0, 1.0, 0.025, 0, 1.0);
|
||||
public static final Gains DRIVE_TURNING_GAINS_LOW = new Gains(0.5, 0.0, 2.0, 0.0, 0, 0.55);
|
||||
public static final Gains DRIVE_MOTION_MAGIC_GAINS_LOW = new Gains(0.1, 0.0, 0, 0.025, 0, 1.0);
|
||||
public static final int DRIVE_CRUISE_VELOCITY = 30000;
|
||||
public static final int DRIVE_ACCELERATION = 23000;
|
||||
|
||||
/* PID Gains */
|
||||
public static final Gains STORAGE_GAINS = new Gains(0.1, 0.0, 0.0, 0.0, 0, 1.0);
|
||||
private static final double[] DRIVE_WITH_JOYSTICK_FACTOR_MODES = { 1.0, 0.8 };
|
||||
public static double DRIVE_WITH_JOYSTICK_FACTOR;
|
||||
|
||||
/* PID Values */
|
||||
public static final int SLOT_DISTANCE = 0;
|
||||
public static final Gains DRIVE_DISTANCE_GAINS_HIGH = new Gains(0.1, 0.0, 0.0, 0.0, 0, 0.5);
|
||||
public static final Gains DRIVE_VELOCITY_GAINS_HIGH = new Gains(0.1, 0.0, 0.0, 0.0, 0, 1.0);
|
||||
public static final Gains DRIVE_TURNING_GAINS_HIGH = new Gains(0.2, 0.0, 0.0, 0.0, 0, 0.55);
|
||||
public static final Gains DRIVE_MOTION_MAGIC_GAINS_HIGH = new Gains(0.1, 0.0, 0.0, 0.0, 0, 1.0);
|
||||
public static final int DRIVE_CRUISE_VELOCITY_HIGH = 20000;
|
||||
public static final int DRIVE_ACCELERATION_HIGH = 7000;
|
||||
|
||||
/* PID Indexes */
|
||||
public static final int PID_PRIMARY = 0;
|
||||
}
|
||||
public static final Gains DRIVE_VELOCITY_GAINS_BACK = new Gains(0.0, 0.0, 0.0, 0.05, 0, 1.0);
|
||||
|
||||
public static final class PneumaticsConstants {
|
||||
public static final int PCM_MODULE_ID = 7;
|
||||
/* Trajectory Constants */
|
||||
public static final double MAX_SPEED_METERS_PER_SECOND = 1.0;
|
||||
public static final double MAX_ACCELERATION_METERS_PER_SECOND_SQUARED = 1.0;
|
||||
public static final double TRACK_WIDTH_METERS = 0.648;
|
||||
public static final DifferentialDriveKinematics kDriveKinematics = new DifferentialDriveKinematics(TRACK_WIDTH_METERS);
|
||||
|
||||
public static final int SPEED_SHIFT_FORWARD_ID = 0;
|
||||
public static final int SPEED_SHIFT_REVERSE_ID = 1;
|
||||
/* Remote Sensors */
|
||||
public static final int REMOTE_0 = 0;
|
||||
public static final int REMOTE_1 = 1;
|
||||
|
||||
public static final int COOL_FALCON_FORWARD_ID = 3;
|
||||
public static final int COOL_FALCON_REVERSE_ID = 2;
|
||||
}
|
||||
/* PID Indexes */
|
||||
public static final int PID_PRIMARY = 0;
|
||||
public static final int PID_TURN = 1;
|
||||
|
||||
public static final class LEDConstants {
|
||||
public static final int LED_SPARK_ID = 0;
|
||||
public static final LEDPatterns DEFAULT_PATTERN = LEDPatterns.FOREST_WAVES;
|
||||
}
|
||||
/* PID SLOTS */
|
||||
public static final int SLOT_DISTANCE = 0;
|
||||
public static final int SLOT_VELOCITY = 1;
|
||||
public static final int SLOT_TURNING = 2;
|
||||
public static final int SLOT_MOTION_MAGIC = 3;
|
||||
|
||||
public static final class VisionConstants {
|
||||
public static final double FOV = 29.8; // Field of view of limelight
|
||||
public static final double TARGET_HEIGHT = 67.5;
|
||||
public static final double LIME_ANGLE = 24.7;
|
||||
public static final double TURN_P_VALUE = 0.8;
|
||||
public static final double X_ANGLE_ERROR = 0.5;
|
||||
public static final double MOTOR_DEAD_ZONE = 0.2;
|
||||
public static final double DISTANCE_ERROR_EQUATION_M = 1.1279;
|
||||
public static final double DISTANCE_ERROR_EQUATION_B = -15.0684;
|
||||
public static final double GRAV = 385.83;
|
||||
/* Ratio Calculation */
|
||||
public static final double INCHES_PER_WHEEL_REV = WHEEL_DIAMETER_INCHES * Math.PI;
|
||||
public static final double TICK_TIME_TO_SECONDS = 0.1;
|
||||
public static final double SECONDS_TO_TICK_TIME = 1 / TICK_TIME_TO_SECONDS;
|
||||
public static final double INCHES_PER_METER = 39.370;
|
||||
public static final double METERS_PER_INCH = 1 / INCHES_PER_METER;
|
||||
|
||||
// Galactic Search
|
||||
public static final double searchError = 2;
|
||||
/*
|
||||
public static final double bothCloseVisibleY = -17.69;
|
||||
public static final double closeLeftVisibleY = -12.57;
|
||||
public static final double closeRightVisibleY = -11.35;
|
||||
public static final double farLeftVisibleX = 3.58;
|
||||
public static final double farRightVisibleX = 7.04;
|
||||
*/
|
||||
public static final double WHEEL_ROT_PER_MOTOR_ROT_HIGH = 1 / MOTOR_ROT_PER_WHEEL_ROT_HIGH;
|
||||
public static final double TICKS_PER_WHEEL_REV_HIGH = TICKS_PER_MOTOR_REV * MOTOR_ROT_PER_WHEEL_ROT_HIGH;
|
||||
public static final double TICKS_PER_INCH_HIGH = TICKS_PER_WHEEL_REV_HIGH / INCHES_PER_WHEEL_REV;
|
||||
public static final double INCHES_PER_TICK_HIGH = 1 / TICKS_PER_INCH_HIGH;
|
||||
|
||||
public static final double[] aRed = { 1.6, -11.7 };
|
||||
public static final double[] bRed = { 2.5, -5.5 };
|
||||
public static final double[] aBlue = { 9.9, 9.0 };
|
||||
public static final double[] bBlue = { 5.5, 13.3 };
|
||||
}
|
||||
public static final double WHEEL_ROT_PER_MOTOR_ROT_LOW = 1 / MOTOR_ROT_PER_WHEEL_ROT_LOW;
|
||||
public static final double TICKS_PER_WHEEL_REV_LOW = TICKS_PER_MOTOR_REV * MOTOR_ROT_PER_WHEEL_ROT_LOW;
|
||||
public static final double TICKS_PER_INCH_LOW = TICKS_PER_WHEEL_REV_LOW / INCHES_PER_WHEEL_REV;
|
||||
public static final double INCHES_PER_TICK_LOW = 1 / TICKS_PER_INCH_LOW;
|
||||
}
|
||||
|
||||
public static final class OIConstants {
|
||||
public static final int XBOX_DRIVER_ID = 0;
|
||||
public static final int XBOX_OPERATOR_ID = 1;
|
||||
public static final int BUTTON_FOX_ID = 2;
|
||||
}
|
||||
public static final class ShooterConstants {
|
||||
/* Motor IDs */
|
||||
public static final int SHOOTER_FALCON_BALLER_ID = 8;
|
||||
public static final int SHOOTER_FALCON_BALLER_FOLLOWER_ID = 15;
|
||||
public static final int SHOOTER_ANGLE_ADJUST_ID = 10;
|
||||
public static final int SHOOTER_ROTATE_ID = 9;
|
||||
|
||||
/* PID Constants Shooter */
|
||||
public static final int SHOOTER_SLOT_IDX = 0;
|
||||
public static final int SHOOTER_PID_LOOP_IDX = 1;
|
||||
public static final int SHOOTER_TIMEOUT_MS = 30;
|
||||
public static final Gains DRUM_SHOOTER_GAINS = new Gains(0.34, 0.0, 0.0, 0.055, 0, 1.0); // Ff was 0.055
|
||||
// public static final Gains DRUM_SHOOTER_GAINS = new Gains(0.55, 0.0, 100, 0.0, 0, 1.0);
|
||||
public static final Gains SHOOTER_TURRET_GAINS = new Gains(0.6, 0.0, 0.0, 0.0, 0, 1.0);
|
||||
public static final Gains SHOOTER_ANGLE_GAINS = new Gains(0.05, 0.0, 0.0, 0.0, 0, 0.3);
|
||||
public static final double SHOOTER_TURRET_MIN = -1.0;
|
||||
public static final double ENCODER_TICKS_PER_REV = 2048;
|
||||
public static final double NEO_UNITS_PER_REV = 42;
|
||||
public static final double DEGREES_PER_ROT = 360;
|
||||
|
||||
public static final SupplyCurrentLimitConfiguration SUPPLY_CURRENT_LIMIT_CONFIG = new SupplyCurrentLimitConfiguration(true, 60, 40, 0.5);
|
||||
|
||||
public static final int TURRET_RIGHT_SOFT_LIMIT = -2;
|
||||
public static final int TURRET_LEFT_SOFT_LIMIT = -55;
|
||||
public static final double TURRET_SPEED_MULTIPLIER = 0.3;
|
||||
public static final double TURRET_CALIBRATE_SPEED = 0.075;
|
||||
public static final double TURRET_MOTOR_ROTS_PER_ROT = 101.04972; // 89.56696;
|
||||
public static final double TURRET_MOTOR_POS_AT_ZERO_ROT = -28.452166;
|
||||
|
||||
public static final int HOOD_UP_SOFT_LIMIT = 33;
|
||||
public static final int HOOD_DOWN_SOFT_LIMIT = 3;
|
||||
public static final double HOOD_CONVERT_SLOPE = 0.47;
|
||||
public static final double HOOD_CONVERT_B = 40.5;
|
||||
public static final double HOOD_CALIBRATE_SPEED = 0.2;
|
||||
public static final double HOOD_MOTOR_ROTS_PER_ROT = 1; // TODO: Find
|
||||
public static final double HOOD_MOTOR_POS_AT_ZERO_ROT = 0; // TODO: Find
|
||||
|
||||
public static final double DRUM_RAMP_LIMIT = 1000;
|
||||
public static final double DRUM_VELOCITY_BOUND = 300;
|
||||
}
|
||||
|
||||
public static final class ClimberConstants {
|
||||
public static final int CLIMBER_SPARK_ID = 14;
|
||||
}
|
||||
|
||||
public static final class LevelerConstants {
|
||||
public static final int LEVELER_CAN_ID = 30;
|
||||
}
|
||||
|
||||
public static final class IntakeConstants {
|
||||
public static final double EXTENDER_SPEED = 0.3;
|
||||
private static final double[] INTAKE_SPEED_MODES = { 1.0, 0.5 };
|
||||
public static double INTAKE_SPEED;
|
||||
|
||||
public static final int INTAKE_SPARK_ID = 12;
|
||||
public static final int EXTENDER_SPARK_ID = 13;
|
||||
}
|
||||
|
||||
public static final class StorageConstants {
|
||||
public static final int STORAGE_CAN_ID = 11;
|
||||
public static final double STORAGE_PARTIAL_BALL = 2;
|
||||
public static final double STORAGE_FULL_BALL = 7;
|
||||
private static final double[] STORAGE_SPEED_MODES = { 0.75, 0.50 };
|
||||
public static double STORAGE_SPEED;
|
||||
public static final double STORAGE_TIMEOUT = 3000;
|
||||
|
||||
/* Storage Characteristics */
|
||||
public static final double MOTOR_ROTS_PER_STORAGE_ROT = 1; // For the first storage belt
|
||||
public static final double INCHES_PER_STORAGE_ROT = 1; // Circumference of the first storage belt
|
||||
|
||||
/* Ball Indexes */
|
||||
public static final int BEAM_SENSOR_SHOOTER = 11;
|
||||
public static final int BEAM_SENSOR_USELESS = 12;
|
||||
public static final int BEAM_SENSOR_STORAGE = 13;
|
||||
public static final int BEAM_SENSOR_INTAKE = 14;
|
||||
|
||||
/* PID Gains */
|
||||
public static final Gains STORAGE_GAINS = new Gains(0.1, 0.0, 0.0, 0.0, 0, 1.0);
|
||||
|
||||
/* PID Values */
|
||||
public static final int SLOT_DISTANCE = 0;
|
||||
|
||||
/* PID Indexes */
|
||||
public static final int PID_PRIMARY = 0;
|
||||
}
|
||||
|
||||
public static final class PneumaticsConstants {
|
||||
public static final int PCM_MODULE_ID = 7;
|
||||
|
||||
public static final int SPEED_SHIFT_FORWARD_ID = 0;
|
||||
public static final int SPEED_SHIFT_REVERSE_ID = 1;
|
||||
|
||||
public static final int COOL_FALCON_FORWARD_ID = 3;
|
||||
public static final int COOL_FALCON_REVERSE_ID = 2;
|
||||
}
|
||||
|
||||
public static final class LEDConstants {
|
||||
public static final int LED_SPARK_ID = 0;
|
||||
public static final LEDPatterns DEFAULT_PATTERN = LEDPatterns.FOREST_WAVES;
|
||||
}
|
||||
|
||||
public static final class VisionConstants {
|
||||
public static final double FOV = 29.8; // Field of view of limelight
|
||||
public static final double TARGET_HEIGHT = 67.5;
|
||||
public static final double LIME_ANGLE = 24.7;
|
||||
public static final double TURN_P_VALUE = 0.8;
|
||||
public static final double X_ANGLE_ERROR = 0.5;
|
||||
public static final double MOTOR_DEAD_ZONE = 0.2;
|
||||
public static final double DISTANCE_ERROR_EQUATION_M = 1.1279;
|
||||
public static final double DISTANCE_ERROR_EQUATION_B = -15.0684;
|
||||
public static final double GRAV = 385.83;
|
||||
|
||||
// Galactic Search
|
||||
public static final double searchError = 2;
|
||||
/*
|
||||
public static final double bothCloseVisibleY = -17.69;
|
||||
public static final double closeLeftVisibleY = -12.57;
|
||||
public static final double closeRightVisibleY = -11.35;
|
||||
public static final double farLeftVisibleX = 3.58;
|
||||
public static final double farRightVisibleX = 7.04;
|
||||
*/
|
||||
|
||||
public static final double[] aRed = { 1.6, -11.7 };
|
||||
public static final double[] bRed = { 2.5, -5.5 };
|
||||
public static final double[] aBlue = { 9.9, 9.0 };
|
||||
public static final double[] bBlue = { 5.5, 13.3 };
|
||||
}
|
||||
|
||||
public static final class OIConstants {
|
||||
public static final int XBOX_DRIVER_ID = 0;
|
||||
public static final int XBOX_OPERATOR_ID = 1;
|
||||
public static final int BUTTON_FOX_ID = 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,10 @@
|
||||
|
||||
package frc4388.robot;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.ctre.phoenix.motorcontrol.NeutralMode;
|
||||
|
||||
import edu.wpi.first.wpilibj.TimedRobot;
|
||||
import edu.wpi.first.wpilibj.geometry.Pose2d;
|
||||
import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder;
|
||||
import edu.wpi.first.wpilibj.smartdashboard.SendableBuilderImpl;
|
||||
import edu.wpi.first.wpilibj.smartdashboard.SendableChooser;
|
||||
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
|
||||
import edu.wpi.first.wpilibj2.command.Command;
|
||||
@@ -46,11 +42,11 @@ public class Robot extends TimedRobot {
|
||||
// Instantiate our RobotContainer. This will perform all our button bindings, and put our
|
||||
// autonomous chooser on the dashboard.
|
||||
m_robotContainer = new RobotContainer();
|
||||
SmartDashboard.putString("Is Auto Start?", "NAH");
|
||||
Mode.set(Mode.COMPETITIVE);
|
||||
m_modeChooser.setDefaultOption(Mode.COMPETITIVE.name(), Mode.COMPETITIVE);
|
||||
m_modeChooser.addOption(Mode.CASUAL.name(), Mode.CASUAL);
|
||||
SmartDashboard.putData("Mode", m_modeChooser);
|
||||
SmartDashboard.putString("Is Auto Start?", "NAH");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,7 +137,7 @@ public class Robot extends TimedRobot {
|
||||
m_robotContainer.setDriveNeutralMode(NeutralMode.Brake);
|
||||
m_robotContainer.setDriveGearState(true);
|
||||
|
||||
m_robotContainer.shiftClimberRachet(false);
|
||||
m_robotContainer.shiftClimberRatchet(false);
|
||||
//m_robotContainer.configDriveTrainSensors(FeedbackDevice.IntegratedSensor);
|
||||
|
||||
// This makes sure that the autonomous stops running when
|
||||
@@ -161,15 +157,4 @@ public class Robot extends TimedRobot {
|
||||
@Override
|
||||
public void teleopPeriodic() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testInit() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called periodically during test mode.
|
||||
*/
|
||||
@Override
|
||||
public void testPeriodic() {
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -10,11 +10,11 @@ package frc4388.robot.commands;
|
||||
import edu.wpi.first.wpilibj2.command.CommandBase;
|
||||
import edu.wpi.first.wpilibj2.command.SubsystemBase;
|
||||
|
||||
public class InterruptSubystem extends CommandBase {
|
||||
public class InterruptSubsystem extends CommandBase {
|
||||
/**
|
||||
* Creates a new InterruptSubystem.
|
||||
* Creates a new InterruptSubsystem.
|
||||
*/
|
||||
public InterruptSubystem(SubsystemBase subsystem) {
|
||||
public InterruptSubsystem(SubsystemBase subsystem) {
|
||||
// Use addRequirements() here to declare subsystem dependencies.
|
||||
addRequirements(subsystem);
|
||||
}
|
||||
+3
-3
@@ -10,13 +10,13 @@ package frc4388.robot.commands.climber;
|
||||
import edu.wpi.first.wpilibj2.command.CommandBase;
|
||||
import frc4388.robot.subsystems.Climber;
|
||||
|
||||
public class DisengageRachet extends CommandBase {
|
||||
public class DisengageRatchet extends CommandBase {
|
||||
Climber m_climber;
|
||||
|
||||
/**
|
||||
* Creates a new DisengageRachet command.
|
||||
* Creates a new DisengageRatchet command.
|
||||
*/
|
||||
public DisengageRachet(Climber subsystem) {
|
||||
public DisengageRatchet(Climber subsystem) {
|
||||
// Use addRequirements() here to declare subsystem dependencies.
|
||||
m_climber = subsystem;
|
||||
}
|
||||
@@ -27,7 +27,7 @@ public class Climber extends SubsystemBase {
|
||||
//Spark m_spark = new Spark(4);
|
||||
|
||||
public boolean m_climberSafety = false;
|
||||
public boolean m_isRachetEngaged = true;
|
||||
public boolean m_isRatchetEngaged = true;
|
||||
|
||||
/**
|
||||
* Creates a new Climber.
|
||||
@@ -50,7 +50,7 @@ public class Climber extends SubsystemBase {
|
||||
public void periodic() {
|
||||
// This method will be called once per scheduler run
|
||||
SmartDashboard.putBoolean("Climber Safety", m_climberSafety);
|
||||
SmartDashboard.putBoolean("Rachet", m_isRachetEngaged);
|
||||
SmartDashboard.putBoolean("Rachet", m_isRatchetEngaged);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,7 +80,7 @@ public class Climber extends SubsystemBase {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param shift true to enage rachet, false to disengage
|
||||
* @param shift true to engage ratchet, false to disengage
|
||||
*/
|
||||
public void shiftServo(boolean shift) {
|
||||
if (shift) {
|
||||
@@ -88,6 +88,6 @@ public class Climber extends SubsystemBase {
|
||||
} else {
|
||||
m_servo.setPosition(0.56);
|
||||
}
|
||||
m_isRachetEngaged = shift;
|
||||
m_isRatchetEngaged = shift;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,11 @@ import com.ctre.phoenix.motorcontrol.TalonFXControlMode;
|
||||
import com.ctre.phoenix.motorcontrol.can.WPI_TalonFX;
|
||||
|
||||
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.SubsystemBase;
|
||||
import frc4388.robot.Constants;
|
||||
import frc4388.robot.Robot;
|
||||
import frc4388.robot.Constants.ShooterConstants;
|
||||
import frc4388.utility.CSV;
|
||||
import frc4388.utility.Trims;
|
||||
@@ -102,7 +104,7 @@ public class Shooter extends SubsystemBase {
|
||||
csv.read(new File(Filesystem.getDeployDirectory(), "Robot Data - Distances.csv").toPath()),
|
||||
csv.read(new File(Filesystem.getDeployDirectory(), "Robot Data - Distances Casual.csv").toPath())
|
||||
};
|
||||
new Thread(() -> System.out.println(CSV.ReflectionTable.create(getActiveShooterTable()))).start();
|
||||
new Thread(() -> System.out.println(CSV.ReflectionTable.create(getActiveShooterTable(), RobotBase.isSimulation()))).start();
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.function.Predicate;
|
||||
@@ -36,204 +37,206 @@ import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class CSV<R> {
|
||||
private static final Pattern SANITIZER = Pattern.compile("[^$\\w,]");
|
||||
private static final Pattern SANITIZER = Pattern.compile("[^$\\w,]");
|
||||
|
||||
private final Supplier<R> generator;
|
||||
private final IntFunction<R[]> arrayGenerator;
|
||||
private final Map<String, BiConsumer<R, String>> setters;
|
||||
private final Supplier<R> generator;
|
||||
private final IntFunction<R[]> arrayGenerator;
|
||||
private final Map<String, BiConsumer<R, String>> setters;
|
||||
|
||||
/**
|
||||
* A binary string operator to be applied to the entire header of the CSV.
|
||||
*/
|
||||
protected String headerSanitizer(final String header) {
|
||||
return SANITIZER.matcher(header).replaceAll("");
|
||||
/**
|
||||
* A binary string operator to be applied to the entire header of the CSV.
|
||||
*/
|
||||
protected String headerSanitizer(final String header) {
|
||||
return SANITIZER.matcher(header).replaceAll("");
|
||||
}
|
||||
|
||||
/**
|
||||
* A binary string operator to be applied to each name in the header of the CSV.
|
||||
*/
|
||||
protected String nameProcessor(final String name) {
|
||||
return Character.toLowerCase(name.charAt(0)) + name.substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code CSV} instance and prepares for populating the fields of objects created by
|
||||
* the given generator. Private fields and fields of primitive types are not supported.
|
||||
* @param generator a parameterless supplier which produces a new object with any number of fields
|
||||
* corresponding to header names from a CSV file. The first character of the names
|
||||
* from the header in the CSV file will be made lowercase and invalid characters
|
||||
* will be removed to match Java naming conventions.
|
||||
* @see #read(Path)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public CSV(final Supplier<R> generator) {
|
||||
final Class<?> clazz = generator.get().getClass();
|
||||
final Map<Class<?>, Function<String, ?>> fieldParsers = new HashMap<>();
|
||||
this.arrayGenerator = size -> (R[]) Array.newInstance(clazz, size);
|
||||
this.generator = generator;
|
||||
this.setters = new HashMap<>();
|
||||
for (final Field field : clazz.getFields()) {
|
||||
final Function<String, ?> parser = Modifier.isStatic(field.getModifiers()) ? null : fieldParsers.computeIfAbsent(field.getType(), CSV::getTypeParser);
|
||||
if (parser != null)
|
||||
this.setters.put(field.getName(), (final R obj, final String rawValue) -> {
|
||||
try {
|
||||
field.set(obj, rawValue.isEmpty() ? null : parser.apply(rawValue));
|
||||
} catch (final IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and parses the contents of the given CSV file, and returns an array filled with populated
|
||||
* objects created with the previously given generator. Cells are parsed using their corresponding
|
||||
* field's {@code valueOf(String)} function.
|
||||
* @param path the path to a CSV file
|
||||
* @return the parsed data from the CSV file
|
||||
* @throws IOException if an I/O error occurs opening the file
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public R[] read(final Path path) throws IOException {
|
||||
try (final BufferedReader reader = Files.newBufferedReader(path)) {
|
||||
final BiConsumer<R, String>[] fieldSetters = Stream.of(headerSanitizer(reader.readLine()).split(",")).map(this::nameProcessor).map(setters::get).toArray(BiConsumer[]::new);
|
||||
final Stream<String> lines = reader.lines();
|
||||
return lines.filter(Predicate.not(String::isBlank)).map(line -> deserializeRecordString(line, fieldSetters, generator.get())).toArray(this.arrayGenerator);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Function<String, ?> getTypeParser(final Class<?> type) {
|
||||
try {
|
||||
return type.isAssignableFrom(String.class) ? Function.identity() : MethodHandleProxies.asInterfaceInstance(Function.class, MethodHandles.publicLookup().findStatic(type, "valueOf", MethodType.methodType(type, String.class)));
|
||||
} catch (final NoSuchMethodException | IllegalAccessException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static <R> R deserializeRecordString(final String recordString, final BiConsumer<R, String>[] fieldParseSetters, final R object) {
|
||||
final int recordStringLength = recordString.length();
|
||||
int fieldBeginIndex = 0, tryFieldEndFromIndex = 0, i = 0;
|
||||
while (tryFieldEndFromIndex < recordStringLength && i < fieldParseSetters.length) {
|
||||
final int tryFieldEndIndex = recordString.indexOf(',', tryFieldEndFromIndex);
|
||||
String field = recordString.substring(fieldBeginIndex, tryFieldEndIndex == -1 ? recordStringLength : tryFieldEndIndex).strip();
|
||||
if (!field.isEmpty() && (tryFieldEndFromIndex != fieldBeginIndex || field.charAt(0) == '"')) {
|
||||
final int fieldLength = field.length();
|
||||
if (countTrailing(field, '"') % 2 == 0) {
|
||||
tryFieldEndFromIndex = tryFieldEndIndex + 1;
|
||||
continue;
|
||||
} else
|
||||
field = field.substring(1, fieldLength - 1).replace("\"\"", "\"");
|
||||
}
|
||||
final BiConsumer<R, String> setter = fieldParseSetters[i];
|
||||
if (setter != null)
|
||||
setter.accept(object, field);
|
||||
tryFieldEndFromIndex = fieldBeginIndex = tryFieldEndIndex + 1;
|
||||
i++;
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
private static int countTrailing(final String str, final char c) {
|
||||
final int l = str.length();
|
||||
int count = 0;
|
||||
while (str.charAt(l - count - 1) == c && count < l)
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
public static final class ReflectionTable {
|
||||
public static <T> String create(final T[] objects, boolean colored) {
|
||||
final Field[] fields = Stream.of(objects).flatMap(object -> Stream.of(object.getClass().getFields())).distinct().toArray(Field[]::new);
|
||||
final List<List<ReflectionTable>> rows = new ArrayList<>();
|
||||
rows.add(Stream.of(fields).map(ReflectionTable::new).collect(Collectors.toList()));
|
||||
rows.addAll(Stream.of(objects).map(obj -> Stream.of(fields).map(field -> new ReflectionTable(obj, field)).collect(Collectors.toList())).collect(Collectors.toList()));
|
||||
final int[] columnWidths = rows.stream().map(row -> row.stream().map(cell -> cell.string).mapToInt(String::length).toArray()).reduce(new int[fields.length], (result, row) -> IntStream.range(0, row.length).map(i -> Math.max(result[i], row[i])).toArray());
|
||||
if (colored)
|
||||
IntStream.range(0, fields.length).forEach(i -> {
|
||||
final var columnSummaryStatistics = rows.stream().skip(1).mapToDouble(row -> row.get(i).getValue().doubleValue()).summaryStatistics();
|
||||
rows.stream().skip(1).forEach(row -> row.get(i).colorByValue(columnSummaryStatistics.getMin(), columnSummaryStatistics.getMax()));
|
||||
});
|
||||
MessageFormat formatFormat = new MessageFormat(colored ? "{2} %{0}{1}s {3}" : " %{0}{1}s ");
|
||||
return rows.stream().map(row -> IntStream.range(0, row.size()).mapToObj(i -> String.format(formatFormat.format(new Object[] { row.get(i).padRight ? "-" : "", columnWidths[i], row.get(i).escape, RESET_STYLE }), row.get(i).string)).collect(Collectors.joining("|"))).collect(Collectors.joining(LF));
|
||||
}
|
||||
|
||||
/**
|
||||
* A binary string operator to be applied to each name in the header of the CSV.
|
||||
*/
|
||||
protected String nameProcessor(final String name) {
|
||||
return Character.toLowerCase(name.charAt(0)) + name.substring(1);
|
||||
private static final Color GRADIENT_MIN = new Color(0x00, 0x33, 0x00);
|
||||
private static final Color GRADIENT_MAX = new Color(0x00, 0xFF, 0x00);
|
||||
private static final String CONTROL = "\033";
|
||||
private static final String CSI = "[";
|
||||
private static final String LF = "\n";
|
||||
private static final String RESET = "0";
|
||||
private static final String BOLD = "1";
|
||||
private static final String ITALIC = "3";
|
||||
private static final String UNDERLINE = "4";
|
||||
private static final String BACKGROUND_RED = "41";
|
||||
private static final String FOREGROUND = "38";
|
||||
private static final String BACKGROUND = "48";
|
||||
private static final String TRUECOLOR = "2";
|
||||
private static final String SEPARATOR = ";";
|
||||
private static final String SGR = "m";
|
||||
private static final String HEADER_STYLE = CONTROL + CSI + BOLD + SEPARATOR + UNDERLINE + SGR;
|
||||
private static final String NULL_STYLE = CONTROL + CSI + ITALIC + SGR;
|
||||
private static final String ERROR_STYLE = CONTROL + CSI + ITALIC + SGR + CONTROL + CSI + BACKGROUND_RED + SGR;
|
||||
private static final String RESET_STYLE = CONTROL + CSI + RESET + SGR;
|
||||
private Object value;
|
||||
private String string;
|
||||
private boolean padRight;
|
||||
private String escape;
|
||||
|
||||
private ReflectionTable(final Object obj, final Field field) {
|
||||
try {
|
||||
value = field.get(obj);
|
||||
string = Objects.toString(value);
|
||||
padRight = !Number.class.isAssignableFrom(field.getType());
|
||||
escape = Objects.isNull(value) ? NULL_STYLE : "";
|
||||
} catch (final IllegalAccessException | IllegalArgumentException e) {
|
||||
value = null;
|
||||
string = e.getClass().getSimpleName();
|
||||
padRight = false;
|
||||
escape = ERROR_STYLE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code CSV} instance and prepares for populating the fields of objects created by
|
||||
* the given generator. Private fields and fields of primitive types are not supported.
|
||||
* @param generator a parameterless supplier which produces a new object with any number of fields
|
||||
* corresponding to header names from a CSV file. The first character of the names
|
||||
* from the header in the CSV file will be made lowercase and invalid characters
|
||||
* will be removed to match Java naming conventions.
|
||||
* @see #read(Path)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public CSV(final Supplier<R> generator) {
|
||||
final Class<?> clazz = generator.get().getClass();
|
||||
final Map<Class<?>, Function<String, ?>> fieldParsers = new HashMap<>();
|
||||
this.arrayGenerator = size -> (R[]) Array.newInstance(clazz, size);
|
||||
this.generator = generator;
|
||||
this.setters = new HashMap<>();
|
||||
for (final Field field : clazz.getFields()) {
|
||||
final Function<String, ?> parser = Modifier.isStatic(field.getModifiers()) ? null : fieldParsers.computeIfAbsent(field.getType(), CSV::getTypeParser);
|
||||
if (parser != null)
|
||||
this.setters.put(field.getName(), (final R obj, final String rawValue) -> {
|
||||
try {
|
||||
field.set(obj, rawValue.isEmpty() ? null : parser.apply(rawValue));
|
||||
} catch (final IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
private ReflectionTable(final Field field) {
|
||||
value = null;
|
||||
string = field.getName();
|
||||
padRight = true;
|
||||
escape = HEADER_STYLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and parses the contents of the given CSV file, and returns an array filled with populated
|
||||
* objects created with the previously given generator. Cells are parsed using their corresponding
|
||||
* field's {@code valueOf(String)} function.
|
||||
* @param path the path to a CSV file
|
||||
* @return the parsed data from the CSV file
|
||||
* @throws IOException if an I/O error occurs opening the file
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public R[] read(final Path path) throws IOException {
|
||||
try (final BufferedReader reader = Files.newBufferedReader(path)) {
|
||||
final BiConsumer<R, String>[] fieldSetters = Stream.of(headerSanitizer(reader.readLine()).split(",")).map(this::nameProcessor).map(setters::get).toArray(BiConsumer[]::new);
|
||||
final Stream<String> lines = reader.lines();
|
||||
return lines.filter(Predicate.not(String::isBlank)).map(line -> deserializeRecordString(line, fieldSetters, generator.get())).toArray(this.arrayGenerator);
|
||||
}
|
||||
private Number getValue() {
|
||||
return padRight ? Objects.hashCode(string) : Objects.requireNonNullElse((Number) value, 0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Function<String, ?> getTypeParser(final Class<?> type) {
|
||||
try {
|
||||
return type.isAssignableFrom(String.class) ? Function.identity() : MethodHandleProxies.asInterfaceInstance(Function.class, MethodHandles.publicLookup().findStatic(type, "valueOf", MethodType.methodType(type, String.class)));
|
||||
} catch (final NoSuchMethodException | IllegalAccessException e) {
|
||||
return null;
|
||||
}
|
||||
private void colorByValue(final Number min, final Number max) {
|
||||
if (Objects.nonNull(value)) {
|
||||
final double range = max.doubleValue() - min.doubleValue();
|
||||
final double normal = range == 0 ? 0 : (getValue().doubleValue() - min.doubleValue()) / range;
|
||||
final Color color = new Color(range(normal, GRADIENT_MIN.getRed(), GRADIENT_MAX.getRed()), range(normal, GRADIENT_MIN.getGreen(), GRADIENT_MAX.getGreen()), range(normal, GRADIENT_MIN.getBlue(), GRADIENT_MAX.getBlue()));
|
||||
escape += (contrastRatio(color, Color.BLACK) > contrastRatio(Color.WHITE, color) ? colorTo24BitSGR(Color.BLACK, false) : colorTo24BitSGR(Color.WHITE, false)) + colorTo24BitSGR(color, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static <R> R deserializeRecordString(final String recordString, final BiConsumer<R, String>[] fieldParseSetters, final R object) {
|
||||
final int recordStringLength = recordString.length();
|
||||
int fieldBeginIndex = 0, tryFieldEndFromIndex = 0, i = 0;
|
||||
while (tryFieldEndFromIndex < recordStringLength && i < fieldParseSetters.length) {
|
||||
final int tryFieldEndIndex = recordString.indexOf(',', tryFieldEndFromIndex);
|
||||
String field = recordString.substring(fieldBeginIndex, tryFieldEndIndex == -1 ? recordStringLength : tryFieldEndIndex).strip();
|
||||
if (!field.isEmpty() && (tryFieldEndFromIndex != fieldBeginIndex || field.charAt(0) == '"')) {
|
||||
final int fieldLength = field.length();
|
||||
if (countTrailing(field, '"') % 2 == 0) {
|
||||
tryFieldEndFromIndex = tryFieldEndIndex + 1;
|
||||
continue;
|
||||
} else
|
||||
field = field.substring(1, fieldLength - 1).replace("\"\"", "\"");
|
||||
}
|
||||
final BiConsumer<R, String> setter = fieldParseSetters[i];
|
||||
if (setter != null)
|
||||
setter.accept(object, field);
|
||||
tryFieldEndFromIndex = fieldBeginIndex = tryFieldEndIndex + 1;
|
||||
i++;
|
||||
}
|
||||
return object;
|
||||
private static String colorTo24BitSGR(final Color color, final boolean background) {
|
||||
return CONTROL + CSI + (background ? BACKGROUND : FOREGROUND) + SEPARATOR + TRUECOLOR + SEPARATOR + color.getRed() + SEPARATOR + color.getGreen() + SEPARATOR + color.getBlue() + SGR;
|
||||
}
|
||||
|
||||
private static int countTrailing(final String str, final char c) {
|
||||
final int l = str.length();
|
||||
int count = 0;
|
||||
while (str.charAt(l - count - 1) == c && count < l)
|
||||
count++;
|
||||
return count;
|
||||
private static int range(final double normal, final int min, final int max) {
|
||||
return (int) (normal * (max - min) + min);
|
||||
}
|
||||
|
||||
public static class ReflectionTable {
|
||||
public static <T> String create(final T[] objects) {
|
||||
final Field[] fields = Stream.of(objects).flatMap(object -> Stream.of(object.getClass().getFields())).distinct().toArray(Field[]::new);
|
||||
final List<List<ReflectionTable>> rows = new ArrayList<>();
|
||||
rows.add(Stream.of(fields).map(ReflectionTable::new).collect(Collectors.toList()));
|
||||
rows.addAll(Stream.of(objects).map(obj -> Stream.of(fields).map(field -> new ReflectionTable(obj, field)).collect(Collectors.toList())).collect(Collectors.toList()));
|
||||
final int[] columnWidths = rows.stream().map(row -> row.stream().map(cell -> cell.string).mapToInt(String::length).toArray()).reduce(new int[fields.length], (result, row) -> IntStream.range(0, row.length).map(i -> Math.max(result[i], row[i])).toArray());
|
||||
IntStream.range(0, fields.length).forEach(i -> {
|
||||
final var columnSummaryStatistics = rows.stream().skip(1).mapToDouble(row -> row.get(i).getValue().doubleValue()).summaryStatistics();
|
||||
rows.stream().skip(1).forEach(row -> row.get(i).colorByValue(columnSummaryStatistics.getMin(), columnSummaryStatistics.getMax()));
|
||||
});
|
||||
return rows.stream().map(row -> IntStream.range(0, row.size()).mapToObj(i -> String.format(MessageFormat.format("{0} %{1}{2}s {3}", row.get(i).escape, row.get(i).padRight ? "-" : "", columnWidths[i], RESET_STYLE), row.get(i).string)).collect(Collectors.joining("|"))).collect(Collectors.joining(LF));
|
||||
}
|
||||
|
||||
private static final Color GRADIENT_MIN = new Color(0x00, 0x33, 0x00);
|
||||
private static final Color GRADIENT_MAX = new Color(0x00, 0xFF, 0x00);
|
||||
private static final String CONTROL = "\033";
|
||||
private static final String CSI = "[";
|
||||
private static final String LF = "\n";
|
||||
private static final String RESET = "0";
|
||||
private static final String BOLD = "1";
|
||||
private static final String ITALIC = "3";
|
||||
private static final String UNDERLINE = "4";
|
||||
private static final String BACKGROUND_RED = "41";
|
||||
private static final String FOREGROUND = "38";
|
||||
private static final String BACKGROUND = "48";
|
||||
private static final String TRUECOLOR = "2";
|
||||
private static final String SEPARATOR = ";";
|
||||
private static final String SGR = "m";
|
||||
private static final String HEADER_STYLE = CONTROL + CSI + BOLD + SEPARATOR + UNDERLINE + SGR;
|
||||
private static final String NULL_STYLE = CONTROL + CSI + ITALIC + SGR;
|
||||
private static final String ERROR_STYLE = CONTROL + CSI + ITALIC + SGR + CONTROL + CSI + BACKGROUND_RED + SGR;
|
||||
private static final String RESET_STYLE = CONTROL + CSI + RESET + SGR;
|
||||
private Object value;
|
||||
private String string;
|
||||
private boolean padRight;
|
||||
private String escape;
|
||||
|
||||
private ReflectionTable(final Object obj, final Field field) {
|
||||
try {
|
||||
value = field.get(obj);
|
||||
string = Objects.toString(value);
|
||||
padRight = !Number.class.isAssignableFrom(field.getType());
|
||||
escape = Objects.isNull(value) ? NULL_STYLE : "";
|
||||
} catch (final IllegalAccessException | IllegalArgumentException e) {
|
||||
value = null;
|
||||
string = e.getClass().getSimpleName();
|
||||
padRight = false;
|
||||
escape = ERROR_STYLE;
|
||||
}
|
||||
}
|
||||
|
||||
private ReflectionTable(final Field field) {
|
||||
value = null;
|
||||
string = field.getName();
|
||||
padRight = true;
|
||||
escape = HEADER_STYLE;
|
||||
}
|
||||
|
||||
private Number getValue() {
|
||||
return padRight ? Objects.hashCode(string) : Objects.requireNonNullElse((Number) value, 0);
|
||||
}
|
||||
|
||||
private void colorByValue(final Number min, final Number max) {
|
||||
if (Objects.nonNull(value)) {
|
||||
final double range = max.doubleValue() - min.doubleValue();
|
||||
final double normal = range == 0 ? 0 : (getValue().doubleValue() - min.doubleValue()) / range;
|
||||
final Color color = new Color(range(normal, GRADIENT_MIN.getRed(), GRADIENT_MAX.getRed()), range(normal, GRADIENT_MIN.getGreen(), GRADIENT_MAX.getGreen()), range(normal, GRADIENT_MIN.getBlue(), GRADIENT_MAX.getBlue()));
|
||||
escape += (contrastRatio(color, Color.BLACK) > contrastRatio(Color.WHITE, color) ? colorTo24BitSGR(Color.BLACK, false) : colorTo24BitSGR(Color.WHITE, false)) + colorTo24BitSGR(color, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static String colorTo24BitSGR(final Color color, final boolean background) {
|
||||
return CONTROL + CSI + (background ? BACKGROUND : FOREGROUND) + SEPARATOR + TRUECOLOR + SEPARATOR + color.getRed() + SEPARATOR + color.getGreen() + SEPARATOR + color.getBlue() + SGR;
|
||||
}
|
||||
|
||||
private static int range(final double normal, final int min, final int max) {
|
||||
return (int) (normal * (max - min) + min);
|
||||
}
|
||||
|
||||
/* https://www.w3.org/TR/WCAG20/#contrast-ratiodef */
|
||||
private static float contrastRatio(final Color lighter, final Color darker) {
|
||||
return (relativeLuminance(lighter) + 0.05f) / (relativeLuminance(darker) + 0.05f);
|
||||
}
|
||||
|
||||
/* https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef */
|
||||
private static float relativeLuminance(final Color color) {
|
||||
final float[] components = color.getRGBComponents(null);
|
||||
final float r = components[0] <= 0.03928f ? components[0] / 12.92f : (float) Math.pow((components[0] + 0.055f) / 1.055f, 2.4f);
|
||||
final float g = components[1] <= 0.03928f ? components[1] / 12.92f : (float) Math.pow((components[1] + 0.055f) / 1.055f, 2.4f);
|
||||
final float b = components[2] <= 0.03928f ? components[2] / 12.92f : (float) Math.pow((components[2] + 0.055f) / 1.055f, 2.4f);
|
||||
return 0.2126f * r + 0.7152f * g + 0.0722f * b;
|
||||
}
|
||||
/* https://www.w3.org/TR/WCAG20/#contrast-ratiodef */
|
||||
private static float contrastRatio(final Color lighter, final Color darker) {
|
||||
return (relativeLuminance(lighter) + 0.05f) / (relativeLuminance(darker) + 0.05f);
|
||||
}
|
||||
|
||||
/* https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef */
|
||||
private static float relativeLuminance(final Color color) {
|
||||
final float[] components = color.getRGBComponents(null);
|
||||
final float r = components[0] <= 0.03928f ? components[0] / 12.92f : (float) Math.pow((components[0] + 0.055f) / 1.055f, 2.4f);
|
||||
final float g = components[1] <= 0.03928f ? components[1] / 12.92f : (float) Math.pow((components[1] + 0.055f) / 1.055f, 2.4f);
|
||||
final float b = components[2] <= 0.03928f ? components[2] / 12.92f : (float) Math.pow((components[2] + 0.055f) / 1.055f, 2.4f);
|
||||
return 0.2126f * r + 0.7152f * g + 0.0722f * b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user