/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2009. All Rights Reserved.                             */
/* Open Source Software - may be modified and shared by FRC teams. The code   */
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib.  */
/*----------------------------------------------------------------------------*/

#include "CANJaguar.h"
#include "Timer.h"
#define tNIRIO_i32 int
#include "NetworkCommunication/CANSessionMux.h"
#include "CAN/can_proto.h"
//#include "NetworkCommunication/UsageReporting.h"
#include "WPIErrors.h"
#include <cstdio>
#include <cassert>
#include "LiveWindow/LiveWindow.h"

/* we are on ARM-LE now, not Freescale so no need to swap */
//TODO: is this defined in a PN way? or is this Jag-specific?
#define swap16(x)	(x)
#define swap32(x)	(x)

/* Compare floats for equality as fixed point numbers */
#define FXP8_EQ(a,b) ((int16_t)((a)*256.0)==(int16_t)((b)*256.0))
#define FXP16_EQ(a,b) ((int16_t)((a)*65536.0)==(int16_t)((b)*65536.0))

const int32_t CANJaguar::kControllerRate;
constexpr double CANJaguar::kApproxBusVoltage;

static const int32_t kSendMessagePeriod = 20;
static const uint32_t kFullMessageIDMask = (CAN_MSGID_API_M | CAN_MSGID_MFR_M | CAN_MSGID_DTYPE_M);

static const int32_t kReceiveStatusAttempts = 5000;

static int32_t sendMessageHelper(uint32_t messageID, const uint8_t *data, uint8_t dataSize, int32_t period)
{
	static const uint32_t kTrustedMessages[] = {
			LM_API_VOLT_T_EN, LM_API_VOLT_T_SET, LM_API_SPD_T_EN, LM_API_SPD_T_SET,
			LM_API_VCOMP_T_EN, LM_API_VCOMP_T_SET, LM_API_POS_T_EN, LM_API_POS_T_SET,
			LM_API_ICTRL_T_EN, LM_API_ICTRL_T_SET};

	int32_t status=0;

	for (uint8_t i=0; i<(sizeof(kTrustedMessages)/sizeof(kTrustedMessages[0])); i++)
	{
		if ((kFullMessageIDMask & messageID) == kTrustedMessages[i])
		{
			uint8_t dataBuffer[8];
			dataBuffer[0] = 0;
			dataBuffer[1] = 0;

			// Make sure the data will still fit after adjusting for the token.
			assert(dataSize <= 6);

			for (uint8_t j=0; j < dataSize; j++)
			{
				dataBuffer[j + 2] = data[j];
			}

			FRC_NetworkCommunication_CANSessionMux_sendMessage(messageID, dataBuffer, dataSize + 2, period, &status);

			return status;
		}
	}

	FRC_NetworkCommunication_CANSessionMux_sendMessage(messageID, data, dataSize, period, &status);

	return status;
}

/**
 * Common initialization code called by all constructors.
 */
void CANJaguar::InitCANJaguar()
{
	m_table = NULL;
	m_safetyHelper = new MotorSafetyHelper(this);

	m_value = 0.0f;
	m_speedReference = LM_REF_NONE;
	m_positionReference = LM_REF_NONE;
	m_p = 0.0;
	m_i = 0.0;
	m_d = 0.0;
	m_busVoltage = 0.0f;
	m_outputVoltage = 0.0f;
	m_outputCurrent = 0.0f;
	m_temperature = 0.0f;
	m_position = 0.0;
	m_speed = 0.0;
	m_limits = 0x00;
	m_faults = 0x0000;
	m_firmwareVersion = 0;
	m_hardwareVersion = 0;
	m_neutralMode = kNeutralMode_Jumper;
	m_encoderCodesPerRev = 0;
	m_potentiometerTurns = 0;
	m_limitMode = kLimitMode_SwitchInputsOnly;
	m_forwardLimit = 0.0;
	m_reverseLimit = 0.0;
	m_maxOutputVoltage = 30.0;
	m_voltageRampRate = 0.0;
	m_faultTime = 0.0f;

	// Parameters only need to be verified if they are set
	m_controlModeVerified = false; // Needs to be verified because it's set in the constructor
	m_speedRefVerified = true;
	m_posRefVerified = true;
	m_pVerified = true;
	m_iVerified = true;
	m_dVerified = true;
	m_neutralModeVerified = true;
	m_encoderCodesPerRevVerified = true;
	m_potentiometerTurnsVerified = true;
	m_limitModeVerified = true;
	m_forwardLimitVerified = true;
	m_reverseLimitVerified = true;
	m_maxOutputVoltageVerified = true;
	m_voltageRampRateVerified = true;
	m_faultTimeVerified = true;

	uint8_t dataBuffer[8];
	uint8_t dataSize;

	// Request all status data periodically
	requestMessage(LM_API_STATUS_VOLTBUS, kSendMessagePeriod);
	requestMessage(LM_API_STATUS_VOUT, kSendMessagePeriod);
	requestMessage(LM_API_STATUS_CURRENT, kSendMessagePeriod);
	requestMessage(LM_API_STATUS_TEMP, kSendMessagePeriod);
	requestMessage(LM_API_STATUS_POS, kSendMessagePeriod);
	requestMessage(LM_API_STATUS_SPD, kSendMessagePeriod);
	requestMessage(LM_API_STATUS_LIMIT, kSendMessagePeriod);
	requestMessage(LM_API_STATUS_FAULT, kSendMessagePeriod);
	requestMessage(LM_API_STATUS_POWER, kSendMessagePeriod);

	// Request firmware and hardware version only once
	requestMessage(CAN_IS_FRAME_REMOTE | CAN_MSGID_API_FIRMVER);
	requestMessage(LM_API_HWVER);

	m_receivedBusVoltage = false;
	m_receivedOutputVoltage = false;
	m_receivedOutputCurrent = false;
	m_receivedTemperature = false;
	m_receivedPosition = false;
	m_receivedSpeed = false;
	m_receivedLimits = false;
	m_receivedFaults = false;

	// Wait until we've gotten all of the status data at least once.
	for(int i = 0; i < kReceiveStatusAttempts; i++)
	{
		Wait(0.001);

		GetBusVoltage();
		GetOutputVoltage();
		GetOutputCurrent();
		GetTemperature();
		GetPosition();
		GetSpeed();
		GetForwardLimitOK();
		GetFaults();

		if(m_receivedBusVoltage &&
			m_receivedOutputVoltage &&
			m_receivedOutputCurrent &&
			m_receivedTemperature &&
			m_receivedPosition &&
			m_receivedSpeed &&
			m_receivedLimits &&
			m_receivedFaults)
		{
			break;
		}
	}

	if(getMessage(CAN_MSGID_API_FIRMVER, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
		m_firmwareVersion = unpackint32_t(dataBuffer);
	else
		wpi_setWPIErrorWithContext(JaguarMessageNotFound, "getMessage");

	if(getMessage(LM_API_HWVER, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
		m_hardwareVersion = dataBuffer[0];

	if (m_deviceNumber < 1 || m_deviceNumber > 63)
	{
		char buf[256];
		snprintf(buf, 256, "device number \"%d\" must be between 1 and 63", m_deviceNumber);
		wpi_setWPIErrorWithContext(ParameterOutOfRange, buf);
		return;
	}

	if (StatusIsFatal())
		return;

	// 3330 was the first shipping RDK firmware version for the Jaguar
	if (m_firmwareVersion >= 3330 || m_firmwareVersion < 101)
	{
		char buf[256];
		if (m_firmwareVersion < 3330)
		{
			snprintf(buf, 256, "Jag #%d firmware (%d) is too old (must be at least version 101 of the FIRST approved firmware)", m_deviceNumber, m_firmwareVersion);
		}
		else
		{
			snprintf(buf, 256, "Jag #%d firmware (%d) is not FIRST approved (must be at least version 101 of the FIRST approved firmware)", m_deviceNumber, m_firmwareVersion);
		}
		wpi_setWPIErrorWithContext(JaguarVersionError, buf);
		return;
	}

	switch (m_controlMode)
	{
	case kPercentVbus:
	case kVoltage:
		// No additional configuration required... start enabled.
		EnableControl();
		break;
	default:
		break;
	}

	HALReport(HALUsageReporting::kResourceType_CANJaguar, m_deviceNumber, m_controlMode);
	LiveWindow::GetInstance()->AddActuator("CANJaguar", m_deviceNumber, this);
}

/**
 * Constructor
 *
 * @param deviceNumber The the address of the Jaguar on the CAN bus.
 */
CANJaguar::CANJaguar(uint8_t deviceNumber)
	: m_deviceNumber (deviceNumber)
	, m_maxOutputVoltage (kApproxBusVoltage)
	, m_safetyHelper (NULL)
{
	SetPercentMode();
	InitCANJaguar();
}

CANJaguar::~CANJaguar()
{
	int32_t status;

	// Disable periodic setpoints
	if(m_controlMode == kPercentVbus)
		FRC_NetworkCommunication_CANSessionMux_sendMessage(m_deviceNumber | LM_API_VOLT_T_SET, NULL, 0, CAN_SEND_PERIOD_STOP_REPEATING, &status);
	else if(m_controlMode == kSpeed)
		FRC_NetworkCommunication_CANSessionMux_sendMessage(m_deviceNumber | LM_API_SPD_T_SET, NULL, 0, CAN_SEND_PERIOD_STOP_REPEATING, &status);
	else if(m_controlMode == kPosition)
		FRC_NetworkCommunication_CANSessionMux_sendMessage(m_deviceNumber | LM_API_POS_T_SET, NULL, 0, CAN_SEND_PERIOD_STOP_REPEATING, &status);
	else if(m_controlMode == kCurrent)
		FRC_NetworkCommunication_CANSessionMux_sendMessage(m_deviceNumber | LM_API_ICTRL_T_SET, NULL, 0, CAN_SEND_PERIOD_STOP_REPEATING, &status);
	else if(m_controlMode == kVoltage)
		FRC_NetworkCommunication_CANSessionMux_sendMessage(m_deviceNumber | LM_API_VCOMP_T_SET, NULL, 0, CAN_SEND_PERIOD_STOP_REPEATING, &status);

	delete m_safetyHelper;
	m_safetyHelper = NULL;
}

/**
 * Set the output set-point value.
 *
 * 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.
 */
void CANJaguar::Set(float outputValue, uint8_t syncGroup)
{
	uint32_t messageID;
	uint8_t dataBuffer[8];
	uint8_t dataSize;

	if (m_safetyHelper && !m_safetyHelper->IsAlive())
	{
		EnableControl();
	}

	switch(m_controlMode)
	{
	case kPercentVbus:
		{
			messageID = LM_API_VOLT_T_SET;
			if (outputValue > 1.0) outputValue = 1.0;
			if (outputValue < -1.0) outputValue = -1.0;
			dataSize = packPercentage(dataBuffer, outputValue);
		}
		break;
	case kSpeed:
		{
			messageID = LM_API_SPD_T_SET;
			dataSize = packFXP16_16(dataBuffer, outputValue);
		}
		break;
	case kPosition:
		{
			messageID = LM_API_POS_T_SET;
			dataSize = packFXP16_16(dataBuffer, outputValue);
		}
		break;
	case kCurrent:
		{
			messageID = LM_API_ICTRL_T_SET;
			dataSize = packFXP8_8(dataBuffer, outputValue);
		}
		break;
	case kVoltage:
		{
			messageID = LM_API_VCOMP_T_SET;
			dataSize = packFXP8_8(dataBuffer, outputValue);
		}
		break;
	default:
		return;
	}
	if (syncGroup != 0)
	{
		dataBuffer[dataSize] = syncGroup;
		dataSize++;
	}

	sendMessage(messageID, dataBuffer, dataSize, kSendMessagePeriod);

	if (m_safetyHelper) m_safetyHelper->Feed();

	m_value = outputValue;

	verify();
}

/**
 * Get the recently set outputValue setpoint.
 *
 * 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.
 *
 * @return The most recently set outputValue setpoint.
 */
float CANJaguar::Get()
{
	return m_value;
}

/**
 * Common interface for disabling a motor.
 *
 * @deprecated Call DisableControl instead.
 */
void CANJaguar::Disable()
{
	DisableControl();
}

/**
 * Write out the PID value as seen in the PIDOutput base object.
 *
 * @deprecated Call Set instead.
 *
 * @param output Write out the PercentVbus value as was computed by the PIDController
 */
void CANJaguar::PIDWrite(float output)
{
	if (m_controlMode == kPercentVbus)
	{
		Set(output);
	}
	else
	{
		wpi_setWPIErrorWithContext(IncompatibleMode, "PID only supported in PercentVbus mode");
	}
}

uint8_t CANJaguar::packPercentage(uint8_t *buffer, double value)
{
	int16_t intValue = (int16_t)(value * 32767.0);
	*((int16_t*)buffer) = swap16(intValue);
	return sizeof(int16_t);
}

uint8_t CANJaguar::packFXP8_8(uint8_t *buffer, double value)
{
	int16_t intValue = (int16_t)(value * 256.0);
	*((int16_t*)buffer) = swap16(intValue);
	return sizeof(int16_t);
}

uint8_t CANJaguar::packFXP16_16(uint8_t *buffer, double value)
{
	int32_t intValue = (int32_t)(value * 65536.0);
	*((int32_t*)buffer) = swap32(intValue);
	return sizeof(int32_t);
}

uint8_t CANJaguar::packint16_t(uint8_t *buffer, int16_t value)
{
	*((int16_t*)buffer) = swap16(value);
	return sizeof(int16_t);
}

uint8_t CANJaguar::packint32_t(uint8_t *buffer, int32_t value)
{
	*((int32_t*)buffer) = swap32(value);
	return sizeof(int32_t);
}

double CANJaguar::unpackPercentage(uint8_t *buffer)
{
	int16_t value = *((int16_t*)buffer);
	value = swap16(value);
	return value / 32767.0;
}

double CANJaguar::unpackFXP8_8(uint8_t *buffer)
{
	int16_t value = *((int16_t*)buffer);
	value = swap16(value);
	return value / 256.0;
}

double CANJaguar::unpackFXP16_16(uint8_t *buffer)
{
	int32_t value = *((int32_t*)buffer);
	value = swap32(value);
	return value / 65536.0;
}

int16_t CANJaguar::unpackint16_t(uint8_t *buffer)
{
	int16_t value = *((int16_t*)buffer);
	return swap16(value);
}

int32_t CANJaguar::unpackint32_t(uint8_t *buffer)
{
	int32_t value = *((int32_t*)buffer);
	return swap32(value);
}

/**
 * Send a message to the Jaguar.
 *
 * @param messageID The messageID to be used on the CAN bus (device number is added internally)
 * @param data The up to 8 bytes of data to be sent with the message
 * @param dataSize Specify how much of the data in "data" to send
 * @param periodic If positive, tell Network Communications to send the message
 * 	every "period" milliseconds.
 */
void CANJaguar::sendMessage(uint32_t messageID, const uint8_t *data, uint8_t dataSize, int32_t period)
{
	int32_t localStatus = sendMessageHelper(messageID | m_deviceNumber, data, dataSize, period);

	if(localStatus < 0)
	{
		wpi_setErrorWithContext(localStatus, "sendMessage");
	}
}

/**
 * Request a message from the Jaguar, but don't wait for it to arrive.
 *
 * @param messageID The message to request
 * @param periodic If positive, tell Network Communications to send the message
 * 	every "period" milliseconds.
 */
void CANJaguar::requestMessage(uint32_t messageID, int32_t period)
{
	sendMessageHelper(messageID | m_deviceNumber, NULL, 0, period);
}

/**
 * Get a previously requested message.
 *
 * Jaguar always generates a message with the same message ID when replying.
 *
 * @param messageID The messageID to read from the CAN bus (device number is added internally)
 * @param data The up to 8 bytes of data that was received with the message
 * @param dataSize Indicates how much data was received
 *
 * @return true if the message was found.  Otherwise, no new message is available.
 */
bool CANJaguar::getMessage(uint32_t messageID, uint32_t messageMask, uint8_t *data, uint8_t *dataSize)
{
	uint32_t targetedMessageID = messageID | m_deviceNumber;
	int32_t status = 0;
	uint32_t timeStamp;

	// Caller may have set bit31 for remote frame transmission so clear invalid bits[31-29]
	targetedMessageID &= CAN_MSGID_FULL_M;

	// Get the data.
	FRC_NetworkCommunication_CANSessionMux_receiveMessage(&targetedMessageID, messageMask, data, dataSize, &timeStamp, &status);

	// Do we already have the most recent value?
	if(status == ERR_CANSessionMux_MessageNotFound)
		return false;
	else
		wpi_setErrorWithContext(status, "receiveMessage");

	return true;
}

/**
 * Check all unverified params and make sure they're equal to their local
 * cached versions. If a value isn't available, it gets requested.  If a value
 * doesn't match up, it gets set again.
 */
void CANJaguar::verify()
{
	uint8_t dataBuffer[8];
	uint8_t dataSize;

	// If the Jaguar lost power, everything should be considered unverified.
	if(getMessage(LM_API_STATUS_POWER, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
	{
		bool powerCycled = (bool)dataBuffer[0];

		if(powerCycled)
		{
			// Clear the power cycled bit
			dataBuffer[0] = 1;
			sendMessage(LM_API_STATUS_POWER, dataBuffer, sizeof(uint8_t));

			// Set all configuration data again.  This will resend it to the
			// Jaguar and mark everything as unverified.
			EnableControl();
			SetSpeedReference(m_speedReference);
			SetPositionReference(m_positionReference);
			ConfigNeutralMode(m_neutralMode);
			ConfigEncoderCodesPerRev(m_encoderCodesPerRev);
			ConfigPotentiometerTurns(m_potentiometerTurns);

			if(m_controlMode == kCurrent || m_controlMode == kSpeed || m_controlMode == kPosition)
				SetPID(m_p, m_i, m_d);

			if(m_limitMode == kLimitMode_SoftPositionLimits)
				ConfigSoftPositionLimits(m_forwardLimit, m_reverseLimit);
			else
				DisableSoftPositionLimits();

			ConfigMaxOutputVoltage(m_maxOutputVoltage);

			if(m_controlMode == kVoltage || m_controlMode == kPercentVbus)
				SetVoltageRampRate(m_voltageRampRate);

			requestMessage(LM_API_STATUS_VOLTBUS, kSendMessagePeriod);
			requestMessage(LM_API_STATUS_VOUT, kSendMessagePeriod);
			requestMessage(LM_API_STATUS_CURRENT, kSendMessagePeriod);
			requestMessage(LM_API_STATUS_TEMP, kSendMessagePeriod);
			requestMessage(LM_API_STATUS_POS, kSendMessagePeriod);
			requestMessage(LM_API_STATUS_SPD, kSendMessagePeriod);
			requestMessage(LM_API_STATUS_LIMIT, kSendMessagePeriod);
			requestMessage(LM_API_STATUS_FAULT, kSendMessagePeriod);
			requestMessage(LM_API_STATUS_POWER, kSendMessagePeriod);
		}
	}

	// Verify that any recently set parameters are correct
	if(!m_controlModeVerified)
	{
		if(getMessage(LM_API_STATUS_CMODE, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
		{
			ControlMode mode = (ControlMode)dataBuffer[0];

			if(m_controlMode == mode)
				m_controlModeVerified = true;
			else
				// Enable control again to resend the control mode
				EnableControl();
		}
		else
		{
			// Verification is needed but not available - request it again.
			requestMessage(LM_API_STATUS_CMODE);
		}
	}

	if(!m_speedRefVerified)
	{
		if(getMessage(LM_API_SPD_REF, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
		{
			uint8_t speedRef = dataBuffer[0];

			if(m_speedReference == speedRef)
				m_speedRefVerified = true;
			else
				// It's wrong - set it again
				SetSpeedReference(m_speedReference);
		}
		else
		{
			// Verification is needed but not available - request it again.
			requestMessage(LM_API_SPD_REF);
		}
	}

	if(!m_posRefVerified)
	{
		if(getMessage(LM_API_POS_REF, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
		{
			uint8_t posRef = dataBuffer[0];

			if(m_positionReference == posRef)
				m_posRefVerified = true;
			else
				// It's wrong - set it again
				SetPositionReference(m_positionReference);
		}
		else
		{
			// Verification is needed but not available - request it again.
			requestMessage(LM_API_POS_REF);
		}
	}

	if(!m_pVerified)
	{
		uint32_t message;

		if(m_controlMode == kSpeed)
			message = LM_API_SPD_PC;
		else if(m_controlMode == kPosition)
			message = LM_API_POS_PC;
		else if(m_controlMode == kCurrent)
			message = LM_API_ICTRL_PC;

		if(getMessage(message, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
		{
			double p = unpackFXP16_16(dataBuffer);

			if(FXP16_EQ(m_p, p))
				m_pVerified = true;
			else
				// It's wrong - set it again
				SetP(m_p);
		}
		else
		{
			// Verification is needed but not available - request it again.
			requestMessage(message);
		}
	}

	if(!m_iVerified)
	{
		uint32_t message;

		if(m_controlMode == kSpeed)
			message = LM_API_SPD_IC;
		else if(m_controlMode == kPosition)
			message = LM_API_POS_IC;
		else if(m_controlMode == kCurrent)
			message = LM_API_ICTRL_IC;

		if(getMessage(message, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
		{
			double i = unpackFXP16_16(dataBuffer);

			if(FXP16_EQ(m_i, i))
				m_iVerified = true;
			else
				// It's wrong - set it again
				SetI(m_i);
		}
		else
		{
			// Verification is needed but not available - request it again.
			requestMessage(message);
		}
	}

	if(!m_dVerified)
	{
		uint32_t message;

		if(m_controlMode == kSpeed)
			message = LM_API_SPD_DC;
		else if(m_controlMode == kPosition)
			message = LM_API_POS_DC;
		else if(m_controlMode == kCurrent)
			message = LM_API_ICTRL_DC;

		if(getMessage(message, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
		{
			double d = unpackFXP16_16(dataBuffer);

			if(FXP16_EQ(m_d, d))
				m_dVerified = true;
			else
				// It's wrong - set it again
				SetD(m_d);
		}
		else
		{
			// Verification is needed but not available - request it again.
			requestMessage(message);
		}
	}

	if(!m_neutralModeVerified)
	{
		if(getMessage(LM_API_CFG_BRAKE_COAST, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
		{
			NeutralMode mode = (NeutralMode)dataBuffer[0];

			if(mode == m_neutralMode)
				m_neutralModeVerified = true;
			else
				// It's wrong - set it again
				ConfigNeutralMode(m_neutralMode);
		}
		else
		{
			// Verification is needed but not available - request it again.
			requestMessage(LM_API_CFG_BRAKE_COAST);
		}
	}

	if(!m_encoderCodesPerRevVerified)
	{
		if(getMessage(LM_API_CFG_ENC_LINES, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
		{
			uint16_t codes = unpackint16_t(dataBuffer);

			if(codes == m_encoderCodesPerRev)
				m_encoderCodesPerRevVerified = true;
			else
				// It's wrong - set it again
				ConfigEncoderCodesPerRev(m_encoderCodesPerRev);
		}
		else
		{
			// Verification is needed but not available - request it again.
			requestMessage(LM_API_CFG_ENC_LINES);
		}
	}

	if(!m_potentiometerTurnsVerified)
	{
		if(getMessage(LM_API_CFG_POT_TURNS, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
		{
			uint16_t turns = unpackint16_t(dataBuffer);

			if(turns == m_potentiometerTurns)
				m_potentiometerTurnsVerified = true;
			else
				// It's wrong - set it again
				ConfigPotentiometerTurns(m_potentiometerTurns);
		}
		else
		{
			// Verification is needed but not available - request it again.
			requestMessage(LM_API_CFG_POT_TURNS);
		}
	}

	if(!m_limitModeVerified)
	{
		if(getMessage(LM_API_CFG_LIMIT_MODE, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
		{
			LimitMode mode = (LimitMode)dataBuffer[0];

			if(mode == m_limitMode)
				m_limitModeVerified = true;
			else
			{
				// It's wrong - set it again
				ConfigLimitMode(m_limitMode);
			}
		}
		else
		{
			// Verification is needed but not available - request it again.
			requestMessage(LM_API_CFG_LIMIT_MODE);
		}
	}

	if(!m_forwardLimitVerified)
	{
		if(getMessage(LM_API_CFG_LIMIT_FWD, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
		{
			double limit = unpackFXP16_16(dataBuffer);

			if(FXP16_EQ(limit, m_forwardLimit))
				m_forwardLimitVerified = true;
			else
			{
				// It's wrong - set it again
				ConfigForwardLimit(m_forwardLimit);
			}
		}
		else
		{
			// Verification is needed but not available - request it again.
			requestMessage(LM_API_CFG_LIMIT_FWD);
		}
	}

	if(!m_reverseLimitVerified)
	{
		if(getMessage(LM_API_CFG_LIMIT_REV, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
		{
			double limit = unpackFXP16_16(dataBuffer);

			if(FXP16_EQ(limit, m_reverseLimit))
				m_reverseLimitVerified = true;
			else
			{
				// It's wrong - set it again
				ConfigReverseLimit(m_reverseLimit);
			}
		}
		else
		{
			// Verification is needed but not available - request it again.
			requestMessage(LM_API_CFG_LIMIT_REV);
		}
	}

	if(!m_maxOutputVoltageVerified)
	{
		if(getMessage(LM_API_CFG_MAX_VOUT, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
		{
			double voltage = unpackFXP16_16(dataBuffer);

			if(FXP16_EQ(voltage, m_maxOutputVoltage))
				m_maxOutputVoltageVerified = true;
			else
			{
				// It's wrong - set it again
				ConfigMaxOutputVoltage(m_maxOutputVoltage);
			}
		}
		else
		{
			// Verification is needed but not available - request it again.
			requestMessage(LM_API_CFG_MAX_VOUT);
		}
	}

	if(!m_voltageRampRateVerified)
	{
		if(m_controlMode == kPercentVbus)
		{
			if(getMessage(LM_API_VOLT_SET_RAMP, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
			{
				double rate = unpackPercentage(dataBuffer);

				if(FXP16_EQ(rate, m_voltageRampRate))
					m_voltageRampRateVerified = true;
				else
				{
					// It's wrong - set it again
					SetVoltageRampRate(m_voltageRampRate);
				}
			}
			else
			{
				// Verification is needed but not available - request it again.
				requestMessage(LM_API_VOLT_SET_RAMP);
			}
		}
		else if(m_controlMode == kVoltage)
		{
			if(getMessage(LM_API_VCOMP_IN_RAMP, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
			{
				double rate = unpackFXP8_8(dataBuffer);

				if(FXP8_EQ(rate, m_voltageRampRate))
					m_voltageRampRateVerified = true;
				else
				{
					// It's wrong - set it again
					SetVoltageRampRate(m_voltageRampRate);
				}
			}
			else
			{
				// Verification is needed but not available - request it again.
				requestMessage(LM_API_VCOMP_IN_RAMP);
			}
		}
	}
}

/**
 * Set the reference source device for speed controller mode.
 *
 * Choose encoder as the source of speed feedback when in speed control mode.
 *
 * @param reference Specify a speed reference.
 */
void CANJaguar::SetSpeedReference(uint8_t reference)
{
	uint8_t dataBuffer[8];

	// Send the speed reference parameter
	dataBuffer[0] = reference;
	sendMessage(LM_API_SPD_REF, dataBuffer, sizeof(uint8_t));

	m_speedReference = reference;
	m_speedRefVerified = false;
}

/**
 * Get the reference source device for speed controller mode.
 *
 * @return A speed reference indicating the currently selected reference device
 * for speed controller mode.
 */
uint8_t CANJaguar::GetSpeedReference()
{
	return m_speedReference;
}

/**
 * Set the reference source device for position controller mode.
 *
 * 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.
 */
void CANJaguar::SetPositionReference(uint8_t reference)
{
	uint8_t dataBuffer[8];

	// Send the position reference parameter
	dataBuffer[0] = reference;
	sendMessage(LM_API_POS_REF, dataBuffer, sizeof(uint8_t));

	m_positionReference = reference;
	m_posRefVerified = false;
}

/**
 * Get the reference source device for position controller mode.
 *
 * @return A PositionReference indicating the currently selected reference device for position controller mode.
 */
uint8_t CANJaguar::GetPositionReference()
{
	return m_positionReference;
}

/**
 * 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.
 */
void CANJaguar::SetPID(double p, double i, double d)
{
	SetP(p);
	SetI(i);
	SetD(d);
}

/**
 * Set the P constant for the closed loop modes.
 *
 * @param p The proportional gain of the Jaguar's PID controller.
 */
void CANJaguar::SetP(double p)
{
	uint8_t dataBuffer[8];
	uint8_t dataSize;

	switch(m_controlMode)
	{
	case kPercentVbus:
	case kVoltage:
		wpi_setWPIErrorWithContext(IncompatibleMode, "PID constants only apply in Speed, Position, and Current mode");
		break;
	case kSpeed:
		dataSize = packFXP16_16(dataBuffer, p);
		sendMessage(LM_API_SPD_PC, dataBuffer, dataSize);
		break;
	case kPosition:
		dataSize = packFXP16_16(dataBuffer, p);
		sendMessage(LM_API_POS_PC, dataBuffer, dataSize);
		break;
	case kCurrent:
		dataSize = packFXP16_16(dataBuffer, p);
		sendMessage(LM_API_ICTRL_PC, dataBuffer, dataSize);
		break;
	}

	m_p = p;
	m_pVerified = false;
}

/**
 * Set the I constant for the closed loop modes.
 *
 * @param i The integral gain of the Jaguar's PID controller.
 */
void CANJaguar::SetI(double i)
{
	uint8_t dataBuffer[8];
	uint8_t dataSize;

	switch(m_controlMode)
	{
	case kPercentVbus:
	case kVoltage:
		wpi_setWPIErrorWithContext(IncompatibleMode, "PID constants only apply in Speed, Position, and Current mode");
		break;
	case kSpeed:
		dataSize = packFXP16_16(dataBuffer, i);
		sendMessage(LM_API_SPD_IC, dataBuffer, dataSize);
		break;
	case kPosition:
		dataSize = packFXP16_16(dataBuffer, i);
		sendMessage(LM_API_POS_IC, dataBuffer, dataSize);
		break;
	case kCurrent:
		dataSize = packFXP16_16(dataBuffer, i);
		sendMessage(LM_API_ICTRL_IC, dataBuffer, dataSize);
		break;
	}

	m_i = i;
	m_iVerified = false;
}

/**
 * Set the D constant for the closed loop modes.
 *
 * @param d The derivative gain of the Jaguar's PID controller.
 */
void CANJaguar::SetD(double d)
{
	uint8_t dataBuffer[8];
	uint8_t dataSize;

	switch(m_controlMode)
	{
	case kPercentVbus:
	case kVoltage:
		wpi_setWPIErrorWithContext(IncompatibleMode, "PID constants only apply in Speed, Position, and Current mode");
		break;
	case kSpeed:
		dataSize = packFXP16_16(dataBuffer, d);
		sendMessage(LM_API_SPD_DC, dataBuffer, dataSize);
		break;
	case kPosition:
		dataSize = packFXP16_16(dataBuffer, d);
		sendMessage(LM_API_POS_DC, dataBuffer, dataSize);
		break;
	case kCurrent:
		dataSize = packFXP16_16(dataBuffer, d);
		sendMessage(LM_API_ICTRL_DC, dataBuffer, dataSize);
		break;
	}

	m_d = d;
	m_dVerified = false;
}

/**
 * Get the Proportional gain of the controller.
 *
 * @return The proportional gain.
 */
double CANJaguar::GetP()
{
	if(m_controlMode == kPercentVbus || m_controlMode == kVoltage)
	{
		wpi_setWPIErrorWithContext(IncompatibleMode, "PID constants only apply in Speed, Position, and Current mode");
		return 0.0;
	}

	return m_p;
}

/**
 * Get the Intregral gain of the controller.
 *
 * @return The integral gain.
 */
double CANJaguar::GetI()
{
	if(m_controlMode == kPercentVbus || m_controlMode == kVoltage)
	{
		wpi_setWPIErrorWithContext(IncompatibleMode, "PID constants only apply in Speed, Position, and Current mode");
		return 0.0;
	}

	return m_i;
}

/**
 * Get the Differential gain of the controller.
 *
 * @return The differential gain.
 */
double CANJaguar::GetD()
{
	if(m_controlMode == kPercentVbus || m_controlMode == kVoltage)
	{
		wpi_setWPIErrorWithContext(IncompatibleMode, "PID constants only apply in Speed, Position, and Current mode");
		return 0.0;
	}

	return m_d;
}

/**
 * Enable the closed loop controller.
 *
 * 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.
 */
void CANJaguar::EnableControl(double encoderInitialPosition)
{
	uint8_t dataBuffer[8];
	uint8_t dataSize = 0;

	switch(m_controlMode)
	{
	case kPercentVbus:
		sendMessage(LM_API_VOLT_T_EN, dataBuffer, dataSize);
		break;
	case kSpeed:
		sendMessage(LM_API_SPD_T_EN, dataBuffer, dataSize);
		break;
	case kPosition:
		dataSize = packFXP16_16(dataBuffer, encoderInitialPosition);
		sendMessage(LM_API_POS_T_EN, dataBuffer, dataSize);
		break;
	case kCurrent:
		sendMessage(LM_API_ICTRL_T_EN, dataBuffer, dataSize);
		break;
	case kVoltage:
		sendMessage(LM_API_VCOMP_T_EN, dataBuffer, dataSize);
		break;
	}

	m_controlModeVerified = false;
}

/**
 * Disable the closed loop controller.
 *
 * Stop driving the output based on the feedback.
 */
void CANJaguar::DisableControl()
{
	uint8_t dataBuffer[8];
	uint8_t dataSize = 0;

	switch(m_controlMode)
	{
	case kPercentVbus:
		sendMessage(LM_API_VOLT_DIS, dataBuffer, dataSize);
		break;
	case kSpeed:
		sendMessage(LM_API_SPD_DIS, dataBuffer, dataSize);
		break;
	case kPosition:
		sendMessage(LM_API_POS_DIS, dataBuffer, dataSize);
		break;
	case kCurrent:
		sendMessage(LM_API_ICTRL_DIS, dataBuffer, dataSize);
		break;
case kVoltage:
		sendMessage(LM_API_VCOMP_DIS, dataBuffer, dataSize);
		break;
	}
}

/**
 * Enable controlling the motor voltage as a percentage of the bus voltage
 * without any position or speed feedback.
 */
void CANJaguar::SetPercentMode()
{
	ChangeControlMode(kPercentVbus);
	SetPositionReference(LM_REF_NONE);
	SetSpeedReference(LM_REF_NONE);
}

/**
 * Enable controlling the motor voltage as a percentage of the bus voltage,
 * and enable speed sensing from a non-quadrature encoder.
 *
 * @param encoder The constant CANJaguar::Encoder
 * @param codesPerRev The counts per revolution on the encoder
 */
void CANJaguar::SetPercentMode(CANJaguar::EncoderStruct, uint16_t codesPerRev)
{
	ChangeControlMode(kPercentVbus);
	SetPositionReference(LM_REF_NONE);
	SetSpeedReference(LM_REF_ENCODER);
	ConfigEncoderCodesPerRev(codesPerRev);
}

/**
* Enable controlling the motor voltage as a percentage of the bus voltage,
* and enable position and speed sensing from a quadrature encoder
*
* @param encoder The constant CANJaguar::QuadEncoder
* @param codesPerRev The counts per revolution on the encoder
*/
void CANJaguar::SetPercentMode(CANJaguar::QuadEncoderStruct, uint16_t codesPerRev)
{
	ChangeControlMode(kPercentVbus);
	SetPositionReference(LM_REF_QUAD_ENCODER);
	SetSpeedReference(LM_REF_QUAD_ENCODER);
	ConfigEncoderCodesPerRev(codesPerRev);
}

/**
* Enable controlling the motor voltage as a percentage of the bus voltage,
* and enable position sensing from a potentiometer and no speed feedback.
*
* @param potentiometer The constant CANJaguar::Potentiometer
*/
void CANJaguar::SetPercentMode(CANJaguar::PotentiometerStruct)
{
	ChangeControlMode(kPercentVbus);
	SetPositionReference(LM_REF_POT);
	SetSpeedReference(LM_REF_NONE);
	ConfigPotentiometerTurns(1);
}

/**
 * Enable controlling the motor current with a PID loop.
 *
 * @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.
 */
void CANJaguar::SetCurrentMode(double p, double i, double d)
{
	ChangeControlMode(kCurrent);
	SetPositionReference(LM_REF_NONE);
	SetSpeedReference(LM_REF_NONE);
	SetPID(p, i, d);
}

/**
* Enable controlling the motor current with a PID loop, and enable speed
* sensing from a non-quadrature encoder.
*
* @param encoder The constant CANJaguar::Encoder
* @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.
*/
void CANJaguar::SetCurrentMode(CANJaguar::EncoderStruct, uint16_t codesPerRev, double p, double i, double d)
{
	ChangeControlMode(kCurrent);
	SetPositionReference(LM_REF_NONE);
	SetSpeedReference(LM_REF_NONE);
	ConfigEncoderCodesPerRev(codesPerRev);
	SetPID(p, i, d);
}

/**
* Enable controlling the motor current with a PID loop, and enable speed and
* position sensing from a quadrature encoder.
*
* @param endoer The constant CANJaguar::QuadEncoder
* @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.
*/
void CANJaguar::SetCurrentMode(CANJaguar::QuadEncoderStruct, uint16_t codesPerRev, double p, double i, double d)
{
	ChangeControlMode(kCurrent);
	SetPositionReference(LM_REF_QUAD_ENCODER);
	SetSpeedReference(LM_REF_QUAD_ENCODER);
	ConfigEncoderCodesPerRev(codesPerRev);
	SetPID(p, i, d);
}

/**
* Enable controlling the motor current with a PID loop, and enable position
* sensing from a potentiometer.
*
* @param potentiometer The constant CANJaguar::Potentiometer
* @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.
*/
void CANJaguar::SetCurrentMode(CANJaguar::PotentiometerStruct, double p, double i, double d)
{
	ChangeControlMode(kCurrent);
	SetPositionReference(LM_REF_POT);
	SetSpeedReference(LM_REF_NONE);
	ConfigPotentiometerTurns(1);
	SetPID(p, i, d);
}

/**
 * Enable controlling the speed with a feedback loop from a non-quadrature
 * encoder.
 *
 * @param encoder The constant CANJaguar::Encoder
 * @param codesPerRev The counts per revolution on the encoder
 * @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.
 */
void CANJaguar::SetSpeedMode(CANJaguar::EncoderStruct, uint16_t codesPerRev, double p, double i, double d)
{
	ChangeControlMode(kSpeed);
	SetPositionReference(LM_REF_NONE);
	SetSpeedReference(LM_REF_ENCODER);
	ConfigEncoderCodesPerRev(codesPerRev);
	SetPID(p, i, d);
}

/**
* Enable controlling the speed with a feedback loop from a quadrature encoder.
*
* @param encoder The constant CANJaguar::QuadEncoder
* @param codesPerRev The counts per revolution on the encoder
* @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.
*/
void CANJaguar::SetSpeedMode(CANJaguar::QuadEncoderStruct, uint16_t codesPerRev, double p, double i, double d)
{
	ChangeControlMode(kSpeed);
	SetPositionReference(LM_REF_QUAD_ENCODER);
	SetSpeedReference(LM_REF_QUAD_ENCODER);
	ConfigEncoderCodesPerRev(codesPerRev);
	SetPID(p, i, d);
}

/**
 * Enable controlling the position with a feedback loop using an encoder
 *
 * @param encoder The constant CANJaguar::QuadEncoder
 *
 */
void CANJaguar::SetPositionMode(CANJaguar::QuadEncoderStruct, uint16_t codesPerRev, double p, double i, double d)
{
	ChangeControlMode(kPosition);
	SetPositionReference(LM_REF_QUAD_ENCODER);
	ConfigEncoderCodesPerRev(codesPerRev);
	SetPID(p, i, d);
}

/**
* Enable controlling the position with a feedback loop using a potentiometer
*/
void CANJaguar::SetPositionMode(CANJaguar::PotentiometerStruct, double p, double i, double d)
{
	ChangeControlMode(kPosition);
	SetPositionReference(LM_REF_POT);
	ConfigPotentiometerTurns(1);
	SetPID(p, i, d);
}

/**
* Enable controlling the motor voltage without any position or speed feedback.
*/
void CANJaguar::SetVoltageMode()
{
	ChangeControlMode(kVoltage);
	SetPositionReference(LM_REF_NONE);
	SetSpeedReference(LM_REF_NONE);
}

/**
* Enable controlling the motor voltage with speed feedback from a
* non-quadrature encoder and no position feedback.
*
* @param encoder The constant CANJaguar::Encoder
* @param codesPerRev The counts per revolution on the encoder
*/
void CANJaguar::SetVoltageMode(CANJaguar::EncoderStruct, uint16_t codesPerRev)
{
	ChangeControlMode(kVoltage);
	SetPositionReference(LM_REF_NONE);
	SetSpeedReference(LM_REF_ENCODER);
	ConfigEncoderCodesPerRev(codesPerRev);
}

/**
* Enable controlling the motor voltage with position and speed feedback from a
* quadrature encoder
*
* @param encoder The constant CANJaguar::QuadEncoder
* @param codesPerRev The counts per revolution on the encoder
*/
void CANJaguar::SetVoltageMode(CANJaguar::QuadEncoderStruct, uint16_t codesPerRev)
{
	ChangeControlMode(kVoltage);
	SetPositionReference(LM_REF_QUAD_ENCODER);
	SetSpeedReference(LM_REF_QUAD_ENCODER);
	ConfigEncoderCodesPerRev(codesPerRev);
}

/**
* Enable controlling the motor voltage with position feedback from a
* potentiometer and no speed feedback.
*
* @param potentiometer The constant CANJaguar::Potentiometer
*/
void CANJaguar::SetVoltageMode(CANJaguar::PotentiometerStruct)
{
	ChangeControlMode(kVoltage);
	SetPositionReference(LM_REF_POT);
	SetSpeedReference(LM_REF_NONE);
	ConfigPotentiometerTurns(1);
}

/**
 * Change the control mode of this Jaguar object.
 *
 * After changing modes, configure any PID constants or other settings needed
 * and then EnableControl() to actually change the mode on the Jaguar.
 *
 * @param controlMode The new mode.
 */
void CANJaguar::ChangeControlMode(ControlMode controlMode)
{
	// Disable the previous mode
	DisableControl();

	// Update the local mode
	m_controlMode = controlMode;

	HALReport(HALUsageReporting::kResourceType_CANJaguar, m_deviceNumber, m_controlMode);
}

/**
 * Get the active control mode from the Jaguar.
 *
 * Ask the Jag what mode it is in.
 *
 * @return ControlMode that the Jag is in.
 */
CANJaguar::ControlMode CANJaguar::GetControlMode()
{
	return m_controlMode;
}

/**
 * Get the voltage at the battery input terminals of the Jaguar.
 *
 * @return The bus voltage in Volts.
 */
float CANJaguar::GetBusVoltage()
{
	uint8_t dataBuffer[8];
	uint8_t dataSize;

	if(getMessage(LM_API_STATUS_VOLTBUS, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
	{
		m_busVoltage = unpackFXP8_8(dataBuffer);
		m_receivedBusVoltage = true;
	}

	return m_busVoltage;
}

/**
 * Get the voltage being output from the motor terminals of the Jaguar.
 *
 * @return The output voltage in Volts.
 */
float CANJaguar::GetOutputVoltage()
{
	uint8_t dataBuffer[8];
	uint8_t dataSize;

	if(getMessage(LM_API_STATUS_VOUT, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
	{
		m_outputVoltage = unpackFXP8_8(dataBuffer);
		m_receivedOutputVoltage = true;
	}

	return m_outputVoltage;
}

/**
 * Get the current through the motor terminals of the Jaguar.
 *
 * @return The output current in Amps.
 */
float CANJaguar::GetOutputCurrent()
{
	uint8_t dataBuffer[8];
	uint8_t dataSize;

	if(getMessage(LM_API_STATUS_CURRENT, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
	{
		m_outputCurrent = unpackFXP8_8(dataBuffer);
		m_receivedOutputCurrent = true;
	}

	return m_outputCurrent;
}

/**
 * Get the internal temperature of the Jaguar.
 *
 * @return The temperature of the Jaguar in degrees Celsius.
 */
float CANJaguar::GetTemperature()
{
	uint8_t dataBuffer[8];
	uint8_t dataSize;

	if(getMessage(LM_API_STATUS_TEMP, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
	{
		m_temperature = unpackFXP8_8(dataBuffer);
		m_receivedTemperature = true;
	}

	return m_temperature;
}

/**
 * Get the position of the encoder or potentiometer.
 *
 * @return The position of the motor in rotations based on the configured feedback.
 */
double CANJaguar::GetPosition()
{
	uint8_t dataBuffer[8];
	uint8_t dataSize;

	if(getMessage(LM_API_STATUS_POS, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
	{
		m_position = unpackFXP16_16(dataBuffer);
		m_receivedPosition = true;
	}

	return m_position;
}

/**
 * Get the speed of the encoder.
 *
 * @return The speed of the motor in RPM based on the configured feedback.
 */
double CANJaguar::GetSpeed()
{
	uint8_t dataBuffer[8];
	uint8_t dataSize;

	if(getMessage(LM_API_STATUS_SPD, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
	{
		m_speed = unpackFXP16_16(dataBuffer);
		m_receivedSpeed = true;
	}

	return m_speed;
}

/**
 * Get the status of the forward limit switch.
 *
 * @return The motor is allowed to turn in the forward direction when true.
 */
bool CANJaguar::GetForwardLimitOK()
{
	uint8_t dataBuffer[8];
	uint8_t dataSize;

	if(getMessage(LM_API_STATUS_LIMIT, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
	{
		m_limits = dataBuffer[0];
		m_receivedLimits = true;
	}

	return m_limits & kForwardLimit;
}

/**
 * Get the status of the reverse limit switch.
 *
 * @return The motor is allowed to turn in the reverse direction when true.
 */
bool CANJaguar::GetReverseLimitOK()
{
	uint8_t dataBuffer[8];
	uint8_t dataSize;

	if(getMessage(LM_API_STATUS_LIMIT, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
	{
		m_limits = dataBuffer[0];
		m_receivedLimits = true;
	}

	return m_limits & kReverseLimit;
}

/**
 * Get the status of any faults the Jaguar has detected.
 *
 * @return A bit-mask of faults defined by the "Faults" enum.
 */
uint16_t CANJaguar::GetFaults()
{
	uint8_t dataBuffer[8];
	uint8_t dataSize;

	if(getMessage(LM_API_STATUS_FAULT, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
	{
		m_faults = unpackint16_t(dataBuffer);
		m_receivedFaults = true;
	}

	return m_faults;
}

/**
 * Set the maximum voltage change rate.
 *
 * When in PercentVbus or 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.
 */
void CANJaguar::SetVoltageRampRate(double rampRate)
{
	uint8_t dataBuffer[8];
	uint8_t dataSize;
	uint32_t message;

	switch(m_controlMode)
	{
	case kPercentVbus:
		dataSize = packPercentage(dataBuffer, rampRate / (m_maxOutputVoltage * kControllerRate));
		message = LM_API_VOLT_SET_RAMP;
		break;
	case kVoltage:
		dataSize = packFXP8_8(dataBuffer, rampRate / kControllerRate);
		message = LM_API_VCOMP_IN_RAMP;
		break;
	default:
		wpi_setWPIErrorWithContext(IncompatibleMode, "SetVoltageRampRate only applies in Voltage and Percent mode");
		return;
	}

	sendMessage(message, dataBuffer, dataSize);

	m_voltageRampRate = rampRate;
	m_voltageRampRateVerified = false;
}

/**
 * Get the version of the firmware running on the Jaguar.
 *
 * @return The firmware version.  0 if the device did not respond.
 */
uint32_t CANJaguar::GetFirmwareVersion()
{
	return m_firmwareVersion;
}

/**
 * Get the version of the Jaguar hardware.
 *
 * @return The hardware version. 1: Jaguar,  2: Black Jaguar
 */
uint8_t CANJaguar::GetHardwareVersion()
{
	return m_hardwareVersion;
}

/**
 * Configure what the controller does to the H-Bridge when neutral (not driving the output).
 *
 * 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.
 */
void CANJaguar::ConfigNeutralMode(NeutralMode mode)
{
	uint8_t dataBuffer[8];

	// Set the neutral mode
	sendMessage(LM_API_CFG_BRAKE_COAST, dataBuffer, sizeof(uint8_t));

	m_neutralMode = mode;
	m_neutralModeVerified = false;
}

/**
 * Configure how many codes per revolution are generated by your encoder.
 *
 * @param codesPerRev The number of counts per revolution in 1X mode.
 */
void CANJaguar::ConfigEncoderCodesPerRev(uint16_t codesPerRev)
{
	uint8_t dataBuffer[8];
	uint8_t dataSize;

	// Set the codes per revolution mode
	dataSize = packint16_t(dataBuffer, codesPerRev);
	sendMessage(LM_API_CFG_ENC_LINES, dataBuffer, sizeof(uint16_t));

	m_encoderCodesPerRev = codesPerRev;
	m_encoderCodesPerRevVerified = false;
}

/**
 * Configure the number of turns on the potentiometer.
 *
 * 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
 */
void CANJaguar::ConfigPotentiometerTurns(uint16_t turns)
{
	uint8_t dataBuffer[8];
	uint8_t dataSize;

	// Set the pot turns
	dataSize = packint16_t(dataBuffer, turns);
	sendMessage(LM_API_CFG_POT_TURNS, dataBuffer, dataSize);

	m_potentiometerTurns = turns;
	m_potentiometerTurnsVerified = false;
}

/**
 * Configure Soft Position Limits when in Position Controller mode.
 *
 * 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.
 */
void CANJaguar::ConfigSoftPositionLimits(double forwardLimitPosition, double reverseLimitPosition)
{
	ConfigLimitMode(kLimitMode_SoftPositionLimits);
	ConfigForwardLimit(forwardLimitPosition);
	ConfigReverseLimit(reverseLimitPosition);
}

/**
 * Disable Soft Position Limits if previously enabled.
 *
 * Soft Position Limits are disabled by default.
 */
void CANJaguar::DisableSoftPositionLimits()
{
	ConfigLimitMode(kLimitMode_SwitchInputsOnly);
}

/**
 * Set the limit mode for position control mode.
 *
 * Use ConfigSoftPositionLimits or DisableSoftPositionLimits to set this
 * automatically.
 */
void CANJaguar::ConfigLimitMode(LimitMode mode)
{
	uint8_t dataBuffer[8];

	dataBuffer[0] = mode;
	sendMessage(LM_API_CFG_LIMIT_MODE, dataBuffer, sizeof(uint8_t));

	m_limitMode = mode;
	m_limitModeVerified = false;
}

/**
* Set the position that if exceeded will disable the forward direction.
*
* Use ConfigSoftPositionLimits to set this and the limit mode automatically.
*/
void CANJaguar::ConfigForwardLimit(double forwardLimitPosition)
{
	uint8_t dataBuffer[8];
	uint8_t dataSize;

	dataSize = packFXP16_16(dataBuffer, forwardLimitPosition);
	dataBuffer[dataSize++] = 1;
	sendMessage(LM_API_CFG_LIMIT_FWD, dataBuffer, dataSize);

	m_forwardLimit = forwardLimitPosition;
	m_forwardLimitVerified = false;
}

/**
* Set the position that if exceeded will disable the reverse direction.
*
* Use ConfigSoftPositionLimits to set this and the limit mode automatically.
*/
void CANJaguar::ConfigReverseLimit(double reverseLimitPosition)
{
	uint8_t dataBuffer[8];
	uint8_t dataSize;

	dataSize = packFXP16_16(dataBuffer, reverseLimitPosition);
	dataBuffer[dataSize++] = 0;
	sendMessage(LM_API_CFG_LIMIT_REV, dataBuffer, dataSize);

	m_reverseLimit = reverseLimitPosition;
	m_reverseLimitVerified = false;
}

/**
 * Configure the maximum voltage that the Jaguar will ever output.
 *
 * 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.
 */
void CANJaguar::ConfigMaxOutputVoltage(double voltage)
{
	uint8_t dataBuffer[8];
	uint8_t dataSize;

	dataSize = packFXP8_8(dataBuffer, voltage);
	sendMessage(LM_API_CFG_MAX_VOUT, dataBuffer, dataSize);

	m_maxOutputVoltage = voltage;
	m_maxOutputVoltageVerified = false;
}

/**
 * Configure how long the Jaguar waits in the case of a fault before resuming operation.
 *
 * 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.
 */
void CANJaguar::ConfigFaultTime(float faultTime)
{
	uint8_t dataBuffer[8];
	uint8_t dataSize;

	// Message takes ms
	dataSize = packint16_t(dataBuffer, (int16_t)(faultTime * 1000.0));
	sendMessage(LM_API_CFG_FAULT_TIME, dataBuffer, dataSize);

	m_faultTime = faultTime;
	m_faultTimeVerified = false;
}

/**
 * Update all the motors that have pending sets in the syncGroup.
 *
 * @param syncGroup A bitmask of groups to generate synchronous output.
 */
void CANJaguar::UpdateSyncGroup(uint8_t syncGroup)
{
	sendMessageHelper(CAN_MSGID_API_SYNC, &syncGroup, sizeof(syncGroup), CAN_SEND_PERIOD_NO_REPEAT);
}


void CANJaguar::SetExpiration(float timeout)
{
	if (m_safetyHelper) m_safetyHelper->SetExpiration(timeout);
}

float CANJaguar::GetExpiration()
{
	if (!m_safetyHelper) return 0.0;
	return m_safetyHelper->GetExpiration();
}

bool CANJaguar::IsAlive()
{
	if (!m_safetyHelper) return false;
	return m_safetyHelper->IsAlive();
}

bool CANJaguar::IsSafetyEnabled()
{
	if (!m_safetyHelper) return false;
	return m_safetyHelper->IsSafetyEnabled();
}

void CANJaguar::SetSafetyEnabled(bool enabled)
{
	if (m_safetyHelper) m_safetyHelper->SetSafetyEnabled(enabled);
}

void CANJaguar::GetDescription(char *desc)
{
	sprintf(desc, "CANJaguar ID %d", m_deviceNumber);
}

/**
 * Common interface for stopping the motor
 * Part of the MotorSafety interface
 *
 * @deprecated Call DisableControl instead.
 */
void CANJaguar::StopMotor()
{
	DisableControl();
}

void CANJaguar::ValueChanged(ITable* source, const std::string& key, EntryValue value, bool isNew)
{
	Set(value.f);
}

void CANJaguar::UpdateTable()
{
	if (m_table != NULL)
	{
		m_table->PutNumber("Value", Get());
	}
}

void CANJaguar::StartLiveWindowMode()
{
	if (m_table != NULL)
	{
		m_table->AddTableListener("Value", this, true);
	}
}

void CANJaguar::StopLiveWindowMode()
{
	if (m_table != NULL)
	{
		m_table->RemoveTableListener(this);
	}
}

std::string CANJaguar::GetSmartDashboardType()
{
	return "Speed Controller";
}

void CANJaguar::InitTable(ITable *subTable)
{
	m_table = subTable;
	UpdateTable();
}

ITable * CANJaguar::GetTable()
{
	return m_table;
}
