/*
This software is subject to the license described in the License.txt file 
included with this software distribution. You may not use this file except in compliance 
with this license.

Copyright (c) Dynastream Innovations Inc. 2012
All rights reserved.
*/


#pragma once

#include "types.h"
#define LEV_REV				0.5					// Device Profile Revision Number that this was written to

public ref class LEV
{

public:
	// Channel Parameters
	static const UCHAR DEVICE_TYPE = 20;
	static const UCHAR TX_TYPE = 5;
	static const USHORT MSG_PERIOD = 8192;	// 4 Hz

	// Data Pages
	static const UCHAR PAGE1_SPEED_SYSTEM1 = 0x01;
	static const UCHAR PAGE2_SPEED_DISTANCE = 0x02;
	static const UCHAR PAGE3_SPEED_SYSTEM2 = 0x03;
	static const UCHAR PAGE4_BATTERY = 0x04;
	static const UCHAR PAGE5_CAPABILITES = 0x05;
	static const UCHAR PAGE16_DISPLAY_DATA = 0x10;
	static const UCHAR PAGE34_SPEED_DISTANCE = 0x22;
	static const UCHAR PAGE70_REQUEST = 0x46;

	// Acknowledged Messages
	static const UCHAR ACK_FAIL = 0;
	static const UCHAR ACK_SUCCESS = 1;
	static const UCHAR ACK_RETRY = 2;
	static const UCHAR MAX_RETRIES = 10;		// defines the max number of times to retry before failing

	// Reserved/invalid values
	static const UCHAR RESERVED = 0xFF;
	
	// Bit Masks
	static const UCHAR BYTE_MASK = 0xFF;

	// Defines how often to send common pages (every 20th message)
	static const UCHAR LEV_COMMON_INTERVAL = 20;	
	

public:

	// LEV Data Page 1
	UCHAR ucTemperature;			  // Indicates current motor / battery temperature (0 = unsued)
	UCHAR ucTravelModeState;		  // Indicates the LEV's current travel mode
	UCHAR ucSystemState;			  // Indicates the current state of the LEV's peripheral sustem
	UCHAR ucGearState;				  // Indicates the current state of the LEV's gears
	UCHAR ucErrorMessage;			  // Indicates LEV error
	USHORT usCurrentLEVSpeed;	      // Instantaneous speed (0.1 km/hr), shared between page1 and page2

	// LEV Data Page 2
	ULONG ulOdometer;				  // 3 bytes, accumulated total distance (0.01 km)
	USHORT usRemainingRange;		  // Remaining range of the LEV battery (km)

	// LEV Data Page 3
	UCHAR ucBatteryCharge;			  // State of battery charge (0-100%)
	UCHAR ucPercentAssist;			  // Percent assistance the LEV motor is providing (0-100%)	

	// LEV Data Page 4
	USHORT usChargeCycleCount;		  // Tracks the number of charge cycles the LEV has gone through
	USHORT usFuelConsumption;		  // Tracks the current rate of battery fuel consumption in watt-hours per km
	UCHAR ucBatteryVoltage;			  // Measured battery voltage (0 = unsued)
	USHORT usDistanceOnCharge;		  // Tracks the distance in km the LEV has traveled on the current charge	

	// LEV Data Page 5
	UCHAR ucSupportedTravelModes;     // Travel Modes Supported bit field
	USHORT usWheelCircumference;	  // Holds the wheel cirumference of the LEV in mm

	// LEV Data Page 16
	USHORT usP16WheelCircum;		  // Used to set the wheel circumference
	UCHAR ucP16TravelMode;			  // Indicates the current travel mode set/requested
	USHORT usP16DisplayCommand;		  // Command sent from the Display to the LEV
	USHORT usP16ManID;				  // Manufacturer ID of the Display

	//LEV Data Page 34
	ULONG ulP34Odometer;				  // Accumulated total distance travelled
	USHORT usP34FuelConsumption;		  // Indicates current rate of battery fuel consumption in watt-hrs per km. 
	USHORT usSpeed;					  // Current LEV speed

	// LEV Data Page 70
	UCHAR ucP70TxTimes;				      // number of times the sensor should send the requested pages (broadcast only supported on LEV)
    UCHAR ucP70RequestedPage;			  // holds the page number to be requested


public:
	LEV()
	{
		// initialize variables to invalid / zero
		ucTravelModeState = 0;
		ucSystemState = 0;
		ucGearState = 0;
		ucErrorMessage = 0;
		usCurrentLEVSpeed = 0;
		ucPercentAssist = 0;
		ulOdometer = 0;
		ucTemperature = 0;
		ucBatteryCharge = 0;
		usRemainingRange = 0;
		usChargeCycleCount = 0;
		usFuelConsumption = 0;
		ucBatteryVoltage = 0;
		usDistanceOnCharge = 0;
		ucSupportedTravelModes = 0;
		usWheelCircumference = 0;
		usP16WheelCircum = 0xFF;
		ucP16TravelMode = 0xFF;
		usP16DisplayCommand = 0;
		usP16ManID = 0;
		ulP34Odometer = 0;
		usP34FuelConsumption = 0;
		usSpeed = 0;
		ucP70TxTimes = 0;
		ucP70RequestedPage = 0;
	}

	~LEV()
	{
	}

public:

/**************************************************************************
 * LEV::Decode
 * 
 * Decodes all main data pages 
 * Exceptions are thrown when dealing with non compliant pages
 * Further breakdowns inside the fields are dealt with in the display method
 *
 * pucRxBuffer_: pointer to the buffer containing the received data
 * 
 * returns: N/A
 *
 **************************************************************************/
	void Decode(UCHAR* pucRxBuffer_)
	{
		ULONG ulTempHolder = 0;

		switch(pucRxBuffer_[0])
		{
			case PAGE1_SPEED_SYSTEM1:
				ucTemperature = pucRxBuffer_[1];
				ucTravelModeState = pucRxBuffer_[2];			
				ucSystemState = pucRxBuffer_[3];
				ucGearState = pucRxBuffer_[4];
				ucErrorMessage = pucRxBuffer_[5];
				usCurrentLEVSpeed = pucRxBuffer_[6] | ((pucRxBuffer_[7] & 0x0F) << 8);		// mask off the top bits which are reserved
	            break;

			case PAGE2_SPEED_DISTANCE:				
				ulOdometer = pucRxBuffer_[1];                    // odometer LSB
                ulTempHolder = pucRxBuffer_[2] & 0xFF;           // odometer 
                ulOdometer = ulOdometer | (ulTempHolder << 8);
                ulTempHolder = pucRxBuffer_[3] & 0xFF;           // odometer MSB
                ulOdometer = ulOdometer | (ulTempHolder << 16);
				usRemainingRange = pucRxBuffer_[4] | ((pucRxBuffer_[5] & 0x0F) << 8);       // mask off top bits of byte 5 which are reserved
				usCurrentLEVSpeed = pucRxBuffer_[6] | ((pucRxBuffer_[7] & 0x0F) << 8);		// mask off the top bits which are reserved
	            break;

			case PAGE3_SPEED_SYSTEM2:
				ucBatteryCharge = pucRxBuffer_[1];
				ucTravelModeState = pucRxBuffer_[2];			
				ucSystemState = pucRxBuffer_[3];
				ucGearState = pucRxBuffer_[4];
				ucPercentAssist = pucRxBuffer_[5];
				usCurrentLEVSpeed = pucRxBuffer_[6] | ((pucRxBuffer_[7] & 0x0F) << 8);		// mask off the top bits which are reserved
				break;

			case PAGE4_BATTERY:
				usChargeCycleCount = pucRxBuffer_[2] | ((pucRxBuffer_[3] & 0x0F) << 8);        // mask off top bits of byte 3 which are part of fuel consumption
				usFuelConsumption = pucRxBuffer_[4] | (((pucRxBuffer_[3] & 0xF0) >> 4) << 8);  // LSB of fuel consumption is byte 4
				ucBatteryVoltage = pucRxBuffer_[5];
				usDistanceOnCharge = pucRxBuffer_[6] | (pucRxBuffer_[7] << 8);
				break;

			case PAGE5_CAPABILITES:
				ucSupportedTravelModes = pucRxBuffer_[2];		
				usWheelCircumference = pucRxBuffer_[3] | ((pucRxBuffer_[4] & 0x0F) << 8);
				break;

			case PAGE16_DISPLAY_DATA:
				usP16WheelCircum = pucRxBuffer_[1] | ((pucRxBuffer_[2] & 0x0F) << 8);
				ucP16TravelMode = pucRxBuffer_[3];
				usP16DisplayCommand = pucRxBuffer_[4] | (pucRxBuffer_[5] << 8);
				usP16ManID = pucRxBuffer_[6] | (pucRxBuffer_[7] << 8);
				break;

			case PAGE34_SPEED_DISTANCE:
				ulP34Odometer = pucRxBuffer_[1] | ((pucRxBuffer_[2] & 0xFF) << 8 ) | ((pucRxBuffer_[3] & 0xFF) << 16);
				usP34FuelConsumption = (pucRxBuffer_[4] | ((pucRxBuffer_[5] & 0xFF) << 8)) & 0x0FFF;
				usSpeed = (pucRxBuffer_[6] | ((pucRxBuffer_[7] & 0xFF) << 8)) & 0x0FFF;
				break;

			default:
				break;
		}
	}

/**************************************************************************
 * LEV::EncodeData
 * 
 * Encodes the LEV data pages:
 * - Data Page 1 - Speed and System Information 1
 * - Data Page 2 - Speed and Distance Information
 * - Data Page 3 - Speed and System Information 2
 * - Data Page 4 - Battery Info
 * - Data Page 5 - Capabilities
 * - Data Page 16 - Display Data
 * - Data Page 70 - Request Data
 *
 * Exceptions are thrown when dealing with invalid data
 *
 * ucPageNum_: number of page to encode
 * pucTxBuffer_: pointer to the buffer that will store the encoded data
 * 
 * returns: N/A
 *
 **************************************************************************/
	void EncodeData(UCHAR ucPageNum_, UCHAR* pucTxBuffer_)
	{
		switch(ucPageNum_)
		{
			case PAGE1_SPEED_SYSTEM1:
				pucTxBuffer_[0] = ucPageNum_;					// page number
				pucTxBuffer_[1] = ucTemperature;				// current temperature state
				pucTxBuffer_[2] = ucTravelModeState;		    // current travel mode
				pucTxBuffer_[3] = ucSystemState;				// current state of peripheral system
				pucTxBuffer_[4] = ucGearState;					// current state of gears
				pucTxBuffer_[5] = ucErrorMessage;				// LEV error
				pucTxBuffer_[6] = usCurrentLEVSpeed & BYTE_MASK;		// mask off top byte
				pucTxBuffer_[7] = (usCurrentLEVSpeed >> 8) & 0x0FFF;	// shift top down and mask off top 
				break;

			case PAGE2_SPEED_DISTANCE:
				pucTxBuffer_[0] = ucPageNum_;
				pucTxBuffer_[1] = ulOdometer & BYTE_MASK;				// accum distance
				pucTxBuffer_[2] = (ulOdometer >> 8) & BYTE_MASK;
				pucTxBuffer_[3] = (ulOdometer >> 16) & BYTE_MASK;
				pucTxBuffer_[4] = usRemainingRange & BYTE_MASK;			// mask off top byte
				pucTxBuffer_[5] = (usRemainingRange >> 8) & 0x0FFF;	    // shift top down and mask off top
				pucTxBuffer_[6] = usCurrentLEVSpeed & BYTE_MASK;		// mask off top byte
				pucTxBuffer_[7] = (usCurrentLEVSpeed >> 8) & 0x0FFF;	// shift top down and mask off top 
				break;

			case PAGE3_SPEED_SYSTEM2:
				pucTxBuffer_[0] = ucPageNum_;
				pucTxBuffer_[1] = ucBatteryCharge;				// battery charge
				pucTxBuffer_[2] = ucTravelModeState;		    // current travel mode
				pucTxBuffer_[3] = ucSystemState;				// current state of peripheral system
				pucTxBuffer_[4] = ucGearState;					// current state of gears
				pucTxBuffer_[5] = ucPercentAssist;				// percent assist of motor
				pucTxBuffer_[6] = usCurrentLEVSpeed & BYTE_MASK;		// mask off top byte
				pucTxBuffer_[7] = (usCurrentLEVSpeed >> 8) & 0x0FFF;	// shift top down and mask off top 
				break;

			case PAGE4_BATTERY:
				pucTxBuffer_[0] = ucPageNum_;					        // page number
				pucTxBuffer_[1] = RESERVED;                  
				pucTxBuffer_[2] = usChargeCycleCount & BYTE_MASK;		// mask off top byte
				pucTxBuffer_[3] = ((usChargeCycleCount >> 8) | (usFuelConsumption >> 4) & 0xF0);  // msn for both                      
				pucTxBuffer_[4] = usFuelConsumption & BYTE_MASK;		// mask off top byte
				pucTxBuffer_[5] = ucBatteryVoltage;						// battery voltage
				pucTxBuffer_[6] = usDistanceOnCharge & BYTE_MASK;		// mask off top byte
				pucTxBuffer_[7] = (usDistanceOnCharge >> 8) & BYTE_MASK;	// shift top down and mask off top 
				break;

			case PAGE5_CAPABILITES:
				pucTxBuffer_[0] = ucPageNum_;					// page number
				pucTxBuffer_[1] = RESERVED;
				pucTxBuffer_[2] = ucSupportedTravelModes;    	// travel modes supported
				pucTxBuffer_[3] = usWheelCircumference & BYTE_MASK;		 // mask off top byte
				pucTxBuffer_[4] = (usWheelCircumference >> 8) & 0x0FFF;	 // shift top down and mask off top 
				pucTxBuffer_[5] = RESERVED;
				pucTxBuffer_[6] = RESERVED;
				pucTxBuffer_[7] = RESERVED;
				break;

			case PAGE16_DISPLAY_DATA:
				pucTxBuffer_[0] = ucPageNum_;
				pucTxBuffer_[1] = usP16WheelCircum & BYTE_MASK;		        // mask off top byte
				pucTxBuffer_[2] = (usP16WheelCircum >> 8) & 0x0FFF;	        // shift top down and mask off top 
				pucTxBuffer_[3] = ucP16TravelMode;							// current travel mode
				pucTxBuffer_[4] = usP16DisplayCommand & BYTE_MASK;			// mask off top byte
				pucTxBuffer_[5] = (usP16DisplayCommand >> 8) & BYTE_MASK;	// shift top down and mask off top
				pucTxBuffer_[6] = usP16ManID & BYTE_MASK;					// mask off top byte
				pucTxBuffer_[7] = (usP16ManID >> 8) & BYTE_MASK;			// shift top down and mask off top
				break;

			case PAGE34_SPEED_DISTANCE:
				pucTxBuffer_[0] = ucPageNum_;
				pucTxBuffer_[1] = ulP34Odometer & BYTE_MASK;
				pucTxBuffer_[2] = (ulP34Odometer >> 8) & BYTE_MASK;
				pucTxBuffer_[3] = (ulP34Odometer >> 16) & BYTE_MASK;
				pucTxBuffer_[4] = usP34FuelConsumption & BYTE_MASK;
				pucTxBuffer_[5] = ((usFuelConsumption >> 8) & BYTE_MASK) | 0xF0;
				pucTxBuffer_[6] = usSpeed & BYTE_MASK;
				pucTxBuffer_[7] = ((usSpeed >> 8) & BYTE_MASK) | 0xF0;
				break;

			case PAGE70_REQUEST:
				pucTxBuffer_[0] = ucPageNum_;
				pucTxBuffer_[1] = RESERVED;
				pucTxBuffer_[2] = RESERVED;
				pucTxBuffer_[3] = RESERVED;
				pucTxBuffer_[4] = RESERVED;
				pucTxBuffer_[5] = ucP70TxTimes;								// number of times the sensor should send the requested pages (broadcast only supported on LEV)
				pucTxBuffer_[6] = ucP70RequestedPage;						// this is the page number that the display is requesting
				pucTxBuffer_[7] = 0x01;										// always 0x01 for Request Data Page
				break;

			default:				
				break;
		}
	}
};