/**
   * Set the P, I, and D constants for the closed loop modes.
   *
   * @param p The proportional gain of the Jaguar's PID controller.
   * @param i The integral gain of the Jaguar's PID controller.
   * @param d The differential gain of the Jaguar's PID controller.
   */
  public void setPID(double p, double i, double d) throws CANTimeoutException {
    byte[] dataBuffer = new byte[8];
    byte dataSize;

    switch (m_controlMode.value) {
      case ControlMode.kPercentVbus_val:
      case ControlMode.kVoltage_val:
        // TODO: Error, Not Valid
        break;
      case ControlMode.kSpeed_val:
        dataSize = packFXP16_16(dataBuffer, p);
        setTransaction(JaguarCANProtocol.LM_API_SPD_PC, dataBuffer, dataSize);
        dataSize = packFXP16_16(dataBuffer, i);
        setTransaction(JaguarCANProtocol.LM_API_SPD_IC, dataBuffer, dataSize);
        dataSize = packFXP16_16(dataBuffer, d);
        setTransaction(JaguarCANProtocol.LM_API_SPD_DC, dataBuffer, dataSize);
        break;
      case ControlMode.kPosition_val:
        dataSize = packFXP16_16(dataBuffer, p);
        setTransaction(JaguarCANProtocol.LM_API_POS_PC, dataBuffer, dataSize);
        dataSize = packFXP16_16(dataBuffer, i);
        setTransaction(JaguarCANProtocol.LM_API_POS_IC, dataBuffer, dataSize);
        dataSize = packFXP16_16(dataBuffer, d);
        setTransaction(JaguarCANProtocol.LM_API_POS_DC, dataBuffer, dataSize);
        break;
      case ControlMode.kCurrent_val:
        dataSize = packFXP16_16(dataBuffer, p);
        setTransaction(JaguarCANProtocol.LM_API_ICTRL_PC, dataBuffer, dataSize);
        dataSize = packFXP16_16(dataBuffer, i);
        setTransaction(JaguarCANProtocol.LM_API_ICTRL_IC, dataBuffer, dataSize);
        dataSize = packFXP16_16(dataBuffer, d);
        setTransaction(JaguarCANProtocol.LM_API_ICTRL_DC, dataBuffer, dataSize);
        break;
    }
  }
  /**
   * Configure Soft Position Limits when in Position Controller mode.
   *
   * <p>When controlling position, you can add additional limits on top of the limit switch inputs
   * that are based on the position feedback. If the position limit is reached or the switch is
   * opened, that direction will be disabled.
   *
   * @param forwardLimitPosition The position that if exceeded will disable the forward direction.
   * @param reverseLimitPosition The position that if exceeded will disable the reverse direction.
   */
  public void configSoftPositionLimits(double forwardLimitPosition, double reverseLimitPosition)
      throws CANTimeoutException {
    byte[] dataBuffer = new byte[8];
    byte dataSize;

    dataSize = packFXP16_16(dataBuffer, forwardLimitPosition);
    dataBuffer[dataSize++] = (forwardLimitPosition > reverseLimitPosition) ? (byte) 1 : (byte) 0;
    setTransaction(JaguarCANProtocol.LM_API_CFG_LIMIT_FWD, dataBuffer, dataSize);

    dataSize = packFXP16_16(dataBuffer, reverseLimitPosition);
    dataBuffer[dataSize++] = forwardLimitPosition <= reverseLimitPosition ? (byte) 1 : (byte) 0;
    setTransaction(JaguarCANProtocol.LM_API_CFG_LIMIT_REV, dataBuffer, dataSize);

    dataBuffer[0] = LimitMode.kSoftPositionLimit_val;
    setTransaction(JaguarCANProtocol.LM_API_CFG_LIMIT_MODE, dataBuffer, (byte) 1);
  }
  /**
   * Configure the number of turns on the potentiometer.
   *
   * <p>There is no special support for continuous turn potentiometers. Only integer numbers of
   * turns are supported.
   *
   * @param turns The number of turns of the potentiometer
   */
  public void configPotentiometerTurns(int turns) throws CANTimeoutException {
    byte[] dataBuffer = new byte[8];
    byte dataSize;

    dataSize = packINT16(dataBuffer, (short) turns);
    setTransaction(JaguarCANProtocol.LM_API_CFG_POT_TURNS, dataBuffer, dataSize);
  }
  /**
   * Configure how many codes per revolution are generated by your encoder.
   *
   * @param codesPerRev The number of counts per revolution in 1X mode.
   */
  public void configEncoderCodesPerRev(int codesPerRev) throws CANTimeoutException {
    byte[] dataBuffer = new byte[8];
    byte dataSize;

    dataSize = packINT16(dataBuffer, (short) codesPerRev);
    setTransaction(JaguarCANProtocol.LM_API_CFG_ENC_LINES, dataBuffer, dataSize);
  }
  /**
   * Configure how long the Jaguar waits in the case of a fault before resuming operation.
   *
   * <p>Faults include over temerature, over current, and bus under voltage. The default is 3.0
   * seconds, but can be reduced to as low as 0.5 seconds.
   *
   * @param faultTime The time to wait before resuming operation, in seconds.
   */
  public void configFaultTime(double faultTime) throws CANTimeoutException {
    byte[] dataBuffer = new byte[8];
    byte dataSize;

    // Message takes ms
    dataSize = packINT16(dataBuffer, (short) (faultTime * 1000.0));
    setTransaction(JaguarCANProtocol.LM_API_CFG_FAULT_TIME, dataBuffer, dataSize);
  }
  /**
   * Configure the maximum voltage that the Jaguar will ever output.
   *
   * <p>This can be used to limit the maximum output voltage in all modes so that motors which
   * cannot withstand full bus voltage can be used safely.
   *
   * @param voltage The maximum voltage output by the Jaguar.
   */
  public void configMaxOutputVoltage(double voltage) throws CANTimeoutException {
    byte[] dataBuffer = new byte[8];
    byte dataSize;

    m_maxOutputVoltage = voltage;
    dataSize = packFXP8_8(dataBuffer, voltage);
    setTransaction(JaguarCANProtocol.LM_API_CFG_MAX_VOUT, dataBuffer, dataSize);
  }
  /**
   * Set the maximum voltage change rate.
   *
   * <p>When in percent voltage output mode, the rate at which the voltage changes can be limited to
   * reduce current spikes. Set this to 0.0 to disable rate limiting.
   *
   * @param rampRate The maximum rate of voltage change in Percent Voltage mode in V/s.
   */
  public void setVoltageRampRate(double rampRate) throws CANTimeoutException {
    byte[] dataBuffer = new byte[8];
    byte dataSize;

    switch (m_controlMode.value) {
      case ControlMode.kPercentVbus_val:
        dataSize = packPercentage(dataBuffer, rampRate / (m_maxOutputVoltage * kControllerRate));
        setTransaction(JaguarCANProtocol.LM_API_VOLT_SET_RAMP, dataBuffer, dataSize);
        break;
      case ControlMode.kVoltage_val:
        dataSize = packFXP8_8(dataBuffer, rampRate / kControllerRate);
        setTransaction(JaguarCANProtocol.LM_API_VCOMP_IN_RAMP, dataBuffer, dataSize);
        break;
      default:
        return;
    }
  }
  /**
   * Disable the closed loop controller.
   *
   * <p>Stop driving the output based on the feedback.
   */
  public void disableControl() throws CANTimeoutException {
    byte[] dataBuffer = new byte[8];
    byte dataSize = 0;

    switch (m_controlMode.value) {
      case ControlMode.kPercentVbus_val:
        setTransaction(JaguarCANProtocol.LM_API_VOLT_DIS, dataBuffer, dataSize);
        break;
      case ControlMode.kSpeed_val:
        setTransaction(JaguarCANProtocol.LM_API_SPD_DIS, dataBuffer, dataSize);
        break;
      case ControlMode.kPosition_val:
        setTransaction(JaguarCANProtocol.LM_API_POS_DIS, dataBuffer, dataSize);
        break;
      case ControlMode.kCurrent_val:
        setTransaction(JaguarCANProtocol.LM_API_ICTRL_DIS, dataBuffer, dataSize);
        break;
      case ControlMode.kVoltage_val:
        setTransaction(JaguarCANProtocol.LM_API_VCOMP_DIS, dataBuffer, dataSize);
        break;
    }
  }
  /**
   * Set the output set-point value.
   *
   * <p>The scale and the units depend on the mode the Jaguar is in. In PercentVbus Mode, the
   * outputValue is from -1.0 to 1.0 (same as PWM Jaguar). In Voltage Mode, the outputValue is in
   * Volts. In Current Mode, the outputValue is in Amps. In Speed Mode, the outputValue is in
   * Rotations/Minute. In Position Mode, the outputValue is in Rotations.
   *
   * @param outputValue The set-point to sent to the motor controller.
   * @param syncGroup The update group to add this set() to, pending updateSyncGroup(). If 0, update
   *     immediately.
   */
  public void setX(double outputValue, byte syncGroup) throws CANTimeoutException {
    int messageID = 0;
    byte[] dataBuffer = new byte[8];
    byte dataSize = 0;

    if (!m_safetyHelper.isAlive()) {
      enableControl();
    }

    switch (m_controlMode.value) {
      case ControlMode.kPercentVbus_val:
        messageID = JaguarCANProtocol.LM_API_VOLT_T_SET;
        if (outputValue > 1.0) outputValue = 1.0;
        if (outputValue < -1.0) outputValue = -1.0;
        packPercentage(dataBuffer, outputValue);
        dataSize = 2;
        break;
      case ControlMode.kSpeed_val:
        {
          messageID = JaguarCANProtocol.LM_API_SPD_T_SET;
          dataSize = packFXP16_16(dataBuffer, outputValue);
        }
        break;
      case ControlMode.kPosition_val:
        {
          messageID = JaguarCANProtocol.LM_API_POS_T_SET;
          dataSize = packFXP16_16(dataBuffer, outputValue);
        }
        break;
      case ControlMode.kCurrent_val:
        {
          messageID = JaguarCANProtocol.LM_API_ICTRL_T_SET;
          dataSize = packFXP8_8(dataBuffer, outputValue);
        }
        break;
      case ControlMode.kVoltage_val:
        {
          messageID = JaguarCANProtocol.LM_API_VCOMP_T_SET;
          dataSize = packFXP8_8(dataBuffer, outputValue);
        }
        break;
      default:
        return;
    }
    if (syncGroup != 0) {
      dataBuffer[dataSize] = syncGroup;
      dataSize++;
    }
    setTransaction(messageID, dataBuffer, dataSize);
    m_safetyHelper.feed();
  }
  /**
   * Enable the closed loop controller.
   *
   * <p>Start actually controlling the output based on the feedback. If starting a position
   * controller with an encoder reference, use the encoderInitialPosition parameter to initialize
   * the encoder state.
   *
   * @param encoderInitialPosition Encoder position to set if position with encoder reference.
   *     Ignored otherwise.
   */
  public void enableControl(double encoderInitialPosition) throws CANTimeoutException {
    byte[] dataBuffer = new byte[8];
    byte dataSize = 0;

    switch (m_controlMode.value) {
      case ControlMode.kPercentVbus_val:
        setTransaction(JaguarCANProtocol.LM_API_VOLT_T_EN, dataBuffer, dataSize);
        break;
      case ControlMode.kSpeed_val:
        setTransaction(JaguarCANProtocol.LM_API_SPD_T_EN, dataBuffer, dataSize);
        break;
      case ControlMode.kPosition_val:
        dataSize = packFXP16_16(dataBuffer, encoderInitialPosition);
        setTransaction(JaguarCANProtocol.LM_API_POS_T_EN, dataBuffer, dataSize);
        break;
      case ControlMode.kCurrent_val:
        setTransaction(JaguarCANProtocol.LM_API_ICTRL_T_EN, dataBuffer, dataSize);
        break;
      case ControlMode.kVoltage_val:
        setTransaction(JaguarCANProtocol.LM_API_VCOMP_T_EN, dataBuffer, dataSize);
        break;
    }
  }
  /**
   * Check if the Jaguar's power has been cycled since this was last called.
   *
   * <p>This should return true the first time called after a Jaguar power up, and false after that.
   *
   * @return The Jaguar was power cycled since the last call to this function.
   */
  public boolean getPowerCycled() throws CANTimeoutException {
    byte[] dataBuffer = new byte[8];
    byte dataSize;

    dataSize = getTransaction(JaguarCANProtocol.LM_API_STATUS_POWER, dataBuffer);
    if (dataSize == 1) {
      boolean powerCycled = dataBuffer[0] != 0;

      // Clear the power cycled bit now that we've accessed it
      if (powerCycled) {
        dataBuffer[0] = 1;
        setTransaction(JaguarCANProtocol.LM_API_STATUS_POWER, dataBuffer, (byte) 1);
      }

      return powerCycled;
    }
    return false;
  }
  /**
   * Set the reference source device for position controller mode.
   *
   * <p>Choose between using and encoder and using a potentiometer as the source of position
   * feedback when in position control mode.
   *
   * @param reference Specify a PositionReference.
   */
  public void setPositionReference(PositionReference reference) throws CANTimeoutException {
    byte[] dataBuffer = new byte[8];

    dataBuffer[0] = reference.value;
    setTransaction(JaguarCANProtocol.LM_API_POS_REF, dataBuffer, (byte) 1);
  }
  /**
   * Disable Soft Position Limits if previously enabled.
   *
   * <p>Soft Position Limits are disabled by default.
   */
  public void disableSoftPositionLimits() throws CANTimeoutException {
    byte[] dataBuffer = new byte[8];

    dataBuffer[0] = LimitMode.kSwitchInputsOnly_val;
    setTransaction(JaguarCANProtocol.LM_API_CFG_LIMIT_MODE, dataBuffer, (byte) 1);
  }
  /**
   * Configure what the controller does to the H-Bridge when neutral (not driving the output).
   *
   * <p>This allows you to override the jumper configuration for brake or coast.
   *
   * @param mode Select to use the jumper setting or to override it to coast or brake.
   */
  public void configNeutralMode(NeutralMode mode) throws CANTimeoutException {
    byte[] dataBuffer = new byte[8];

    dataBuffer[0] = mode.value;
    setTransaction(JaguarCANProtocol.LM_API_CFG_BRAKE_COAST, dataBuffer, (byte) 1);
  }