/*******************************************************************************
********************************************************************************
**
**  Copyright(c) 2022, Alliance for Automotive Innovation
**  Used only under license from the Alliance for Automotive Innovation. All Rights Reserved.
**
**  Project:  J1699-5
**  FileName: Test10_InUseCounters.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/04/2024  Initial Version
**
********************************************************************************
*******************************************************************************/

#include <stdio.h>    // C Library input and output declarations
#include <stdlib.h>   // C Library general function declarations
#include <string.h>   // C Library character array declarations
#include <time.h>     // C Library time and date declarations
#include <conio.h>    // MS-DOS console input and output declarations
#include <windows.h>  // Windows API declarations
#include "j2534.h"    // j1699 project j2534 declarations
#include "j1699.h"    // j1699 project general declarations


STATUS VerifyDTCExtDataList   ( BYTE  ExtDataId );
void   LogDTCIUMPRList        ( void );
void   LogDTCTestResultList   ( void );
void   LogDTCSMADList         ( void );

STATUS VerifyDTCExtData       ( BYTE  ExtDataID );
STATUS GetDTCExtData          ( BYTE  ExtDataID,
                                WORD  Index );
STATUS SaveDTCIUMPRData       ( BYTE  EcuIdx,
                                WORD *pIndex );
STATUS SaveDTCTestResultData  ( BYTE  EcuIdx,
                                WORD *pIndex );
STATUS SaveDTCSMADData        ( BYTE  EcuIdx,
                                WORD *pIndex );
void   LogDTCIUMPRData        ( BYTE  EcuIdx,
                                WORD  Index );
void   LogDTCTestResultData   ( BYTE  EcuIdx,
                                WORD  Index );
void   LogDTCSMADData         ( BYTE  EcuIdx,
                                WORD  Index );

STATUS VerifyDTCIMReadinessList ( void );
void SaveDTCIMReadinessList     ( BYTE  EcuIdx );
void LogDTCIMReadinessList      ( BYTE  EcuIdx,
                                  BYTE  Index );

boolean isDTCinList           ( BYTE  EcuIdx,
                                DTC  *pID,
                                BYTE  ExtDataId );


extern const char szEcuId[];

const char szIumprListTitle[]       =  "IUMPR LIST";
const char szIumprSize[]            =  "IUMPR SIZE";
const char szIumprName[]            =  "DTC";
const char szIumprData1[]           =  "NUMERATOR";
const char szIumprData2[]           =  "DENOMINATOR";

const char szTestResultListTitle[]  =  "TestResult LIST";
const char szTestResultSize[]       =  "TestResult SIZE";
const char szTestResultName[]       =  "DTC";
const char szTestResultData1[]      =  "Number of Test Results";
const char szTestResultData2[]      =  "Unit and Scaling ID";
const char szTestResultData3[]      =  "Test Value";
const char szTestResultData4[]      =  "Min. Test Limit";
const char szTestResultData5[]      =  "Max. Test Limit";

const char szSmadListTitle[]        =  "SMAD LIST";
const char szSmadSize[]             =  "SMAD SIZE";
const char szSmadName[]             =  "DTC";
const char szSmadData1[]            =  "NUMERATOR";
const char szSmadData2[]            =  "ACTIVITY RATIO";

const char szUnassigned[]  =   "UNASSIGNED";

const char *szIMReadinessGroup [] =
{
	"No Readiness Group",
	"CAT  Catalyst Monitor",
	"HCAT Heated Catalyst Monitor",
	"MIS  Misfire Monitor",
	"EVAP Evaporative System Monitoring",
	"AIR  Secondary Air System Monitoring",
	"FUEL Fuel System Monitoring",
	"EGS  Exhaust Gas Sensor Monitoring",
	"EGR  Exhaust Gas Recirculation System Monitoring",
	"CV   Positive/Crankcase Ventilation System Monitoring",
	"COOL Engine Cooling System Monitoring",
	"CSER Cold Start Emission Reduction Strategy Monitoring",
	"VVT  Variable Valve Timing, Lift, and/or Control System Monitoring",
	"DOR  Direct Ozone Reduction System Monitoring",
	"CCM  Comprehensive Component Monitoring",
	"OTH  Other Emission Control or Source System Monitoring",
	"HCCAT Non-Methane Hydrocarbon Converting Catalyst Monitoring",
	"NCAT Oxides of Nitrogen Converting Catalyst Monitoring",
	"BP   Boost Pressure Control System Monitoring",
	"NAC  NOx Adsorber Monitoring",
	"PF   Particulate Matter Filter Monitoring"
};
#define MAX_IM_READINESS_GROUP 0x14


/*******************************************************************************
**
**  Function:  VerifyDTCExtDataList
**
**  Purpose:   
**
*******************************************************************************/
STATUS VerifyDTCExtDataList ( BYTE ExtDataID )
{
	REQ_MSG        stReqMsg;
	STATUS         eRetCode = PASS;


	Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "Request DTC Extended Data list (SID $19 LEV $1A DTCEDRN $%02X) Physically Addressed\n",
	      ExtDataID );

	stReqMsg.SID     = 0x19;
	stReqMsg.NumIds  = 2;
	stReqMsg.u.ID[0] = 0x1A;       // ReportDTCExtDataIdentification
	stReqMsg.u.ID[1] = ExtDataID;  // DTCExtendedDTCDataRecordNumber
	if ( (eRetCode = RequestSID_PhysicallyAddressed_All ( &stReqMsg, REQ_MSG_NORMAL|REQ_MSG_ALLOW_NO_RESPONSE )) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "SID $19 $1A $%02X request failed\n",
		      ExtDataID );
	}

	// Display the responses
	if ( ExtDataID == 0x91 )
	{
		LogDTCIUMPRList ( );
	}
	else if ( ExtDataID == 0x92 )
	{
		LogDTCTestResultList ( );
	}
	else if ( ExtDataID == 0x93 )
	{
		LogDTCSMADList ( );
	}

	return eRetCode;
}


/*******************************************************************************
**
**  Function:  LogDTCIUMPRList
**
**  Purpose:
**
*******************************************************************************/
void LogDTCIUMPRList ( void )
{
	BYTE     EcuIdx;

	WORD     EcuDTCIndex;
	WORD     ListDTCIndex;


	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( gstResponse[EcuIdx].bDTCIUMPRSupported != TRUE )
		{
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %-8X  supports no DTCs with Extdata $91 (IUMPR).\n",
			      GetEcuId ( EcuIdx ) );
		}
		else
		{
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %-8X  supports the following DTCs with Extdata $91 (IUMPR):\n",
			      GetEcuId ( EcuIdx ) );

			// log IUMPR names and values
			for ( EcuDTCIndex = 0;
			      EcuDTCIndex < gstResponse[EcuIdx].DTCIUMPRCount;
			      EcuDTCIndex++ )
			{

				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "              %c%02X%02X %02X (Status = $%02X)\n",
				       DTCTypeCharacter[(gstResponse[EcuIdx].DTCIUMPRList[EcuDTCIndex].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				       gstResponse[EcuIdx].DTCIUMPRList[EcuDTCIndex].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				       gstResponse[EcuIdx].DTCIUMPRList[EcuDTCIndex].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
				       gstResponse[EcuIdx].DTCIUMPRList[EcuDTCIndex].Record.Dtc.LowByte,                                   // Failure Type Byte
				       gstResponse[EcuIdx].DTCIUMPRList[EcuDTCIndex].Record.Status );                                      // Status of DTC

				// check if the DTC is already in the list
				for ( ListDTCIndex = 0;
				      ListDTCIndex < DTCIUMPRListCount;
				      ListDTCIndex++ )
				{
					if ( DTCIUMPRList[ListDTCIndex].HighByte == gstResponse[EcuIdx].DTCIUMPRList[EcuDTCIndex].Record.Dtc.HighByte &&
					     DTCIUMPRList[ListDTCIndex].MidByte  == gstResponse[EcuIdx].DTCIUMPRList[EcuDTCIndex].Record.Dtc.MidByte &&
					     DTCIUMPRList[ListDTCIndex].LowByte  == gstResponse[EcuIdx].DTCIUMPRList[EcuDTCIndex].Record.Dtc.LowByte )
					{
						break;
					}
				}

				// if the DTC wasn't in the list, add it
				if ( ListDTCIndex == DTCIUMPRListCount )
				{
					if ( DTCIUMPRListCount < MAX_DTCIUMPR_COUNT )
					{
						DTCIUMPRList[ListDTCIndex].HighByte = gstResponse[EcuIdx].DTCIUMPRList[EcuDTCIndex].Record.Dtc.HighByte;
						DTCIUMPRList[ListDTCIndex].MidByte  = gstResponse[EcuIdx].DTCIUMPRList[EcuDTCIndex].Record.Dtc.MidByte;
						DTCIUMPRList[ListDTCIndex].LowByte  = gstResponse[EcuIdx].DTCIUMPRList[EcuDTCIndex].Record.Dtc.LowByte;
						DTCIUMPRListCount++;
					}
					else
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "More than the allocated number of DTCs with Extdata $91:\n" );
					}
				}
			}
		}
	}
}


/*******************************************************************************
**
**  Function:  LogDTCTestResultList
**
**  Purpose:
**
*******************************************************************************/
void LogDTCTestResultList ( void )
{
	BYTE     EcuIdx;

	WORD     EcuDTCIndex;
	WORD     ListDTCIndex;


	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( gstResponse[EcuIdx].bDTCTestResultSupported != TRUE )
		{
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %-8X  supports no DTCs with Extdata $92 (TestResult).\n",
			      GetEcuId ( EcuIdx ) );
		}
		else
		{
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %-8X  supports the following DTCs with Extdata $92 (TestResult):\n",
			      GetEcuId ( EcuIdx ) );

			// log TestResult names and values
			for ( EcuDTCIndex = 0;
			      EcuDTCIndex < gstResponse[EcuIdx].DTCTestResultCount;
			      EcuDTCIndex++ )
			{

				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "              %c%02X%02X %02X (Status = $%02X)\n",
				      DTCTypeCharacter[(gstResponse[EcuIdx].DTCTestResultList[EcuDTCIndex].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				      gstResponse[EcuIdx].DTCTestResultList[EcuDTCIndex].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				      gstResponse[EcuIdx].DTCTestResultList[EcuDTCIndex].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
				      gstResponse[EcuIdx].DTCTestResultList[EcuDTCIndex].Record.Dtc.LowByte,                                   // Failure Type Byte
				      gstResponse[EcuIdx].DTCTestResultList[EcuDTCIndex].Record.Status );                                      // Status of DTC

				// check if the DTC is already in the list
				for ( ListDTCIndex = 0;
				      ListDTCIndex < DTCTestResultListCount;
				      ListDTCIndex++ )
				{
					if ( DTCTestResultList[ListDTCIndex].HighByte == gstResponse[EcuIdx].DTCTestResultList[EcuDTCIndex].Record.Dtc.HighByte &&
					     DTCTestResultList[ListDTCIndex].MidByte  == gstResponse[EcuIdx].DTCTestResultList[EcuDTCIndex].Record.Dtc.MidByte &&
					     DTCTestResultList[ListDTCIndex].LowByte  == gstResponse[EcuIdx].DTCTestResultList[EcuDTCIndex].Record.Dtc.LowByte )
					{
						break;
					}
				}

				// if the DTC wasn't in the list, add it
				if ( ListDTCIndex == DTCTestResultListCount )
				{
					if ( DTCTestResultListCount < MAX_DTCTESTRESULT_COUNT )
					{
						DTCTestResultList[ListDTCIndex].HighByte = gstResponse[EcuIdx].DTCTestResultList[EcuDTCIndex].Record.Dtc.HighByte;
						DTCTestResultList[ListDTCIndex].MidByte  = gstResponse[EcuIdx].DTCTestResultList[EcuDTCIndex].Record.Dtc.MidByte;
						DTCTestResultList[ListDTCIndex].LowByte  = gstResponse[EcuIdx].DTCTestResultList[EcuDTCIndex].Record.Dtc.LowByte;
						DTCTestResultListCount++;
					}
					else
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "More than the allocated number of DTCs with Extdata $92:\n" );
					}

				}
			}
		}
	}
}


/*******************************************************************************
**
**  Function:  LogDTCSMADList
**
**  Purpose:
**
*******************************************************************************/
void LogDTCSMADList ( void )
{
	BYTE     EcuIdx;
	WORD     EcuDTCIndex;
	WORD     DTCIndex;

	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( gstResponse[EcuIdx].bDTCSMADSupported  != TRUE )
		{
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %-8X  supports no DTCs with Extdata $93 (SMAD).\n",
			      GetEcuId ( EcuIdx ) );
		}
		else
		{
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %-8X  supports the following DTCs with Extdata $93 (SMAD):\n",
			      GetEcuId ( EcuIdx ) );

			// log SMAD names and values
			for ( EcuDTCIndex = 0;
			      EcuDTCIndex < gstResponse[EcuIdx].DTCSMADCount;
			      EcuDTCIndex++ )
			{

				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					  "              %c%02X%02X %02X (Status = $%02X)\n",
				       DTCTypeCharacter[(gstResponse[EcuIdx].DTCSMADList[EcuDTCIndex].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				       gstResponse[EcuIdx].DTCSMADList[EcuDTCIndex].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				       gstResponse[EcuIdx].DTCSMADList[EcuDTCIndex].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
				       gstResponse[EcuIdx].DTCSMADList[EcuDTCIndex].Record.Dtc.LowByte,                                   // Failure Type Byte
				       gstResponse[EcuIdx].DTCSMADList[EcuDTCIndex].Record.Status );                                      // Status of DTC

				// check if the DTC is already in the list
				for ( DTCIndex = 0;
				      DTCIndex < DTCSMADListCount;
				      DTCIndex++ )
				{
					if ( DTCSMADList[DTCIndex].HighByte == gstResponse[EcuIdx].DTCSMADList[EcuDTCIndex].Record.Dtc.HighByte &&
					     DTCSMADList[DTCIndex].MidByte  == gstResponse[EcuIdx].DTCSMADList[EcuDTCIndex].Record.Dtc.MidByte &&
					     DTCSMADList[DTCIndex].LowByte  == gstResponse[EcuIdx].DTCSMADList[EcuDTCIndex].Record.Dtc.LowByte )
					{
						break;
					}
				}

				// if the DTC wasn't in the list, add it
				if ( DTCIndex == DTCSMADListCount )
				{
					if ( DTCSMADListCount < MAX_DTCSMAD_COUNT )
					{
						DTCSMADList[DTCIndex].HighByte = gstResponse[EcuIdx].DTCSMADList[EcuDTCIndex].Record.Dtc.HighByte;
						DTCSMADList[DTCIndex].MidByte = gstResponse[EcuIdx].DTCSMADList[EcuDTCIndex].Record.Dtc.MidByte;
						DTCSMADList[DTCIndex].LowByte = gstResponse[EcuIdx].DTCSMADList[EcuDTCIndex].Record.Dtc.LowByte;
						DTCSMADListCount++;
					}
					else
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "More than the allocated number of DTCs with Extdata $93:\n" );
					}
				}
			}
		}
	}
}


/*******************************************************************************
**
**  Function:  VerifyDTCExtData
**
**  Purpose:   
**
*******************************************************************************/
STATUS VerifyDTCExtData ( BYTE ExtDataId )
{
	BYTE       EcuIdx;
	STATUS     eRetCode = PASS;

	char      *pString = (char*)&szUnassigned;

	WORD       Index;
	WORD       IndexMax = 0;

	WORD       DTCIndex;


	if ( ExtDataId == 0x91 )
	{
		IndexMax = DTCIUMPRListCount;
	}
	else if ( ExtDataId == 0x92 )
	{
		IndexMax = DTCTestResultListCount;
	}
	else if ( ExtDataId == 0x93 )
	{
		IndexMax = DTCSMADListCount;
	}

	// Request all DTC Extended Data
	for ( Index = 0;
	      Index < IndexMax;
	      Index++ )
	{
		// Request the DTC Extended Data
		eRetCode |= GetDTCExtData ( ExtDataId, Index );

		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			// if a response was received from this ECU
			if ( gstResponse[EcuIdx].bResponseReceived  == TRUE )
			{
				// Save and log the response
				// if this was an IUMPR request
				if ( ExtDataId == 0x91 &&
				     gstResponse[EcuIdx].bDTCIUMPRSupported == TRUE )
				{
					eRetCode |= SaveDTCIUMPRData ( EcuIdx, &DTCIndex );

					// if not TEST 10.10, log each response individually
					if ( !(geTestPhase == eTestInUseCounters && gTestSubsection == 10) )
					{
						LogDTCIUMPRData ( EcuIdx, DTCIndex );
					}
				}
				// if this was an Test Result request
				else if ( ExtDataId == 0x92 &&
				          gstResponse[EcuIdx].bDTCTestResultSupported  == TRUE )
				{
					eRetCode |= SaveDTCTestResultData ( EcuIdx, &DTCIndex );

					// if not TEST 10.10, log each response individually
					if ( !(geTestPhase == eTestInUseCounters && gTestSubsection == 10) )
					{
						LogDTCTestResultData ( EcuIdx, DTCIndex );
					}
				}
				// if this was an SMAD request
				else if ( ExtDataId == 0x93 &&
				          gstResponse[EcuIdx].bDTCSMADSupported  == TRUE )
				{
					eRetCode |= SaveDTCSMADData ( EcuIdx, &DTCIndex );

					// if not TEST 10.10, log each response individually
					if ( !(geTestPhase == eTestInUseCounters && gTestSubsection == 10) )
					{
						LogDTCSMADData ( EcuIdx, DTCIndex );
					}
				}
			}  // end if ( gstResponse[EcuIdx].bResponseReceived  == TRUE )
		}  // end for ( EcuIdx )
	}  // end for ( Index )

	// if TEST 10.10,for each ECU, log all the IUMPR and SMAD data as a group
	if ( geTestPhase == eTestInUseCounters && gTestSubsection == 10 )
	{
		char       TitleString[19];
		
		if ( ExtDataId == 0x91 )
		{
			sprintf_s ( TitleString, 19,
			          "%s", szIumprListTitle );
		}
		else if ( ExtDataId == 0x92 )
		{
			sprintf_s ( TitleString, 19,
			          "%s", szTestResultListTitle );
		}
		else if ( ExtDataId == 0x93 )
		{
			sprintf_s ( TitleString, 19,
			          "%s", szSmadListTitle );
		}

		// log Section Title
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "%s:\n",
		      TitleString,
		      GetEcuId ( EcuIdx ) );

		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			// log ECU ID
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "%-10s = 0x%X\n",
			      szEcuId,
			      GetEcuId ( EcuIdx ) );

			if ( ExtDataId == 0x91 )
			{
				IndexMax = gstResponse[EcuIdx].DTCIUMPRCount;
				pString = (char *)&szIumprSize[0];
			}
			else if ( ExtDataId == 0x92 )
			{
				IndexMax = gstResponse[EcuIdx].DTCTestResultCount;
				pString = (char *)&szTestResultSize[0];
			}
			else if ( ExtDataId == 0x93 )
			{
				IndexMax = gstResponse[EcuIdx].DTCSMADCount;
				pString = (char *)&szSmadSize[0];
			}

			// log number of data items
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "%-10s = %d\n",
			      pString,
			      IndexMax );

			for ( Index = 0;
			      Index < IndexMax;
			      Index++ )
			{
				if ( ExtDataId == 0x91 && gstResponse[EcuIdx].bDTCIUMPRSupported == TRUE )
				{
					LogDTCIUMPRData ( EcuIdx, Index );
				}
				else if ( ExtDataId == 0x92 && gstResponse[EcuIdx].bDTCTestResultSupported  == TRUE )
				{
					LogDTCTestResultData ( EcuIdx, Index );
				}
				else if ( ExtDataId == 0x93 && gstResponse[EcuIdx].bDTCSMADSupported  == TRUE )
				{
					LogDTCSMADData ( EcuIdx, Index );
				}
			}  // end for ( Index )
		}  // end for ( EcuIdx )
	}  // end if ( TEST 10.14 )

	return eRetCode;
}


/*******************************************************************************
**
**  Function:  GetDTCExtData
**
**  Purpose:   Copy from gstResponse[EcuIdx].DTCIUMPRCurrent into the matching DTC
**             in gstResponse[EcuIdx].DTCIUMPRList.
**
*******************************************************************************/
STATUS GetDTCExtData ( BYTE ExtDataId,
                       WORD Index )
{
	REQ_MSG    stReqMsg;
	BYTE       EcuIdx;
	STATUS     eRetCode = PASS;

	char       DataTypeString[12];


	// Request the DTC data
	stReqMsg.SID     = 0x19;
	stReqMsg.NumIds  = 5;
	stReqMsg.u.ID[0] = 0x06;                              // ReportDTCExtDataIdentification
	if ( ExtDataId == 0x91 )
	{
		stReqMsg.u.ID[1] = DTCIUMPRList[Index].HighByte;  // High byte of DTC
		stReqMsg.u.ID[2] = DTCIUMPRList[Index].MidByte;   // Mid  byte of DTC
		stReqMsg.u.ID[3] = DTCIUMPRList[Index].LowByte;   // Low  byte of DTC

		strcpy_s ( DataTypeString, 6, "IUMPR" );
	}
	else if ( ExtDataId == 0x92 )
	{
		stReqMsg.u.ID[1] = DTCTestResultList[Index].HighByte;  // High byte of DTC
		stReqMsg.u.ID[2] = DTCTestResultList[Index].MidByte;   // Mid  byte of DTC
		stReqMsg.u.ID[3] = DTCTestResultList[Index].LowByte;   // Low  byte of DTC

		strcpy_s ( DataTypeString, 12, "Test Result" );
	}
	else
	{
		stReqMsg.u.ID[1] = DTCSMADList[Index].HighByte;   // High byte of DTC
		stReqMsg.u.ID[2] = DTCSMADList[Index].MidByte;    // Mid  byte of DTC
		stReqMsg.u.ID[3] = DTCSMADList[Index].LowByte;    // Low  byte of DTC

		strcpy_s ( DataTypeString, 5, "SMAD" );
	}
	stReqMsg.u.ID[4] = ExtDataId;                         // DTCExtendedDTCDataRecordNumber

	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( isDTCinList ( EcuIdx, (DTC *)&stReqMsg.u.ID[1], ExtDataId ) )
		{
			if ( (eRetCode = RequestSID_PhysicallyAddressed_Single ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_ALLOW_NO_RESPONSE, gstResponse[EcuIdx].ReqId )) == FAIL )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "SID $19 $06 DTC %c%02X%02X %02X ExtData $%02X %s request failed\n",
				       DTCTypeCharacter[(stReqMsg.u.ID[1] & 0xC0) >> 6],  // 1st character (P, C, B, U)
				       stReqMsg.u.ID[1] & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				       stReqMsg.u.ID[2],                                  // 4th and 5th characters (0-F)
				       stReqMsg.u.ID[3],                                  // Failure Type Byte
				       ExtDataId,                                         // Extended Data ID
				       DataTypeString );                                  // Extended Data ID type
				return FAIL;
			}
		}
	}

	return eRetCode;
}


/*******************************************************************************
**
**  Function:  SaveDTCIUMPRData
**
**  Purpose:   Copy from gstResponse[EcuIdx].DTCIUMPRCurrent into the matching DTC
**             in gstResponse[EcuIdx].DTCIUMPRList.
**
*******************************************************************************/
STATUS SaveDTCIUMPRData ( BYTE  EcuIdx,
                          WORD *pIndex )
{
	STATUS        eRetCode = PASS;  // function result

	DTCIUMPR     *pIUMPR = NULL;


	// find current DTC in DTC IUMPR list and copy the data
	for ( *pIndex = 0;
	      *pIndex < gstResponse[EcuIdx].DTCIUMPRCount;
	      (*pIndex)++ )
	{
		// if the received DTC is in this ECUs list
		if ( gstResponse[EcuIdx].DTCIUMPRList[*pIndex].Record.Dtc.HighByte == gstResponse[EcuIdx].DTCIUMPRCurrent.Record.Dtc.HighByte &&
		     gstResponse[EcuIdx].DTCIUMPRList[*pIndex].Record.Dtc.MidByte  == gstResponse[EcuIdx].DTCIUMPRCurrent.Record.Dtc.MidByte &&
		     gstResponse[EcuIdx].DTCIUMPRList[*pIndex].Record.Dtc.LowByte  == gstResponse[EcuIdx].DTCIUMPRCurrent.Record.Dtc.LowByte )
		{
			// save the data to the ECU's IUMPR list
			gstResponse[EcuIdx].DTCIUMPRList[*pIndex].CompCounts = gstResponse[EcuIdx].DTCIUMPRCurrent.CompCounts;
			gstResponse[EcuIdx].DTCIUMPRList[*pIndex].CondCounts = gstResponse[EcuIdx].DTCIUMPRCurrent.CondCounts;

			// if this is a test phase that needs it's data saved for later testing,
			// also save the data to that phase's copy of the IUMPR list
			if ( (geTestPhase == eTestInUseCounters       && gTestSubsection == 10) ||
			     (geTestPhase == eTestPerformanceCounters && gTestSubsection == 7)  ||
			     (geTestPhase == eTestPerformanceCounters && gTestSubsection == 12) )
			{
				if ( geTestPhase == eTestInUseCounters && gTestSubsection == 10 )
				{
					pIUMPR = &gstResponse[EcuIdx].DTCIUMPR_Test10_10[0];
				}
				else if ( geTestPhase == eTestPerformanceCounters && gTestSubsection == 7 )
				{
					pIUMPR = &gstResponse[EcuIdx].DTCIUMPR_Test11_7[0];
				}
				else if ( geTestPhase == eTestPerformanceCounters && gTestSubsection == 12 )
				{
					pIUMPR = &gstResponse[EcuIdx].DTCIUMPR_Test11_12[0];
				}

				pIUMPR[*pIndex].Record.Dtc.HighByte = gstResponse[EcuIdx].DTCIUMPRCurrent.Record.Dtc.HighByte;
				pIUMPR[*pIndex].Record.Dtc.MidByte  = gstResponse[EcuIdx].DTCIUMPRCurrent.Record.Dtc.MidByte;
				pIUMPR[*pIndex].Record.Dtc.LowByte  = gstResponse[EcuIdx].DTCIUMPRCurrent.Record.Dtc.LowByte;

				pIUMPR[*pIndex].CompCounts = gstResponse[EcuIdx].DTCIUMPRCurrent.CompCounts;
				pIUMPR[*pIndex].CondCounts = gstResponse[EcuIdx].DTCIUMPRCurrent.CondCounts;
			}
			break;
		}  // end if ( matching DTC found )
	}  // end for ( Index )

	// if the current DTC was not in the DTC IUMPR list, warn
	if ( *pIndex >= gstResponse[EcuIdx].DTCIUMPRCount )
	{
		Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "ECU %-8X  received unexpected response to SID $19 $06 DTC %c%02X%02X %02X ExtData $91 (IUMPR) request (Response not stored)\n",
		       GetEcuId ( EcuIdx ),
		       DTCTypeCharacter[(gstResponse[EcuIdx].DTCIUMPRCurrent.Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
		       gstResponse[EcuIdx].DTCIUMPRCurrent.Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
		       gstResponse[EcuIdx].DTCIUMPRCurrent.Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
		       gstResponse[EcuIdx].DTCIUMPRCurrent.Record.Dtc.LowByte );                                 // Failure Type Byte

		eRetCode = ERRORS;
	}

	return eRetCode;
}


/*******************************************************************************
**
**  Function:  SaveDTCTestResultData
**
**  Purpose:   Copy from gstResponse[EcuIdx].DTCTestResultCurrent into the matching DTC
**             in gstResponse[EcuIdx].DTCTestResultList.
**
*******************************************************************************/
STATUS SaveDTCTestResultData ( BYTE  EcuIdx,
                               WORD *pIndex )
{
	STATUS        eRetCode = PASS;  // function result

	DTCTestResult     *pTestResult = NULL;


	// find current DTC in DTC TestResult list and copy the data
	for ( *pIndex = 0;
	      *pIndex < gstResponse[EcuIdx].DTCTestResultCount;
	      (*pIndex)++ )
	{
		// if the received DTC is in this ECUs list
		if ( gstResponse[EcuIdx].DTCTestResultList[*pIndex].Record.Dtc.HighByte == gstResponse[EcuIdx].DTCTestResultCurrent.Record.Dtc.HighByte &&
		     gstResponse[EcuIdx].DTCTestResultList[*pIndex].Record.Dtc.MidByte  == gstResponse[EcuIdx].DTCTestResultCurrent.Record.Dtc.MidByte &&
		     gstResponse[EcuIdx].DTCTestResultList[*pIndex].Record.Dtc.LowByte  == gstResponse[EcuIdx].DTCTestResultCurrent.Record.Dtc.LowByte )
		{
			// save the data to the ECU's TestResult list
			gstResponse[EcuIdx].DTCTestResultList[*pIndex].NumTstRslts = gstResponse[EcuIdx].DTCTestResultCurrent.NumTstRslts;
			for ( int i = 0; i < gstResponse[EcuIdx].DTCTestResultList[*pIndex].NumTstRslts; i++ )
			{
				gstResponse[EcuIdx].DTCTestResultList[*pIndex].Result[i].UnitScaling = gstResponse[EcuIdx].DTCTestResultCurrent.Result[i].UnitScaling;
				gstResponse[EcuIdx].DTCTestResultList[*pIndex].Result[i].TestValue   = gstResponse[EcuIdx].DTCTestResultCurrent.Result[i].TestValue;
				gstResponse[EcuIdx].DTCTestResultList[*pIndex].Result[i].MinTstLmt   = gstResponse[EcuIdx].DTCTestResultCurrent.Result[i].MinTstLmt;
				gstResponse[EcuIdx].DTCTestResultList[*pIndex].Result[i].MaxTstLmt   = gstResponse[EcuIdx].DTCTestResultCurrent.Result[i].MaxTstLmt;
			}

			// if this is a test phase that needs it's data saved for later testing,
			// also save the data to that phase's copy of the TestResult list
			if ( (geTestPhase == eTestInUseCounters       && gTestSubsection == 10) ||
			     (geTestPhase == eTestPerformanceCounters && gTestSubsection == 7) )
			{
				if ( geTestPhase == eTestInUseCounters && gTestSubsection == 10 )
				{
					pTestResult = &gstResponse[EcuIdx].DTCTestResult_Test10_10[0];
				}
				else if ( geTestPhase == eTestPerformanceCounters && gTestSubsection == 6 )
				{
					pTestResult = &gstResponse[EcuIdx].DTCTestResult_Test11_6[0];
				}

				if ( pTestResult != NULL )
				{
					pTestResult[*pIndex].Record.Dtc.HighByte = gstResponse[EcuIdx].DTCTestResultCurrent.Record.Dtc.HighByte;
					pTestResult[*pIndex].Record.Dtc.MidByte  = gstResponse[EcuIdx].DTCTestResultCurrent.Record.Dtc.MidByte;
					pTestResult[*pIndex].Record.Dtc.LowByte  = gstResponse[EcuIdx].DTCTestResultCurrent.Record.Dtc.LowByte;

					pTestResult[*pIndex].NumTstRslts = gstResponse[EcuIdx].DTCTestResultCurrent.NumTstRslts;
					for ( int i = 0; i < pTestResult[*pIndex].NumTstRslts; i++ )
					{
						pTestResult[*pIndex].Result[i].UnitScaling = gstResponse[EcuIdx].DTCTestResultCurrent.Result[i].UnitScaling;
						pTestResult[*pIndex].Result[i].TestValue   = gstResponse[EcuIdx].DTCTestResultCurrent.Result[i].TestValue;
						pTestResult[*pIndex].Result[i].MinTstLmt   = gstResponse[EcuIdx].DTCTestResultCurrent.Result[i].MinTstLmt;
						pTestResult[*pIndex].Result[i].MaxTstLmt   = gstResponse[EcuIdx].DTCTestResultCurrent.Result[i].MaxTstLmt;
					}
				}
			}
			break;
		}  // end if ( matching DTC found )
	}  // end for ( Index )

	// if the current DTC was not in the DTC TestResult list, warn
	if ( *pIndex >= gstResponse[EcuIdx].DTCTestResultCount )
	{
		Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "ECU %-8X  received unexpected response to SID $19 $06 DTC %c%02X%02X %02X ExtData $92 (TestResult) request (Response not stored)\n",
		      GetEcuId ( EcuIdx ),
		      DTCTypeCharacter[(gstResponse[EcuIdx].DTCTestResultCurrent.Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
		      gstResponse[EcuIdx].DTCTestResultCurrent.Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
		      gstResponse[EcuIdx].DTCTestResultCurrent.Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
		      gstResponse[EcuIdx].DTCTestResultCurrent.Record.Dtc.LowByte );                                 // Failure Type Byte

		eRetCode = ERRORS;
	}

	return eRetCode;
}


/*******************************************************************************
**
**  Function:  SaveDTCSMADData
**
**  Purpose:   Copy from gstResponse[EcuIdx].DTCSMADCurrent into the matching DTC
**             in gstResponse[EcuIdx].DTCSMADList.
**
*******************************************************************************/
STATUS SaveDTCSMADData ( BYTE  EcuIdx,
                         WORD *pIndex )
{
	STATUS        eRetCode = PASS;  // function result

	DTCSMAD      *pSMAD = NULL;


	// find current DTC in DTC SMAD list and copy the data
	for ( *pIndex = 0;
	      *pIndex < gstResponse[EcuIdx].DTCSMADCount;
	      (*pIndex)++ )
	{
		if ( gstResponse[EcuIdx].DTCSMADList[*pIndex].Record.Dtc.HighByte == gstResponse[EcuIdx].DTCSMADCurrent.Record.Dtc.HighByte &&
		     gstResponse[EcuIdx].DTCSMADList[*pIndex].Record.Dtc.MidByte  == gstResponse[EcuIdx].DTCSMADCurrent.Record.Dtc.MidByte &&
		     gstResponse[EcuIdx].DTCSMADList[*pIndex].Record.Dtc.LowByte  == gstResponse[EcuIdx].DTCSMADCurrent.Record.Dtc.LowByte )
		{
			// save the data to the main SMAD list
			gstResponse[EcuIdx].DTCSMADList[*pIndex].NumCounts   = gstResponse[EcuIdx].DTCSMADCurrent.NumCounts;
			gstResponse[EcuIdx].DTCSMADList[*pIndex].ActRatio    = gstResponse[EcuIdx].DTCSMADCurrent.ActRatio;

			// if this is a test phase that needs it's data saved for later testing,
			// also save the data to that phase's copy of the SMAD list
			if ( (geTestPhase == eTestInUseCounters       && gTestSubsection == 10) ||
			     (geTestPhase == eTestPerformanceCounters && gTestSubsection == 7)  ||
			     (geTestPhase == eTestPerformanceCounters && gTestSubsection == 12) )
			{
				if ( geTestPhase == eTestInUseCounters && gTestSubsection == 10 )
				{
					pSMAD = &gstResponse[EcuIdx].DTCSMAD_Test10_10[0];
				}
				else if ( geTestPhase == eTestPerformanceCounters && gTestSubsection == 7 )
				{
					pSMAD = &gstResponse[EcuIdx].DTCSMAD_Test11_7[0];
				}
				else if ( geTestPhase == eTestPerformanceCounters && gTestSubsection == 12 )
				{
					pSMAD = &gstResponse[EcuIdx].DTCSMAD_Test11_12[0];
				}

				pSMAD[*pIndex].Record.Dtc.HighByte = gstResponse[EcuIdx].DTCSMADCurrent.Record.Dtc.HighByte;
				pSMAD[*pIndex].Record.Dtc.MidByte  = gstResponse[EcuIdx].DTCSMADCurrent.Record.Dtc.MidByte;
				pSMAD[*pIndex].Record.Dtc.LowByte  = gstResponse[EcuIdx].DTCSMADCurrent.Record.Dtc.LowByte;

				pSMAD[*pIndex].NumCounts   = gstResponse[EcuIdx].DTCSMADCurrent.NumCounts;
				pSMAD[*pIndex].ActRatio    = gstResponse[EcuIdx].DTCSMADCurrent.ActRatio;
			}
			break;
		}
	}

	// if the current DTC was not in the DTC SMAD list, warn
	if ( *pIndex >= gstResponse[EcuIdx].DTCSMADCount )
	{
		Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "ECU %-8X  received unexpected response to SID $19 $06 DTC %c%02X%02X %02X ExtData $93 (SMAD) request (Response not stored)\n",
		       GetEcuId ( EcuIdx ),
		       DTCTypeCharacter[(gstResponse[EcuIdx].DTCSMADCurrent.Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
		       gstResponse[EcuIdx].DTCSMADCurrent.Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
		       gstResponse[EcuIdx].DTCSMADCurrent.Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
		       gstResponse[EcuIdx].DTCSMADCurrent.Record.Dtc.LowByte );                                 // Failure Type Byte

		eRetCode = ERRORS;
	}

	return eRetCode;
}


/*******************************************************************************
**
**  Function:  LogDTCIUMPRData
**
**  Purpose:
**
*******************************************************************************/
void LogDTCIUMPRData ( BYTE  EcuIdx,
                       WORD  Index )
{
	char  String[15];


	if ( gstResponse[EcuIdx].bDTCIUMPRSupported == TRUE &&
	     gstResponse[EcuIdx].DTCIUMPRCount != 0 )
	{
		if ( geTestPhase == eTestInUseCounters && gTestSubsection == 10 )
		{
			strcpy_s ( String, 15, "            " );
		}
		else
		{
			sprintf_s ( String, 15, "ECU %-8X",
		              GetEcuId ( EcuIdx ) );
		}

		// log IUMPR names and values
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "%s  IUMPR DTC %c%02X%02X %02X  %-11s = %3d\n"
		      "                                               %-11s = %3d\n",
		      String ,
		      DTCTypeCharacter[(gstResponse[EcuIdx].DTCIUMPRList[Index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
		      gstResponse[EcuIdx].DTCIUMPRList[Index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
		      gstResponse[EcuIdx].DTCIUMPRList[Index].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
		      gstResponse[EcuIdx].DTCIUMPRList[Index].Record.Dtc.LowByte,                                   // Failure Type Byte
		      szIumprData1,
		      gstResponse[EcuIdx].DTCIUMPRList[Index].CompCounts,
		      szIumprData2,
		      gstResponse[EcuIdx].DTCIUMPRList[Index].CondCounts );
	}
}


/*******************************************************************************
**
**  Function:  LogDTCTestResultData
**
**  Purpose:
**
*******************************************************************************/
void LogDTCTestResultData ( BYTE  EcuIdx,
                            WORD  Index )
{
	char  String[15];


	if ( gstResponse[EcuIdx].bDTCTestResultSupported == TRUE &&
	     gstResponse[EcuIdx].DTCTestResultCount != 0 )
	{
		if ( geTestPhase == eTestInUseCounters && gTestSubsection == 10 )
		{
			strcpy_s ( String, 15, "            " );
		}
		else
		{
			sprintf_s ( String, 15, "ECU %-8X",
		              GetEcuId ( EcuIdx ) );
		}

		// log TestResult names and values
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "%s  Test Result DTC %c%02X%02X %02X  %-22s = %3d\n",
		      String,
		      DTCTypeCharacter[(gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
		      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
		      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
		      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.LowByte,                                   // Failure Type Byte
		      szTestResultData1,
		      gstResponse[EcuIdx].DTCTestResultList[Index].NumTstRslts );

		for ( BYTE i = 0; i < gstResponse[EcuIdx].DTCTestResultList[Index].NumTstRslts; i++ )
		{
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "                                        Test Result %d:\n"
			      "                                                     %-22s = %5d\n"
			      "                                                     %-22s = %5d\n"
			      "                                                     %-22s = %5d\n"
			      "                                                     %-22s = %5d\n",
			      i+1,
			      szTestResultData2,
			      gstResponse[EcuIdx].DTCTestResultList[Index].Result[i].UnitScaling,
			      szTestResultData3,
			      gstResponse[EcuIdx].DTCTestResultList[Index].Result[i].TestValue,
			      szTestResultData4,
			      gstResponse[EcuIdx].DTCTestResultList[Index].Result[i].MinTstLmt,
			      szTestResultData5,
			      gstResponse[EcuIdx].DTCTestResultList[Index].Result[i].MaxTstLmt );

			if ( (geTestPhase == eTestPendingDTC    && gTestSubsection == 5) ||  // Test 6.5 or
			     (geTestPhase == eTestInUseCounters && gTestSubsection == 7) )   // Test 10.7
			{
				if ( gstResponse[EcuIdx].DTCTestResultList[Index].Result[i].TestValue != 0 )
				{
					Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %-8X  Test Result DTC %c%02X%02X %02X  Test Value should be 0\n",
					      GetEcuId ( EcuIdx ),
					      DTCTypeCharacter[(gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
					      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
					      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
					      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.LowByte );                                 // Failure Type Byte
				}

				if ( gstResponse[EcuIdx].DTCTestResultList[Index].Result[i].MinTstLmt != 0 )
				{
					Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %-8X  Test Result DTC %c%02X%02X %02X  Minimum Test Limit should be 0\n",
					      GetEcuId ( EcuIdx ),
					      DTCTypeCharacter[(gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
					      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
					      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
					      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.LowByte );                                 // Failure Type Byte
				}

				if ( gstResponse[EcuIdx].DTCTestResultList[Index].Result[i].MaxTstLmt != 0 )
				{
					Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %-8X  Test Result DTC %c%02X%02X %02X  Maximum Test Limit should be 0\n",
					      GetEcuId ( EcuIdx ),
					      DTCTypeCharacter[(gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
					      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
					      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
					      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.LowByte );                                 // Failure Type Byte
				}
			}
			else if ( geTestPhase == eTestPerformanceCounters   && gTestSubsection == 6 )   // Test 11.6
			{
				if ( gstResponse[EcuIdx].DTCTestResultList[Index].Result[i].TestValue < gstResponse[EcuIdx].DTCTestResultList[Index].Result[i].MinTstLmt )
				{
					Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %-8X  Test Result DTC %c%02X%02X %02X  Test Value must be greater than Minimum Test Limit\n",
					      GetEcuId ( EcuIdx ),
					      DTCTypeCharacter[(gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
					      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
					      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
					      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.LowByte );                                 // Failure Type Byte
				}

				if ( gstResponse[EcuIdx].DTCTestResultList[Index].Result[i].TestValue > gstResponse[EcuIdx].DTCTestResultList[Index].Result[i].MaxTstLmt )
				{
					Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %-8X  Test Result DTC %c%02X%02X %02X  Test Value must be less than Maximum Test Limit\n",
					      GetEcuId ( EcuIdx ),
					      DTCTypeCharacter[(gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
					      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
					      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
					      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.LowByte );                                 // Failure Type Byte
				}
			}
		}

		if ( !(geTestPhase == eTestInUseCounters && gTestSubsection == 10) &&
		     gstResponse[EcuIdx].DTCTestResultList[Index].NumTstRslts > 1 )
		{
			Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %-8X  Test Result DTC %c%02X%02X %02X  has more than 1 test result\n",
			      GetEcuId ( EcuIdx ),
			      DTCTypeCharacter[(gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
			      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
			      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
			      gstResponse[EcuIdx].DTCTestResultList[Index].Record.Dtc.LowByte );                                 // Failure Type Byte
		}
	}
}


/*******************************************************************************
**
**  Function:  LogDTCSMADData
**
**  Purpose:
**
*******************************************************************************/
void LogDTCSMADData ( BYTE  EcuIdx,
                      WORD  Index )
{
	char  String[15];


	if ( gstResponse[EcuIdx].bDTCSMADSupported == TRUE &&
	     gstResponse[EcuIdx].DTCSMADCount != 0 )
	{
		if ( geTestPhase == eTestInUseCounters && gTestSubsection == 10 )
		{
			strcpy_s ( String, 15, "            " );
		}
		else
		{
			sprintf_s ( String, 15, "ECU %-8X",
		              GetEcuId ( EcuIdx ) );
		}

		// log SMAD names and values
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "%s  SMAD DTC %c%02X%02X %02X  %-16s = %d\n"
		      "                                              %-16s = %-.3f\n",
		      String,
		      DTCTypeCharacter[(gstResponse[EcuIdx].DTCSMADList[Index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
		      gstResponse[EcuIdx].DTCSMADList[Index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
		      gstResponse[EcuIdx].DTCSMADList[Index].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
		      gstResponse[EcuIdx].DTCSMADList[Index].Record.Dtc.LowByte,                                   // Failure Type Byte
		      szSmadData1,
		      gstResponse[EcuIdx].DTCSMADList[Index].NumCounts,
		      szSmadData2,
		      gstResponse[EcuIdx].DTCSMADList[Index].ActRatio );
	}
}




/*******************************************************************************
**
**  Function:  VerifyDTCIMReadinessList
**
**  Purpose:   
**
*******************************************************************************/
STATUS VerifyDTCIMReadinessList ( void )
{
	BYTE     EcuIdx;
	REQ_MSG  stReqMsg;
	STATUS   eRetCode = PASS;

	BYTE     Index;


	//.clear previous count for all ECUs
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		gstResponse[EcuIdx].DTCIMReadinessCount = 0;
	}

	for ( Index = 0x01;
	      Index <= MAX_IM_READINESS_GROUP;
	      Index++ )
	{
		stReqMsg.SID     = 0x19;
		stReqMsg.NumIds  = 3;
		stReqMsg.u.ID[0] = 0x56;   // ReportDTCInformationByDTCReadinessGroupIdentifier
		stReqMsg.u.ID[1] = 0x33;   // FunctionalGroupIdentifier: Emission
		stReqMsg.u.ID[2] = Index;  // DTCReadinessGroupIdentifier

		// Request the DTCs which support Extended Data $xx
		if ( RequestSID ( &stReqMsg, REQ_MSG_NORMAL|REQ_MSG_ALLOW_NO_RESPONSE ) == FAIL )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "SID $19 $56 $33 $%02X (IM Readiness) request failed\n",
			      Index );
			eRetCode = FAIL;
		}
		else
		{
			for ( EcuIdx = 0;
			      EcuIdx < gNumOfECUs;
			      EcuIdx++ )
			{
				// Save the responses
				SaveDTCIMReadinessList ( EcuIdx );

				// Display the responses
				LogDTCIMReadinessList ( EcuIdx, Index );
			}
		}
	}

	return eRetCode;
}


/*******************************************************************************
**
**  Function:  SaveDTCIMReadinessList
**
**  Purpose:   Copy from gstResponse[EcuIdx].DTCIMRCurrent into the matching DTC
**             in gstResponse[EcuIdx].DTCSMADList.
**
*******************************************************************************/
void SaveDTCIMReadinessList ( BYTE  EcuIdx )
{
	WORD      Index1;
	WORD      Index2;


	// find current DTC in DTC IM Readiness list and copy the data
	for ( Index1 = 0;
	      Index1 < gstResponse[EcuIdx].DTCIMReadinessCurrentCount;
	      Index1++ )
	{
		for ( Index2 = 0;
		      Index2 < gstResponse[EcuIdx].DTCIMReadinessCount;
		      Index2++ )
		{
			if ( gstResponse[EcuIdx].DTCIMReadinessList[Index2].Record.Dtc.HighByte == gstResponse[EcuIdx].DTCIMReadinessCurrent[Index1].Record.Dtc.HighByte &&
			     gstResponse[EcuIdx].DTCIMReadinessList[Index2].Record.Dtc.MidByte  == gstResponse[EcuIdx].DTCIMReadinessCurrent[Index1].Record.Dtc.MidByte  &&
			     gstResponse[EcuIdx].DTCIMReadinessList[Index2].Record.Dtc.LowByte  == gstResponse[EcuIdx].DTCIMReadinessCurrent[Index1].Record.Dtc.LowByte  &&
			     gstResponse[EcuIdx].DTCIMReadinessList[Index2].RGID                == gstResponse[EcuIdx].DTCIMReadinessCurrent[Index1].RGID )
			{
				break;
			}
		}  // end for ( Index2 )

		// if the current DTC was not in the DTC IM Readiness list, add it
		if ( Index2 >= gstResponse[EcuIdx].DTCIMReadinessCount )
		{
			// save the data to the main IM Readiness list
			memcpy ( &gstResponse[EcuIdx].DTCIMReadinessList[Index2],
			         &gstResponse[EcuIdx].DTCIMReadinessCurrent[Index1],
			         sizeof ( DTCIMREADINESS ) );

			gstResponse[EcuIdx].DTCIMReadinessCount++;
		}
	}  // end for ( Index1 )
}


/*******************************************************************************
**
**  Function:  LogDTCIMReadinessList
**
**  Purpose:
**
*******************************************************************************/
void LogDTCIMReadinessList ( BYTE EcuIdx,
                             BYTE Index )
{
	WORD  DTCIndex;


	if ( gstResponse[EcuIdx].bDTCIMReadinessSupported == TRUE )
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "ECU %-8X  supports the following DTCs with IM Readiness Group $%02X (%s):\n",
		      GetEcuId ( EcuIdx ),
		      Index,
		      szIMReadinessGroup[Index] );

		// log IM Readiness names and values
		for ( DTCIndex = 0;
		      DTCIndex < gstResponse[EcuIdx].DTCIMReadinessCount;
		      DTCIndex++ )
		{
			if ( gstResponse[EcuIdx].DTCIMReadinessList[DTCIndex].RGID == Index )
			{
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "              %c%02X%02X %02X (Status = $%02X)\n",
				      DTCTypeCharacter[(gstResponse[EcuIdx].DTCIMReadinessList[DTCIndex].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				      gstResponse[EcuIdx].DTCIMReadinessList[DTCIndex].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				      gstResponse[EcuIdx].DTCIMReadinessList[DTCIndex].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
				      gstResponse[EcuIdx].DTCIMReadinessList[DTCIndex].Record.Dtc.LowByte,                                   // Failure Type Byte
				      gstResponse[EcuIdx].DTCIMReadinessList[DTCIndex].Record.Status );                                      // Status of DTC
			}  // end if ( RGID == Index )
		}  // end for ( DTCIndex )
	}  // end if ( bDTCIMReadinessSupported == TRUE )
	else
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "ECU %-8X  supports no DTCs with IM Readiness Group $%02X (%s)\n",
		      GetEcuId ( EcuIdx ),
		      Index,
		      szIMReadinessGroup[Index] );
	}
}




/*******************************************************************************
**
**  Function:  isDTCinList
**
**  Purpose:
**
*******************************************************************************/
boolean isDTCinList           ( BYTE  EcuIdx,
                                DTC  *pID,
                                BYTE  ExtDataID )
{
	WORD     DTCIndex;
	DTC     *pDTC = NULL ;
	WORD     DTCCount;


	// Display the responses
	if ( ExtDataID == 0x91 )
	{
		DTCCount = gstResponse[EcuIdx].DTCIUMPRCount;
	}
	else if ( ExtDataID == 0x92 )
	{
		DTCCount = gstResponse[EcuIdx].DTCTestResultCount;
	}
	else if ( ExtDataID == 0x93 )
	{
		DTCCount = gstResponse[EcuIdx].DTCSMADCount;
	}

	for ( DTCIndex = 0;
	      DTCIndex < DTCCount;
	      DTCIndex++ )
	{
		if ( ExtDataID == 0x91 )
		{
			pDTC = &gstResponse[EcuIdx].DTCIUMPRList[DTCIndex].Record.Dtc;
		}
		else if ( ExtDataID == 0x92 )
		{
			pDTC = &gstResponse[EcuIdx].DTCTestResultList[DTCIndex].Record.Dtc;
		}
		else if ( ExtDataID == 0x93 )
		{
			pDTC = &gstResponse[EcuIdx].DTCSMADList[DTCIndex].Record.Dtc;
		}

		if ( pID->HighByte == pDTC->HighByte &&
		     pID->MidByte  == pDTC->MidByte  &&
		     pID->LowByte  == pDTC->LowByte )
		{
			return TRUE;
		}
	}

	return FALSE;
}
