/*
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 "StdAfx.h"
#include "MSMSensor.h"

/**************************************************************************
* MSMSensor::InitializeSim
* 
* Initialize the simulator variables
*
* returns: N/A
*
**************************************************************************/
void MSMSensor::InitializeSim()
{
	ulTimerInterval = 250; // in ms - simulates the sensor sending the most relevant information every message period

	dbDispAcumTime = 0;
	dbDispAcumDist = 0;
	usLastEventTime = 0;
	msmData->usInstSpeed1000 = System::Convert::ToUInt16 (this->numericUpDownSpeed->Value * msmData->SPEED_SCALE_FACTOR);  // Set the speed to the what is shown in the menu
	eCalStatus = MSM::CalibrationStatus::NONE;
	ulTotalTime = 0;

	// required common pages
	commonData->ulSerialNum = System::Convert::ToUInt32(this->textBox_Glb_SerialNumChange->Text);
	commonData->ucSwVersion = System::Convert::ToByte(this->textBox_Glb_SoftwareVerChange->Text);
	commonData->ucHwVersion = System::Convert::ToByte(this->textBox_Glb_HardwareVerChange->Text);
	commonData->usMfgID = System::Convert::ToUInt16(this->textBox_Glb_ManfIDChange->Text);
	commonData->usModelNum = System::Convert::ToUInt16(this->textBox_Glb_ModelNumChange->Text);
	//battery common page
	commonData->eTimeResolution = CommonData::TimeResolution::TWO;
	commonData->ulOpTime = ulTotalTime;
	commonData->bBattPageEnabled = FALSE;
	// time common page
	commonData->bTimePageEnabled = FALSE;
	commonData->ucSeconds = System::Convert::ToByte(this->numericUpDownSeconds->Value);
	commonData->ucMinutes = System::Convert::ToByte(this->numericUpDownMinutes->Value);
	commonData->ucHours = System::Convert::ToByte(this->numericUpDownHours->Value);
	UpdateDayOfWeek();
	commonData->ucDays = System::Convert::ToByte(this->numericUpDownDay->Value);
	commonData->ucMonth = System::Convert::ToByte(this->numericUpDownMonth->Value);
	commonData->ucYears = System::Convert::ToByte(this->numericUpDownYear->Value);

	UpdateTxPattern();
	UpdateCommonPattern();
}

/**************************************************************************
* MSMSensor::ANT_eventNotification
* 
* Process ANT channel event
*
* ucEventCode_: code of ANT channel event
* pucEventBuffer_: pointer to buffer containing data received from ANT,
*		or a pointer to the transmit buffer in the case of an EVENT_TX
* 
* returns: N/A
*
**************************************************************************/
void MSMSensor::ANT_eventNotification(UCHAR ucEventCode_, UCHAR* pucEventBuffer_)
{
	switch(ucEventCode_)
	{
	case EVENT_TX:
		HandleTransmit((UCHAR*) pucEventBuffer_);
		UpdateDisplay();
		break;

	case EVENT_RX_ACKNOWLEDGED:
		HandleCalibration((UCHAR*) pucEventBuffer_);
		break;

	case EVENT_TRANSFER_TX_COMPLETED:
		UpdateDisplayAckStatus(msmData->ACK_SUCCESS);
		break;

	case EVENT_ACK_TIMEOUT:	
	case EVENT_TRANSFER_TX_FAILED: // intentional fall through
		UpdateDisplayAckStatus(msmData->ACK_FAIL);
		break;

	default:
		break;
	}
}

/**************************************************************************
 * MSMSensor::HandleTransmit
 * 
 * Encode data generated by simulator for transmission
 *
 * ucPageNum_: The page number to be transmitted
 * pucTxBuffer_: pointer to the transmit buffer
 * 
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::HandleTransmit(UCHAR* pucTxBuffer_)
{
	static ULONG ulMessageCount = 1;	
	static UCHAR ucMsmPatternCount = 0;
	static UCHAR ucCommonPatternCount = 0;

	if (ulMessageCount % 65)
	{
		if(ucMsmPatternCount >= ucTxPattern.Count)
			ucMsmPatternCount = 0;
		// send MSM data pages
		switch (ucTxPattern[ucMsmPatternCount])
		{
		case msmData->PAGE_1:
			msmData->EncodeData(msmData->PAGE_1, pucTxBuffer_);
			break;

		case msmData->PAGE_2:
			msmData->EncodeData(msmData->PAGE_2, pucTxBuffer_);
			break;

		case msmData->PAGE_3:
			msmData->EncodeData(msmData->PAGE_3, pucTxBuffer_);
			break;
		default:
			break;
		}

		ucMsmPatternCount++;

		//if (ucMsmPatternCount >= ucTxPattern.Count)
		//	ucMsmPatternCount = 0;  // reset the message count at the end
	}

	else
	{
		if(ucCommonPatternCount >= ucCommonPattern.Count)
			ucCommonPatternCount = 0;

		switch(ucCommonPattern[ucCommonPatternCount])
		{
		case commonData->PAGE80:
			commonData->Encode(commonData->PAGE80, pucTxBuffer_);
			break;
		case commonData->PAGE81:
			commonData->Encode(commonData->PAGE81, pucTxBuffer_);
			break;
		case commonData->PAGE82:
			commonData->Encode(commonData->PAGE82, pucTxBuffer_);
			break;
		case commonData->PAGE83:
			commonData->Encode(commonData->PAGE83, pucTxBuffer_);
			break;
		}
		ucCommonPatternCount++;
	}

	ulMessageCount++;

	static UCHAR ucTimeCount = 0;
	// update the batt op time every second
	if (ucTimeCount == 3)
	{
		ulTotalTime++;
		commonData->ulOpTime = ulTotalTime / (UCHAR) commonData->eTimeResolution;
		ucTimeCount = 0;
	}
	else
		ucTimeCount++;
}

/**************************************************************************
 * MSMSensor::Update Display
 * 
 * Update the GUI with all the changes
 * 
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::UpdateDisplay()
{
	this->labelAcumTime->Text = dbDispAcumTime.ToString("N2");  // Update the accumulated time label
	this->labelAcumDistance->Text = dbDispAcumDist.ToString("N2");  // Update the accumulated distance label

	// the tx variables will show the raw transmitted data decoded, but not scaled into proper units
	this->labelTxDist->Text = msmData->usAcumDist10.ToString();  // Update the tx box distance label
	this->labelTxTime->Text = msmData->usAcumTime1024.ToString();  // update the tx box time label
	this->labelTxSpeed->Text = msmData->usInstSpeed1000.ToString();  // update the tx box speed label

	if (msmData->IsCalScaleValid(msmData->usScaleFactor10000))
		this->labelCurrentScaleFactor->Text = ((double) msmData->usScaleFactor10000 / msmData->CAL_SF_SCALE_FACTOR).ToString("N4");
	else
		this->labelCurrentScaleFactor->Text = "Invalid";

	if (msmData->bPage2Enabled)
	{
		this->labelTxLat->Text = msmData->slLatitude_SC.ToString();
		this->lableTxLon->Text = msmData->slLongitude_SC.ToString();
	}

	if (msmData->bPage3Enabled)
	{
		this->labelTxElev->Text = msmData->usElevation5.ToString();
		this->labelTxHeading->Text = msmData->usHeading10.ToString();
		this->labelTxFix->Text = msmData->ucFixType.ToString();
	}

	// update the operating time for the battery common page
	this->label_Bat_ElpTimeDisplay->Text = ulTotalTime.ToString();
}

/**************************************************************************
 * MSMSensor::onTimerTock
 * 
 * Simulates a sensor event, updating simulator data based on this event
 * Modifications to the timer interval are applied immediately after this
 * at ANTChannel
 *
 * usEventTime_: current time (ms)
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::onTimerTock(USHORT eventTime)
{

	msmData->usAcumTime1024 += (USHORT) (ulTimerInterval << 10 / 1000);     // time base is 1/1024 second
	dbDispAcumTime += (double) ulTimerInterval / 1000;  // Update the display time

	dbDispAcumDist += (double) (msmData->usInstSpeed1000 * ulTimerInterval) / 1000000;  
	msmData->usAcumDist10 = (USHORT) (dbDispAcumDist * 10);  // distance is transmitted in 0.1m intervals

}

/**************************************************************************
 * MSMSensor::HandleCalibration
 * 
 * Handlest teh Calibration mode of the MSM
 *
 * pucEventBuffer_: data buffer after receving an acknowleged message
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::HandleCalibration(UCHAR* pucEventBuffer_)
{
	msmData->Decode(pucEventBuffer_);

	// check if the sensor supports calibration
	if (this->checkBoxCalibrationSupport->Checked == FALSE)
	{
		// set scale factor to unsupported value
		msmData->usScaleFactor10000 = msmData->CAL_SCALE_INVALID;
		msmData->ucMode = msmData->CAL_MODE_INVALID;
		eCalStatus = MSM::CalibrationStatus::NONE;

		// send the response
		msmData->EncodeData(msmData->PAGE_CALIBRATION, pucEventBuffer_);
		requestAckMsg(pucEventBuffer_);

		this->labelCalibrationProgress->Text = "Calibration is not supported";
		this->labelCalibrationProgress->BackColor = System::Drawing::SystemColors::Control;
	}

	else
	{
		// check if the mode is supported
		if (this->checkBoxModeStorageSupport->Checked == FALSE)
			{
				// only if mode storage is not supported does the mode need to change from the decoded value
				msmData->ucMode = msmData->CAL_MODE_INVALID;
			}

		// check if it is a Set Scale Command
		if ((msmData->usScaleFactor10000 != msmData->CAL_SCALE_REQUEST) && (msmData->IsCalScaleValid(msmData->usScaleFactor10000)))
		{
			// set the status
			eCalStatus = MSM::CalibrationStatus::SET_SCALE_IN_PROGRESS;

			// calibration request response state
			this->labelCalibrationProgress->Text = "Set Scale Factor Received";
			this->labelCalibrationProgress->BackColor = System::Drawing::Color::Yellow;
			
			// send the calibration request response
			msmData->EncodeData(msmData->PAGE_CALIBRATION, pucEventBuffer_);
			requestAckMsg(pucEventBuffer_);

			// update the display values
			this->labelCalibrationProgress->Text = "Scale Factor \nConfirmation Sent";
			this->labelCalibrationProgress->BackColor = System::Drawing::Color::Orange;
		}

		// check if it is a calibration request
		if (msmData->usScaleFactor10000 == msmData->CAL_SCALE_REQUEST && eCalStatus != MSM::CalibrationStatus::SET_SCALE_IN_PROGRESS)
		{
			// set the status
			eCalStatus = MSM::CalibrationStatus::REQUEST_IN_PROGRESS;

			// calibration request response state
			this->labelCalibrationProgress->Text = "Calibration Request Received";
			this->labelCalibrationProgress->BackColor = System::Drawing::Color::Yellow;

			msmData->usScaleFactor10000 = msmData->CAL_SCALE_CONF;
			// the mode will already be decoded by msmData->Decode()

			// send the calibration request response
			msmData->EncodeData(msmData->PAGE_CALIBRATION, pucEventBuffer_);
			requestAckMsg(pucEventBuffer_);
			
			// update the display values
			this->labelCalibrationProgress->Text = "Calibration Request \nResponse Sent";
			this->labelCalibrationProgress->BackColor = System::Drawing::Color::Orange;		
		}
	}

	// update the display variables
	UpdateCalibDisplay();
}

/**************************************************************************
 * MSMSensor::UpdateCalibDisplay
 * 
 * Adjusts the current speed via the GUI
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::UpdateCalibDisplay()
{
	ULONG ulNewSpeed;

	// update the mode label
	if (msmData->IsCalModeValid(msmData->ucMode))
		this->labelCurrentMode->Text = msmData->ucMode.ToString();
	else
		this->labelCurrentMode->Text = "Mode Invalid";
		
	// update the scale factor label
	if (msmData->IsCalScaleValid(msmData->usScaleFactor10000))
		this->labelCurrentScaleFactor->Text = ((double) msmData->usScaleFactor10000 / msmData->CAL_SF_SCALE_FACTOR).ToString("N4");
	else
		this->labelCurrentScaleFactor->Text = "Scale Factor Invalid";

	if (eCalStatus != MSM::CalibrationStatus::COMPLETE)
	{
		// set the scale to change the speed and distance data that is now sent
		if (msmData->IsCalScaleValid(msmData->usScaleFactor10000))
		{
			// calc the new speed
			ulNewSpeed = System::Convert::ToUInt32(this->numericUpDownSpeed->Value * msmData->SPEED_SCALE_FACTOR * (System::Convert::ToDecimal(msmData->usScaleFactor10000) / msmData->CAL_SF_SCALE_FACTOR));

			// check if the new speed is within the range
			if (ulNewSpeed < msmData->INVALID_SPEED)
				msmData->usInstSpeed1000 = System::Convert::ToUInt16(this->numericUpDownSpeed->Value * msmData->SPEED_SCALE_FACTOR * (System::Convert::ToDecimal(msmData->usScaleFactor10000) / msmData->CAL_SF_SCALE_FACTOR));
			else
			{
				msmData->usInstSpeed1000 = msmData->MAX_SPEED;
				MessageBox::Show("Maximum speed reached. \nTx speed set to max speed: 65.534 m/s", "Speed Error", MessageBoxButtons::OK,MessageBoxIcon::Error, MessageBoxDefaultButton::Button1);
			}
		}
		else
			msmData->usInstSpeed1000 = System::Convert::ToUInt16(this->numericUpDownSpeed->Value * msmData->SPEED_SCALE_FACTOR);
	}

	// if process completed
	else if (eCalStatus == MSM::CalibrationStatus::COMPLETE)
	{
		this->labelCalibrationProgress->Text = "Calibration Process \nSuccessfully Completed";
		this->labelCalibrationProgress->BackColor = System::Drawing::SystemColors::Control;
	}
}

/**************************************************************************
 * MSMSensor::UpdateDisplayAckStatus
 * 
 * Adjusts the GUI depending on the ACK status of the tx message
 *
 * UCHAR status_:  gives the success or fail status of the tx message
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::UpdateDisplayAckStatus(UCHAR status_)
{
	switch (eCalStatus)
	{
	case MSM::CalibrationStatus::REQUEST_IN_PROGRESS:
		if (status_ == msmData->ACK_FAIL)
		{
			this->labelCalibrationProgress->Text = "Cal Request Response \nFailed to Transmit";
			this->labelCalibrationProgress->BackColor = System::Drawing::Color::Red;
		}

		if (status_ == msmData->ACK_SUCCESS)
		{
			this->labelCalibrationProgress->Text = "Cal Request Response \nTransmission Success";
			this->labelCalibrationProgress->BackColor = System::Drawing::Color::Green;
		}
		break;
		
	case MSM::CalibrationStatus::SET_SCALE_IN_PROGRESS:
		if (status_ == msmData->ACK_FAIL)
		{
			this->labelCalibrationProgress->Text = "Set Scale Response \nFailed to Transmit";
			this->labelCalibrationProgress->BackColor = System::Drawing::Color::Red;
		}

		if (status_ == msmData->ACK_SUCCESS)
		{
			this->labelCalibrationProgress->Text = "Set Scale Response \nTransmission Success";
			this->labelCalibrationProgress->BackColor = System::Drawing::Color::Green;
			
			// update the display to tell the user the calibration has successfully finished
			eCalStatus = MSM::CalibrationStatus::COMPLETE;
			UpdateCalibDisplay();
		}
		break;

	default:
		break;
	}
}

/**************************************************************************
 * MSMSensor::numericUpDownSpeed_ValueChanged
 * 
 * Adjusts the current speed via the GUI
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::numericUpDownSpeed_ValueChanged(System::Object^  sender, System::EventArgs^  e)
{
	ULONG ulNewSpeed;

	if (msmData->IsCalScaleValid(msmData->usScaleFactor10000))
	{
		// calc the new speed
		ulNewSpeed = System::Convert::ToUInt32(this->numericUpDownSpeed->Value * msmData->SPEED_SCALE_FACTOR * (System::Convert::ToDecimal(msmData->usScaleFactor10000) / msmData->CAL_SF_SCALE_FACTOR));

		// check if the new speed is within the range
		if (ulNewSpeed < msmData->INVALID_SPEED)
			msmData->usInstSpeed1000 = System::Convert::ToUInt16(this->numericUpDownSpeed->Value * msmData->SPEED_SCALE_FACTOR * (System::Convert::ToDecimal(msmData->usScaleFactor10000) / msmData->CAL_SF_SCALE_FACTOR));
		else
		{
			msmData->usInstSpeed1000 = msmData->MAX_SPEED;
			MessageBox::Show("Maximum speed reached. \nTx speed set to max speed: 65.534 m/s", "Speed Error", MessageBoxButtons::OK,MessageBoxIcon::Error, MessageBoxDefaultButton::Button1);
		}
	}
	else
		msmData->usInstSpeed1000 = System::Convert::ToUInt16(this->numericUpDownSpeed->Value * msmData->SPEED_SCALE_FACTOR);		
}

/**************************************************************************
 * MSMSensor::checkBoxTxSpeed_CheckedChanged
 * 
 * Adjusts the value if the speed data is invalid or not
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::checkBoxTxSpeed_CheckedChanged(System::Object^  sender, System::EventArgs^  e)
{
	if (this->checkBoxTxSpeed->Checked == FALSE)
		msmData->usInstSpeed1000 = msmData->INVALID_SPEED;
	else
		msmData->usInstSpeed1000 = System::Convert::ToUInt16(this->numericUpDownSpeed->Value * msmData->SPEED_SCALE_FACTOR);
}

/**************************************************************************
 * MSMSensor::checkBoxCalibrationSupport_CheckedChanged
 * 
 * Sets the calibration to be enabled or not
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::checkBoxCalibrationSupport_CheckedChanged(System::Object^  sender, System::EventArgs^  e)
{
	if (this->checkBoxCalibrationSupport->Checked == FALSE)
	{
		msmData->usScaleFactor10000 = msmData->CAL_SCALE_INVALID;
		msmData->ucMode = msmData->CAL_MODE_INVALID;

		this->checkBoxModeStorageSupport->Checked = FALSE;
		this->checkBoxModeStorageSupport->Enabled = FALSE;

		// make the speed not use a scale factor any longer
		msmData->usInstSpeed1000 = System::Convert::ToUInt16(this->numericUpDownSpeed->Value * msmData->SPEED_SCALE_FACTOR);
	}
	else
	{
		this->checkBoxModeStorageSupport->Enabled = TRUE;
	}
}

/**************************************************************************
 * MSMSensor::checkBoxModeStorageSupport_CheckedChanged
 * 
 * Sets the calibration to be enabled or not
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::checkBoxModeStorageSupport_CheckedChanged(System::Object^  sender, System::EventArgs^  e)
{
	if (this->checkBoxModeStorageSupport->Checked == FALSE)
		msmData->ucMode = msmData->CAL_MODE_INVALID;
}

/**************************************************************************
 * MSMSensor::checkBoxSecondaryData_CheckedChanged
 * 
 * Enables and sets the GPS parameters
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::checkBoxSecondaryData_CheckedChanged(System::Object^  sender, System::EventArgs^  e)
{
	if (this->checkBoxSecondaryData->Checked == TRUE)
	{
		this->numericUpDownLatitude->Enabled = TRUE;
		this->numericUpDownLongitude->Enabled = TRUE;
		this->checkBoxPage3Enable->Enabled = TRUE;

		msmData->slLatitude_SC = System::Convert::ToInt32(double (this->numericUpDownLatitude->Value) * msmData->SEMI_CIRCLE_CONVERSION);
		msmData->slLongitude_SC = System::Convert::ToInt32(double (this->numericUpDownLongitude->Value) * msmData->SEMI_CIRCLE_CONVERSION);
	}

	else
	{
		this->numericUpDownLatitude->Enabled = FALSE;
		this->numericUpDownLongitude->Enabled = FALSE;
		msmData->slLatitude_SC = 0;
		msmData->slLongitude_SC = 0;

		// also disable the page 3 stuff
		this->checkBoxPage3Enable->Enabled = FALSE;
		this->checkBoxPage3Enable->Checked = FALSE;
		this->groupBoxPage3->Enabled = FALSE;
	}
	UpdateTxPattern();
}

/**************************************************************************
 * MSMSensor::checkBoxPage3Enable_CheckedChanged
 * 
 * Sets the calibration to be enabled or not
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::checkBoxPage3Enable_CheckedChanged(System::Object^  sender, System::EventArgs^  e)
{
	if (this->checkBoxPage3Enable->Checked == TRUE)
	{
		this->groupBoxPage3->Enabled = TRUE;
		msmData->usElevation5 = System::Convert::ToUInt16((this->numericUpDownElevation->Value + msmData->ELEVATION_OFFSET) * msmData->ELEVATION_SCALE_FACTOR);
		msmData->usHeading10 = System::Convert::ToUInt16(this->numericUpDownHeading->Value * msmData->HEADING_SCALE_FACTOR);
		UpdateGpsFixType();
	}

	else
	{
		this->groupBoxPage3->Enabled = FALSE;
	}

	UpdateTxPattern();
}

/**************************************************************************
 * MSMSensor::UpdateGpsFixType
 * 
 * Updates the gps fix based on the settings in the combo box
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::UpdateGpsFixType()
{
	if (this->comboBoxGPSFix->SelectedItem->Equals("None"))
		msmData->ucFixType = System::Convert::ToByte(MSM::GpsFix::NONE);

	else if (this->comboBoxGPSFix->SelectedItem->Equals("Propagating"))
		msmData->ucFixType = System::Convert::ToByte(MSM::GpsFix::PROPAGATING);

	else if (this->comboBoxGPSFix->SelectedItem->Equals("Searching"))
		msmData->ucFixType = System::Convert::ToByte(MSM::GpsFix::SEARCHING);

   else if (this->comboBoxGPSFix->SelectedItem->Equals("Last Known"))
      msmData->ucFixType = System::Convert::ToByte(MSM::GpsFix::LAST_KNOWN);

	else if (this->comboBoxGPSFix->SelectedItem->Equals("3D"))
		msmData->ucFixType = System::Convert::ToByte(MSM::GpsFix::THREE_D);

	else if (this->comboBoxGPSFix->SelectedItem->Equals("3D WAAS"))
		msmData->ucFixType = System::Convert::ToByte(MSM::GpsFix::THREE_D_WAAS);

	else if (this->comboBoxGPSFix->SelectedItem->Equals("3D Differential"))
		msmData->ucFixType = System::Convert::ToByte(MSM::GpsFix::THREE_D_DIF);

	else if (this->comboBoxGPSFix->SelectedItem->Equals("2D"))
		msmData->ucFixType = System::Convert::ToByte(MSM::GpsFix::TWO_D);

	else if (this->comboBoxGPSFix->SelectedItem->Equals("2D WAAS"))
		msmData->ucFixType = System::Convert::ToByte(MSM::GpsFix::TWO_D_WAAS);

	else if (this->comboBoxGPSFix->SelectedItem->Equals("2D Differential"))
		msmData->ucFixType = System::Convert::ToByte(MSM::GpsFix::TWO_D_DIF);

	else if (this->comboBoxGPSFix->SelectedItem->Equals("Invalid"))
		msmData->ucFixType = System::Convert::ToByte(MSM::GpsFix::INVALID);
}

/**************************************************************************
 * MSMSensor::numericUpDownLongitude_ValueChanged
 * 
 * Adjusts the current longitude via the GUI
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::numericUpDownLongitude_ValueChanged(System::Object^  sender, System::EventArgs^  e)
{
	msmData->slLongitude_SC = System::Convert::ToInt32(double (this->numericUpDownLongitude->Value) * msmData->SEMI_CIRCLE_CONVERSION);
}

/**************************************************************************
 * MSMSensor::numericUpDownLatitude_ValueChanged
 * 
 * Adjusts the current latitude via the GUI
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::numericUpDownLatitude_ValueChanged(System::Object^  sender, System::EventArgs^  e)
{
	msmData->slLatitude_SC = System::Convert::ToInt32(double (this->numericUpDownLatitude->Value) * msmData->SEMI_CIRCLE_CONVERSION);
}

/**************************************************************************
 * MSMSensor::numericUpDownElevation_ValueChanged
 * 
 * Adjusts the current elevation via the GUI
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::numericUpDownElevation_ValueChanged(System::Object^  sender, System::EventArgs^  e)
{
	msmData->usElevation5 = System::Convert::ToUInt16((this->numericUpDownElevation->Value + msmData->ELEVATION_OFFSET) * msmData->ELEVATION_SCALE_FACTOR);
}

/**************************************************************************
 * MSMSensor::numericUpDownHeading_ValueChanged
 * 
 * Adjusts the current heading via the GUI
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::numericUpDownHeading_ValueChanged(System::Object^  sender, System::EventArgs^  e)
{
	msmData->usHeading10 = System::Convert::ToUInt16(this->numericUpDownHeading->Value * msmData->HEADING_SCALE_FACTOR);
}

/**************************************************************************
 * MSMSensor::comboBoxGPSFix_SelectedIndexChanged
 * 
 * Adjusts the current heading via the GUI
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::comboBoxGPSFix_SelectedIndexChanged(System::Object^  sender, System::EventArgs^  e)
{
	UpdateGpsFixType();
}

/**************************************************************************
 * MSMSensor::button_Glb_GlobalDataUpdate_Click
 * 
 * Adjusts the global pages 80 and 81 via the GUI
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::button_Glb_GlobalDataUpdate_Click(System::Object^  sender, System::EventArgs^  e)
{
	this->label_Error->Visible = false;
	// required common pages
	try{
		if(this->checkBox_InvalidSerial->Checked)
			commonData->ulSerialNum = 0xFFFFFFFF;
		else
			commonData->ulSerialNum = System::Convert::ToUInt32(this->textBox_Glb_SerialNumChange->Text);
		commonData->ucSwVersion = System::Convert::ToByte(this->textBox_Glb_SoftwareVerChange->Text);
		commonData->ucHwVersion = System::Convert::ToByte(this->textBox_Glb_HardwareVerChange->Text);
		commonData->usMfgID = System::Convert::ToUInt16(this->textBox_Glb_ManfIDChange->Text);
		commonData->usModelNum = System::Convert::ToUInt16(this->textBox_Glb_ModelNumChange->Text);
	}
	catch(...)
	{
		label_Error->Visible = true;
	}
}


/**************************************************************************
 * MSMSensor::checkBox_Bat_Status_CheckedChanged
 * 
 * Enables and disables the battery voltage GUI elements
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::checkBox_Bat_Status_CheckedChanged(System::Object^  sender, System::EventArgs^  e)
{
	if (this->checkBox_Bat_Status->Checked == TRUE)
	{
		commonData->bBattPageEnabled = TRUE;
		this->comboBoxBatStatus->Enabled = TRUE;
		this->label_Bat_ElpTimeDisplay->Enabled = TRUE;
		this->textBox_Bat_ElpTimeChange->Enabled = TRUE;
		this->checkBox_Bat_Voltage->Enabled = TRUE;
		this->checkBox_Bat_Voltage->Checked = TRUE;
		this->numericUpDown_Bat_VoltFrac->Enabled = TRUE;
		this->numericUpDown_Bat_VoltInt->Enabled = TRUE;
		this->groupBox_Resol->Enabled = TRUE;
		this->button_Bat_ElpTimeUpdate->Enabled = TRUE;

		// update the variables
		UpdateBatStatus();
		if (this->radioButton_Bat_Elp16Units->Checked)
			commonData->eTimeResolution = CommonData::TimeResolution::SIXTEEN;
		
		if (this->radioButton_Bat_Elp2Units->Checked)
			commonData->eTimeResolution = CommonData::TimeResolution::TWO;

		commonData->ulOpTime = ulTotalTime / (UCHAR) commonData->eTimeResolution;
		commonData->usBatVoltage256 = ((USHORT) System::Convert::ToByte(this->numericUpDown_Bat_VoltInt->Value)) << 8 | (commonData->usBatVoltage256 & 0xFF);	// Integer portion in high byte
		commonData->usBatVoltage256 = System::Convert::ToByte(this->numericUpDown_Bat_VoltFrac->Value) | (commonData->usBatVoltage256 & 0xFF00);	// Fractional portion in low byte
	}

	else
	{
		commonData->bBattPageEnabled = FALSE;
		this->comboBoxBatStatus->Enabled = FALSE;
		this->label_Bat_ElpTimeDisplay->Enabled = FALSE;
		this->textBox_Bat_ElpTimeChange->Enabled = FALSE;
		this->checkBox_Bat_Voltage->Enabled = FALSE;
		this->checkBox_Bat_Voltage->Checked = FALSE;
		this->numericUpDown_Bat_VoltFrac->Enabled = FALSE;
		this->numericUpDown_Bat_VoltInt->Enabled = FALSE;
		this->groupBox_Resol->Enabled = FALSE;
		this->button_Bat_ElpTimeUpdate->Enabled = FALSE;
	}

	UpdateCommonPattern();
}

/**************************************************************************
 * MSMSensor::checkBox_Bat_Voltage_CheckedChanged
 * 
 * Enables and disables the battery voltage numeric upDown
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::checkBox_Bat_Voltage_CheckedChanged(System::Object^  sender, System::EventArgs^  e)
{
	if (this->checkBox_Bat_Voltage->Checked == TRUE)
	{
		this->numericUpDown_Bat_VoltInt->Enabled = TRUE;
		this->numericUpDown_Bat_VoltFrac->Enabled = TRUE;
		commonData->usBatVoltage256 = ((USHORT) System::Convert::ToByte(this->numericUpDown_Bat_VoltInt->Value)) << 8 | (commonData->usBatVoltage256 & 0xFF);	// Integer portion in high byte
		commonData->usBatVoltage256 = System::Convert::ToByte(this->numericUpDown_Bat_VoltFrac->Value) | (commonData->usBatVoltage256 & 0xFF00);	// Fractional portion in low byte
	}
	else
	{
		this->numericUpDown_Bat_VoltInt->Enabled = FALSE;
		this->numericUpDown_Bat_VoltFrac->Enabled = FALSE;
		commonData->usBatVoltage256 = commonData->BATTERY_VOLTAGE_INVALID;
	}
}

/**************************************************************************
 * MSMSensor::numericUpDown_Bat_VoltInt_ValueChanged
 * 
 * changes the coarse battery voltage
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::numericUpDown_Bat_VoltInt_ValueChanged(System::Object^  sender, System::EventArgs^  e)
{
	commonData->usBatVoltage256 = ((USHORT) System::Convert::ToByte(this->numericUpDown_Bat_VoltInt->Value)) << 8 | (commonData->usBatVoltage256 & 0xFF);	// Integer portion in high byte
}

/**************************************************************************
 * MSMSensor::numericUpDown_Bat_VoltFrac_ValueChanged
 * 
 * changes the fractional battery voltage
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::numericUpDown_Bat_VoltFrac_ValueChanged(System::Object^  sender, System::EventArgs^  e)
{
	commonData->usBatVoltage256 = System::Convert::ToByte(this->numericUpDown_Bat_VoltFrac->Value) | (commonData->usBatVoltage256 & 0xFF00);	// Fractional portion in low byte
}

/**************************************************************************
 * MSMSensor::comboBoxBatStatus_SelectedIndexChanged
 * 
 * changes the battery status
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::comboBoxBatStatus_SelectedIndexChanged(System::Object^  sender, System::EventArgs^  e)
{
	UpdateBatStatus();
}

/**************************************************************************
 * MSMSensor::UpdateBatStatus
 * 
 * Updates the battery status
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::UpdateBatStatus()
{
	if (this->comboBoxBatStatus->Text->Equals("New"))
	{
		commonData->eBatStatus = CommonData::BatStatus::NEW;
	}

	else if (this->comboBoxBatStatus->Text->Equals("Good"))
	{
		commonData->eBatStatus = CommonData::BatStatus::GOOD;
	}

	else if (this->comboBoxBatStatus->Text->Equals("Ok"))
	{
		commonData->eBatStatus = CommonData::BatStatus::OK;
	}

	else if (this->comboBoxBatStatus->Text->Equals("Low"))
	{
		commonData->eBatStatus = CommonData::BatStatus::LOW;
	}

	else if (this->comboBoxBatStatus->Text->Equals("Critical"))
	{
		commonData->eBatStatus = CommonData::BatStatus::CRITICAL;
	}

	else if (this->comboBoxBatStatus->Text->Equals("Invalid"))
	{
		commonData->eBatStatus = CommonData::BatStatus::INVALID;
	}
}

/**************************************************************************
 * MSMSensor::button_Bat_ElpTimeUpdate_Click
 * 
 * changes the elapsed battery time
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::button_Bat_ElpTimeUpdate_Click(System::Object^  sender, System::EventArgs^  e)
{
	ulTotalTime = System::Convert::ToUInt32(this->textBox_Bat_ElpTimeChange->Text);
	commonData->ulOpTime = ulTotalTime / (UCHAR) commonData->eTimeResolution;
}

/**************************************************************************
 * MSMSensor::radioButton_Bat_Elp2Units_CheckedChanged
 * 
 * changes the elapsed battery time units
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::radioButton_Bat_Elp2Units_CheckedChanged(System::Object^  sender, System::EventArgs^  e)
{
	if (this->radioButton_Bat_Elp2Units->Checked)
		commonData->eTimeResolution = CommonData::TimeResolution::TWO;
}

/**************************************************************************
 * MSMSensor::radioButton_Bat_Elp2Units_CheckedChanged
 * 
 * changes the elapsed battery time units
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::radioButton_Bat_Elp16Units_CheckedChanged(System::Object^  sender, System::EventArgs^  e)
{
	if (this->radioButton_Bat_Elp16Units->Checked)
		commonData->eTimeResolution = CommonData::TimeResolution::SIXTEEN;
}

/**************************************************************************
 * MSMSensor::checkBoxEnableDateTime_CheckedChanged
 * 
 * Checks if the date and time page should be enabled and sent
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::checkBoxEnableDateTime_CheckedChanged(System::Object^  sender, System::EventArgs^  e)
{
	if (this->checkBoxEnableDateTime->Checked == TRUE)
	{
		// enable all of the parameters
		commonData->bTimePageEnabled = TRUE;
		this->panelTimeDate->Enabled = TRUE;
	}
	else
	{
		commonData->bTimePageEnabled = FALSE;
		this->panelTimeDate->Enabled = FALSE;
	}
	UpdateCommonPattern();

}

/**************************************************************************
 * MSMSensor::comboBoxDayOfWeek_SelectedIndexChanged
 * 
 * Sets the day of the week
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::comboBoxDayOfWeek_SelectedIndexChanged(System::Object^  sender, System::EventArgs^  e)
{
	UpdateDayOfWeek();
}

/**************************************************************************
 * MSMSensor::UpdateDayOfWeek
 * 
 * Sets the day of the week
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::UpdateDayOfWeek()
{
	if (this->comboBoxDayOfWeek->Text->Equals("Monday"))
		commonData->eDayOfWeek = CommonData::DayOfWeek::MONDAY;
	else if (this->comboBoxDayOfWeek->Text->Equals("Tuesday"))
		commonData->eDayOfWeek = CommonData::DayOfWeek::TUESDAY;
	else if (this->comboBoxDayOfWeek->Text->Equals("Wednesday"))
		commonData->eDayOfWeek = CommonData::DayOfWeek::WEDNESDAY;
	else if (this->comboBoxDayOfWeek->Text->Equals("Thursday"))
		commonData->eDayOfWeek = CommonData::DayOfWeek::THURSDAY;
	else if (this->comboBoxDayOfWeek->Text->Equals("Friday"))
		commonData->eDayOfWeek = CommonData::DayOfWeek::FRIDAY;
	else if (this->comboBoxDayOfWeek->Text->Equals("Saturday"))
		commonData->eDayOfWeek = CommonData::DayOfWeek::SATURDAY;
	else if (this->comboBoxDayOfWeek->Text->Equals("Sunday"))
		commonData->eDayOfWeek = CommonData::DayOfWeek::SUNDAY;
	else if (this->comboBoxDayOfWeek->Text->Equals("Invalid"))
		commonData->eDayOfWeek = CommonData::DayOfWeek::INVALID;
}

/**************************************************************************
 * MSMSensor::numericUpDownDay_ValueChanged
 * 
 * Sets value of the day
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::numericUpDownDay_ValueChanged(System::Object^  sender, System::EventArgs^  e)
{
	commonData->ucDays = System::Convert::ToByte(this->numericUpDownDay->Value);
}

/**************************************************************************
 * MSMSensor::numericUpDownMonth_ValueChanged
 * 
 * Sets value of the month
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::numericUpDownMonth_ValueChanged(System::Object^  sender, System::EventArgs^  e)
{
	commonData->ucMonth = System::Convert::ToByte(this->numericUpDownMonth->Value);
}

/**************************************************************************
 * MSMSensor::numericUpDownYear_ValueChanged
 * 
 * Sets value of the year
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::numericUpDownYear_ValueChanged(System::Object^  sender, System::EventArgs^  e)
{
	commonData->ucYears = System::Convert::ToByte(this->numericUpDownYear->Value);
}

/**************************************************************************
 * MSMSensor::numericUpDownHours_ValueChanged
 * 
 * Sets value of the hours
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::numericUpDownHours_ValueChanged(System::Object^  sender, System::EventArgs^  e)
{
	commonData->ucHours = System::Convert::ToByte(this->numericUpDownHours->Value);
}

/**************************************************************************
 * MSMSensor::numericUpDownMinutes_ValueChanged
 * 
 * Sets value of the minutes
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::numericUpDownMinutes_ValueChanged(System::Object^  sender, System::EventArgs^  e)
{
	commonData->ucMinutes = System::Convert::ToByte(this->numericUpDownMinutes->Value);
}

/**************************************************************************
 * MSMSensor::numericUpDownSeconds_ValueChanged
 * 
 * Sets value of the minutes
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::numericUpDownSeconds_ValueChanged(System::Object^  sender, System::EventArgs^  e)
{
	commonData->ucSeconds = System::Convert::ToByte(this->numericUpDownSeconds->Value);
}

/**************************************************************************
 * MSMSensor::UpdateTxPattern
 * 
 * Updates the transmission timing pattern according to what pages are
 * enabled. 
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::UpdateTxPattern()
{
	if(this->checkBoxSecondaryData->Checked)
	{
		if(this->checkBoxPage3Enable->Checked)
		{
				//Data pages 1, 2, and 3 enabled
				ucTxPattern.Clear();
				ucTxPattern.Add(msmData->PAGE_1);
				ucTxPattern.Add(msmData->PAGE_1);
				ucTxPattern.Add(msmData->PAGE_2);
				ucTxPattern.Add(msmData->PAGE_3);
				ucTxPattern.Add(msmData->PAGE_1);
				ucTxPattern.Add(msmData->PAGE_1);
				ucTxPattern.Add(msmData->PAGE_3);
				ucTxPattern.Add(msmData->PAGE_2);
				ucTxPattern.TrimExcess();
		}
		else
		{
				//Data pages 1 and 2 enabled
				ucTxPattern.Clear();
				ucTxPattern.Add(msmData->PAGE_1);
				ucTxPattern.Add(msmData->PAGE_1);
				ucTxPattern.Add(msmData->PAGE_2);
				ucTxPattern.Add(msmData->PAGE_2);
				ucTxPattern.TrimExcess();
		}
	}
	else
	{
		if(this->checkBoxPage3Enable->Checked)
		{
				//Data pages 1 and 3 enabled
				ucTxPattern.Clear();
				ucTxPattern.Add(msmData->PAGE_1);
				ucTxPattern.Add(msmData->PAGE_1);
				ucTxPattern.Add(msmData->PAGE_3);
				ucTxPattern.Add(msmData->PAGE_3);
				ucTxPattern.TrimExcess();
		}
		else
		{
				//Only data page 1 enabled
				ucTxPattern.Clear();
				ucTxPattern.Add(msmData->PAGE_1);
				ucTxPattern.TrimExcess();
		}
	}
}
/**************************************************************************
 * MSMSensor::UpdateCommonPattern
 * 
 * Updates the common pages pattern depending on which common pages
 * are supported. 
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::UpdateCommonPattern()
{
	switch(this->checkBox_Bat_Status->Checked)
	{
	case TRUE:
		switch(this->checkBoxEnableDateTime->Checked)
		{
		case TRUE:
			//All common pages enabled
			ucCommonPattern.Clear();
			ucCommonPattern.Add(commonData->PAGE80);
			ucCommonPattern.Add(commonData->PAGE80);
			ucCommonPattern.Add(commonData->PAGE81);
			ucCommonPattern.Add(commonData->PAGE81);
			ucCommonPattern.Add(commonData->PAGE82);
			ucCommonPattern.Add(commonData->PAGE82);
			ucCommonPattern.Add(commonData->PAGE83);
			ucCommonPattern.Add(commonData->PAGE83);
			ucCommonPattern.TrimExcess();
			break;
		default:
			//Common Time NOT enabled
			ucCommonPattern.Clear();
			ucCommonPattern.Add(commonData->PAGE80);
			ucCommonPattern.Add(commonData->PAGE80);
			ucCommonPattern.Add(commonData->PAGE81);
			ucCommonPattern.Add(commonData->PAGE81);
			ucCommonPattern.Add(commonData->PAGE82);
			ucCommonPattern.Add(commonData->PAGE82);
			ucCommonPattern.TrimExcess();
			break;
		}
		break;
	default:
		switch(this->checkBoxEnableDateTime->Checked)
		{
		case TRUE:
			//Common Battery page NOT enabled
			ucCommonPattern.Clear();
			ucCommonPattern.Add(commonData->PAGE80);
			ucCommonPattern.Add(commonData->PAGE80);
			ucCommonPattern.Add(commonData->PAGE81);
			ucCommonPattern.Add(commonData->PAGE81);
			ucCommonPattern.Add(commonData->PAGE83);
			ucCommonPattern.Add(commonData->PAGE83);
			ucCommonPattern.TrimExcess();
			break;
		default:
			//No additional common pages enabled
			ucCommonPattern.Clear();
			ucCommonPattern.Add(commonData->PAGE80);
			ucCommonPattern.Add(commonData->PAGE80);
			ucCommonPattern.Add(commonData->PAGE81);
			ucCommonPattern.Add(commonData->PAGE81);
			ucCommonPattern.TrimExcess();
			break;
		}
		break;
	}
}
/**************************************************************************
 * MSMSensor::checkBox_InvalidSerial_CheckedChanged
 * 
 * Handles the CheckedChanged event for the checkBox
 *
 * returns: N/A
 *
 **************************************************************************/
void MSMSensor::checkBox_InvalidSerial_CheckedChanged(System::Object ^sender, System::EventArgs ^e)
{
	if(this->checkBox_InvalidSerial->Checked)
		this->textBox_Glb_SerialNumChange->Enabled = false;
	else
		this->textBox_Glb_SerialNumChange->Enabled = true;
}