//---------------------------------------------------------------------------

#pragma hdrstop

#include "commFTDI.h"
#include "ftd2xx.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)


/****************************************************************************
 * Trida CommFTDI zajistuje Rx a TX na FTDI chip.
 * Je implementovano odesilani a prijimani na CAN protokolem ASCI ADAM.
 ****************************************************************************/


static const UCHAR dechex[] =
   {'0', '1', '2', '3', '4', '5', '6', '7',
	'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

//---------------------------------------------------------------------------
TCommFTDI::TCommFTDI()
{
	isFTDIOpen = 0;
	RxBufferStartIdx = 0;
	RxBufferEndIdx = 0;
}
//---------------------------------------------------------------------------

BOOL TCommFTDI::CommFTDIIsOpen(void)
{
  	return isFTDIOpen;
}
//---------------------------------------------------------------------------
FT_STATUS TCommFTDI::CommFTDICheckStatus(void)
{

	DWORD numDevs;

	FT_CreateDeviceInfoList(&numDevs);

	return numDevs;

}

//---------------------------------------------------------------------------
FT_STATUS TCommFTDI::CommFTDIOpen(unsigned int ftBaudRate)
{
  FT_STATUS ftStatus;
  DWORD numDevs;


  // create the device information list

  ftStatus = FT_CreateDeviceInfoList(&numDevs);

  if (numDevs == 0) {
  	  isFTDIOpen = 0;
	  return ftStatus;
  }

  if (isFTDIOpen) {
  	return 100;    				// FTDI already opened
  }

  ftStatus = FT_Open(0,&ftHandle);

  if (ftStatus != FT_OK) {
	  return ftStatus;
  }

  ftStatus = FT_SetBaudRate(ftHandle, ftBaudRate); // Set baud rate to user-defined
  if (ftStatus != FT_OK) {
	  return ftStatus;
  }

  ftStatus = FT_SetDataCharacteristics(ftHandle, FT_BITS_8, FT_STOP_BITS_1, FT_PARITY_NONE);
  if (ftStatus != FT_OK) {
	  return ftStatus;
  }

  isFTDIOpen = 1;

  return ftStatus;
}
//---------------------------------------------------------------------------

FT_STATUS TCommFTDI::CommFTDIClose(void)
{
  FT_STATUS ftStatus;
  DWORD numDevs;

  ftStatus = FT_CreateDeviceInfoList(&numDevs);
  if ((ftStatus != FT_OK) || (numDevs == 0)) {
	isFTDIOpen = 0;
	return ftStatus;
  }

  if (!isFTDIOpen) {
	return 101;        			// FTDI already closed
  }

  ftStatus = FT_Close(ftHandle);
  if (ftStatus != FT_OK) {
	  return ftStatus;
  }

  isFTDIOpen = 0;

  return ftStatus;
}
//---------------------------------------------------------------------------

FT_STATUS TCommFTDI::SetDTR(void)
{
	FT_STATUS ftStatus;

	if (!isFTDIOpen) {
		return false;        	// FTDI not opened
	}

	ftStatus = FT_SetDtr(ftHandle);

	return ftStatus;
}
//---------------------------------------------------------------------------

FT_STATUS TCommFTDI::ClrDTR(void)
{
	FT_STATUS ftStatus;

	if (!isFTDIOpen) {
		return false;        	// FTDI not opened
	}

	ftStatus = FT_ClrDtr(ftHandle);

	return ftStatus;
}
//---------------------------------------------------------------------------

FT_STATUS TCommFTDI::SetRTS(void)
{
	FT_STATUS ftStatus;

	if (!isFTDIOpen) {
		return false;        	// FTDI not opened
	}

	ftStatus = FT_SetRts(ftHandle);

	return ftStatus;
}
//---------------------------------------------------------------------------

FT_STATUS TCommFTDI::ClrRTS(void)
{
	FT_STATUS ftStatus;

	if (!isFTDIOpen) {
		return false;        	// FTDI not opened
	}

	ftStatus = FT_ClrRts(ftHandle);

	return ftStatus;
}
//---------------------------------------------------------------------------

BOOL TCommFTDI::CommFTDIReceiveCANPacket(CAN_MSG *can_message)
{
	FT_STATUS ftStatus;
	DWORD EventDWord;
	DWORD TxBytes;
	DWORD RxBytes;
	DWORD BytesReceived;

	char FTDIRxBuffer[256];

	DWORD i;


	if (!isFTDIOpen) {
		return false;
	}

	FT_GetStatus(ftHandle,&RxBytes,&TxBytes,&EventDWord);

	if (RxBytes > 0) {

		ftStatus = FT_Read(ftHandle,FTDIRxBuffer,RxBytes,&BytesReceived);

		if (ftStatus == FT_OK) {
			for (i = 0; i < BytesReceived; i++) {
				RxBuffer[RxBufferEndIdx++] = FTDIRxBuffer[i];
				if (RxBufferEndIdx > RX_BUFF_LEN) RxBufferEndIdx = 0;
			}
		}

	}

	return CommFTDIRecMessage(can_message);			// kontrola RxBuffer zda neprisel CAN ramec

}
//---------------------------------------------------------------------------

BOOL TCommFTDI::CommFTDISendCANPacket(CAN_MSG *can_messsage)
{
  FT_STATUS ftStatus;
  DWORD BytesWritten;
  char TxBuffer[32]; // Contains data to write to device
  int i = 0, idCorr = 0, buffEnd = 0;

  if (!isFTDIOpen) {
	return false;
  }

  TxBuffer[buffEnd++] = dechex[(can_messsage->msg_len)/16];
  TxBuffer[buffEnd++] = dechex[(can_messsage->msg_len)%16];

  idCorr = can_messsage->can_id * 32;

  TxBuffer[buffEnd++] = dechex[(idCorr/256)/16];
  TxBuffer[buffEnd++] = dechex[(idCorr/256)%16];
  TxBuffer[buffEnd++] = dechex[(idCorr%256)/16];
  TxBuffer[buffEnd++] = dechex[(idCorr%256)%16];

  for(i = 0; i < can_messsage->msg_len; i++){
	TxBuffer[buffEnd++] = dechex[(can_messsage->msg_data[i])/16];
	TxBuffer[buffEnd++] = dechex[(can_messsage->msg_data[i])%16];
  }

  TxBuffer[buffEnd++] = 0x0d;

  ftStatus = FT_Write(ftHandle, TxBuffer, buffEnd, &BytesWritten);

  if (ftStatus == FT_OK) return TRUE;
   else return FALSE;


}
//---------------------------------------------------------------------------
BOOL TCommFTDI::CommFTDISendAeroPacket(unsigned short can_id, unsigned char len, CANAERO_MSG *canaero_msg)
{
	CAN_MSG *can_message;
	BOOL result = false;

	can_message = new CAN_MSG;

	can_message->can_id = can_id;
	can_message->msg_len = len;

	can_message->msg_data[0] = canaero_msg->node_id;
	can_message->msg_data[1] = canaero_msg->data_type;
	can_message->msg_data[2] = canaero_msg->service_code;
	can_message->msg_data[3] = canaero_msg->message_code;

	can_message->msg_data[4] = canaero_msg->message_data.char4_val[0];
	can_message->msg_data[5] = canaero_msg->message_data.char4_val[1];
	can_message->msg_data[6] = canaero_msg->message_data.char4_val[2];
	can_message->msg_data[7] = canaero_msg->message_data.char4_val[3];

	result = CommFTDISendCANPacket(can_message);

	delete can_message;

	return result;
}




/* ****************************************************************
 * Popis:
 *    Prevede blok hexadecimalnich dat na pole jednobajtovych hodnot
 * Predavane parametry:
 *    s - ukazatel na pole nactenych znaku, ktere se maji prevest.
 *    d - pole, do ktereho se ukladaji prevedene hodnoty.
 *    len - velikost pole s.
 * Navratova hodnota:
 *    TRUE - pokud prevod probehl v poradku,
 *    FALSE - pokud se pri prevodu vyskytla chyba (spatny znak)
 * **************************************************************** */
BOOL TCommFTDI::CommFTDIConvertData(unsigned char *s, unsigned char *d, unsigned short len)
{
   UCHAR v1, v2, i;

   for (i = 0; i<len/2; i++, d++) {
	  if (CommFTDIhexdec(*s, &v1) == FALSE)
         return(FALSE);
      s++;
	  if (CommFTDIhexdec(*s, &v2) == FALSE)
         return(FALSE);
      s++;
      *d = (v1<<4)+v2;
   }
   return(TRUE);
}


/* *******************************************************************
 * Prevede hexadecimalni cifru na decimalni
 * c - hexadecimalni cifra, d - prevedena cifra na decimalni cislo
 * Vrati NOERROR, pokud prevod probehl v poradku, jinak vrati  ERROR.
 * ******************************************************************* */
BOOL TCommFTDI::CommFTDIhexdec(unsigned char c, unsigned char *d)
{
   if (c < '0')
      return(FALSE);
   if (c <= '9') {
      *d = c - '0';
       return(TRUE);
   } else {
       if (c < 'A')
	  return(FALSE);
       if (c > 'F')
	  return(FALSE);
   }
   *d = c - 'A' + 10;
   return(TRUE);
}



/* ****************************************************************
 * Popis:
 *    Prijme jeden ramec dat. Pokud neni ramec
 *    kompletni, je ulozen stav, v jakem je ramec prijat.
 *    Po prijmuti kompletni zpravy je vyvolano zpracovani zpravy.
 * **************************************************************** */
BOOL TCommFTDI::CommFTDIRecMessage(CAN_MSG *can_message)
{
   static unsigned char idx = 0;    /* Aktualni index v bufferu */
   unsigned char c;
   BOOL result = 0;

   for(;;) {
	  c = CommFTDISerialGetBuffer();
      if ((CHAR)c == EOF)
		 return false;
      if (c == END_OF_MESSAGE)
		 break;
      if (idx >= MSG_BUF_LEN)
		 idx = MSG_BUF_LEN -1;
      msg_buf[idx] = c;
      idx++;
   }
   /* Otestovat min. delku zpravy:        */
   /* 2 bajty  - delka ramce              */
   /* 4 bajty - ID zpravy                 */
   /* x bajtu - data                      */
   /* 1 bajt  - CR						  */
   /* ---------------------------------   */
   /* 7 bajtu min. delka zpravy           */

   if (idx >= 7){
	 result = CommFTDIProcessMessage(idx, can_message);
   }

   idx = 0;

   return result;

}

/******************************************************************************
 * Vycte znak ze vstupniho SW bufferu
 * Pokud je buffer prazdny, vraci EOF
 ******************************************************************************/
unsigned char TCommFTDI::CommFTDISerialGetBuffer(void)
{
   unsigned char val;

   if (RxBufferStartIdx == RxBufferEndIdx) {
	  val = EOF; /* Buffer je prazdny */
   } else {
	  val = RxBuffer[RxBufferStartIdx];
	  RxBufferStartIdx++;
	  if (RxBufferStartIdx >= RX_BUFF_LEN)
		 RxBufferStartIdx = 0;
   }
   return(val);
}


/* ****************************************************************
 * Popis:
 *    Zpracuje prichozi zpravu. Je zjistena delka ramce a ID ramce.
 *	  Ramec je pak predan ke zpracovani dle protokolu CANAerospace.
 * Predavane parametry:
 *    len - delka zpravy
 * **************************************************************** */
BOOL TCommFTDI::CommFTDIProcessMessage(unsigned char msg_len, CAN_MSG *can_message)
{
   BOOL status;
   unsigned char frame_len;     /* Delka ramce */
   unsigned short frame_id;     /* ID ramce */
   char frame_buf[8];  			/* Buffer ramce */

   status = CommFTDIProcessHeader(&frame_id, &frame_len);
   if ((msg_len - 6) != (frame_len * 2))
	  return false;
   status &= CommFTDIConvertData(msg_buf+6, frame_buf, frame_len*2);

   if (status == TRUE){		// platny CAN ramec

	 can_message->can_id = frame_id;
	 can_message->msg_len = frame_len;

	 for( int i=0; i < frame_len; i++){
	   can_message->msg_data[i] = frame_buf[i];
	 }

	 return true;

   }

   return false;
}


/* ****************************************************************
 * Popis:
 *    Zpracuje hlavicku zpravy. Urci ID ramce a delku ramce.
 * Navratova hodnota:
 *    TRUE - hlavicka je O.K.
 *    FALSE - hlavicka je porusena.
 * **************************************************************** */
BOOL TCommFTDI::CommFTDIProcessHeader(unsigned short *id, unsigned char *len)
{
  BOOL status;
  UCHAR n[2];

  status = CommFTDIhexdec(*(msg_buf+1), len);
  status  &= CommFTDIConvertData(msg_buf+2, n, 4);
  *id = (n[0]*256 + n[1]) / 32;
  if (*len > 8)
     return(FALSE);
  return(status);
}
