/*******************************************************************************
********************************************************************************
**
**  Copyright(c) 2022, Alliance for Automotive Innovation
**  Used only under license from the Alliance for Automotive Innovation. All Rights Reserved.
**
**  Project:  J1699-5
**  FileName: VehicleReport.c
**  Author:   EnGenius
**  Date:     2/25/2022
**  Email:    <support@autosinnovate.org>
**
**  Purpose:  SAE J1699-5 Vehicle OBD II Compliance Test Cases Source Code.
**            This source code is intended to run the tests described in
**            the SAE J1699-5 document in an automated manner, when compiled
**            and used with an SAE J2534-compatible pass-thru device.
**
**            File j1699.c contains information on building and running this test.
**
**  Description:
**
**  Modifications:  03/13/2023  Initial Version
**
********************************************************************************
*******************************************************************************/

#include <stdio.h>    // C Library input and output declarations
#include <string.h>   // C Library character array declarations
#include <windows.h>  // Windows API declarations
#include "j2534.h"    // j1699 project j2534 declarations
#include "j1699.h"    // j1699 project general declarations


// Function Prototypes
STATUS ReadECUNAME    ( void );
STATUS ReadVIN        ( void );
STATUS ReadCALIDs     ( void );
STATUS ReadCVNs       ( void );
STATUS ReadOBDSUP     ( void );
STATUS ReadFuelType   ( void );
STATUS ReadHybridType ( void );

extern void   LogVersionInformation ( SCREENOUTPUT bDisplay, LOGOUTPUT bLog );
extern STATUS AppendLogFile     ( void );
extern STATUS VerifyPid1C       ( PID *pPid,
                                  unsigned long SIDIdx,
                                  BYTE EcuIdx );
extern STATUS VerifyECUNameData ( BYTE EcuIdx );
extern STATUS VerifyVINData     ( BYTE EcuIdx );
extern STATUS VerifyCALIDData   ( BYTE EcuIdx );
extern STATUS VerifyCVNData     ( BYTE EcuIdx );
extern STATUS VerifyINF10Data   ( BYTE EcuIdx );
extern STATUS VerifyINF12Data   ( BYTE EcuIdx );
extern STATUS VerifyINF13Data   ( BYTE EcuIdx );
extern STATUS VerifyINF1BData   ( BYTE EcuIdx );
extern STATUS VerifyINF1CData   ( BYTE EcuIdx );


extern const char* szFUEL_TYPE[];


/*******************************************************************************
**
**  Function:  VehicleReport
**
**  Purpose:
**
*******************************************************************************/
STATUS VehicleReport ( void )
{
	STATUS  eRetCode = PASS;


	// Create the log file
	gLogFileHandle = NULL;
	sprintf_s ( gLogFileNameString, MAX_LOGFILENAME, "Vehicle Report.log" );

	// Open the log file
	if ( fopen_s ( &gLogFileHandle, gLogFileNameString, "w+" ) != 0 )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
		      "Cannot open log file %s\n",
		      gLogFileNameString );
		StopTest ( FAIL, geTestPhase );
		exit (FAIL);
	}

	// Application version, build date, OS, etc
	LogVersionInformation ( SCREENOUTPUTON, LOGOUTPUTON );

	// Copy temp log file to actual log file
	if ( AppendLogFile ( ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
		      "Unable to copy temp log file to %s\n",
		      gLogFileNameString );
		StopTest ( FAIL, geTestPhase );
		exit (FAIL);
	}


	Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
	      "Turn key ON with engine OFF. Do not crank engine." );


	// Determine Protocol
	gTestSubsection = 1;
	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "Vehicle Report: Determine Protocol" );
	if ( DetermineOBDProtocol ( ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Protocol determination unsuccessful.\n" );
		Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
		return FAIL;
	}


	// Request INF support data
	if ( RequestIDSupportData ( INFREQUEST, FALSE ) != PASS )
	{
		return FAIL;
	}

	// Request PID support data
	if ( RequestIDSupportData ( PIDREQUEST, FALSE) != PASS )
	{
		return FAIL;
	}

	// Request PID support data
	if ( RequestIDSupportData ( PF5REQUEST, FALSE) != PASS )
	{
		return FAIL;
	}


	// get ECUNAME
	Log ( BLANK, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "\n\n" );
	Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "Read ECUNAME(s)\n" );
	if ( ReadECUNAME ( ) != PASS )
	{
		eRetCode = FAIL;
	}
	Log ( BLANK, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "\n\n" );


	// get VIN
	Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "Read VIN\n" );
	if ( ReadVIN ( ) != PASS )
	{
		eRetCode = FAIL;
	}
	Log ( BLANK, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "\n\n" );


	// get CALID
	Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "Read CALID(s)\n" );
	if ( ReadCALIDs ( ) != PASS )
	{
		eRetCode = FAIL;
	}
	Log ( BLANK, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "\n\n" );


	// get CVNs
	Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "Read CVN(s)\n" );
	if ( ReadCVNs ( ) != PASS )
	{
		eRetCode = FAIL;
	}
	Log ( BLANK, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "\n\n" );


	// get OBDSUP
	Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "Read OBD Compliance\n" );
	if ( ReadOBDSUP ( ) != PASS )
	{
		eRetCode = FAIL;
	}
	Log ( BLANK, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "\n\n" );


	// get FUEL TYPE
	Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	     "Read Fuel_TYP\n" );
	if ( ReadFuelType ( ) != PASS )
	{
		eRetCode = FAIL;
	}
	Log ( BLANK, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "\n\n" );


	// get Hybrid INFormation
	Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	     "Read Hybrid INFormation\n" );
	if ( ReadHybridType ( ) != PASS )
	{
		eRetCode = FAIL;
	}


	Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
	      "Turn key OFF." );


	Log ( BLANK, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "\n\n**** END OF VECHICLE REPORT ****\n" );


	// End of report, disconnect
	DisconnectOBDProtocol ( );
	gbProtocolDetermined = FALSE;

	// save Vehicle Report.log
	fclose ( gLogFileHandle );

	// reset temp file to beginning
	fseek ( gTempLogFileHandle, 0, SEEK_SET );


	return eRetCode;
}




/*******************************************************************************
**
**  Function:  ReadECUNAME
**
**  Purpose:   Purpose of this function is to read the ReadECUNAME from the ECUs.
**             If an ECU returns a correctly formatted ReadECUNAME, return TRUE,
**             otherwise return FAIL.
**
*******************************************************************************/
STATUS ReadECUNAME ( void )
{
	BYTE     EcuIdx;
	REQ_MSG  stReqMsg;
	INF     *pINF;

	STATUS   RetCode = PASS;


	// If INF is supported by any ECU, request it
	if ( IsIDSupported ( ALLECUS, INFREQUEST, INF_TYPE_ECUNAME ) == TRUE )
	{
		stReqMsg.SID      = 0x22;
		stReqMsg.NumIds   = 1;
		stReqMsg.u.DID[0] = INF_TYPE_ECUNAME;
		if ( RequestSID ( &stReqMsg, REQ_MSG_NORMAL ) == FAIL )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "INF $F80A (ECUNAME) request\n" );
			RetCode = FAIL;
		}

		// check responses
		for ( EcuIdx = 0; EcuIdx < gUserNumOfECUs; EcuIdx++ )
		{
			// If INF is not supported, skip to next ECU
			if ( IsIDSupported ( EcuIdx, INFREQUEST, INF_TYPE_ECUNAME ) == FALSE )
			{
				continue;
			}

			if ( gstResponse[EcuIdx].INFSize > 0 )
			{
				// Check the data to see if it is valid
				pINF = (INF*)&gstResponse[EcuIdx].INF[0];

				if ( pINF->INFLSB == (INF_TYPE_ECUNAME & 0x00FF) )
				{
					// Validate ECUNAME data
					if ( VerifyECUNameData ( EcuIdx ) == FAIL )
					{
						RetCode = FAIL;
					}
				}
			}
			else
			{
				// No ECUNAME data
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  INF $F80A (ECUNAME) no data\n",
				      GetEcuId ( EcuIdx ) );
				RetCode = FAIL;
			}
		}
	}  // end  if INFSupported
	else
	{
		// No ECUNAME support
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "INF $F80A (ECUNAME) not supported by any ECUs\n" );
		RetCode = FAIL;
	}

	return RetCode;
}


/*******************************************************************************
**
**  Function:  ReadVIN
**
**  Purpose:   Purpose of this function is to read the VIN from the ECUs.
**             If an ECU returns a correctly formatted VIN, return TRUE,
**             otherwise return FAIL.
**
*******************************************************************************/
STATUS ReadVIN ( void )
{
	BYTE           EcuIdx;
	unsigned long  NumResponses;
	REQ_MSG        stReqMsg;
	INF           *pINF;

	STATUS         RetCode = PASS;


	// If INF is supported by any ECU, request it
	if ( IsIDSupported ( ALLECUS, INFREQUEST, INF_TYPE_VIN ) == TRUE )
	{
		stReqMsg.SID      = 0x22;
		stReqMsg.NumIds   = 1;
		stReqMsg.u.DID[0] = INF_TYPE_VIN;
		if ( RequestSID ( &stReqMsg, REQ_MSG_NORMAL ) == FAIL )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "INF $F802 (VIN) request\n" );
			RetCode = FAIL;
		}

		NumResponses = 0;

		// check responses
		for ( EcuIdx = 0; EcuIdx < gUserNumOfECUs; EcuIdx++ )
		{
			// If INF is supported
			if ( IsIDSupported ( EcuIdx, INFREQUEST, INF_TYPE_VIN ) == TRUE )
			{
				// Check the data to see if it is valid
				pINF = (INF*)&gstResponse[EcuIdx].INF[0];
	
				if ( pINF->INFLSB == (INF_TYPE_VIN & 0x00FF) )
				{
					NumResponses++;
	
					// Copy the VIN into the global array
					memcpy ( gVINString, &pINF[0].Data[0], 17 );
				}
	
				if ( VerifyVINData ( EcuIdx ) != PASS )
				{
					RetCode = FAIL;
				}
			}
		}

		// only 1 ECU allowed to support VIN
		if ( NumResponses != 1 )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "%u ECUs responded to INF $F802 (VIN).  Only 1 is allowed.\n",
			      NumResponses );
			RetCode = FAIL;
		}
	}  // end  if INFSupported
	else
	{
		// No VIN support
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "INF $F802 (VIN) not reported by any ECU (must be reported by one ECU)\n" );
		RetCode = FAIL;
	}

	return RetCode;
}


/*******************************************************************************
**
**  Function:  ReadCALIDs
**
**  Purpose:   Print CALIDs to the log file
**
*******************************************************************************/
STATUS ReadCALIDs ( void )
{
	BYTE     EcuIdx;
	REQ_MSG  stReqMsg;
	STATUS   RetCode = PASS;

	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = INF_TYPE_CALID;
	if ( RequestSID ( &stReqMsg, REQ_MSG_NORMAL ) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "INF $F804 (CALID) request\n" );
		return FAIL;
	}

	for ( EcuIdx = 0; EcuIdx < gUserNumOfECUs; EcuIdx++ )
	{
		if ( VerifyCALIDData ( EcuIdx ) == FAIL )
		{
			RetCode = FAIL;
		}
	}

	return RetCode;
}


/*******************************************************************************
**
**  Function:  ReadCVNs
**
**  Purpose:   print CVNs to the log file
**
*******************************************************************************/
STATUS ReadCVNs ( void )
{
	BYTE     EcuIdx;
	REQ_MSG  stReqMsg;
	STATUS   RetCode = PASS;

	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = INF_TYPE_CVN;
	if ( RequestSID ( &stReqMsg, REQ_MSG_NORMAL ) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "INF $F806 (CVN) request\n" );
		return FAIL;
	}

	for ( EcuIdx = 0; EcuIdx < gUserNumOfECUs; EcuIdx++ )
	{
		if ( VerifyCVNData ( EcuIdx ) == FAIL )
		{
			RetCode = FAIL;
		}
	}

	return RetCode;
}


/*******************************************************************************
**
**  Function:  ReadProtocolID
**
**  Purpose:   Read the PID $F810 (Protocol ID) data from each ECU
**
*******************************************************************************/
STATUS ReadProtocolID ( void )
{
	BYTE     EcuIdx;
	REQ_MSG  stReqMsg;
	INF     *pINF;
	STATUS   RetCode = PASS;

	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = 0xF810;
	if ( (gbPhysicalAddressing == FALSE &&
	      (RetCode = RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE )) == FAIL ) ||
	     (gbPhysicalAddressing == TRUE  &&
	      (RetCode = RequestSID_PhysicallyAddressed_All ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE|REQ_MSG_RETURN_AFTER_ALL_RESPONSES )) == FAIL) )
	{
		return FAIL;
	}

	for ( EcuIdx = 0; EcuIdx < gUserNumOfECUs; EcuIdx++ )
	{
		// If INF is not supported, skip to next ECU
		if ( IsIDSupported ( EcuIdx, INFREQUEST, 0xF810 ) == FALSE )
		{
			continue;
		}

		if ( gstResponse[EcuIdx].INFSize == 0 )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %X  PID $F810 (Protocol ID) no data\n" );
			RetCode = FAIL;
		}
		else
		{
			pINF = (INF*)&gstResponse[EcuIdx].INF[0];

			if ( pINF->INFLSB == 0x10 )
			{
				if ( VerifyINF10Data ( EcuIdx ) == FAIL )
				{
					RetCode = FAIL;
				}
				else
				{
					gstResponse[EcuIdx].ProtocolID = pINF->Data[0];
				}
			}
		}
	}

	return RetCode;
}


/*******************************************************************************
**
**  Function:  ReadTGEFN
**
**  Purpose:   Read the PID $F813 (TG/EFN) data from each ECU
**
*******************************************************************************/
STATUS ReadTGEFN ( void )
{
	BYTE     EcuIdx;
	REQ_MSG  stReqMsg;
	INF     *pINF;
	STATUS   RetCode = PASS;

	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = 0xF813;
	if ( RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE ) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "INF $F813 (TG/EFN) request\n" );
		return FAIL;
	}

	for ( EcuIdx = 0; EcuIdx < gUserNumOfECUs; EcuIdx++ )
	{
		// If INF is not supported, skip to next ECU
		if ( IsIDSupported ( EcuIdx, INFREQUEST, 0xF813 ) == FALSE )
		{
			continue;
		}

		if ( gstResponse[EcuIdx].INFSize > 0 )
		{
			pINF = (INF*)&gstResponse[EcuIdx].PID[0];

			if ( pINF->INFLSB == 0x13 )
			{
				if ( VerifyINF13Data ( EcuIdx ) == FAIL )
				{
					RetCode = FAIL;
				}
				else
				{
					// Copy the TG/EFN into the global array
					memcpy ( gstResponse[EcuIdx].TGEFN_String, &pINF[0].Data[0], 13 );
				}
			}
		}
		else
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %X  INF $F813 (TG/EFN) no data\n" );
			return FAIL;
		}
	}

	return RetCode;
}


/*******************************************************************************
**
**  Function:  ReadOBDSUP
**
**  Purpose:   Read the PID $F41C data from each ECU
**
*******************************************************************************/
STATUS ReadOBDSUP ( void )
{
	BYTE     EcuIdx;
	REQ_MSG  stReqMsg;
	PID     *pPid;
	STATUS   RetCode = PASS;

	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = 0xF41C;
	if ( RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE ) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "PID $F41C (OBDSUP) request\n" );
		return FAIL;
	}

	for ( EcuIdx = 0; EcuIdx < gUserNumOfECUs; EcuIdx++ )
	{
		// If INF is not supported, skip to next ECU
		if ( IsIDSupported ( EcuIdx, PIDREQUEST, 0xF41C ) == FALSE )
		{
			continue;
		}
	
		if ( gstResponse[EcuIdx].PIDSize > 0 )
		{
			pPid = (PID*)&gstResponse[EcuIdx].PID[0];
	
			if ( pPid->PIDLSB == 0x1c )
			{
				if ( VerifyPid1C ( pPid, 0, EcuIdx ) == FAIL )
				{
					RetCode = FAIL;
				}
				else
				{
					gstResponse[EcuIdx].ODB_Compliance_Level = pPid->Data[0];
				}
			}
		}
		else
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %X  PID $F41C (OBD_Comp) no data\n" );
			return FAIL;
		}
	}

	return RetCode;
}


/*******************************************************************************
**
**  Function:  ReadFuelType
**
**  Purpose:   Read the PID $F451 data from each ECU
**
*******************************************************************************/
STATUS ReadFuelType ( void )
{
	BYTE          EcuIdx;
	REQ_MSG       stReqMsg;
	PID          *pPid;
	STATUS        RetCode = PASS;
	unsigned int  PID51_SupCnt = 0;
	unsigned int  PID51_Cnt    = 0;
	unsigned int  PID51_Saved  = 0;


	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = 0xF451;
	if ( RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE ) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "PID $F451 (FUEL_TYP) request\n" );
		return FAIL;
	}

	for ( EcuIdx = 0; EcuIdx < gUserNumOfECUs; EcuIdx++ )
	{
		// If INF is not supported, skip to next ECU
		if ( IsIDSupported ( EcuIdx, PIDREQUEST, 0xF451 ) == FALSE )
		{
			continue;
		}
	
		if ( gstResponse[EcuIdx].PIDSize > 0 )
		{
			pPid = (PID*)&gstResponse[EcuIdx].PID[0];
	
			if ( pPid->PIDLSB == 0x51 )
			{
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  PID $F451  FUEL_TYP = $%02X (%s)\n",
				      GetEcuId ( EcuIdx ),
				      pPid->Data[0],
				      (pPid->Data[0] <= 0x1F ? szFUEL_TYPE[pPid->Data[0]] : szFUEL_TYPE[0x20]) );
	
				gstResponse[EcuIdx].PID51.Data[0] = pPid->Data[0];
	
				if ( PID51_Saved == 0x00 )
				{
					PID51_Saved = pPid->Data[0];
				}
	
				PID51_SupCnt++;
	
				if ( pPid->Data[0] >= 0x20 )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X  Invalid FUEL_TYP ($20-$FF reserved)\n",
					      GetEcuId ( EcuIdx ) );
	
					RetCode = FAIL;
				}
//				else if ( (pPid->Data[0] >= 0x0F && pPid->Data[0] <= 0x17) )
//				{
//					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
//					     "ECU %X  Invalid FUEL_TYP ($0F-$17 not to be used)\n",
//					     GetEcuId ( EcuIdx ) );
//
//					RetCode = FAIL;
//				}
	
				// if the value from the current ECU matches the value from the 1st responding ECU, increment the number of matching values
				if ( PID51_Saved == pPid->Data[0] )
				{
					PID51_Cnt++;
				}
			}
		}
		else
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %X  PID $F451 (FUEL_TYP) no data\n" );
			RetCode = FAIL;
		}
	}

	// if the number of matching values doesn't match the number ECUs which support FUEL_TYP, this is a failure
	if ( PID51_Cnt != PID51_SupCnt )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		     "Not all ECUs had the same FUEL_TYP\n" );

		RetCode = FAIL;
	}

	return RetCode;
}


/*******************************************************************************
**
**  Function:  ReadHybridType
**
**  Purpose:   Read the PID $F45B, INF $F812, $F81B, $F81C data from each ECU
**
*******************************************************************************/
STATUS ReadHybridType ( void )
{
	BYTE     EcuIdx;
	REQ_MSG  stReqMsg;
	PID     *pPid;
	STATUS   RetCode = PASS;


	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = 0xF45B;
	if ( RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE ) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "PID $F45B BAT_PWR request\n" );
		RetCode = FAIL;
	}

	for ( EcuIdx = 0;
	      EcuIdx < gUserNumOfECUs;
	      EcuIdx++ )
	{
		// If INF is not supported, skip to next ECU
		if ( IsIDSupported ( EcuIdx, PIDREQUEST, 0xF45B ) == FALSE )
		{
			continue;
		}

		if ( gstResponse[EcuIdx].PIDSize > 0 )
		{
			pPid = (PID*)&gstResponse[EcuIdx].PID[0];

			if ( pPid->PIDLSB == 0x5B )
			{
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  PID $F45B  BAT_PWR = %f %%\n",
				      GetEcuId ( EcuIdx ),
				      (float)pPid->Data[0] * (float)(100.0 / 255.0));
			}
		}
		else
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %X  PID $F45B BAT_PWR no data\n" );
			RetCode = FAIL;
		}
	}


	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = 0xF812;
	if ( RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE ) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "INF $F812 (FEOCNTR) request\n" );
		RetCode = FAIL;
	}

	for ( EcuIdx = 0; EcuIdx < gUserNumOfECUs; EcuIdx++ )
	{
		// If INF is not supported, skip to next ECU
		if ( IsIDSupported ( EcuIdx, INFREQUEST, 0xF812 ) == FALSE )
		{
			continue;
		}

		VerifyINF12Data ( EcuIdx );
	}


	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = 0xF81B;
	if ( RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE ) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "INF $F81B (Plug-in Hybrid Vehicle Fuel Data) request\n" );
		RetCode = FAIL;
	}

	for ( EcuIdx = 0; EcuIdx < gUserNumOfECUs; EcuIdx++ )
	{
		// If INF is not supported, skip to next ECU
		if ( IsIDSupported ( EcuIdx, INFREQUEST, 0xF81B ) == FALSE )
		{
			continue;
		}

		VerifyINF1BData ( EcuIdx );
	}


	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = 0xF81C;
	if ( RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE ) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "INF $F81C (Plug-in Hybrid Vehicle Grid Data) request\n" );
		RetCode = FAIL;
	}

	for ( EcuIdx = 0;
	      EcuIdx < gUserNumOfECUs;
	      EcuIdx++ )
	{
		// If INF is not supported, skip to next ECU
		if ( IsIDSupported ( EcuIdx, INFREQUEST, 0xF81C ) == FALSE )
		{
			continue;
		}

		VerifyINF1CData ( EcuIdx );
	}

	return RetCode;
}
