/*
* Copyright 2014-2016 Cel Skeggs
*
* This file is part of the CCRE, the Common Chicken Runtime Engine.
*
* The CCRE is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* The CCRE is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the CCRE. If not, see <http://www.gnu.org/licenses/>.
*/
package ccre.ctrl;
import ccre.channel.BooleanInput;
import ccre.channel.BooleanOutput;
import ccre.channel.DerivedBooleanInput;
import ccre.channel.EventInput;
import ccre.channel.FloatInput;
import ccre.channel.FloatOutput;
import ccre.frc.FRC;
import ccre.verifier.FlowPhase;
import ccre.verifier.SetupPhase;
/**
* Sometimes there's more to control about a motor than just a power level, and
* so FloatOutput is insufficient. That's where ExtendedMotor comes in. An
* example of an ExtendedMotor would be a CAN Jaguar or CAN Talon.
*
* An extended motor may have one or more Diagnostics (error reports, such as
* faults), Statuses (state reports, such as voltage), and Control Modes
* (methods of controlling an output, such as voltage-based or current-based.)
*
* It may also be disabled and enabled.
*
* It may also have an internal PID state.
*
* @author skeggsc
*/
public abstract class ExtendedMotor {
/**
* The different possible diagnostics for an ExtendedMotor to provide.
*
* Some of these are bitsets of fault conditions, and some are
* boolean-reportable faults (on or off.)
*
* @author skeggsc
*/
public static enum DiagnosticType {
// Mask-based: (long)
/**
* A bitset of CAN Jaguar faults, reported raw.
*
* The CAN Jaguar supports CURRENT_FAULT, TEMPERATURE_FAULT,
* BUS_VOLTAGE_FAULT, and GATE_DRIVER_FAULT.
*/
CAN_JAGUAR_FAULTS(false),
/**
* A bitset of faults, reported raw. What this means depends on the
* specific device.
*/
GENERIC_FAULT_MASK(false),
// Boolean-based:
/**
* If any fault is occurring.
*/
ANY_FAULT(true),
/**
* A boolean fault based on current.
*/
CURRENT_FAULT(true),
/**
* A boolean fault based on temperature.
*/
TEMPERATURE_FAULT(true),
/**
* A boolean fault based on low bus voltage.
*/
BUS_VOLTAGE_FAULT(true),
/**
* A boolean fault based on a hardware issue.
*/
HARDWARE_FAULT(true),
/**
* A boolean fault based on the gate driver.
*/
GATE_DRIVER_FAULT(true),
/**
* A boolean fault based on a communications failure
*/
COMMS_FAULT(true);
/**
* Specifies if this type of fault is reported as a boolean.
*/
public final boolean isBooleanDiagnostic;
private DiagnosticType(boolean isBoolean) {
this.isBooleanDiagnostic = isBoolean;
}
}
/**
* The different possible output control modes for an ExtendedMotor to
* provide.
*
* @author skeggsc
*/
public static enum OutputControlMode {
/**
* A generic fractional output. This takes a range of -1.0 (full
* reverse) to 1.0 (full forward). This will always map to something,
* but it may not always mean anything useful for a device.
*/
GENERIC_FRACTIONAL,
/**
* A voltage-based fractional output. This takes a range of -1.0 (full
* reverse) to 1.0 (full forward). This is calculated based on voltage.
*/
VOLTAGE_FRACTIONAL,
/**
* A voltage-based fixed output. This takes a voltage (in Volts) to
* attempt to produce.
*/
VOLTAGE_FIXED,
/**
* A current-based fixed output. This takes a current (in Amps) to
* attempt to produce.
*/
CURRENT_FIXED,
/**
* A position-based fixed output. This takes a position, in the target
* units, to attempt to match.
*/
POSITION_FIXED,
/**
* A speed-based fixed output. This takes a speed, in the target units,
* to attempt to match.
*/
SPEED_FIXED,
}
/**
* The different possible status types for an ExtendedMotor to provide.
*
* @author skeggsc
*/
public static enum StatusType {
/**
* Provides the present bus voltage.
*/
BUS_VOLTAGE,
/**
* Provides the present output voltage.
*/
OUTPUT_VOLTAGE,
/**
* Provides the present output current.
*/
OUTPUT_CURRENT,
/**
* Provides the present controller temperature, in degrees Celsius.
*/
TEMPERATURE
}
/**
* Enables the output, if possible. A control mode should be specified
* first.
*
* @throws ExtendedMotorFailureException if the output cannot be enabled.
*/
@FlowPhase
public abstract void enable() throws ExtendedMotorFailureException;
/**
* Disables the output, if possible.
*
* @throws ExtendedMotorFailureException if the output cannot be disabled.
*/
@FlowPhase
public abstract void disable() throws ExtendedMotorFailureException;
/**
* Constructs a BooleanOutput to control the enablement status. A control
* mode should be specified before enabling.
*
* @return a BooleanOutput controlling whether this controller is enabled.
*/
@SetupPhase
public abstract BooleanOutput asEnable();
/**
* Opens the controller in the specified output mode. This may override any
* previous output modes, so don't use any previous results from this method
* once it has been called.
*
* @param mode the mode to put the controller into.
* @return the output representing the controller. The meaning is based on
* the mode, or null if this mode is not allowed for this device.
* @throws ExtendedMotorFailureException if an error occurs while opening
* the output with this mode.
*/
@SetupPhase
public abstract FloatOutput asMode(OutputControlMode mode) throws ExtendedMotorFailureException;
/**
* Gets access to one of the status readouts from the ExtendedMotor.
*
* @param type the type of status to get access to.
* @return the FloatInput representing this status readout, or null if it
* cannot be acquired.
*/
@SetupPhase
public FloatInput asStatus(StatusType type) {
return asStatus(type, FRC.sensorPeriodic);
}
/**
* Gets access to one of the status readouts from the ExtendedMotor.
*
* @param type the type of status to get access to.
* @param updateOn when to update the sensor.
* @return the FloatInput representing this status readout, or null if it
* cannot be acquired.
*/
@SetupPhase
public abstract FloatInput asStatus(StatusType type, EventInput updateOn);
/**
* Gets the current diagnostic value from the ExtendedMotor. This is usually
* either an Integer or a Boolean based on the DiagnosticType.
*
* @param type the type of diagnostic to read.
* @return the current diagnostic value.
*/
@FlowPhase
public abstract Object getDiagnostics(DiagnosticType type);
/**
* Gets a channel representing a boolean diagnostic channel.
*
* @param type the type of diagnostic to monitor.
* @return a channel representing the diagnostic state, or null if it cannot
* be acquired.
*/
@SetupPhase
public BooleanInput getDiagnosticChannel(final DiagnosticType type) {
return getDiagnosticChannel(type, FRC.sensorPeriodic);
}
/**
* Gets a channel representing a boolean diagnostic channel.
*
* @param type the type of diagnostic to monitor.
* @param updateOn when to update the sensor.
* @return a channel representing the diagnostic state, or null if it cannot
* be acquired.
*/
@SetupPhase
public BooleanInput getDiagnosticChannel(final DiagnosticType type, EventInput updateOn) {
if (!type.isBooleanDiagnostic || !(getDiagnostics(type) instanceof Boolean)) {
return null;
}
return new DerivedBooleanInput(updateOn) { // TODO: fix this
@Override
protected boolean apply() {
Object out = getDiagnostics(type);
if (out instanceof Boolean) {
return (Boolean) out;
} else {
throw new RuntimeException("Diagnostic type changed to not be a boolean anymore!");
}
}
};
}
/**
* Checks if internal PID is included in this ExtendedMotor. This does not
* necessarily mean that it is in use - the output mode must support PID.
*
* This may return either true or false if the current output mode does not
* support PID but the ExtendedMotor does in other cases.
*
* @return if internal PID is included.
*/
public abstract boolean hasInternalPID();
/**
* Sets the internal PID tuning in this ExtendedMotor. Don't call this
* unless hasInternalPID() returns true.
*
* @param P the proportional factor in the tuning.
* @param I the integral factor in the tuning.
* @param D the derivative factor in the tuning.
* @throws ExtendedMotorFailureException if the PID cannot be set.
*/
@FlowPhase
public abstract void setInternalPID(float P, float I, float D) throws ExtendedMotorFailureException;
/**
* Opens the controller in a simple output mode:
* {@link OutputControlMode#GENERIC_FRACTIONAL}.
*
* @return the output representing the controller, which can be varied from
* -1.0 to +1.0 to control the speed.
* @throws ExtendedMotorFailureException if an error occurs while setting up
* the motor.
* @see #asMode(OutputControlMode) for how this works.
*/
@SetupPhase
public FloatOutput simpleControl() throws ExtendedMotorFailureException {
FloatOutput output = asMode(OutputControlMode.GENERIC_FRACTIONAL);
if (output == null) {
throw new ExtendedMotorFailureException("GENERIC_FRACTIONAL mode not supported!");
}
this.enable();
return output;
}
/**
* Opens the controller in a simple output mode:
* {@link OutputControlMode#GENERIC_FRACTIONAL}. If it cannot be opened in
* this mode, a {@link RuntimeException} will be thrown.
*
* @return the output representing the controller, which can be varied from
* -1.0 to +1.0 to control the speed.
* @see #asMode(OutputControlMode) for how this works.
*/
@SetupPhase
public FloatOutput simpleControlSafe() {
try {
return simpleControl();
} catch (ExtendedMotorFailureException e) {
throw new RuntimeException("Could not control Extended Motor", e);
}
}
/**
* Opens the controller in a simple output mode:
* {@link OutputControlMode#GENERIC_FRACTIONAL}, optionally reversed.
*
* @param reversed if the values sent to this motor should be negated. You
* can use {@link FRC#MOTOR_FORWARD} and {@link FRC#MOTOR_REVERSE} for
* clarity.
* @return the output representing the controller, which can be varied from
* -1.0 to +1.0 to control the speed.
* @throws ExtendedMotorFailureException if an error occurs while setting up
* the motor.
* @see #asMode(OutputControlMode) for how this works.
*/
@SetupPhase
public FloatOutput simpleControl(boolean reversed) throws ExtendedMotorFailureException {
FloatOutput motor = simpleControl();
if (reversed != FRC.MOTOR_FORWARD) {
motor = motor.negate();
}
return motor;
}
/**
* Opens the controller in a simple output mode:
* {@link OutputControlMode#GENERIC_FRACTIONAL}, optionally reversed. If it
* cannot be opened in this mode, a {@link RuntimeException} will be thrown.
*
* @param reversed if the values sent to this motor should be negated. You
* can use {@link FRC#MOTOR_FORWARD} and {@link FRC#MOTOR_REVERSE} for
* clarity.
* @return the output representing the controller, which can be varied from
* -1.0 to +1.0 to control the speed.
* @see #asMode(OutputControlMode) for how this works.
*/
@SetupPhase
public FloatOutput simpleControlSafe(boolean reversed) {
try {
return simpleControl(reversed);
} catch (ExtendedMotorFailureException e) {
throw new RuntimeException("Could not control Extended Motor", e);
}
}
}