Skip to content

EVStates

Johan Vandegriff edited this page Dec 16, 2016 · 2 revisions

EVStates | EVEndConditions | EVStateMachineBuilder


EVStates is a factory class that extends States from the state-machine-framework. It inherits all the factory methods from States, and adds its own that relate to FTC.

Some that we use frequently are:

  • mecanumDrive -- drives with the mecanum wheels and stabilizes with the gyro
  • servoTurn -- turns a servo to a preset at a given speed
  • calibrateGyro -- waits for the gyro sensor to finish calibrating

Some others that are also useful are:

  • drive -- drives with a TwoMotors object
  • turn -- tuns with a TwoMotors object
  • oneWheelTurn -- powers one wheel to turn
  • motorTurn -- turns a motor

ftc/evlib/statemachine/EVStates.java

package ftc.evlib.statemachine;

import com.google.common.collect.ImmutableList;
import com.qualcomm.robotcore.hardware.GyroSensor;

import org.firstinspires.ftc.robotcore.external.navigation.VuforiaTrackable;

import java.util.List;

import ftc.electronvolts.statemachine.AbstractState;
import ftc.electronvolts.statemachine.BasicAbstractState;
import ftc.electronvolts.statemachine.EndCondition;
import ftc.electronvolts.statemachine.EndConditions;
import ftc.electronvolts.statemachine.State;
import ftc.electronvolts.statemachine.StateName;
import ftc.electronvolts.statemachine.States;
import ftc.electronvolts.statemachine.Transition;
import ftc.electronvolts.util.InputExtractor;
import ftc.electronvolts.util.ResultReceiver;
import ftc.electronvolts.util.TeamColor;
import ftc.electronvolts.util.units.Angle;
import ftc.electronvolts.util.units.Distance;
import ftc.electronvolts.util.units.Time;
import ftc.evlib.driverstation.Telem;
import ftc.evlib.hardware.control.LineUpControl;
import ftc.evlib.hardware.control.MecanumControl;
import ftc.evlib.hardware.control.RotationControl;
import ftc.evlib.hardware.control.RotationControls;
import ftc.evlib.hardware.control.TranslationControl;
import ftc.evlib.hardware.control.TranslationControls;
import ftc.evlib.hardware.mechanisms.Shooter;
import ftc.evlib.hardware.motors.MecanumMotors;
import ftc.evlib.hardware.motors.Motor;
import ftc.evlib.hardware.motors.NMotors;
import ftc.evlib.hardware.motors.TwoMotors;
import ftc.evlib.hardware.sensors.DigitalSensor;
import ftc.evlib.hardware.sensors.DistanceSensor;
import ftc.evlib.hardware.sensors.DoubleLineSensor;
import ftc.evlib.hardware.sensors.LineSensorArray;
import ftc.evlib.hardware.servos.ServoControl;
import ftc.evlib.hardware.servos.Servos;
import ftc.evlib.vision.framegrabber.FrameGrabber;
import ftc.evlib.vision.framegrabber.VuforiaFrameFeeder;
import ftc.evlib.vision.processors.BeaconColorResult;
import ftc.evlib.vision.processors.BeaconName;
import ftc.evlib.vision.processors.CloseUpColorProcessor;
import ftc.evlib.vision.processors.ImageProcessor;
import ftc.evlib.vision.processors.ImageProcessorResult;
import ftc.evlib.vision.processors.Location;
import ftc.evlib.vision.processors.RGBBeaconProcessor;
import ftc.evlib.vision.processors.VuforiaBeaconColorProcessor;

import static ftc.evlib.driverstation.Telem.telemetry;
import static ftc.evlib.vision.framegrabber.GlobalFrameGrabber.frameGrabber;
import static ftc.evlib.vision.framegrabber.VuforiaFrameFeeder.beacons;

/**
 * This file was made by the electronVolts, FTC team 7393
 * Date Created: 5/10/16
 *
 * @see State
 * @see EVStateMachineBuilder
 */
public class EVStates extends States {

    /**
     * Displays the left and right color of a BeaconColorResult
     *
     * @param stateName the name of the state
     * @param receiver  the ResultReceiver to get the color from
     * @return the created State
     * @see BeaconColorResult
     */
    public static State displayBeaconColorResult(StateName stateName, final ResultReceiver<BeaconColorResult> receiver) {
        return new BasicAbstractState(stateName) {
            @Override
            public void init() {

            }

            @Override
            public boolean isDone() {
                Telem.displayBeaconColorResult(receiver);
                return false;
            }

            @Override
            public StateName getNextStateName() {
                return null;
            }
        };
    }

    /**
     * Displays the color of a BeaconColorResult.BeaconColor
     *
     * @param stateName the name of the state
     * @param receiver  the ResultReceiver to get the color from
     * @return the created State
     * @see BeaconColorResult
     */
    public static State displayBeaconColor(StateName stateName, final ResultReceiver<BeaconColorResult.BeaconColor> receiver) {
        return new BasicAbstractState(stateName) {
            @Override
            public void init() {

            }

            @Override
            public boolean isDone() {
                Telem.displayBeaconColor(receiver);
                return false;
            }

            @Override
            public StateName getNextStateName() {
                return null;
            }
        };
    }

    /**
     * Uses vuforia to find the beacon target image, then uses opencv to determine the beacon color
     *
     * @param stateName         the name of the state
     * @param successState      the state to go to if it succeeds
     * @param failState         the state to go to if it fails
     * @param timeoutState      the state to go to if it times out
     * @param timeoutTime       the time before it will time out
     * @param vuforiaReceiver   the ResultReceiver to get the VuforiaFramFeeder object from
     * @param beaconColorResult the ResultReceiver to store the result in
     * @param teamColor         your team's color to decide which beacons to look for
     * @param numFrames         the number of frames to process
     * @param saveImages        whether or not to save the frames for logging
     * @return the created State
     * @see VuforiaFrameFeeder
     * @see VuforiaBeaconColorProcessor
     */
    //TODO assume that vuforia is initialized in findBeaconColorState
    public static State findBeaconColorState(StateName stateName, final StateName successState, final StateName failState, final StateName timeoutState, Time timeoutTime, final ResultReceiver<VuforiaFrameFeeder> vuforiaReceiver, final ResultReceiver<BeaconColorResult> beaconColorResult, TeamColor teamColor, final int numFrames, final boolean saveImages) {
        final List<BeaconName> beaconNames = BeaconName.getNamesForTeamColor(teamColor);
        final EndCondition timeout = EndConditions.timed(timeoutTime);

        return new BasicAbstractState(stateName) {
            private VuforiaFrameFeeder vuforia = null;
            private VuforiaBeaconColorProcessor processor = null;
            private BeaconName beaconName;
            private int beaconIndex = 0; //index of the beaconNames list
            private boolean timedOut = false;

            @Override
            public void init() {
                timeout.init();
                timedOut = false;

                if (beaconIndex >= beaconNames.size()) {
                    beaconIndex = 0;
                    //we should never go here
                }
                beaconName = beaconNames.get(beaconIndex);
                if (processor != null) {
                    processor.setBeaconName(beaconName);
                }
            }

            @Override
            public boolean isDone() {
                if (vuforia == null && vuforiaReceiver.isReady()) {
                    vuforia = vuforiaReceiver.getValue();
//                    if (vuforia == null) {
//                        Log.e("EVStates", "vuforia is null!!!!!!!!!!!!!");
//                    }

                    processor = new VuforiaBeaconColorProcessor(vuforia);
                    processor.setBeaconName(beaconName);

                    VuforiaTrackable beacon = beacons.get(beaconName);
                    beacon.getTrackables().activate();

                    frameGrabber.setImageProcessor(processor);
                    frameGrabber.setSaveImages(saveImages);
                    frameGrabber.grabContinuousFrames();
                }
                timedOut = timeout.isDone();
                return timedOut || processor != null && processor.getResultsFound() >= numFrames;
            }

            @Override
            public StateName getNextStateName() {
                beaconIndex++;
                frameGrabber.stopFrameGrabber();
                VuforiaTrackable beacon = beacons.get(beaconName);
                beacon.getTrackables().deactivate();

                BeaconColorResult result = processor.getAverageResult();
                processor.reset();
                BeaconColorResult.BeaconColor leftColor = result.getLeftColor();
                BeaconColorResult.BeaconColor rightColor = result.getRightColor();
                if ((leftColor == BeaconColorResult.BeaconColor.RED && rightColor == BeaconColorResult.BeaconColor.BLUE)
                        || (leftColor == BeaconColorResult.BeaconColor.BLUE && rightColor == BeaconColorResult.BeaconColor.RED)) {
                    beaconColorResult.setValue(result);
                    return successState;
                } else {
                    beaconColorResult.setValue(new BeaconColorResult());
                    if (timedOut) {
                        return timeoutState;
                    } else {
                        return failState;
                    }
                }
            }
        };
    }

    public static State findColorState(StateName stateName, final StateName successState, final StateName unknownState, final ResultReceiver<VuforiaFrameFeeder> vuforiaReceiver, final ResultReceiver<BeaconColorResult.BeaconColor> colorResult, final boolean saveImages) {
        return new BasicAbstractState(stateName) {
            private VuforiaFrameFeeder vuforia = null;
            private CloseUpColorProcessor processor = null;
            private boolean timedOut = false;

            @Override
            public void init() {
                vuforia = vuforiaReceiver.getValue();
                processor = new CloseUpColorProcessor();

                frameGrabber.setImageProcessor(processor);
                frameGrabber.setSaveImages(saveImages);
                frameGrabber.grabSingleFrame();
            }

            @Override
            public boolean isDone() {
                return frameGrabber.isResultReady();
            }

            @Override
            public StateName getNextStateName() {
                BeaconColorResult.BeaconColor result = (BeaconColorResult.BeaconColor) frameGrabber.getResult().getResult();
                colorResult.setValue(result);
                if (result == BeaconColorResult.BeaconColor.RED || result == BeaconColorResult.BeaconColor.BLUE) {
                    return successState;
                } else {
                    return unknownState;
                }
            }
        };
    }

    public static State beaconColorSwitch(StateName stateName, final StateName redState, final StateName blueState, final StateName unknownState, ResultReceiver<BeaconColorResult.BeaconColor> colorResult) {
        return new BasicAbstractState(stateName) {
            @Override
            public void init() {

            }

            @Override
            public boolean isDone() {
                return true;
            }

            @Override
            public StateName getNextStateName() {
                BeaconColorResult.BeaconColor result = (BeaconColorResult.BeaconColor) frameGrabber.getResult().getResult();
                switch (result) {
                    case RED:
                        return redState;
                    case BLUE:
                        return blueState;
                    default:
                        return unknownState;
                }
            }
        };
    }

    /**
     * use the camera to detect and drive up to the beacon
     *
     * @param stateName       the name of the state
     * @param doneState       the state to go to if it works
     * @param lostObjectState the state to go to if it cannot find the beacon
     * @param timeoutState    the state to go to if it times out
     * @param timeoutMillis   the number of milliseconds before the timeout
     * @param mecanumControl  the mecanum wheels
     * @param frameGrabber    access to the camera frames
     * @return the created State
     */
    public static State mecanumCameraTrack(StateName stateName, final StateName doneState, final StateName lostObjectState, final StateName timeoutState, long timeoutMillis, final MecanumControl mecanumControl, final FrameGrabber frameGrabber, ImageProcessor<? extends Location> imageProcessor) {
        mecanumControl.setDriveMode(MecanumMotors.MecanumDriveMode.NORMALIZED);
        final TranslationControl beaconTrackingControl = TranslationControls.cameraTracking(frameGrabber, imageProcessor);
        final long timeoutTime = System.currentTimeMillis() + timeoutMillis;

        return new BasicAbstractState(stateName) {
            @Override
            public void init() {
                mecanumControl.setTranslationControl(beaconTrackingControl);
            }

            @Override
            public boolean isDone() {
                return !mecanumControl.translationWorked() || beaconTrackingControl.getTranslation().getLength() < 0.1 || System.currentTimeMillis() >= timeoutTime;
            }

            @Override
            public StateName getNextStateName() {
                mecanumControl.stop();

                if (beaconTrackingControl.getTranslation().getLength() < 0.1) {
                    return doneState;
                } else {
                    if (!mecanumControl.translationWorked()) {
                        return lostObjectState;
                    } else {
                        return timeoutState;
                    }
                }
            }
        };
    }

    /**
     * @param stateName      the name of the state
     * @param nextStateName  the name of the next state
     * @param imageProcessor the object that processes the image
     * @param resultReceiver the object that stores the image
     * @return the created State
     */
    public static State processFrame(StateName stateName, final StateName nextStateName, final ImageProcessor imageProcessor, final ResultReceiver<ImageProcessorResult> resultReceiver) {
        return new BasicAbstractState(stateName) {
            @Override
            public void init() {
                frameGrabber.setImageProcessor(imageProcessor);
                frameGrabber.grabSingleFrame();
            }

            @Override
            public boolean isDone() {
                return frameGrabber.isResultReady();
            }

            @Override
            public StateName getNextStateName() {
                resultReceiver.setValue(frameGrabber.getResult());
                return nextStateName;
            }
        };
    }

    /**
     * @param stateName           the name of the state
     * @param unknownUnknownState if both sides are unknown
     * @param unknownRedState     if the left is unknown and the right is red
     * @param unknownBlueState    if the left is unknown and the right is blue
     * @param redUnknownState     if the left is red and the right is unknown
     * @param redRedState         if the left is red and the right is red
     * @param redBlueState        if the left is red and the right is blue
     * @param blueUnknownState    if the left is blue and the right is unknown
     * @param blueRedState        if the left is blue and the right is red
     * @param blueBlueState       if the left is blue and the right is blue
     * @return the created State
     */
    public static State processBeaconPicture(StateName stateName,
                                             final StateName unknownUnknownState, final StateName unknownRedState, final StateName unknownBlueState,
                                             final StateName redUnknownState, final StateName redRedState, final StateName redBlueState,
                                             final StateName blueUnknownState, final StateName blueRedState, final StateName blueBlueState
    ) {

        return new BasicAbstractState(stateName) {
            @Override
            public void init() {
                frameGrabber.setImageProcessor(new RGBBeaconProcessor());
                frameGrabber.grabSingleFrame();
            }

            @Override
            public boolean isDone() {
                return frameGrabber.isResultReady();
            }

            @Override
            public StateName getNextStateName() {
                BeaconColorResult beaconColorResult = (BeaconColorResult) frameGrabber.getResult().getResult();
                BeaconColorResult.BeaconColor leftColor = beaconColorResult.getLeftColor();
                BeaconColorResult.BeaconColor rightColor = beaconColorResult.getRightColor();

                if (leftColor == BeaconColorResult.BeaconColor.RED) {
                    if (rightColor == BeaconColorResult.BeaconColor.RED) {
                        return redRedState;
                    } else if (rightColor == BeaconColorResult.BeaconColor.BLUE) {
                        return redBlueState;
                    } else {
                        return redUnknownState;
                    }
                } else if (leftColor == BeaconColorResult.BeaconColor.BLUE) {
                    if (rightColor == BeaconColorResult.BeaconColor.RED) {
                        return blueRedState;
                    } else if (rightColor == BeaconColorResult.BeaconColor.BLUE) {
                        return blueBlueState;
                    } else {
                        return blueUnknownState;
                    }
                } else {
                    if (rightColor == BeaconColorResult.BeaconColor.RED) {
                        return unknownRedState;
                    } else if (rightColor == BeaconColorResult.BeaconColor.BLUE) {
                        return unknownBlueState;
                    } else {
                        return unknownUnknownState;
                    }
                }
            }
        };
    }

    /**
     * @param stateName the name of the state
     * @param nMotors   the motors to turn off
     * @return the created State
     */
    public static State stop(StateName stateName, final NMotors nMotors) {
        return new BasicAbstractState(stateName) {
            @Override
            public void init() {
                nMotors.stop();
            }

            @Override
            public boolean isDone() {
                return false;
            }

            @Override
            public StateName getNextStateName() {
                return null;
            }
        };
    }

    /**
     * @param stateName      the name of the state
     * @param mecanumControl the motors to turn off
     * @return the created State
     */
    public static State stop(StateName stateName, final MecanumControl mecanumControl) {
        return new BasicAbstractState(stateName) {
            @Override
            public void init() {
                mecanumControl.stop();
            }

            @Override
            public boolean isDone() {
                return false;
            }

            @Override
            public StateName getNextStateName() {
                return null;
            }
        };
    }

    /**
     * @param stateName the name of the state
     * @param message   the message to display to the driver station
     * @param value     the value associated with that message
     * @return the created State
     */
    public static State telemetry(StateName stateName, final String message, final double value) {
        return new BasicAbstractState(stateName) {
            @Override
            public void init() {
            }

            @Override
            public boolean isDone() {
                telemetry.addData(message, value);
                return false;
            }

            @Override
            public StateName getNextStateName() {
                return null;
            }
        };
    }

    /**
     * @param stateName the name of the state
     * @param message1  the first message to display to the driver station
     * @param input1    an InputExtractor that returns the value associated with the first message
     * @param message2  the second message to display to the driver station
     * @param input2    an InputExtractor that returns the value associated with the second message
     * @return the created State
     */
    public static State telemetry(StateName stateName, final String message1, final InputExtractor<Double> input1, final String message2, final InputExtractor<Double> input2) {
        return new BasicAbstractState(stateName) {
            @Override
            public void init() {
            }

            @Override
            public boolean isDone() {
                telemetry.addData(message1, input1.getValue());
                telemetry.addData(message2, input2.getValue());
                return false;
            }

            @Override
            public StateName getNextStateName() {
                return null;
            }
        };
    }

    /**
     * @param stateName   the name of the state
     * @param transitions the list of transitions to the next states
     * @param message     the message to display to the driver station
     * @param value       the value associated with that message
     * @return the created State
     */
    public static State telemetry(StateName stateName, List<Transition> transitions, final String message, final double value) {
        return new AbstractState(stateName, transitions) {
            @Override
            public void init() {
            }

            @Override
            public void run() {
                telemetry.addData(message, value);
            }

            @Override
            public void dispose() {
            }
        };
    }

    /**
     * @param stateName   the name of the state
     * @param transitions the list of transitions to the next states
     * @param message1    the first message to display to the driver station
     * @param input1      an InputExtractor that returns the value associated with the first message
     * @param message2    the second message to display to the driver station
     * @param input2      an InputExtractor that returns the value associated with the second message
     * @return the created State
     */
    public static State telemetry(StateName stateName, List<Transition> transitions, final String message1, final InputExtractor<Double> input1, final String message2, final InputExtractor<Double> input2) {
        return new AbstractState(stateName, transitions) {
            @Override
            public void init() {
            }

            @Override
            public void run() {
                telemetry.addData(message1, input1.getValue());
                telemetry.addData(message2, input2.getValue());
            }

            @Override
            public void dispose() {
            }
        };
    }

    /**
     * @param stateName     the name of the state
     * @param nextStateName the name of the next state
     * @param servos        the servos to be initialized
     * @return the created State
     * @see Servos
     */
    public static State servoInit(StateName stateName, final StateName nextStateName, final Servos servos) {
        return new BasicAbstractState(stateName) {
            @Override
            public void init() {
            }

            @Override
            public boolean isDone() {
                return servos.areServosDone();
            }

            @Override
            public StateName getNextStateName() {
                return nextStateName;
            }
        };
    }

    /**
     * @param stateName     the name of the state
     * @param nextStateName the name of the next state
     * @param gyro          the gyro sensor to be calibrated
     * @return the created State
     * @see GyroSensor
     */
    public static State calibrateGyro(StateName stateName, final StateName nextStateName, final GyroSensor gyro) {
        gyro.calibrate();
        return new BasicAbstractState(stateName) {
            @Override
            public void init() {
            }

            @Override
            public boolean isDone() {
                return !gyro.isCalibrating();
            }

            @Override
            public StateName getNextStateName() {
                return nextStateName;
            }
        };
    }

    /**
     * @param stateName        the name of the state
     * @param nextStateName    the name of the next state
     * @param doubleLineSensor the 2 line sensors to be calibrated
     * @return the created State
     * @see DoubleLineSensor
     */
    public static State calibrateLineSensor(StateName stateName, final StateName nextStateName, final DoubleLineSensor doubleLineSensor) {
        doubleLineSensor.calibrate();
        return new BasicAbstractState(stateName) {
            @Override
            public void init() {
            }

            @Override
            public boolean isDone() {
                return doubleLineSensor.isReady();
            }

            @Override
            public StateName getNextStateName() {
                return nextStateName;
            }
        };
    }

    /**
     * Turn a servo to a preset at max speed
     *
     * @param stateName     the name of the state
     * @param nextStateName the name of the state to go to next
     * @param servoControl  the servo
     * @param servoPreset   the preset to go to
     * @param waitForDone   whether to wait for the servo to finish turning or move to the next state immediately
     * @return the created State
     * @see ServoControl
     */
    public static State servoTurn(StateName stateName, StateName nextStateName, ServoControl servoControl, Enum servoPreset, boolean waitForDone) {
        return servoTurn(stateName, nextStateName, servoControl, servoPreset, ServoControl.MAX_SPEED, waitForDone);
    }

    /**
     * Turn a servo to a preset at a given speed
     *
     * @param stateName     the name of the state
     * @param nextStateName the name of the state to go to next
     * @param servoControl  the servo
     * @param servoPreset   the preset to go to
     * @param speed         the speed to turn the servo at
     * @param waitForDone   whether to wait for the servo to finish turning or move to the next state immediately
     * @return the created State
     * @see ServoControl
     */
    public static State servoTurn(StateName stateName, final StateName nextStateName, final ServoControl servoControl, final Enum servoPreset, final double speed, final boolean waitForDone) {
        return new BasicAbstractState(stateName) {

            @Override
            public void init() {
                servoControl.goToPreset(servoPreset, speed);
            }

            @Override
            public boolean isDone() {
                return !waitForDone || servoControl.isDone();
            }

            @Override
            public StateName getNextStateName() {
                return nextStateName;
            }
        };
    }

    /**
     * Turn a servo to a position at max speed
     *
     * @param stateName     the name of the state
     * @param nextStateName the name of the state to go to next
     * @param servoControl  the servo
     * @param servoPosition the position to go to
     * @param waitForDone   whether to wait for the servo to finish turning or move to the next state immediately
     * @return the created State
     * @see ServoControl
     */
    public static State servoTurn(StateName stateName, StateName nextStateName, ServoControl servoControl, double servoPosition, boolean waitForDone) {
        return servoTurn(stateName, nextStateName, servoControl, servoPosition, ServoControl.MAX_SPEED, waitForDone);
    }

    /**
     * Turn a servo to a position at a given speed
     *
     * @param stateName     the name of the state
     * @param nextStateName the name of the state to go to next
     * @param servoControl  the servo
     * @param servoPosition the position to go to
     * @param speed         the speed to turn the servo at
     * @param waitForDone   whether to wait for the servo to finish turning or move to the next state immediately
     * @return the created State
     * @see ServoControl
     */
    public static State servoTurn(StateName stateName, final StateName nextStateName, final ServoControl servoControl, final double servoPosition, final double speed, final boolean waitForDone) {
        return new BasicAbstractState(stateName) {

            @Override
            public void init() {
                servoControl.setPosition(servoPosition, speed);
            }

            @Override
            public boolean isDone() {
                return !waitForDone || servoControl.isDone();
            }

            @Override
            public StateName getNextStateName() {
                return nextStateName;
            }
        };
    }


    /**
     * drive using the mecanum wheels
     * travels for a certain amount of time defined by the robots speed and a desired distance
     *
     * @param stateName       the name of the state
     * @param nextStateName   the next state to go to
     * @param distance        the distance to travel
     * @param mecanumControl  the mecanum wheels
     * @param gyro            the gyro sensor
     * @param velocity        the velocity to drive at
     * @param direction       the direction to drive
     * @param orientation     the angle to rotate to
     * @param maxAngularSpeed the max speed to rotate to that angle
     * @return the created State
     * @see MecanumControl
     * @see GyroSensor
     * @see Distance
     */
    public static State mecanumDrive(StateName stateName, final StateName nextStateName, Distance distance, final MecanumControl mecanumControl, final GyroSensor gyro, final double velocity, final Angle direction, final Angle orientation, final Angle tolerance, final double maxAngularSpeed) {
        mecanumControl.setDriveMode(MecanumMotors.MecanumDriveMode.NORMALIZED);
        double speedMetersPerMillisecond = mecanumControl.getMaxRobotSpeed().metersPerMillisecond() * velocity;
        final double durationMillis = Math.abs(distance.meters() / speedMetersPerMillisecond);
        final EndCondition gyroEC = EVEndConditions.gyroCloseTo(gyro, orientation, tolerance);
        return new BasicAbstractState(stateName) {
            long startTime = 0;

            @Override
            public void init() {
                mecanumControl.setControl(
                        TranslationControls.constant(velocity, direction),
                        RotationControls.gyro(gyro, orientation, maxAngularSpeed)
                );
                startTime = System.currentTimeMillis();
            }

            @Override
            public boolean isDone() {
                long now = System.currentTimeMillis();
                long elapsedTime = now - startTime;
                if (elapsedTime >= durationMillis) {
                    mecanumControl.setTranslationControl(TranslationControls.ZERO);
                    return gyroEC.isDone();
                }
                return false;
            }

            @Override
            public StateName getNextStateName() {
                mecanumControl.stop();
                return nextStateName;
            }
        };
    }

    /**
     * drive using the mecanum wheels
     *
     * @param stateName       the name of the state
     * @param transitions     the list of transitions to the next states
     * @param mecanumControl  the mecanum wheels
     * @param gyro            the gyro sensor
     * @param velocity        the velocity to drive at
     * @param direction       the direction to drive
     * @param orientation     the angle to rotate to
     * @param maxAngularSpeed the max speed to rotate to that angle
     * @return the created State
     * @see MecanumControl
     * @see GyroSensor
     */
    public static State mecanumDrive(StateName stateName, List<Transition> transitions, final MecanumControl mecanumControl, final GyroSensor gyro, final double velocity, final Angle direction, final Angle orientation, final double maxAngularSpeed) {
        mecanumControl.setDriveMode(MecanumMotors.MecanumDriveMode.NORMALIZED);
        return new AbstractState(stateName, transitions) {
            @Override
            public void init() {
//                OptionsFile optionsFile = new OptionsFile(EVConverters.getInstance(), FileUtil.getOptionsFile("AutoOptions.txt"));
//
//                double max = optionsFile.get("gyro_max", Double.class);
//                double gain = optionsFile.get("gyro_gain", Double.class);

                mecanumControl.setControl(
                        TranslationControls.constant(velocity, direction),
                        RotationControls.gyro(gyro, orientation, maxAngularSpeed)
//                        RotationControls.gyro(gyro, orientation, max, false, gain)
                );
            }

            @Override
            public void run() {

            }

            @Override
            public void dispose() {
                mecanumControl.stop();
            }
        };
    }

    /**
     * @param stateName      the name of the state
     * @param transitions    the list of transitions to the next states
     * @param mecanumControl the mecanum wheels
     * @param gyro           the gyro sensor
     * @param velocity       the velocity to drive at
     * @param direction      the direction to drive
     * @param orientation    the angle to rotate to
     * @return the created State
     * @see MecanumControl
     * @see GyroSensor
     */
    public static State mecanumDrive(StateName stateName, List<Transition> transitions, MecanumControl mecanumControl, GyroSensor gyro, double velocity, Angle direction, Angle orientation) {
        return mecanumDrive(stateName, transitions, mecanumControl, gyro, velocity, direction, orientation, RotationControl.DEFAULT_MAX_ANGULAR_SPEED);
    }

    /**
     * drive using the mecanum wheels
     * travels for a certain amount of time defined by the robots speed and a desired distance
     *
     * @param stateName      the name of the state
     * @param nextStateName  the next state to go to
     * @param distance       the distance to travel
     * @param mecanumControl the mecanum wheels
     * @param gyro           the gyro sensor
     * @param velocity       the velocity to drive at
     * @param direction      the direction to drive
     * @param orientation    the angle to rotate to
     * @return the created State
     * @see MecanumControl
     * @see GyroSensor
     * @see Distance
     */
    public static State mecanumDrive(StateName stateName, StateName nextStateName, Distance distance, final MecanumControl mecanumControl, final GyroSensor gyro, final double velocity, final Angle direction, final Angle orientation, final Angle tolerance) {
        return mecanumDrive(stateName, nextStateName, distance, mecanumControl, gyro, velocity, direction, orientation, tolerance, RotationControl.DEFAULT_MAX_ANGULAR_SPEED);
    }

    /**
     * follow a line with the mecanum wheels
     *
     * @param stateName           the name of the state
     * @param transitions         the list of transitions to the next states
     * @param lostLineState       what state to go to if the line is lost
     * @param mecanumControl      the mecanum wheels
     * @param doubleLineSensor    the 2 line sensors
     * @param velocity            the velocity to drive at
     * @param lineFollowDirection the direction (left or right) to follow the line at
     * @return the created State
     * @see MecanumControl
     * @see TranslationControls
     */
    public State mecanumLineFollow(StateName stateName, List<Transition> transitions, StateName lostLineState, final MecanumControl mecanumControl, final DoubleLineSensor doubleLineSensor, final double velocity, final TranslationControls.LineFollowDirection lineFollowDirection) {
        transitions.add(new Transition(new EndCondition() {
            @Override
            public void init() {
            }

            @Override
            public boolean isDone() {
                return !mecanumControl.translationWorked();
            }
        }, lostLineState));

        mecanumControl.setDriveMode(MecanumMotors.MecanumDriveMode.NORMALIZED);

        return new AbstractState(stateName, transitions) {
            @Override
            public void init() {
                mecanumControl.setTranslationControl(TranslationControls.lineFollow(doubleLineSensor, lineFollowDirection, velocity));
            }

            @Override
            public void run() {

            }

            @Override
            public void dispose() {
                mecanumControl.stop();
            }
        };
    }


    /**
     * Drive forward or backward with two motors
     *
     * @param stateName   the name of the state
     * @param transitions the transitions to new states
     * @param twoMotors   the motors to move
     * @param velocity    the velocity to drive at (negative for backwards)
     * @return the created State
     * @see TwoMotors
     */
    public static State drive(StateName stateName, List<Transition> transitions, final TwoMotors twoMotors, final double velocity) {
        return new AbstractState(stateName, transitions) {
            @Override
            public void init() {
                twoMotors.runMotors(velocity, velocity);
            }

            @Override
            public void run() {

            }

            @Override
            public void dispose() {
                twoMotors.runMotors(0, 0);
            }
        };
    }

    /**
     * Turn left or right
     *
     * @param stateName   the name of the state
     * @param transitions the transitions to new states
     * @param twoMotors   the motors to move
     * @param velocity    the velocity to turn at (negative for turning left)
     * @return the created State
     * @see TwoMotors
     */
    public static State turn(StateName stateName, List<Transition> transitions, final TwoMotors twoMotors, final double velocity) {
        return new AbstractState(stateName, transitions) {
            @Override
            public void init() {
                twoMotors.runMotors(velocity, -velocity);
            }

            @Override
            public void run() {

            }

            @Override
            public void dispose() {
                twoMotors.runMotors(0, 0);
            }
        };
    }

    /**
     * Turn with one wheel
     *
     * @param stateName    the name of the state
     * @param transitions  the transitions to new states
     * @param twoMotors    the motors to move
     * @param isRightWheel tells which wheel to turn
     * @param velocity     the velocity to turn the wheel at (negative for backwards)
     * @return the created State
     * @see TwoMotors
     */
    public static State oneWheelTurn(StateName stateName, List<Transition> transitions, final TwoMotors twoMotors, final boolean isRightWheel, final double velocity) {
        return new AbstractState(stateName, transitions) {
            @Override
            public void init() {
                if (isRightWheel) {
                    twoMotors.runMotors(0, velocity);
                } else {
                    twoMotors.runMotors(velocity, 0);
                }
            }

            @Override
            public void run() {

            }

            @Override
            public void dispose() {
                twoMotors.runMotors(0, 0);
            }
        };
    }


    /**
     * Drive for a certain distance
     *
     * @param stateName     the name of the state
     * @param nextStateName the state to go to after the drive is done
     * @param distance      the distance to drive
     * @param twoMotors     the motors to move
     * @param velocity      the velocity to drive at (negative for backwards)
     * @return the created State
     * @see TwoMotors
     */
    public static State drive(StateName stateName, StateName nextStateName, Distance distance, TwoMotors twoMotors, double velocity) {
        double speedMetersPerMillisecond = twoMotors.getMaxRobotSpeed().metersPerMillisecond() * velocity;
        double durationMillis = Math.abs(distance.meters() / speedMetersPerMillisecond);
        return drive(stateName, ImmutableList.of(
                new Transition(
                        EndConditions.timed((long) durationMillis),
                        nextStateName
                )
        ), twoMotors, velocity);
    }

    /**
     * Turn for a certain angle by calculating the time required for that angle
     *
     * @param stateName        the name of the state
     * @param nextStateName    the state to go to when done turning
     * @param angle            the angle to turn
     * @param minRobotTurnTime the time it takes for the robot to turn
     * @param twoMotors        the motors to run
     * @param velocity         the velocity to turn (negative for turning left)
     * @return the created State
     * @see TwoMotors
     */
    public static State turn(StateName stateName, StateName nextStateName, Angle angle, Time minRobotTurnTime, TwoMotors twoMotors, double velocity) {
        double speedRotationsPerMillisecond = velocity / minRobotTurnTime.milliseconds();
        double durationMillis = Math.abs(angle.degrees() / 360 / speedRotationsPerMillisecond);
        return turn(stateName, ImmutableList.of(
                new Transition(
                        EndConditions.timed((long) durationMillis),
                        nextStateName
                )
        ), twoMotors, velocity);
    }

    /**
     * Turn for a certain angle using a gyro sensor
     *
     * @param stateName     the name of the state
     * @param nextStateName the state to go to when done turning
     * @param angle         the angle to turn
     * @param gyro          the gyro sensor to use
     * @param twoMotors     the motors to turn
     * @param velocity      the velocity to turn at (negative to turn left)
     * @return the created State
     * @see TwoMotors
     */
    public static State turn(StateName stateName, StateName nextStateName, Angle angle, GyroSensor gyro, TwoMotors twoMotors, double velocity) {
        return turn(stateName, ImmutableList.of(
                new Transition(
                        EVEndConditions.gyroCloseToRelative(gyro, angle, Angle.fromDegrees(5)),
                        nextStateName
                )
        ), twoMotors, velocity);
    }

    /**
     * Turn with one wheel for a certain angle by calculating the time needed to turn that angle
     *
     * @param stateName        the name of the state
     * @param nextStateName    the state to go to when done turning
     * @param angle            the angle to turn
     * @param minRobotTurnTime the time it takes for the robot to turn
     * @param twoMotors        the motors to turn
     * @param isRightWheel     tells which wheel to turn
     * @param velocity         the velocity to turn the wheel at (negative for backwards)
     * @return the created State
     * @see TwoMotors
     */
    public static State oneWheelTurn(StateName stateName, StateName nextStateName, Angle angle, Time minRobotTurnTime, TwoMotors twoMotors, boolean isRightWheel, double velocity) {
        double speedRotationsPerMillisecond = velocity / minRobotTurnTime.milliseconds();
        double durationMillis = Math.abs(2 * angle.degrees() / 360 / speedRotationsPerMillisecond);
        velocity = Math.abs(velocity) * Math.signum(angle.radians());
        if (isRightWheel) {
            velocity *= -1;
        }
        return oneWheelTurn(stateName, ImmutableList.of(
                new Transition(
                        EndConditions.timed((long) durationMillis),
                        nextStateName
                )
        ), twoMotors, isRightWheel, velocity);
    }

    /**
     * Turn with one wheel for a certain angle using a gyro sensor
     *
     * @param stateName     the name of the state
     * @param nextStateName the state to go to after the turn is done
     * @param angle         the angle to turn
     * @param gyro          the gyro sensor to use
     * @param twoMotors     the motors to turn
     * @param isRightWheel  which wheel to use
     * @param velocity      the velocity to turn the wheel at (negative for backwards)
     * @return the created State
     * @see TwoMotors
     */
    public static State turn(StateName stateName, StateName nextStateName, Angle angle, GyroSensor gyro, TwoMotors twoMotors, boolean isRightWheel, double velocity) {
        velocity = Math.abs(velocity) * Math.signum(angle.radians());
        if (isRightWheel) {
            velocity *= -1;
        }
        return oneWheelTurn(stateName, ImmutableList.of(
                new Transition(
                        EVEndConditions.gyroCloseToRelative(gyro, angle, Angle.fromDegrees(5)),
                        nextStateName
                )
        ), twoMotors, isRightWheel, velocity);
    }

    /**
     * Turn a motor at a given power
     *
     * @param stateName   the name of the state
     * @param transitions the transitions to new states
     * @param motor       the motor to be turned
     * @param power       the power to turn the motor at
     * @return the created State
     * @see TwoMotors
     */
    public static State motorTurn(StateName stateName, List<Transition> transitions, final Motor motor, final double power) {
        return new AbstractState(stateName, transitions) {
            @Override
            public void init() {
                motor.setPower(power);
            }

            @Override
            public void run() {

            }

            @Override
            public void dispose() {
                motor.setPower(0);
            }
        };
    }

    /**
     * Line up with the beacon using the line sensor array and distance sensor
     *
     * @param stateName         the name of the state
     * @param successState      the state to go to if the line up succeeds
     * @param failState         the state to go to if the line up fails
     * @param mecanumControl    the mecanum wheels
     * @param direction         the direction angle to face
     * @param gyro              the gyro to use for rotation stabilization
     * @param distSensor        the distance sensor to detect distance from the beacon
     * @param lineSensorArray   the line sensor array to line up sideways with the line
     * @param teamColor         the team you are on and ...
     * @param beaconColorResult ... the beacon configuration to decide which button to line up with
     * @param distance          the distance from the beacon to line up to
     * @return the created State
     * @see LineUpControl
     */
    public static State beaconLineUp(StateName stateName, final StateName successState, final StateName failState, final MecanumControl mecanumControl, final Angle direction, final GyroSensor gyro, final DistanceSensor distSensor, final LineSensorArray lineSensorArray, TeamColor teamColor, final ResultReceiver<BeaconColorResult> beaconColorResult, final Distance distance) {
//        final EndCondition distEndCondition = EVEndConditions.distanceSensorLess(distSensor, Distance.add(distance, Distance.fromInches(4)));
        final EndCondition distEndCondition = EVEndConditions.distanceSensorLess(distSensor, distance);
        final EndCondition gyroEndCondition = EVEndConditions.gyroCloseTo(gyro, direction, 2);

        final BeaconColorResult.BeaconColor myColor = BeaconColorResult.BeaconColor.fromTeamColor(teamColor);
        final BeaconColorResult.BeaconColor opponentColor = BeaconColorResult.BeaconColor.fromTeamColor(teamColor.opposite());

        return new BasicAbstractState(stateName) {
            private boolean success;
            LineUpControl.Button buttonToLineUpWith;

            @Override
            public void init() {
                buttonToLineUpWith = null;
                if (beaconColorResult.isReady()) {
                    BeaconColorResult result = beaconColorResult.getValue();
                    BeaconColorResult.BeaconColor leftColor = result.getLeftColor();
                    BeaconColorResult.BeaconColor rightColor = result.getRightColor();
                    if (leftColor == myColor && rightColor == opponentColor) {
                        buttonToLineUpWith = LineUpControl.Button.LEFT;
                    }
                    if (leftColor == opponentColor && rightColor == myColor) {
                        buttonToLineUpWith = LineUpControl.Button.RIGHT;
                    }
                }

                success = buttonToLineUpWith != null;

                LineUpControl lineUpControl = new LineUpControl(lineSensorArray, buttonToLineUpWith, distSensor, distance, gyro, direction);

                mecanumControl.setTranslationControl(lineUpControl);
                mecanumControl.setRotationControl(lineUpControl);

                distEndCondition.init();
                gyroEndCondition.init();
            }

            @Override
            public boolean isDone() {

                if (!mecanumControl.translationWorked()) {
                    success = false;
                }
                return !success || distEndCondition.isDone();
            }

            @Override
            public StateName getNextStateName() {
                mecanumControl.stop();
                return success ? successState : failState;
            }
        };
    }

    public static State shoot(StateName stateName, final StateName nextStateName, final Shooter shooter, final int shots) {
        return new BasicAbstractState(stateName) {
            @Override
            public void init() {
                shooter.shoot(shots);
            }

            @Override
            public boolean isDone() {
//                shooter.act();
                return shooter.isDone();
            }

            @Override
            public StateName getNextStateName() {
                return nextStateName;
            }
        };
    }

    public static State gyroStabilize(StateName stateName, StateName nextStateName, final MecanumControl mecanumControl, final GyroSensor gyro, final Angle orientation, Angle tolerance) {
        List<Transition> transitions = ImmutableList.of(
                new Transition(EVEndConditions.gyroCloseTo(gyro, orientation, tolerance), nextStateName)
        );

        return new AbstractState(stateName, transitions) {
            @Override
            public void init() {
                mecanumControl.setTranslationControl(TranslationControls.ZERO);
                mecanumControl.setRotationControl(RotationControls.gyro(gyro, orientation));
            }

            @Override
            public void run() {

            }

            @Override
            public void dispose() {
                mecanumControl.stop();
            }
        };
    }

    public static State switchPressed(StateName stateName, StateName pressedStateName, StateName timeoutStateName, DigitalSensor digitalSensor, Time timeout) {
        return empty(stateName, ImmutableList.of(
                new Transition(EndConditions.inputExtractor(digitalSensor), pressedStateName),
                new Transition(EndConditions.timed((long) timeout.milliseconds()), timeoutStateName)
        ));
    }

    public static State initShooter(StateName stateName, final StateName nextStateName, final Shooter shooter) {
        return new BasicAbstractState(stateName) {
            @Override
            public void init() {
                shooter.initialize();
            }

            @Override
            public boolean isDone() {
                return shooter.isDone();
            }

            @Override
            public StateName getNextStateName() {
                return nextStateName;
            }
        };
    }


    public static State beaconColorSwitch(StateName stateName, final StateName leftButtonState, final StateName rightButtonState, final StateName unknownState, TeamColor teamColor, final ResultReceiver<BeaconColorResult> beaconColorResult) {

        final BeaconColorResult.BeaconColor myColor = BeaconColorResult.BeaconColor.fromTeamColor(teamColor);
        final BeaconColorResult.BeaconColor opponentColor = BeaconColorResult.BeaconColor.fromTeamColor(teamColor.opposite());

        return new BasicAbstractState(stateName) {
            private StateName nextState = unknownState;

            @Override
            public void init() {
                if (beaconColorResult.isReady()) {
                    BeaconColorResult result = beaconColorResult.getValue();
                    BeaconColorResult.BeaconColor leftColor = result.getLeftColor();
                    BeaconColorResult.BeaconColor rightColor = result.getRightColor();
                    if (leftColor == myColor && rightColor == opponentColor) {
                        nextState = leftButtonState;
                    }
                    if (leftColor == opponentColor && rightColor == myColor) {
                        nextState = rightButtonState;
                    }
                }

            }

            @Override
            public boolean isDone() {
                return true;
            }

            @Override
            public StateName getNextStateName() {
                return nextState;
            }
        };
    }

}