/*******************************************************************************
********************************************************************************
**
**  Copyright(c) 2022, Alliance for Automotive Innovation
**  Used only under license from the Alliance for Automotive Innovation. All Rights Reserved.
**
**  Project:  J1699-3
**  FileName: TestToVerifyInUseCounters.c
**  Author:   EnGenius
**  Date:     2/25/2022
**  Email:    <support@autosinnovate.org>
**
**  Purpose:  SAE J1699-3 Vehicle OBD II Compliance Test Cases Source Code.
**            This source code is intended to run the tests described in
**            the SAE J1699-3 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:  12/07/2022  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
#include "ScreenOutput.h"  // j1699 project console output declarations


// Functio Prototypes
STATUS LogIpt        ( void );
STATUS GetIptData    ( BYTE  EcuIdx,
                       IPT *pIpt );
STATUS VerifyIPTData ( BOOL *pbIptSupported );
STATUS LogInf12      ( void );
STATUS VerifyInf12   ( void );
STATUS StartLogFile  ( BOOL *pbTestReEntered );
STATUS RunDynamicTest10 ( unsigned long EngineStartTimestampMsecs );
void   DisplayDynamicTest10Results ( void );

extern void   LogVersionInformation ( SCREENOUTPUT bDisplay, LOGOUTPUT    bLog );
extern STATUS VerifyLogFile   ( FILE *hFileHandle );  // upon re-entering Dynamic test, verifies previous data
extern STATUS AppendLogFile   ( void );
extern STATUS VerifyVINData   ( BYTE EcuIdx );
extern STATUS VerifyCALIDData ( BYTE EcuIdx, unsigned long Inf3NumItems );
extern STATUS VerifyCVNData   ( BYTE EcuIdx, unsigned long Inf5NumItems );
extern STATUS VerifyInf8Data  ( BYTE EcuIdx );
extern STATUS VerifyInfBData  ( BYTE EcuIdx );
extern STATUS VerifyInf12Data ( BYTE EcuIdx );
extern STATUS ReadVIN         ( void );
extern STATUS ReadCALIDs      ( void );
extern STATUS PrintCALIDs     ( void );
extern STATUS ReadCVNs        ( void );


// Variables
extern unsigned short gProtocolSrchIdx;

extern IPT  *pTest10_10_Ipt;
extern FILE *gLogFileHandle;
extern char *gComplianceTestTypeStrings[];
extern char *gDisplayStrings[];
extern char *gVehicleTypeStrings[];
extern char *gPwrTrnTypeStrings[];
extern char *gEngineTypeStrings[];


/*******************************************************************************
**  Test 10.x dynamic screen mapping
*******************************************************************************/
#define SPACE             7
#define ECU_WIDTH         7
#define NUM_WIDTH         5
#define STRING_WIDTH      7

#define COL1              18

#define COND_IDLE_ROW     7
#define COND_SPEED_ROW    9
#define COND_PHEV_ROW     11

#define CONDITIONS_ROW    13

#define IDLE_TIME_ROW     15
#define SPEED_ROW         15
#define RUN_TIME_ROW      16
#define TOTAL_TIME_ROW    17
#define PHEV_TIME_ROW     18
#define TEST_STATUS_ROW   18
#define ECU_ID_ROW        20
#define INI_OBD_COND_ROW  22
#define CUR_OBD_COND_ROW  23
#define INI_IGN_CNT_ROW   25
#define CUR_IGN_CNT_ROW   26
#define INI_FEO_CNT_ROW   28
#define CUR_FEO_CNT_ROW   29
#define BOTTOM_ROW        31

StaticTextElement  _string_elements_1st_9[] =
{
	{"Drive the vehicle in the following manner, so that the OBD Condition Counter will increment:", 1, 0}
};

StaticTextElement  _string_elements_1st_10[] =
{
	{"Drive the vehicle in the following manner, at an altitude < 8000 ft", 1, 0},
	{"(BARO < 22 in Hg) and ambient temperature > or = 20 deg F,", 1, 1},
	{"so that the OBD Condition Counter will increment:", 1, 2}
};

StaticTextElement  _string_elements10[] =
{
	{"NOTE: Some powertrain control systems have engine controls that can start and", 1, 4},
	{"stop the engine without regard to ignition position.", 1, 5},

	{"- Continuous time > or = 30 seconds with vehicle speed < or = 1 MPH", 1, COND_IDLE_ROW},
	{"  and accelerator pedal released.", 1, 8},
	{"- Cumulative time > or = 300 seconds with vehicle speed > 25MPH/40KPH.", 1, COND_SPEED_ROW},
	{"- Cumulative time since engine start > or = 600 seconds.", 1, 10},

	{"OBD Drive Cycle Status", 1, 13},
	{"BARO:    in Hg      AAT:     C (    F)", 38, CONDITIONS_ROW},

	{" 30 Seconds Idle Timer:", 1, IDLE_TIME_ROW},
	{"300 Seconds at speeds > 25MPH/40KPH Timer:", 1, RUN_TIME_ROW},
	{"600 Seconds Total Drive Timer:", 1, TOTAL_TIME_ROW},

	{"PAGE:    ECU ID:", 1, ECU_ID_ROW},

	{"Initial OBDCOND:", 1, INI_OBD_COND_ROW},
	{"Current OBDCOND:", 1, CUR_OBD_COND_ROW},

	{"Initial IGNCTR:", 1, INI_IGN_CNT_ROW},
	{"Current IGNCTR:", 1, CUR_IGN_CNT_ROW},

	{"Speed:", 60, SPEED_ROW},
	{"       MPH    KPH", 60, SPEED_ROW+1},

	{"Test Status:", 60, TEST_STATUS_ROW},

	{"Press ESC to abort the Drive Cycle and fail the test", 1, BOTTOM_ROW},
	{"", 0, BOTTOM_ROW+1}
};

StaticTextElement  _string_elements10_RPM[] =
{
	{"- Continuous time > or = 30 seconds with engine speed < 1150 RPM.  ", 1, COND_IDLE_ROW},

	{"- Cumulative time > or = 300 seconds with engine speed > or = 1150 RPM.", 1, COND_SPEED_ROW},

	{"300 Seconds at speeds > 1150  RPM  Timer:", 1, RUN_TIME_ROW},

	{"       RPM       ", 60, SPEED_ROW+1}
};

StaticTextElement  _string_elements10_PHEV[] =
{
	{"- Cumulative fueled engine time since engine start > or = 10 seconds for PHEV.", 1, COND_PHEV_ROW},

	{" 10 Seconds Fueled Engine Operation (PHEV only):", 1, PHEV_TIME_ROW},

	{"Initial FEOCNTR:", 1, INI_FEO_CNT_ROW},
	{"Current FEOCNTR:", 1, CUR_FEO_CNT_ROW}
};

const int _num_string_elements_1st_9  = sizeof ( _string_elements_1st_9)  / sizeof ( _string_elements_1st_9[0] );
const int _num_string_elements_1st_10 = sizeof ( _string_elements_1st_10) / sizeof ( _string_elements_1st_10[0] );
const int _num_string_elements10      = sizeof ( _string_elements10)      / sizeof ( _string_elements10[0] );
const int _num_string_elements10_RPM  = sizeof ( _string_elements10_RPM)  / sizeof ( _string_elements10_RPM[0] );
const int _num_string_elements10_PHEV = sizeof ( _string_elements10_PHEV) / sizeof ( _string_elements10_PHEV[0] );

DynamicValueElement  _dynamic_elements10[] =
{
	{26, IDLE_TIME_ROW, NUM_WIDTH},                 // (Index 0) 30 seconds idle drive cycle timer
	{45, RUN_TIME_ROW, NUM_WIDTH},                  // (Index 1) 300 seconds at speeds greater then 25 MPH timer
	{33, TOTAL_TIME_ROW, NUM_WIDTH},                // (Index 2) 600 seconds total drive timer
	{51, PHEV_TIME_ROW, NUM_WIDTH},                 // (Index 3) 10 Seconds Fueled Engine Operation (PHEV only)

	{COL1 + 0*SPACE, ECU_ID_ROW, ECU_WIDTH},        // (Index 4) ECU ID 1
	{COL1 + 1*SPACE, ECU_ID_ROW, ECU_WIDTH},        // (Index 5) ECU ID 2
	{COL1 + 2*SPACE, ECU_ID_ROW, ECU_WIDTH},        // (Index 6) ECU ID 3
	{COL1 + 3*SPACE, ECU_ID_ROW, ECU_WIDTH},        // (Index 7) ECU ID 4
	{COL1 + 4*SPACE, ECU_ID_ROW, ECU_WIDTH},        // (Index 8) ECU ID 5
	{COL1 + 5*SPACE, ECU_ID_ROW, ECU_WIDTH},        // (Index 9) ECU ID 6
	{COL1 + 6*SPACE, ECU_ID_ROW, ECU_WIDTH},        // (Index 10) ECU ID 7
	{COL1 + 7*SPACE, ECU_ID_ROW, ECU_WIDTH},        // (Index 11) ECU ID 8

	{COL1 + 0*SPACE, INI_OBD_COND_ROW, NUM_WIDTH},  // (Index 12) initial obdcond, ECU 1
	{COL1 + 1*SPACE, INI_OBD_COND_ROW, NUM_WIDTH},  // (Index 13) initial obdcond, ECU 2
	{COL1 + 2*SPACE, INI_OBD_COND_ROW, NUM_WIDTH},  // (Index 14) initial obdcond, ECU 3
	{COL1 + 3*SPACE, INI_OBD_COND_ROW, NUM_WIDTH},  // (Index 15) initial obdcond, ECU 4
	{COL1 + 4*SPACE, INI_OBD_COND_ROW, NUM_WIDTH},  // (Index 16) initial obdcond, ECU 5
	{COL1 + 5*SPACE, INI_OBD_COND_ROW, NUM_WIDTH},  // (Index 17) initial obdcond, ECU 6
	{COL1 + 6*SPACE, INI_OBD_COND_ROW, NUM_WIDTH},  // (Index 18) initial obdcond, ECU 7
	{COL1 + 7*SPACE, INI_OBD_COND_ROW, NUM_WIDTH},  // (Index 19) initial obdcond, ECU 8

	{COL1 + 0*SPACE, CUR_OBD_COND_ROW, NUM_WIDTH},  // (Index 20) current obdcond, ECU 1
	{COL1 + 1*SPACE, CUR_OBD_COND_ROW, NUM_WIDTH},  // (Index 21) current obdcond, ECU 2
	{COL1 + 2*SPACE, CUR_OBD_COND_ROW, NUM_WIDTH},  // (Index 22) current obdcond, ECU 3
	{COL1 + 3*SPACE, CUR_OBD_COND_ROW, NUM_WIDTH},  // (Index 23) current obdcond, ECU 4
	{COL1 + 4*SPACE, CUR_OBD_COND_ROW, NUM_WIDTH},  // (Index 24) current obdcond, ECU 5
	{COL1 + 5*SPACE, CUR_OBD_COND_ROW, NUM_WIDTH},  // (Index 25) current obdcond, ECU 6
	{COL1 + 6*SPACE, CUR_OBD_COND_ROW, NUM_WIDTH},  // (Index 26) current obdcond, ECU 7
	{COL1 + 7*SPACE, CUR_OBD_COND_ROW, NUM_WIDTH},  // (Index 27) current obdcond, ECU 8

	{COL1 + 0*SPACE, INI_IGN_CNT_ROW, NUM_WIDTH},   // (Index 28) initial ignctr, ECU 1
	{COL1 + 1*SPACE, INI_IGN_CNT_ROW, NUM_WIDTH},   // (Index 29) initial ignctr, ECU 2
	{COL1 + 2*SPACE, INI_IGN_CNT_ROW, NUM_WIDTH},   // (Index 30) initial ignctr, ECU 3
	{COL1 + 3*SPACE, INI_IGN_CNT_ROW, NUM_WIDTH},   // (Index 31) initial ignctr, ECU 4
	{COL1 + 4*SPACE, INI_IGN_CNT_ROW, NUM_WIDTH},   // (Index 32) initial ignctr, ECU 5
	{COL1 + 5*SPACE, INI_IGN_CNT_ROW, NUM_WIDTH},   // (Index 33) initial ignctr, ECU 6
	{COL1 + 6*SPACE, INI_IGN_CNT_ROW, NUM_WIDTH},   // (Index 34) initial ignctr, ECU 7
	{COL1 + 7*SPACE, INI_IGN_CNT_ROW, NUM_WIDTH},   // (Index 35) initial ignctr, ECU 8

	{COL1 + 0*SPACE, CUR_IGN_CNT_ROW, NUM_WIDTH},   // (Index 36) current ignctr, ECU 1
	{COL1 + 1*SPACE, CUR_IGN_CNT_ROW, NUM_WIDTH},   // (Index 37) current ignctr, ECU 2
	{COL1 + 2*SPACE, CUR_IGN_CNT_ROW, NUM_WIDTH},   // (Index 38) current ignctr, ECU 3
	{COL1 + 3*SPACE, CUR_IGN_CNT_ROW, NUM_WIDTH},   // (Index 39) current ignctr, ECU 4
	{COL1 + 4*SPACE, CUR_IGN_CNT_ROW, NUM_WIDTH},   // (Index 40) current ignctr, ECU 5
	{COL1 + 5*SPACE, CUR_IGN_CNT_ROW, NUM_WIDTH},   // (Index 41) current ignctr, ECU 6
	{COL1 + 6*SPACE, CUR_IGN_CNT_ROW, NUM_WIDTH},   // (Index 42) current ignctr, ECU 7
	{COL1 + 7*SPACE, CUR_IGN_CNT_ROW, NUM_WIDTH},   // (Index 43) current ignctr, ECU 8

	{COL1 + 0*SPACE, INI_FEO_CNT_ROW, NUM_WIDTH},   // (Index 44) initial feocntr, ECU 1
	{COL1 + 1*SPACE, INI_FEO_CNT_ROW, NUM_WIDTH},   // (Index 45) initial feocntr, ECU 2
	{COL1 + 2*SPACE, INI_FEO_CNT_ROW, NUM_WIDTH},   // (Index 46) initial feocntr, ECU 3
	{COL1 + 3*SPACE, INI_FEO_CNT_ROW, NUM_WIDTH},   // (Index 47) initial feocntr, ECU 4
	{COL1 + 4*SPACE, INI_FEO_CNT_ROW, NUM_WIDTH},   // (Index 48) initial feocntr, ECU 5
	{COL1 + 5*SPACE, INI_FEO_CNT_ROW, NUM_WIDTH},   // (Index 49) initial feocntr, ECU 6
	{COL1 + 6*SPACE, INI_FEO_CNT_ROW, NUM_WIDTH},   // (Index 50) initial feocntr, ECU 7
	{COL1 + 7*SPACE, INI_FEO_CNT_ROW, NUM_WIDTH},   // (Index 51) initial feocntr, ECU 8

	{COL1 + 0*SPACE, CUR_FEO_CNT_ROW, NUM_WIDTH},   // (Index 52) current feocntr, ECU 1
	{COL1 + 1*SPACE, CUR_FEO_CNT_ROW, NUM_WIDTH},   // (Index 53) current feocntr, ECU 2
	{COL1 + 2*SPACE, CUR_FEO_CNT_ROW, NUM_WIDTH},   // (Index 54) current feocntr, ECU 3
	{COL1 + 3*SPACE, CUR_FEO_CNT_ROW, NUM_WIDTH},   // (Index 55) current feocntr, ECU 4
	{COL1 + 4*SPACE, CUR_FEO_CNT_ROW, NUM_WIDTH},   // (Index 56) current feocntr, ECU 5
	{COL1 + 5*SPACE, CUR_FEO_CNT_ROW, NUM_WIDTH},   // (Index 57) current feocntr, ECU 6
	{COL1 + 6*SPACE, CUR_FEO_CNT_ROW, NUM_WIDTH},   // (Index 58) current feocntr, ECU 7
	{COL1 + 7*SPACE, CUR_FEO_CNT_ROW, NUM_WIDTH},   // (Index 59) current feocntr, ECU 8

	{72, IDLE_TIME_ROW, 3},                         // (Index 60) RPM label
	{67, IDLE_TIME_ROW, NUM_WIDTH},                 // (Index 61) RPM value
	{67, SPEED_ROW, NUM_WIDTH},                     // (Index 62) Speed MPH
	{67 + SPACE, SPEED_ROW, NUM_WIDTH},             // (Index 63) Speed KPH

	{44, CONDITIONS_ROW, 2},                        // (Index 64) Barometric Pressure
	{63, CONDITIONS_ROW, 3},                        // (Index 65) Ambient Air Temperature C
	{70, CONDITIONS_ROW, 3},                        // (Index 66) Ambient Air Temperature F

	{73, TEST_STATUS_ROW, STRING_WIDTH},            // (Index 67) Test Status
	{6,  ECU_ID_ROW, 2}                             // (Index 68) ECU Page number
};

const int _num_dynamic_elements10 = sizeof ( _dynamic_elements10 ) / sizeof ( _dynamic_elements10[0] );

#define IDLE_TIMER          0
#define SPEED_25_MPH_TIMER  1
#define TOTAL_DRIVE_TIMER   2
#define PHEV_TIMER          3
#define ECU_ID              4

#define INITIAL_OBDCOND     12
#define CURRENT_OBDCOND     20
#define INITIAL_IGNCTR      28
#define CURRENT_IGNCTR      36
#define INITIAL_FEOCNTR     44
#define CURRENT_FEOCNTR     52

#define RPM_LABEL_INDEX     60
#define RPM_INDEX           61
#define SPEED_INDEX_MPH     62
#define SPEED_INDEX_KPH     63
#define BARO_PRESS_INDEX    64
#define AMB_AIR_TEMP_INDEX  65
#define TESTSTATUS_INDEX    67
#define ECUPAGE_INDEX       68

// used with the on-screen test status
#define SUB_TEST_NORMAL     0x00
#define SUB_TEST_FAIL       0x01
#define SUB_TEST_DTC        0x02
#define SUB_TEST_INITIAL    0x80


#define NORMAL_TEXT         -1
#define HIGHLIGHTED_TEXT    8

#define SetFieldDec(index,val)  (update_screen_dec(_dynamic_elements10,_num_dynamic_elements10,index,val))
#define SetFieldHex(index,val)  (update_screen_hex(_dynamic_elements10,_num_dynamic_elements10,index,val))
#define SetFieldText(index,val) (update_screen_text(_dynamic_elements10,_num_dynamic_elements10,index,val))


/*******************************************************************************
**
**  Function:  Test10_InUseCounters
**
**  Purpose:   Run Test 10, verify in-use counters with no faults
**
*******************************************************************************/
STATUS Test10_InUseCounters ( BOOL *pbTestReEntered )
{
	STATUS        eRetVal;
	unsigned long EngineStartTimestampMsecs;
	BOOL          bIptSupported = FALSE;

	BOOL          bSubTestFailed = FALSE;


//*******************************************************************************
//  Test 10.1 - perform drive cycle to clear I/M readiness bits
//*******************************************************************************
	gTestSubsection = 1;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Establish Communication, Engine Off)" );

	Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
	      "Turn key on without cranking or starting engine.\n" );

	// Engine should now be not running
	gbEngineRunning = FALSE;

	// Determine the OBD protocol to use
	gProtocolSrchIdx = 0;

	if ( DetermineOBDProtocol ( ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Protocol determination unsuccessful.\n" );
		Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
		return FAIL;
	}

	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 10.2 - Get SID $01 data
//*******************************************************************************
	gTestSubsection = 2;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify Diagnostic Data (Service $01), Engine Off)" );

	if ( VerifyPidSupportAndData ( TRUE ) == FAIL )
	{
		bSubTestFailed = TRUE;
	}

	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 10. - Get VIN and create (or append) log file
//*******************************************************************************
	gTestSubsection = 3;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify Vehicle Information (Service $09), Engine Off)" );

	eRetVal = StartLogFile ( pbTestReEntered );
	if ( eRetVal == FAIL )
	{
		Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
		return FAIL;
	}
	else if ( eRetVal == EXIT )
	{
		// Dynamic Tests are all done, just exit
		return EXIT;
	}

	// Add CALIDs to the log file
	if ( ReadCALIDs () != PASS )
	{
		bSubTestFailed = TRUE;
	}

	// Add CVNs to the log file
	if ( ReadCVNs () != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}

	if ( *pbTestReEntered == TRUE )
	{
		return PASS;
	}


//*******************************************************************************
//  Test 10.4 - Request current powertrain diagnostic data, Engine Off
//*******************************************************************************
	gTestSubsection = 4;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Request current powertrain diagnostic data (Service $01), Engine Off)" );

	if ( RequestIDSupportData ( PIDREQUEST, FALSE ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


	if ( *pbTestReEntered == TRUE )
	{
		return PASS;
	}


//*******************************************************************************
//  Test 10.5 - Clear DTCs, Engine Off
//*******************************************************************************
	gTestSubsection = 5;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Clear DTCs (Service $04), Engine Off)" );

	if ( ClearCodes ( ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 10.6 - Verify I/M readiness is "not ready"
//*******************************************************************************
	gbDTCHistorical = FALSE;

	gTestSubsection = 6;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify IM Readiness (Service $01), Engine Off)" );

	if ( VerifyIM_Ready ( ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 10.7 - Verify Monitor Test Results, Engine Off
//*******************************************************************************
	gTestSubsection = 7;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify Monitor Test Results (Service $06), Engine Off)" );

	if ( VerifyMIDSupportAndResults ( ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 10.8 - Verify No Pending DTCs, Engine Off
//*******************************************************************************
	gTestSubsection = 8;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify Pending DTCs (Service $07), Engine Off)" );

	if ( VerifyPendingDTCData ( ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 10.9 - Verify no confirmed DTCs, Engine Off
//*******************************************************************************
	gTestSubsection = 9;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify DTCs (Service $03), Engine Off)" );

	if ( VerifyStoredDTCData ( ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 10.10 - Verify Vehicle Information, Engine Off
//*******************************************************************************
	gTestSubsection = 10;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify Vehicle Information (Service $09), Engine Off)" );

	// Get in-use performance tracking (service 9 infotype 8 and B)
	if ( VerifyIPTData ( &bIptSupported ) != PASS || gbTestSubsectionFailed == TRUE )
	{
		if ( Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT,
		          "Verify IPT Stored Data.\n" ) == 'N' )
		{
			Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
			return FAIL;
		}
		bSubTestFailed = TRUE;
		gbTestSubsectionFailed = FALSE;
	}
	else if ( bIptSupported == FALSE &&
	          (((gstUserInput.eComplianceType == US_OBDII || gstUserInput.eComplianceType == HD_OBD) && gModelYear < 2005) ||
	           gstUserInput.eComplianceType == EOBD_NO_IUMPR ||
	           gstUserInput.eComplianceType == IOBD_NO_IUMPR ||
	           gstUserInput.eComplianceType == HD_IOBD_NO_IUMPR ||
	           gstUserInput.eComplianceType == OBDBr_NO_IUMPR) )
	{
		// allowed to skip the CARB Drive Cycle
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
		return PASS;
	}

	if ( gbPlugIn )
	{
		if ( LogInf12 ( ) != PASS )
		{
			bSubTestFailed = TRUE;
		}
	}

	Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
	      "Turn ignition off (engine off) for 60 seconds.\n" );

	Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
	      "Turn ignition to crank position and start engine.\n" );

	EngineStartTimestampMsecs = GetTickCount ( );

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 10.11 - Establish Communication, Engine Running
//*******************************************************************************
	// Engine should now be running
	gbEngineRunning = TRUE;

	gTestSubsection = 11;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Establish Communication, Engine Running)" );

	if ( DetermineOBDProtocol ( ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Protocol determination unsuccessful.\n" );
		Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
		return FAIL;
	}

	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 10.12 - Complete CARB drive cycle
//*******************************************************************************
	gTestSubsection = 12;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Complete CARB drive cycle)" );

	StopPeriodicMsg ( TRUE );
	Sleep ( gRequestDelayTimeMsecs );

	gbSuspendScreenOutput = TRUE;
	eRetVal = RunDynamicTest10 ( EngineStartTimestampMsecs );
	DisplayDynamicTest10Results ( );
	gbSuspendScreenOutput = FALSE;

	// re-start tester-present message
	StartPeriodicMsg ( );

	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( eRetVal == FAIL || gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Errors detected during dynamic test.  View Logfile for more details.\n" );

		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}

	Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
	      "Stop the vehicle in a safe location without turning the ignition off.\n" );


//*******************************************************************************
//  Test 10.13 - Verify Diagnostic Data, Engine Running
//*******************************************************************************
	// Verify engine warm data (service 1)
	gbEngineWarm = TRUE;

	gTestSubsection = 13;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify Diagnostic Data (Service $01), Engine Running)" );

	if ( VerifyPidSupportAndData ( FALSE ) == FAIL )
	{
		bSubTestFailed = TRUE;
	}

	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 10.14 - Verify Vehicle Information, Engine Running
//*******************************************************************************
	gTestSubsection = 14;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify Vehicle Information (Service $09), Engine Running)" );

	// Get in-use performance tracking (service 9 infotype 8)
	if ( LogIpt ( ) != PASS || gbTestSubsectionFailed == TRUE )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "SID $09 IPT (INF $08 or $0B) data error\n" );
		bSubTestFailed = TRUE;
	}

	if ( gbPlugIn )
	{
		if ( VerifyInf12 (  ) != PASS )
		{
			bSubTestFailed = TRUE;
		}
	}

	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}

	return  PASS;
}


/*******************************************************************************
**
**  Function:  StartLogFile
**
**  Purpose:   Create new log file or open and append to existing one.
**             use VIN as log filename.
**
*******************************************************************************/
STATUS StartLogFile ( BOOL *pbTestReEntered )
{
	char          Buffer[256];
	char          CharacterSet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 -_.";
	STATUS        eRetVal = PASS;
	FILE         *hTempFileHandle; // temporary file handle
	unsigned int  NumChar;


	// Request SID $9 INF support data
	if ( RequestIDSupportData ( INFREQUEST, TRUE ) != PASS )
	{
		return FAIL;
	}


	// Get VIN
	if ( ReadVIN ( ) == PASS )
	{
		// use VIN as log filename
		strcpy_s ( gLogFileNameString, MAX_LOGFILENAME, gVINString );
	}
	else
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Unable to obtain VIN\n" );
		do
		{
			Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, CUSTOM_PROMPT,
			      "Enter a valid filename (<%d characters) for the log file: ",
			      MAX_LOGFILENAME );
			_cgets_s ( Buffer, sizeof ( Buffer ), &NumChar );
			Log ( BLANK, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "\n" );
		} while ( strlen ( Buffer ) == 0 ||                           // empty (NULL) string
		          strspn ( Buffer, CharacterSet ) < strlen (Buffer) ||   // contains nonalphanumeric characters
		          (strlen ( Buffer ) + 1) > MAX_LOGFILENAME );        // longer than max filename

		strcpy_s ( gLogFileNameString, MAX_LOGFILENAME, Buffer );
	}

	// Check for log file from Tests 5.xx - 9.xx
	strcat_s ( gLogFileNameString, MAX_LOGFILENAME - strlen ( gLogFileNameString ), ".log" );
	if ( gTempLogFileHandle != gLogFileHandle &&
	     gLogFileHandle != NULL )
	{
		fclose ( gLogFileHandle );
	}

	// Test if log file already exists
	if ( fopen_s ( &hTempFileHandle, gLogFileNameString, "r+" ) != 0 )
	{
		// file does not exist - create file and start with test 10
		if ( fopen_s ( &gLogFileHandle, gLogFileNameString, "w+" ) != 0 )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
			      "Cannot open log file %s\n",
			      gLogFileNameString );
			return FAIL;
		}

		// log file doesn't exist. continue with Test 10.x
		*pbTestReEntered = FALSE;
	}
	else
	{
		// check software version, user input, test already complete, and get results totals in log file
		eRetVal = VerifyLogFile ( hTempFileHandle );
		gLogFileHandle = hTempFileHandle;  // copy filehandle, even though it will not be used, to allow fclose to work
		if ( eRetVal == FAIL ||
		     eRetVal == EXIT )
		{
			return eRetVal;
		}


		// log file already exists, go to Test 11.x
		fputs ( "\n****************************************************\n", gLogFileHandle );
		*pbTestReEntered = TRUE;
	}

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

	// Copy temp log file to actual log file
	if ( AppendLogFile ( ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
		      "Error copying temp log file to %s\n",
		       gLogFileNameString);
		return FAIL;
	}

	// Echo user responses to the log file
	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "%s%d\n",
	      gDisplayStrings[DSPSTR_PRMPT_MODEL_YEAR], 
	      gModelYear );

	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "%s%d\n",
	      gDisplayStrings[DSPSTR_PRMPT_OBD_ECU],
	      gUserNumOfECUs );

	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "%s%d\n",
	      gDisplayStrings[DSPSTR_PRMPT_RPGM_ECU],
	      gUserNumOfECUsReprgm );

	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "%s%s\n",
	      gDisplayStrings[DSPSTR_PRMPT_ENG_TYPE],
	      gEngineTypeStrings[gstUserInput.eEngineType] );

	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "%s%s\n",
	      gDisplayStrings[DSPSTR_PRMPT_PWRTRN_TYPE],
	      gPwrTrnTypeStrings[gstUserInput.ePwrTrnType] );

	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "%s%s\n",
	      gDisplayStrings[DSPSTR_PRMPT_VEH_TYPE],
	      gVehicleTypeStrings[gstUserInput.eVehicleType] );

	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "%s%s\n",
	      gDisplayStrings[DSPSTR_STMT_COMPLIANCE_TYPE],
	      gComplianceTestTypeStrings[gstUserInput.eComplianceType] );

	// done
	return PASS;
}


/*******************************************************************************
**
**  Function:  ReadVIN
**
**  Purpose:   Read the VIN from the ECUs.
**             If an ECU returns a correctly formatted VIN, return TRUE,
**             otherwise return FAIL.
**
*******************************************************************************/
STATUS ReadVIN ( void )
{
	REQ_MSG        stReqMsg;
	INF           *pInf;
	BYTE           EcuIdx;
	STATUS         eRetCode = PASS;

	unsigned long  NumResponses;
	unsigned long  Index;


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

	// INF Type Vin Count for non-ISO15765 only
	if ( gstProtocolList[gProtocolIdx].eProtocol != ISO15765 )
	{
		// If INF is supported by any ECU, request it
		if ( IsIDSupported ( ALLECUS, INFREQUEST, INF_TYPE_VIN_COUNT ) == TRUE )
		{
			stReqMsg.SID    = 0x09;
			stReqMsg.NumIds = 1;
			stReqMsg.ID[0]  = INF_TYPE_VIN_COUNT;
			if ( RequestSid ( &stReqMsg, REQ_MSG_NORMAL ) == FAIL )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "INF $1 (VIN COUNT) request\n" );
				eRetCode = FAIL;
			}
			else
			{
				NumResponses = 0;

				// check responses
				for ( EcuIdx = 0;
				      EcuIdx < gNumOfECUs;
				      EcuIdx++ )
				{
					// Check the data to see if it is valid
					pInf = (INF *)&gstResponse[EcuIdx].Inf[0];

					if ( gstResponse[EcuIdx].InfSize != 0 )
					{
						NumResponses++;
						Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  Responded to INF $1 (VIN COUNT) request.\n",
						      GetEcuId ( EcuIdx ) );

						if ( pInf->ID == INF_TYPE_VIN_COUNT )
						{
							if ( pInf->NODI != 0x05 )
							{
								Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
								      "ECU %X  INF $1 (VIN Count) NODI = %d (should be 5)\n",
								      GetEcuId ( EcuIdx ),
								      pInf->NODI );
								eRetCode = FAIL;
							}
						}

					}
				}

				if ( NumResponses > 1 &&
				     (gstUserInput.eComplianceType == US_OBDII || gstUserInput.eComplianceType == HD_OBD) )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "%u ECUs responded to INF $1 (VIN COUNT).  Only 1 is allowed.\n",
					      NumResponses );
					eRetCode = FAIL;
				}
			}
		}
	}

	// If INF is supported by any ECU, request it
	if ( IsIDSupported ( ALLECUS, INFREQUEST, INF_TYPE_VIN ) == TRUE )
	{
		stReqMsg.SID    = 0x09;
		stReqMsg.NumIds = 1;
		stReqMsg.ID[0]  = INF_TYPE_VIN;
		if ( RequestSid ( &stReqMsg, REQ_MSG_NORMAL ) == FAIL )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "INF $2 (VIN) request\n" );
			eRetCode = FAIL;
		}
		else
		{
			NumResponses = 0;

			// check responses
			for ( EcuIdx = 0;
			      EcuIdx < gNumOfECUs;
			      EcuIdx++ )
			{
				// Check the data to see if it is valid
				pInf = (INF *)&gstResponse[EcuIdx].Inf[0];

				if ( pInf->ID == INF_TYPE_VIN )
				{
					// Copy the VIN into the global array
					if ( gstProtocolList[gProtocolIdx].eProtocol == ISO15765 )
					{
						memcpy ( gVINString,
						         &pInf[0].Data[0],
						         17 );

						// check that there are no pad bytes included in message.
						if ( pInf[0].Data[20] != 0x00 )
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  INF $2 (VIN) format error, must be 17 chars!\n",
							      GetEcuId ( EcuIdx ) );
							eRetCode = FAIL;
						}
					}
					else
					{
						// INF $2 (VIN) should not be supported if INF $1 (VIN msg count) not supported for non-15765 protocols
						if ( IsIDSupported ( EcuIdx, INFREQUEST, INF_TYPE_VIN_COUNT ) == FALSE )
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  INF $2 (VIN) is supported but not INF $1 (VIN msg count)\n",
							      GetEcuId ( EcuIdx ) );
							eRetCode = FAIL;
						}

						for ( Index = 0;
						      Index < (gstResponse[EcuIdx].InfSize / sizeof ( INF ));
						      Index++ )
						{
							if ( Index == 0 )
							{
								gVINString[0] = pInf[Index].Data[3];
							}
							else if ( Index < 5 )
							{
								memcpy ( &gVINString[Index*4 - 3],
								         &pInf[Index].Data[0],
								         4 );
							}
						}
					}

					NumResponses++;
					Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X Responded to INF $2 (VIN) request.\n",
					      GetEcuId ( EcuIdx ) );

					if ( VerifyVINData ( EcuIdx ) != PASS )
					{
						eRetCode = FAIL;
					}
				}
			}

			if ( NumResponses != 1 && // only 1 ECU allowed to support VIN
			     (gstUserInput.eComplianceType == US_OBDII || gstUserInput.eComplianceType == HD_OBD) )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "%u ECUs responded to INF $2 (VIN).  Only 1 is allowed.\n",
				      NumResponses );
				eRetCode = FAIL;
			}
		}
	}
	else
	{
		// No VIN support
		if ( gstUserInput.eComplianceType == US_OBDII || gstUserInput.eComplianceType == HD_OBD )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "INF $2 (VIN) must be reported by one ECU\n" );
			eRetCode = FAIL;
		}
		else
		{
			Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "INF $2 (VIN) not reported by any ECUs\n" );
		}
	 }

	return eRetCode;
}


/*******************************************************************************
**
**  Function:  PrintCALIDs
**
**  Purpose:   Print CALIDs to the log file
**
*******************************************************************************/
STATUS PrintCALIDs ( void )
{
	REQ_MSG  stReqMsg;
	INF     *pInf;
	BYTE     EcuIdx;

	BYTE     Inf3NumItems[MAX_ECUS] = {0};


	if ( gstProtocolList[gProtocolIdx].eProtocol != ISO15765 )
	{
		if ( IsIDSupported ( ALLECUS, INFREQUEST, 3 ) == TRUE )
		{
			stReqMsg.SID    = 0x09;
			stReqMsg.NumIds = 1;
			stReqMsg.ID[0]  = 0x03;
			if ( RequestSid ( &stReqMsg, REQ_MSG_NORMAL ) == FAIL )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "INF $3 (CALID Count) request\n" );
				return FAIL;
			}

			for ( EcuIdx = 0;
			      EcuIdx < gNumOfECUs;
			      EcuIdx++ )
			{
				// If INF is not supported, skip to next ECU
				if ( IsIDSupported ( EcuIdx, INFREQUEST, 3 ) == TRUE )
				{
					// Check the data to see if it is valid
					pInf = (INF *)&gstResponse[EcuIdx].Inf[0];

					if ( gstResponse[EcuIdx].InfSize == 0 )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  No INF $3 (CALID Count) data\n",
						      GetEcuId ( EcuIdx ) );
						return FAIL;
					}

					if ( pInf[0].NODI & 0x03 )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  INF $3 (CALID Count) NODI = %d (should be a multiple of 4)\n",
						      GetEcuId ( EcuIdx ),
						      pInf[0].NODI );
						return FAIL;
					}

					Inf3NumItems[EcuIdx] = pInf[0].NODI;
				}
			}
		}
	}

	stReqMsg.SID    = 0x09;
	stReqMsg.NumIds = 1;
	stReqMsg.ID[0]  = 0x04;
	if ( RequestSid ( &stReqMsg, REQ_MSG_NORMAL ) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "INF $4 (CALID) request\n" );
		return FAIL;
	}

	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		VerifyCALIDData ( EcuIdx, Inf3NumItems[EcuIdx] );
	}

	return PASS;
}


/*******************************************************************************
**
**  Function:  PrintCVNs
**
**  Purpose:   Print CVNs to the log file
**
*******************************************************************************/
STATUS PrintCVNs ( void )
{
	REQ_MSG  stReqMsg;
	INF     *pInf;
	BYTE     EcuIdx;

	unsigned long  Inf5NumItems[MAX_ECUS] = {0};


	if ( gstProtocolList[gProtocolIdx].eProtocol != ISO15765 )
	{
		if ( IsIDSupported ( ALLECUS, INFREQUEST, 5 ) == TRUE )
		{
			stReqMsg.SID    = 0x09;
			stReqMsg.NumIds = 1;
			stReqMsg.ID[0]  = 0x05;
			if ( RequestSid ( &stReqMsg, REQ_MSG_NORMAL ) == FAIL )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "INF $5 (CVN Count) request\n" );
				return FAIL;
			}

			for ( EcuIdx = 0;
			      EcuIdx < gNumOfECUs;
			      EcuIdx++ )
			{
				// If INF is not supported, skip to next ECU
				if ( IsIDSupported ( EcuIdx, INFREQUEST, 5 ) == TRUE )
				{
					// Save the data
					pInf = (INF *)&gstResponse[EcuIdx].Inf[0];
					Inf5NumItems[EcuIdx] = pInf[0].NODI;
				}
			}
		}
	}

	stReqMsg.SID    = 0x09;
	stReqMsg.NumIds = 1;
	stReqMsg.ID[0]  = 0x06;
	if ( RequestSid ( &stReqMsg, REQ_MSG_NORMAL ) == FAIL )
	{
		if ( gstUserInput.eComplianceType == US_OBDII || gstUserInput.eComplianceType == HD_OBD )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "INF $6 (CVN) request\n" );
			return FAIL;
		}
		else
		{
			return PASS;
		}
	}

	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		// If INF is not supported, skip to next ECU
		if ( IsIDSupported ( EcuIdx, INFREQUEST, 6 ) == TRUE )
		{
			VerifyCVNData ( EcuIdx, Inf5NumItems[EcuIdx] );
		}
	}

	return PASS;
}


/*******************************************************************************
**
**  Function:  VerifyIPTData
**
**  Purpose:
**
*******************************************************************************/
STATUS VerifyIPTData ( BOOL *pbIPTSupported )
{
	REQ_MSG  stReqMsg;
	BYTE     EcuIdx;

	BOOL     bInf8Supported = FALSE;
	BOOL     bInfBSupported = FALSE;


	stReqMsg.SID    = 0x09;
	stReqMsg.NumIds = 1;

	// Sid 9 Inf 8 request
	if ( IsIDSupported ( ALLECUS, INFREQUEST, INF_TYPE_IPT_GAS ) == TRUE )
	{
		bInf8Supported = TRUE;
		*pbIPTSupported = TRUE;

		stReqMsg.ID[0] = INF_TYPE_IPT_GAS;
	}

	// Sid 9 Inf B request
	if ( IsIDSupported ( ALLECUS, INFREQUEST, INF_TYPE_IPT_DIESEL ) == TRUE )
	{
		bInfBSupported = TRUE;
		*pbIPTSupported = TRUE;

		stReqMsg.ID[0] = INF_TYPE_IPT_DIESEL;
	}


	if ( bInf8Supported == FALSE && bInfBSupported == FALSE )
	{
		if ( ((gstUserInput.eComplianceType == US_OBDII || gstUserInput.eComplianceType == HD_OBD) &&
		      gModelYear >= 2005) ||
		     (gstUserInput.eComplianceType == EOBD_WITH_IUMPR ||
		      gstUserInput.eComplianceType == HD_EOBD) )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "SID $09 IPT (INF $8 or INF $B) not supported\n" );
			return FAIL;
		}
		else
		{
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "SID $09 IPT (INF $8 or INF $B) not supported\n" );
			return PASS;
		}
	 }
	 else if ( bInf8Supported == TRUE && bInfBSupported == TRUE )
	{
		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			if ( IsIDSupported ( EcuIdx, INFREQUEST, INF_TYPE_IPT_GAS ) == TRUE )
			{
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  INF $8 (IPT) supported\n",
				      GetEcuId ( EcuIdx ) );
			}
			if ( IsIDSupported ( EcuIdx, INFREQUEST, INF_TYPE_IPT_DIESEL ) == TRUE )
			{
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  INF $B (IPT) supported\n",
				      GetEcuId ( EcuIdx ) );
			}
		}
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "SID $09 IPT (INF $8 and INF $B) both supported\n" );
		return FAIL;
	}


	if ( RequestSid ( &stReqMsg, REQ_MSG_NORMAL ) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "INF $%X (IPT) request\n",
		      stReqMsg.ID[0] );
		return FAIL;
	}

	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( IsIDSupported ( EcuIdx, INFREQUEST, INF_TYPE_IPT_GAS ) == TRUE )
		{
			if ( VerifyInf8Data ( EcuIdx ) != FAIL )
			{
				if ( GetIptData ( EcuIdx, gstEcuSIDData[EcuIdx].pTest10_10_Ipt ) != PASS )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X  Get SID $09 IPT Data\n",
					      GetEcuId ( EcuIdx ) );
					return FAIL;
				}
			}
			else
			{
				return FAIL;
			}
		}
		else if ( IsIDSupported ( EcuIdx, INFREQUEST, INF_TYPE_IPT_DIESEL ) == TRUE )
		{
			if ( VerifyInfBData ( EcuIdx ) == PASS )
			{
				if ( GetIptData ( EcuIdx, gstEcuSIDData[EcuIdx].pTest10_10_Ipt ) != PASS )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X  Get SID $09 IPT Data\n",
					      GetEcuId ( EcuIdx ) );
					return FAIL;
				}
			}
			else
			{
				return FAIL;
			}
		}
	}

	if ( LogIpt ( ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Log SID $09 IPT Data\n" );
		return FAIL;
	}

	return PASS;
}


/*******************************************************************************
**
**  Function:  GetIptData
**
**  Purpose:   Copy from gstResponse[EcuIdx].Inf into common,
**             protocol-independent, format.
**
*******************************************************************************/
STATUS GetIptData ( BYTE EcuIdx,
                    IPT *pIpt )
{
	INF            *pInf;
	STATUS          eRetCode = FAIL;

	unsigned int    Index;
	unsigned int    IptIndex;
	unsigned short *pData;
	unsigned int    IptSize;


	// clear IPT structure
	memset ( pIpt,
	         0,
	         sizeof ( IPT ) );

	// point to IPT structure in gstResponse
	pInf = (INF *)&gstResponse[EcuIdx].Inf[0];

	// check to ensure that gstResponse contains IPT data
	if ( gstResponse[EcuIdx].InfSize == 0 ||
	     (pInf->ID != 0x08 && pInf->ID != 0x0B) )
	{
		return FAIL;
	}

	// if ISO15765 protocol
	if ( gstProtocolList[gProtocolIdx].eProtocol == ISO15765 )
	{
		memcpy ( pIpt,
		         &(gstResponse[EcuIdx].Inf[0]),
		         sizeof ( IPT ) );
		memcpy ( &gstEcuSIDData[EcuIdx].pIpt[0],
		         &(gstResponse[EcuIdx].Inf[0]),
		         sizeof ( IPT ) );

		// If NOT test 9.19, 10.12 or 11.2, log failures
		if ( (geTestPhase != eTestNoFault3DriveCycle  || gTestSubsection != 19) &&
		     (geTestPhase != eTestInUseCounters       || gTestSubsection != 12) &&
		     (geTestPhase != eTestPerformanceCounters || gTestSubsection != 2) )
		{
			if ( pInf->ID == 0x08 )
			{
				eRetCode = VerifyInf8Data ( EcuIdx );
			}
			else if ( pInf->ID == 0x0B )
			{
				eRetCode = VerifyInfBData ( EcuIdx );
			}

			if ( eRetCode == FAIL )
			{
				return eRetCode;
			}
		} //end if !test 9.19 and !test 10.12 and !test 11.2

		// point to start of IPT data in structure
		pData = &(pIpt->Data[0]);

		// calculate number of IPT data bytes
		IptSize = (pIpt->NODI <= MAX_IPT_NODI) ? (pIpt->NODI)*2 : MAX_IPT_NODI*2;

		// swap low and high bytes
		for ( Index = 2, IptIndex = 0;
		      Index <= IptSize;
		      Index += 2 )
		{
			*pData++ = (gstResponse[EcuIdx].Inf[Index] << 8) +
			            gstResponse[EcuIdx].Inf[Index+1];
			gstEcuSIDData[EcuIdx].pIpt->Data[IptIndex++] = (gstResponse[EcuIdx].Inf[Index] << 8) +
			                                                gstResponse[EcuIdx].Inf[Index+1];
		}
	} // end if ( gstProtocolList[gProtocolIdx].eProtocol == ISO15765 )

	// else Legacy protocols
	else
	{
		pIpt->ID   = gstResponse[EcuIdx].Inf[0];
		pIpt->NODI = (gstResponse[EcuIdx].InfSize / sizeof ( INF )) * 2; // MessageCount * 2

		gstEcuSIDData[EcuIdx].pIpt->ID   = gstResponse[EcuIdx].Inf[0];
		gstEcuSIDData[EcuIdx].pIpt->NODI = (gstResponse[EcuIdx].InfSize / sizeof ( INF )) * 2; // MessageCount * 2

		// If NOT test 9.19, 10.12 or 11.2, log failures
		if ( (geTestPhase != eTestNoFault3DriveCycle  || gTestSubsection != 19) &&
		     (geTestPhase != eTestInUseCounters       || gTestSubsection != 12) &&
		     (geTestPhase != eTestPerformanceCounters || gTestSubsection != 2) )
		{
			if ( pInf->ID == 0x08 )
			{
				eRetCode = VerifyInf8Data ( EcuIdx );
			}
			else if ( pInf->ID == 0x0B )
			{
				eRetCode = VerifyInfBData ( EcuIdx );
			}

			if ( eRetCode != PASS )
			{
				return eRetCode;
			}
		}

		// point to start of IPT data
		pData = &(pIpt->Data[0]);

		// save IPT data to structure
		for ( Index = 0, IptIndex = 0;
		      Index < (gstResponse[EcuIdx].InfSize / sizeof ( INF ));
		      Index++ )
		{
			*pData++ = (pInf[Index].Data[0] << 8) +
			            pInf[Index].Data[1];
			*pData++ = (pInf[Index].Data[2] << 8) +
			            pInf[Index].Data[3];

			gstEcuSIDData[EcuIdx].pIpt->Data[IptIndex++] = (pInf[Index].Data[0] << 8) +
			                                                pInf[Index].Data[1];
			gstEcuSIDData[EcuIdx].pIpt->Data[IptIndex++] = (pInf[Index].Data[2] << 8) +
			                                                pInf[Index].Data[3];
		}
	}

	// data structure is valid
	pIpt->Flags = VALIDDATA;

	return PASS;
}


/*******************************************************************************
**
**  Function:  LogIpt
**
**  Purpose:
**
*******************************************************************************/
const char szECUID[]     = "ECU ID:";
const char szINF_SIZE[]  = "INF SIZE:";

const char szINF8[][10]  = { "OBDCOND",
	                         "IGNCNTR",
	                         "CATCOMP1",
	                         "CATCOND1",
	                         "CATCOMP2",
	                         "CATCOND2",
	                         "O2SCOMP1",
	                         "O2SCOND1",
	                         "O2SCOMP2",
	                         "O2SCOND2",
	                         "EGRCOMP",
	                         "EGRCOND",
	                         "AIRCOMP",
	                         "AIRCOND",
	                         "EVAPCOMP",
	                         "EVAPCOND",
	                         "SO2SCOMP1",
	                         "SO2SCOND1",
	                         "SO2SCOMP2",
	                         "SO2SCOND2",
	                         "AFRICOMP1",
	                         "AFRICOND1",
	                         "AFRICOMP2",
	                         "AFRICOND2",
	                         "PFCOMP1",
	                         "PFCOND1",
	                         "PFCOMP2",
	                         "PFCOND2" };

const char szINFB[][10]  = { "OBDCOND",
	                         "IGNCNTR",
	                         "HCCATCOMP",
	                         "HCCATCOND",
	                         "NCATCOMP",
	                         "NCATCOND",
	                         "NADSCOMP",
	                         "NADSCOND",
	                         "PMCOMP",
	                         "PMCOND",
	                         "EGSCOMP",
	                         "EGSCOND",
	                         "EGRCOMP",
	                         "EGRCOND",
	                         "BPCOMP",
	                         "BPCOND",
	                         "FUELCOMP",
	                         "FUELCOND" };

STATUS LogIpt ( void )
{
	REQ_MSG       stReqMsg;
	BYTE          EcuIdx;

	unsigned int  IptIndex;
	IPT           Ipt;
	BOOL          bInf8Supported = FALSE;
	BOOL          bInfBSupported = FALSE;


	stReqMsg.SID    = 0x09;
	stReqMsg.NumIds = 1;

	// Is Sid 9 Inf 8 supported by any ECU?
	if ( IsIDSupported ( ALLECUS, INFREQUEST, INF_TYPE_IPT_GAS ) == TRUE )
	{
		bInf8Supported = TRUE;

		// Sid 9 Inf 8 request
		stReqMsg.ID[0] = INF_TYPE_IPT_GAS;
	}

	// Is Sid 9 Inf B supported by any ECU?
	if ( IsIDSupported ( ALLECUS, INFREQUEST, INF_TYPE_IPT_DIESEL ) == TRUE )
	{
		bInfBSupported = TRUE;

		// Sid 9 Inf 8 reques
		stReqMsg.ID[0] = INF_TYPE_IPT_DIESEL;
	}

	if ( bInf8Supported == FALSE && bInfBSupported == FALSE )
	{
		if ( ((gstUserInput.eComplianceType == US_OBDII || gstUserInput.eComplianceType == HD_OBD) &&
		       gModelYear >= 2005) ||
		     (gstUserInput.eComplianceType == EOBD_WITH_IUMPR || gstUserInput.eComplianceType == HD_EOBD) )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "SID $09 IPT (INF $8 or INF $B) not supported\n" );
			return FAIL;
		}
		else
		{
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "SID $09 IPT (INF $8 or INF $B) not supported\n" );
			return PASS;
		}
	}
	else if ( bInf8Supported == TRUE && bInfBSupported == TRUE )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "SID $09 IPT (INF $8 and INF $B) both supported\n" );
		return FAIL;
	}

	if ( RequestSid ( &stReqMsg, REQ_MSG_NORMAL ) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "INF $%02X (IPT) request\n",
		      stReqMsg.ID[0] );
		return FAIL;
	}

	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( GetIptData ( EcuIdx, &Ipt ) == PASS )
		{
			// log OBDCOND, IGNCTR, all OBD monitor and completion counters
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "%-9s 0x%X\n",
			      szECUID,
			      GetEcuId ( EcuIdx ) );
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "%-9s 0x%X %d\n",
			      szINF_SIZE,
			      Ipt.ID,
			      Ipt.NODI );

			for ( IptIndex = 0;
			      IptIndex < Ipt.NODI &&
			      IptIndex < MAX_IPT_NODI;
			      IptIndex++ )
			{
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "%-9s = %u\n",
				      (Ipt.ID == INF_TYPE_IPT_GAS) ? szINF8[IptIndex] : szINFB[IptIndex],
				      Ipt.Data[IptIndex] );
			}
		}
	}

	return PASS;
}


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

	BOOL     bInf12Supported = FALSE;


	// Is Sid 9 Inf 12 supported by any ECU?
	if ( IsIDSupported ( ALLECUS, INFREQUEST, INF_TYPE_FEOCNTR ) == FALSE )
	{
		if ( gstUserInput.eComplianceType == US_OBDII &&
		     gbPlugIn == TRUE &&
		     gModelYear >= 2014 )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "INF $12 not supported (Required for Plug-In Hybrid vehicles MY 2014 and later).\n" );
			return FAIL;
		}
		else
		{
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "INF $12 not supported\n" );
			return PASS;
		}
	}

	stReqMsg.SID    = 0x09;
	stReqMsg.NumIds = 1;
	stReqMsg.ID[0]  = INF_TYPE_FEOCNTR;
	if ( RequestSid ( &stReqMsg, REQ_MSG_NORMAL ) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		       "INF $%02X request\n",
		       stReqMsg.ID[0] );
		return FAIL;
	}


	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		// clear INF structure
		memset ( &gstEcuSIDData[EcuIdx].pFEOCNTR,
		         0,
		         sizeof ( INF ) );

		// point to IPT structure in gstResponse
		pInf = (INF *)&gstResponse[EcuIdx].Inf[0];

		// check to ensure that gstResponse contains appropriate data
		if ( IsIDSupported ( EcuIdx, INFREQUEST, INF_TYPE_FEOCNTR ) == TRUE )
		{
			if ( gstResponse[EcuIdx].InfSize == 0 ||
		             pInf->ID != INF_TYPE_FEOCNTR )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  Invalid response to INF $12 request\n",
				      GetEcuId ( EcuIdx ) );
				eRetCode = FAIL;
			}
			else
			{
				// If the data is good, save it
				if ( VerifyInf12Data ( EcuIdx ) == PASS )
				{
					// if ISO15765 protocol
					if ( gstProtocolList[gProtocolIdx].eProtocol == ISO15765 )
					{
						memcpy ( &gstEcuSIDData[EcuIdx].pFEOCNTR,
						         &(gstResponse[EcuIdx].Inf[0]),
						         sizeof ( INF ) - 2 );  // don't include ID or NODI
					}
					// else Legacy protocols
					else
					{
						gstEcuSIDData[EcuIdx].pFEOCNTR->ID      = gstResponse[EcuIdx].Inf[0];
						gstEcuSIDData[EcuIdx].pFEOCNTR->NODI    = (gstResponse[EcuIdx].InfSize / sizeof ( INF )) * 2; // MessageCount * 2
						gstEcuSIDData[EcuIdx].pFEOCNTR->Data[0] = gstResponse[EcuIdx].Inf[2];
						gstEcuSIDData[EcuIdx].pFEOCNTR->Data[0] = gstResponse[EcuIdx].Inf[3];
					}
				}
			}
		}
	}

	return eRetCode;
}


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

	unsigned int FEOCNTR;
	BOOL         bInf12Supported = FALSE;


	// Is Sid 9 Inf 12 supported by any ECU?
	if ( IsIDSupported ( ALLECUS, INFREQUEST, INF_TYPE_FEOCNTR ) == FALSE )
	{
		if ( gstUserInput.eComplianceType == US_OBDII &&
		     gbPlugIn == TRUE &&
		     gModelYear >= 2014 )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "INF $12 not supported (Required for Plug-In Hybrid vehicles MY 2014 and later).\n" );
			return FAIL;
		}
		else
		{
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "INF $12 not supported\n" );
			return PASS;
		}
	}

	stReqMsg.SID    = 0x09;
	stReqMsg.NumIds = 1;
	stReqMsg.ID[0]  = INF_TYPE_FEOCNTR;
	if ( RequestSid ( &stReqMsg, REQ_MSG_NORMAL ) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "INF $12 request\n" );
		return FAIL;
	}


	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		// point to IPT structure in gstResponse
		pInf = (INF *)&gstResponse[EcuIdx].Inf[0];


		// check to ensure that gstResponse contains appropriate data
		if ( IsIDSupported ( EcuIdx, INFREQUEST, INF_TYPE_FEOCNTR ) == TRUE )
		{
			if ( gstResponse[EcuIdx].InfSize == 0 ||
		          pInf->ID != INF_TYPE_FEOCNTR )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  Invalid response to INF $12 request\n",
				      GetEcuId ( EcuIdx ) );
				eRetCode = FAIL;
			}
			else
			{
				FEOCNTR = (gstResponse[EcuIdx].Inf[2] << 8) +
				           gstResponse[EcuIdx].Inf[3];

				// log FEOCNTRs
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  INF $12  FEOCNTR = %d\n",
				      GetEcuId ( EcuIdx ),
				      FEOCNTR );

				if ( FEOCNTR <= (unsigned int)(gstEcuSIDData[EcuIdx].pFEOCNTR->ID) )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X  INF $12 FEOCNTR has not incremented since Test 10.10.4\n",
					      GetEcuId ( EcuIdx ) );
					eRetCode = FAIL;
				}
			}
		}
	}

	return eRetCode;
}


/*******************************************************************************
**
**  Function:  RunDynamicTest10
**
**  Purpose:
**
*******************************************************************************/
STATUS RunDynamicTest10 ( unsigned long EngineStartTimestampMsecs )
{
	REQ_MSG        stReqMsg;
	PID           *pPid;
	BYTE           EcuIdx;
	STATUS         eRetCode = PASS;

	BYTE           SubTestStatusFlags  = SUB_TEST_NORMAL;
	BYTE           LastTestStatusFlags = SUB_TEST_INITIAL;  // set to force an update the first time
	BYTE           Pid1C;
	BYTE           key;
	unsigned int   EcuPageOffset = 0;
	unsigned int   EcuPage = 1;
	BOOL           bECUPageUpdate = FALSE;
	unsigned int   TestState;
	unsigned int   TimeStatusFlags = 0;
	BOOL           bLoop;
	BOOL           bFail;
	BOOL           bRunTimeSupport;
	BOOL           bVSSSupport;
	BOOL           bRPMSupport;
	BOOL           bBAROSupport;
	BOOL           bAATEMPSupport;
	BOOL           bOBDTypeSupport;
	unsigned int   Baro_InHg;
	unsigned int   AAT_C;
	BOOL           bUseRPM = FALSE;  // indicate use of RPM for Idle and Drive time calculations
	unsigned int   IptID = 0;        // support INF for IPT data, default 0 = no support

	BOOL           bErrorPrinted = FALSE;    // indicates whether or not the failure message has already been printed

	BOOL           bPermDTC;

	unsigned long  CurrentTimestampMsecs;
	unsigned long  LastCheckTimestampMsecs;
	unsigned short TestStartTimestampSecs;
	unsigned short TestCompleteTimestampSecs;
	unsigned short LastCheckTimestampSecs;
	unsigned short IdleTimeSecs;        // time at idle (stops at 30)
	unsigned short AtSpeedTimeSecs;     // time at speeds > 25 mph (stops at 300)
	unsigned short FueledRunTimeSecs;
	unsigned short LastFRCheckTimestampSecs;
	unsigned short OBDCondTimestampSecs[MAX_ECUS];
	unsigned short RPM;
	unsigned short SpeedMPH;
	unsigned short SpeedKPH;
	unsigned short RunTimeSecs;


	// determine PID support
	bRunTimeSupport = IsIDSupported ( ALLECUS, PIDREQUEST, 0x1F );

	bVSSSupport     = IsIDSupported ( ALLECUS, PIDREQUEST, 0x0D );

	bRPMSupport     = IsIDSupported ( ALLECUS, PIDREQUEST, 0x0C );

	bOBDTypeSupport = IsIDSupported ( ALLECUS, PIDREQUEST, 0x1C );

	bBAROSupport    = IsIDSupported ( ALLECUS, PIDREQUEST, 0x33 );

	bAATEMPSupport  = IsIDSupported ( ALLECUS, PIDREQUEST, 0x46 );

	if ( bRPMSupport == FALSE && bRunTimeSupport == FALSE )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "SID $01 PIDs $0C (RPM) and $1F (RUNTM) not supported\n" );
		return FAIL;
	}

	//-------------------------------------------------------------------------
	// Request OBD_Type - SID 1 PID $1C.  Needed to determine wether to use
	// Vehicle Speed (KPH/MPH) or Egine Speed (RPM) to determine idel and drive time.
	//-------------------------------------------------------------------------
	stReqMsg.SID    = 0x01;
	stReqMsg.NumIds = 1;
	stReqMsg.ID[0]  = 0x1C;
	if ( RequestSid ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE ) == PASS )
	{
		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			if ( gstResponse[EcuIdx].PidSize > 0 )
			{
				pPid = (PID *)&gstResponse[EcuIdx].Pid[0];

				if ( pPid->ID == 0x1C )
				{
					Pid1C = pPid->Data[0];
					break;  // leave ECU for loop
				}
			}
		}
	}
	if ( EcuIdx == gNumOfECUs )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "No response to SID $1 PID $1C (OBD Type)\n" );
		return FAIL;
	}


	//-------------------------------------------------------------------------
	// Determine whether to use Vehicle Speed (KPH/MPH) or Egine Speed (RPM) for idel and drive time calculations.
	//-------------------------------------------------------------------------
	if ( (gstUserInput.eComplianceType == HD_OBD && gbDiesel == TRUE) ||
	     (gstUserInput.eComplianceType == US_OBDII && gbDiesel == TRUE && Pid1C == 0x22) )
	{
		bUseRPM = TRUE;
	}
	else if ( gstUserInput.eComplianceType == HD_EOBD ||
	          (gstUserInput.eComplianceType == US_OBDII && gbDiesel == TRUE && gstUserInput.eVehicleType == MD) )
	{
		if ( bVSSSupport )
		{
			bUseRPM = FALSE;
		}
		else
		{
			bUseRPM = TRUE;
		}
	}
	else
	{
		bUseRPM = FALSE;
	}


	if ( IsIDSupported ( ALLECUS, INFREQUEST, INF_TYPE_IPT_GAS ) == TRUE )
	{
		IptID = INF_TYPE_IPT_GAS;
	}
	else if ( IsIDSupported ( ALLECUS, INFREQUEST, INF_TYPE_IPT_DIESEL ) == TRUE )
	{
		IptID = INF_TYPE_IPT_DIESEL;
	}
	else if ( ((gstUserInput.eComplianceType == US_OBDII || gstUserInput.eComplianceType == HD_OBD) &&
	           gModelYear >= 2005) ||
	          (gstUserInput.eComplianceType == EOBD_WITH_IUMPR || gstUserInput.eComplianceType == HD_EOBD) )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "SID $09 IPT (INF $8 or INF $B) not supported\n" );
		return FAIL;
	}
	else
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "SID $09 IPT (INF $8 or INF $B) not supported\n" );
	}


	//-------------------------------------------
	// initialize static text elements
	//-------------------------------------------
	if ( geTestPhase == eTestNoFault3DriveCycle )
	{
		init_screen ( _string_elements_1st_9, _num_string_elements_1st_9 );
	}
	else
	{
		init_screen ( _string_elements_1st_10, _num_string_elements_1st_10 );
	}

	place_screen_text ( _string_elements10, _num_string_elements10 );
	// if using RPM for speed monitoring, update appropriate text
	if ( bUseRPM == TRUE )
	{
		place_screen_text ( _string_elements10_RPM, _num_string_elements10_RPM );
	}

	// if this is a PHEV, update appropriate text
	if ( gstUserInput.ePwrTrnType == PHEV )
	{
		place_screen_text ( _string_elements10_PHEV, _num_string_elements10_PHEV );
	}

	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs &&
	      EcuIdx < MAX_ECUS_PER_PAGE;
	      EcuIdx++ )
	{
		// initialize ECU IDs
		SetFieldHex ( ECU_ID+EcuIdx, GetEcuId ( EcuIdx ) );

		// initialize initial OBDCONDs
		SetFieldDec ( INITIAL_OBDCOND+EcuIdx, gstEcuSIDData[EcuIdx].pTest10_10_Ipt->Data[0] );

		// initialize initial IGNCTRs
		SetFieldDec ( INITIAL_IGNCTR+EcuIdx, gstEcuSIDData[EcuIdx].pTest10_10_Ipt->Data[1] );

		if ( gbPlugIn == TRUE )
		{
			// initialize initial FEOCNTRs
			SetFieldDec ( INITIAL_FEOCNTR+EcuIdx, gstEcuSIDData[EcuIdx].pFEOCNTR->Data[0] );
		}

		OBDCondTimestampSecs[EcuIdx] = 0;

		// if INF 8/B not supported, print 0 (INF8/B will never update)
		if ( IptID == 0 )
		{
			SetFieldDec ( CURRENT_OBDCOND+EcuIdx, gstEcuSIDData[EcuIdx].pIpt->Data[0] );
			SetFieldDec ( CURRENT_IGNCTR+EcuIdx,  gstEcuSIDData[EcuIdx].pIpt->Data[1] );
		 }
	}
	SetFieldDec ( ECUPAGE_INDEX, EcuPage );

	// flush the STDIN stream of any user input before loop
	ClearKeyboardBuffer ( );

	RPM = SpeedMPH = SpeedKPH = RunTimeSecs = 0;
	FueledRunTimeSecs = 0;
	LastFRCheckTimestampSecs = 0;
	LastCheckTimestampSecs = 0;
	AtSpeedTimeSecs = 0;
	IdleTimeSecs = 0;
	TestCompleteTimestampSecs = 0;
	TestStartTimestampSecs = (unsigned short)(EngineStartTimestampMsecs / 1000);
	CurrentTimestampMsecs = LastCheckTimestampMsecs = GetTickCount ( );

	TestState = 0xFF;

	//-------------------------------------------
	// loop until test completes
	//-------------------------------------------
	for ( ; ; )
	{
		if ( geTestPhase == eTestNoFault3DriveCycle )
		{
			//-------------------------------------------
			// request Permanent Codes - SID A
			//-------------------------------------------
			stReqMsg.SID    = 0x0A;
			stReqMsg.NumIds = 0;
			if ( (eRetCode = RequestSid ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE )) == FAIL )
			{
				if ( bErrorPrinted == FALSE )
				{
					Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
					      "SID $0A request\n" );
					SubTestStatusFlags |= SUB_TEST_FAIL;
				}
				bErrorPrinted = TRUE;
			}
			else
			{
				if ( eRetCode != PASS )
				{
					// cover the case where a response was early/late
					SubTestStatusFlags |= SUB_TEST_FAIL;
				}

				bPermDTC = FALSE;
				for ( EcuIdx = 0;
				      EcuIdx < gNumOfECUs;
				      EcuIdx++ )
				{
					if ( gstResponse[EcuIdx].PermDTC[0] != 0x00 || gstResponse[EcuIdx].PermDTC[1] != 0x00 )
					{
						bPermDTC = TRUE;
					}
				}

				if ( bPermDTC == FALSE )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "SID $0A - DTC erased prematurely.\n" );

					gotoxy ( 0, BOTTOM_ROW+2 );
					return FAIL;
				}
			}
		}


		//-------------------------------------------
		// request RPM - SID 1 PID $0C
		//-------------------------------------------
		if ( bRPMSupport == TRUE )
		{
			stReqMsg.SID    = 0x01;
			stReqMsg.NumIds = 1;
			stReqMsg.ID[0]  = 0x0C;
			if ( (eRetCode = RequestSid ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE )) == FAIL )
			{
				Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
				      "SID $01 PID $0C request\n" );
				SubTestStatusFlags |= SUB_TEST_FAIL;
			}
			else
			{
				if ( eRetCode != PASS )
				{
					// cover the case where a response was early/late
					SubTestStatusFlags |= SUB_TEST_FAIL;
				}

				for ( EcuIdx = 0;
				      EcuIdx < gNumOfECUs;
				      EcuIdx++ )
				{
					if ( gstResponse[EcuIdx].PidSize > 0 )
					{
						pPid = (PID *)&gstResponse[EcuIdx].Pid[0];

						if ( pPid->ID == 0x0C )
						{
							RPM = pPid->Data[0];
							RPM = (RPM << 8) | (pPid->Data[1]);

							// convert from 1 cnt = 1/4 RPM to 1 cnt = 1 RPM
							RPM >>= 2;
							break;  // leave ECU for loop
						}
					}
				}

				if ( EcuIdx >= gNumOfECUs )
				{
					Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
					      "SID $01 PID $0C missing response\n" );
					SubTestStatusFlags |= SUB_TEST_FAIL;
				}
			}
		}

		//-------------------------------------------
		// request Speed - SID 1 PID $0D
		//-------------------------------------------
		if ( bVSSSupport == TRUE )
		{
			stReqMsg.SID    = 0x01;
			stReqMsg.NumIds = 1;
			stReqMsg.ID[0]  = 0x0D;
			if ( (eRetCode = RequestSid ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE )) == FAIL )
			{
				Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
				      "SID $01 PID $0D request\n" );
				SubTestStatusFlags |= SUB_TEST_FAIL;
			}
			else
			{
				if ( eRetCode != PASS )
				{
					// cover the case where a response was early/late
					SubTestStatusFlags |= SUB_TEST_FAIL;
				}

				for ( EcuIdx = 0;
				      EcuIdx < gNumOfECUs;
				      EcuIdx++ )
				{
					if ( gstResponse[EcuIdx].PidSize > 0 )
					{
						pPid = (PID *)&gstResponse[EcuIdx].Pid[0];

						if ( pPid->ID == 0x0D )
						{
							double dTemp = pPid->Data[0];

							SpeedKPH = pPid->Data[0];

							// convert from km/hr to mile/hr
							SpeedMPH = (unsigned short)( ((dTemp * 6214) / 10000) + 0.5 );
							break;  // leave ECU for loop
						}
					}
				}

				if ( EcuIdx >= gNumOfECUs )
				{
					Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
					      "SID $01 PID $0D missing response\n" );
					SubTestStatusFlags |= SUB_TEST_FAIL;
				}
			}
		}

		//-------------------------------------------
		// request engine RunTime - SID 1 PID $1F
		//-------------------------------------------
		if ( bRunTimeSupport == TRUE )
		{
			stReqMsg.SID    = 0x01;
			stReqMsg.NumIds = 1;
			stReqMsg.ID[0]  = 0x1F;
			if ( (eRetCode = RequestSid ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE )) == FAIL )
			{
				Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
				      "SID $01 PID $1F request\n" );
				SubTestStatusFlags |= SUB_TEST_FAIL;
			}
			else
			{
				if ( eRetCode != PASS )
				{
					// cover the case where a response was early/late
					SubTestStatusFlags |= SUB_TEST_FAIL;
				}

				for ( EcuIdx = 0;
				      EcuIdx < gNumOfECUs;
				      EcuIdx++ )
				{
					if ( gstResponse[EcuIdx].PidSize > 0 )
					{
						pPid = (PID *)&gstResponse[EcuIdx].Pid[0];

						if ( pPid->ID == 0x1F )
						{
							RunTimeSecs = pPid->Data[0];
							RunTimeSecs = (RunTimeSecs << 8) | (pPid->Data[1]);    // 1 cnt = 1 sec
							break;  // leave ECU for loop
						}
					}
				}

				if ( EcuIdx >= gNumOfECUs )
				{
					Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
					      "SID $01 PID $1F missing response\n" );
					SubTestStatusFlags |= SUB_TEST_FAIL;
				}
			}
		}
		else
		{
			RunTimeSecs = (unsigned short)(GetTickCount ( ) / 1000) - TestStartTimestampSecs;
		}


		//-------------------------------------------
		// request Barometric Pressure - SID 1 PID $33
		//-------------------------------------------
		if ( bBAROSupport == TRUE )
		{
			stReqMsg.SID    = 0x01;
			stReqMsg.NumIds = 1;
			stReqMsg.ID[0]  = 0x33;
			if ( RequestSid ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE ) != FAIL )
			{
				for ( EcuIdx = 0;
				      EcuIdx < gNumOfECUs;
				      EcuIdx++ )
				{
					if ( gstResponse[EcuIdx].PidSize > 0 )
					{
						pPid = (PID *)&gstResponse[EcuIdx].Pid[0];

						if ( pPid->ID == 0x33 )
						{
							Baro_InHg = (unsigned int)((float)(pPid->Data[0]) * 0.295301);  // Convert from kPa to in Hg
						}
					}
				}
			}
		}


		//-------------------------------------------
		// request Ambient Air Temperature - SID 1 PID $46
		//-------------------------------------------
		if ( bAATEMPSupport == TRUE )
		{
			stReqMsg.SID    = 0x01;
			stReqMsg.NumIds = 1;
			stReqMsg.ID[0]  = 0x46;
			if ( RequestSid ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE ) != FAIL )
			{
				for ( EcuIdx = 0;
				      EcuIdx < gNumOfECUs;
				      EcuIdx++ )
				{
					if ( gstResponse[EcuIdx].PidSize > 0 )
					{
						pPid = (PID *)&gstResponse[EcuIdx].Pid[0];

						if ( pPid->ID == 0x46 )
						{
							AAT_C = pPid->Data[0] - 40;  // 1 C with -40 offset
						}
					}
				}
			}
		}


		//-------------------------------------------
		// Get SID 9 IPT
		//-------------------------------------------
		if ( IptID != 0 )
		{
			stReqMsg.SID    = 0x09;
			stReqMsg.NumIds = 1;
			stReqMsg.ID[0]  = IptID;
			if ( (eRetCode = RequestSid ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE )) == FAIL )
			{
				Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
				      "INF $%X request\n",
				      stReqMsg.ID[0] );
				SubTestStatusFlags |= SUB_TEST_FAIL;
			}
			else
			{
				if ( eRetCode != PASS )
				{
					// cover the case where a response was early/late
					SubTestStatusFlags |= SUB_TEST_FAIL;
				}

				for ( EcuIdx = 0;
				      EcuIdx < gNumOfECUs;
				      EcuIdx++ )
				{
					if ( GetIptData ( EcuIdx, gstEcuSIDData[EcuIdx].pIpt ) == PASS )
					{
						if ( EcuIdx >= EcuPageOffset &&
						     EcuIdx < (EcuPageOffset + MAX_ECUS_PER_PAGE) )
						{
							SetFieldDec ( CURRENT_OBDCOND+(EcuIdx-EcuPageOffset), gstEcuSIDData[EcuIdx].pIpt->Data[0] );
							SetFieldDec ( CURRENT_IGNCTR+(EcuIdx-EcuPageOffset),  gstEcuSIDData[EcuIdx].pIpt->Data[1] );
						}

						// check when OBDCOND counters increment
						if ( OBDCondTimestampSecs[EcuIdx] == 0 &&
						     gstEcuSIDData[EcuIdx].pIpt->Data[0] > gstEcuSIDData[EcuIdx].pTest10_10_Ipt->Data[0] )
						{
							OBDCondTimestampSecs[EcuIdx] = RunTimeSecs;
							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "Idle Time = %d;  Speed Time = %d;  Run Time = %d  *OBDCOND DONE*\n",
							      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );

							// check for extremely early increments of OBDCOND and FAIL
							// (that is, OBDCOND that increment more than 30 seconds before the test will end)
							if ( IdleTimeSecs == 0 || AtSpeedTimeSecs < 270 || RunTimeSecs < 570 )
							{
								Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
								      "OBDCOND incremented much too soon\n" );
								SubTestStatusFlags |= SUB_TEST_FAIL;
							}
						}
					}
					else
					{
						if ( EcuIdx >= EcuPageOffset &&
						     EcuIdx < (EcuPageOffset + MAX_ECUS_PER_PAGE) )
						{
							SetFieldDec ( CURRENT_OBDCOND+(EcuIdx-EcuPageOffset), 0 );
							SetFieldDec ( CURRENT_IGNCTR+(EcuIdx-EcuPageOffset),  0 );
						}
					}
				}
			}
		}

		//-------------------------------------------
		// Get SID 9 FEOCNTR
		//-------------------------------------------
		if ( gbPlugIn == TRUE )
		{
			if ( LogInf12 ( ) == PASS )
			{
				for ( EcuIdx = 0;
				      EcuIdx < gNumOfECUs;
				      EcuIdx++ )
				{
					if ( EcuIdx >= EcuPageOffset &&
					     EcuIdx < (EcuPageOffset + MAX_ECUS_PER_PAGE) )
					{
						SetFieldDec ( CURRENT_FEOCNTR+(EcuIdx-EcuPageOffset), gstEcuSIDData[EcuIdx].pFEOCNTR->Data[0] );
					}
				}
			}
		}


		//-------------------------------------------
		// Update current PIDs, total engine time
		//-------------------------------------------
		if ( bRunTimeSupport == FALSE || bUseRPM == TRUE )
		{
			SetFieldDec ( RPM_INDEX, RPM );
		}
		else
		{
			SetFieldDec ( SPEED_INDEX_MPH, SpeedMPH );
			SetFieldDec ( SPEED_INDEX_KPH, SpeedKPH );
		}
		SetFieldDec ( TOTAL_DRIVE_TIMER, RunTimeSecs );

		if ( bBAROSupport == TRUE )
		{
			SetFieldDec ( BARO_PRESS_INDEX, Baro_InHg );
		}

		if ( bAATEMPSupport == TRUE )
		{
			SetFieldDec ( AMB_AIR_TEMP_INDEX, AAT_C );
			SetFieldDec ( AMB_AIR_TEMP_INDEX+1, ((AAT_C*9)/5)+32 );
		}


		//-------------------------------------------
		// Check for Vehicle Speed > 1 MPH when 0 RPM and 0 Run Time
		//-------------------------------------------
		if ( bVSSSupport == TRUE && SpeedMPH > 1 &&
		     bRPMSupport == TRUE && RPM == 0 &&
		     bRunTimeSupport == TRUE && RunTimeSecs == 0 )
		{
			Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
			      "Vehicle Speed > 1 mph with 0 rpm and 0 runtime\n" );
			SubTestStatusFlags |= SUB_TEST_FAIL;
		}


		//-------------------------------------------
		// Update test status
		//-------------------------------------------
		if ( SubTestStatusFlags != LastTestStatusFlags )
		{
			if ( (SubTestStatusFlags & SUB_TEST_FAIL) == SUB_TEST_FAIL )
			{
				setrgb ( HIGHLIGHTED_TEXT );
				SetFieldText ( TESTSTATUS_INDEX, "FAILURE" );
				setrgb ( NORMAL_TEXT );
			}
			else
			{
				SetFieldText ( TESTSTATUS_INDEX, "Normal" );
			}

			LastTestStatusFlags = SubTestStatusFlags;
		}


		//-------------------------------------------
		// Determine phase of dynamic test
		// update screen
		//-------------------------------------------
		bLoop = TRUE;
		while ( bLoop )
		{
			bLoop = FALSE;
			switch ( TestState )
			{
				case 0xFF:   // First Time ONLY - covers the case were the engine has already been idling
				{
					LastCheckTimestampSecs = RunTimeSecs;
					LastFRCheckTimestampSecs = RunTimeSecs;

					if ( (bRunTimeSupport == TRUE) ? (RunTimeSecs > 0) : (RPM > 450) )
					{
						if ( SpeedMPH <= 1 )
						{
							TestState = 1;
							LastCheckTimestampSecs = RunTimeSecs;
							IdleTimeSecs = RunTimeSecs;
							bLoop = TRUE;
							break;
						}
					}

					//
					// engine is not running or SpeedMPH >1... don't know if there was an idle
					// set-up to intentionally fall thru to case 0
					//
					TestState = 0;
			}
				//  0xFF falls through to 0x00

				case 0:     // wait until idle  (RunTimeSecs > 0 or RPM > 450)
				{
					if ( (bRunTimeSupport == TRUE) ? (RunTimeSecs > 0) : (RPM > 450) )
					{
						// check 600 seconds cumulative time
						if ( (TimeStatusFlags & CUMULATIVE_TIME) != CUMULATIVE_TIME &&
						     RunTimeSecs >= 600 )
						{
							TestCompleteTimestampSecs = RunTimeSecs;
							TimeStatusFlags |= CUMULATIVE_TIME;
							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "Idle Time = %d;  Speed Time = %d;  Run Time = %d  *RUN TIME DONE*\n",
							      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );
						}

						// Check for idle speed
						if ( ((bUseRPM == FALSE) ? (SpeedMPH <= 1 ) : (RPM > 450)) &&
						     IdleTimeSecs < 30 )
						{
							TestState = 1;
							IdleTimeSecs = 0;
							bLoop = TRUE;
						}
						// Check for drive speed
						else if ( ((bUseRPM == FALSE) ? (SpeedMPH >= 25 ) : (RPM > 1150)) &&
						          AtSpeedTimeSecs < 300 )
						{
							TestState = 2;
							bLoop = TRUE;
						}
						// Check for test complete
						else if ( TimeStatusFlags == (IDLE_TIME | ATSPEED_TIME | CUMULATIVE_TIME | FEO_TIME) )
						{
							TestState = 3;
							bLoop = TRUE;
						}
						else
						{
							LastCheckTimestampSecs = RunTimeSecs;
						}
					}
				}
				break;

				case 1:     // 30 seconds continuous time at idle
				{
					// (   (VSS <= 1mph (if supported) or 450 < RPM < 1150 (if VSS not supported)) AND
					//     (RunTimeSecs > 0 (if supported) or RPM > 450 (if RUNTIME not supported)) )
					if ( ((bUseRPM == FALSE) ? (SpeedMPH <= 1 ) : (RPM > 450 && RPM < 1150)) &&
					     ((bRunTimeSupport == FALSE) ? (RPM > 450) : 1) )
					{
						// check 600 seconds cumulative time
						if ( (TimeStatusFlags & CUMULATIVE_TIME) != CUMULATIVE_TIME &&
						     RunTimeSecs >= 600 )
						{
							TestCompleteTimestampSecs = RunTimeSecs;
							TimeStatusFlags |= CUMULATIVE_TIME;
							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "Idle Time = %d;  Speed Time = %d;  Run Time = %d  *RUN TIME DONE*\n",
							      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );
						}

						// check idle time
						IdleTimeSecs = min ( IdleTimeSecs + (RunTimeSecs - LastCheckTimestampSecs), 30 );
						LastCheckTimestampSecs = RunTimeSecs;

						SetFieldDec ( IDLE_TIMER, IdleTimeSecs );

						if ( IdleTimeSecs >= 30 )
						{
							TestCompleteTimestampSecs = RunTimeSecs;
							TimeStatusFlags |= IDLE_TIME;
							TestState = 0;
							bLoop = TRUE;
							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "Idle Time = %d;  Speed Time = %d;  Run Time = %d  *IDLE DONE*\n",
							      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );
						}

					}
					else
					{
						TestState = 0;
						bLoop = TRUE;
					}
				}
				break;

				case 2:     // 300 seconds cumulative time at SpeedMPH >= 25 KPH
				{
					// (   (VSS >= 25mph (if supported) or RPM >= 1150 (if VSS not supported)) AND
					//     (RunTimeSecs > 0 (if supported) or RPM >= 450 (if RUNTIME not supported)) )
					if ( ((bUseRPM == FALSE) ? (SpeedMPH >= 25) : (RPM >= 1150 )) &&
					     ((bRunTimeSupport == FALSE) ? (RPM > 450 ) : 1) )
					{
						// check 600 seconds cumulative time
						if ( (TimeStatusFlags & CUMULATIVE_TIME) != CUMULATIVE_TIME &&
						     RunTimeSecs >= 600 )
						{
							TestCompleteTimestampSecs = RunTimeSecs;
							TimeStatusFlags |= CUMULATIVE_TIME;
							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "Idle Time = %d;  Speed Time = %d;  Run Time = %d  *RUN TIME DONE*\n",
							      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );
						}

						// check at speed time
						AtSpeedTimeSecs = min ( AtSpeedTimeSecs + (RunTimeSecs - LastCheckTimestampSecs), 300 );
						LastCheckTimestampSecs = RunTimeSecs;

						SetFieldDec ( SPEED_25_MPH_TIMER, AtSpeedTimeSecs );

						if ( AtSpeedTimeSecs >= 300 )
						{
							TestCompleteTimestampSecs = RunTimeSecs;
							TimeStatusFlags |= ATSPEED_TIME;
							TestState = 0;
							bLoop = TRUE;
							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "Idle Time = %d;  Speed Time = %d;  Run Time = %d  *AT SPEED DONE*\n",
							      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );
						}
					}
					else
					{
						TestState = 0;
						bLoop = TRUE;
					}
				}
				break;

				case 3:     // check for test pass/fail
				{
					if ( IptID != 0 )
					{
						// check if any OBDCOND counters increment too soon or too late
						bFail = FALSE;
						for ( EcuIdx = 0;
						      EcuIdx < gNumOfECUs;
						      EcuIdx++ )
						{
							if ( (gstEcuSIDData[EcuIdx].pTest10_10_Ipt->Flags & VALIDDATA) != 0 &&
							     OBDCondTimestampSecs[EcuIdx] != 0 )
							{
								if ( OBDCondTimestampSecs[EcuIdx] < (TestCompleteTimestampSecs - 20) )
								{
									Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
									      "ECU %X  OBDCOND incremented too soon (RUNTM = %u)\n",
									      GetEcuId ( EcuIdx ),
									      OBDCondTimestampSecs[EcuIdx] );
									bFail = TRUE;
								}

								if ( OBDCondTimestampSecs[EcuIdx] > (TestCompleteTimestampSecs + 20) )
								{
									Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
									      "ECU %X  OBDCOND incremented too late (RUNTM = %u)\n",
									      GetEcuId ( EcuIdx ),
									      OBDCondTimestampSecs[EcuIdx] );
									bFail = TRUE;
								}
							}
						}

						if ( bFail == TRUE )
						{
							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "Idle Time = %d;  Speed Time = %d;  Run Time = %d\n",
							      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );

							gotoxy ( 0, BOTTOM_ROW+2 );
							return FAIL;
						}

						// check for OBDCOND for increment
						for ( EcuIdx = 0;
						      EcuIdx < gNumOfECUs;
						      EcuIdx++ )
						{
							if ( (gstEcuSIDData[EcuIdx].pTest10_10_Ipt->Flags & VALIDDATA) != 0 &&
							     OBDCondTimestampSecs[EcuIdx] == 0 )
							{
								break;      // OBDCOND counter not yet incremented - keep running test
							}
						}

						if ( EcuIdx >= gNumOfECUs )
						{
							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "Idle Time = %d;  Speed Time = %d;  Run Time = %d  *OBDCOND TEST DONE*\n",
							      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );

							gotoxy ( 0, BOTTOM_ROW+2 );
							return PASS;  // Test complete
						}

						// check for timeout
						if ( RunTimeSecs >= (TestCompleteTimestampSecs + 20) )
						{
							gotoxy ( 0, BOTTOM_ROW+2 );

							if ( geTestPhase == eTestInUseCounters )
							{
								Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
								      "More than 20 seconds has elapsed since the test completed "
								      "and not all the ECUs have incremented OBDCOND\n" );
								Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
								      "Idle Time = %d;  Speed Time = %d;  Run Time = %d\n",
								      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );
								return FAIL;
							}
							else
							{
								Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
								      "Idle Time = %d;  Speed Time = %d;  Run Time = %d  *PDTC TEST DONE*\n",
								     IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );
								return PASS;
							}
						}
					}
					else
					{
						// check for timeout
						if ( RunTimeSecs >= (TestCompleteTimestampSecs + 20) )
						{
							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "Idle Time = %d;  Speed Time = %d;  Run Time = %d  *TEST DONE*\n",
							      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );

							gotoxy ( 0, BOTTOM_ROW+2 );
							return PASS;
						}
					}
				}
				break;

			} // end switch (TestState)

			// Check for 10s of fueled operation for PHEV
			if ( gbPlugIn == TRUE &&
			     (TimeStatusFlags & FEO_TIME) != FEO_TIME &&
			     FueledRunTimeSecs < 10 )
			{
				if ( RPM > 450 )
				{
					FueledRunTimeSecs = min ( FueledRunTimeSecs + (RunTimeSecs - LastFRCheckTimestampSecs), 10 );
					LastFRCheckTimestampSecs = RunTimeSecs;

					SetFieldDec ( PHEV_TIMER, FueledRunTimeSecs );

					if ( FueledRunTimeSecs >= 10 )
					{
						TimeStatusFlags |= FEO_TIME;
						Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "Idle Time = %d;  Speed Time = %d;  Run Time = %d  *FUELED ENGINE TIME DONE*\n",
						      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );
					}
				}
				else // RPM < 450
				{
					LastFRCheckTimestampSecs = RunTimeSecs;
				}
			}
			else if ( gbPlugIn == FALSE )
			{
				TimeStatusFlags |= FEO_TIME;
			}

		} // end while (bLoop)

		//-------------------------------------------
		// Check for ESC key and sleep
		//-------------------------------------------
		do
		{
			if ( _kbhit ( ) != 0 )
			{
				key = _getch ( );
				if ( key == 27 )    // ESC key
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "Drive Cycle Test aborted by user\n\n" );
					Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "Idle Time = %d;  Speed Time = %d;  Run Time = %d\n",
					      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );

					gotoxy ( 0, BOTTOM_ROW+2 );
					return FAIL;
				}
				else if ( key == 0 || key == 224 )
				{
					key = _getch ( );
					if ( key == 73 )  //PGUP
					{
						if ( EcuPage < MAX_ECU_PAGE &&
						     EcuPageOffset+MAX_ECUS_PER_PAGE < gNumOfECUs )
						{
							EcuPageOffset += MAX_ECUS_PER_PAGE;
							EcuPage++;
							bECUPageUpdate = TRUE;
						}
					}
					else if ( key == 81 )  //PGDN
					{
						if ( EcuPage > 1 )
						{
							EcuPageOffset -= MAX_ECUS_PER_PAGE;
							EcuPage--;
							bECUPageUpdate = TRUE;
						}
					}
				}
			}


			CurrentTimestampMsecs = GetTickCount ( );

			Sleep ( min ( 1000 - (CurrentTimestampMsecs - LastCheckTimestampMsecs), 50 ) );

		} while ( CurrentTimestampMsecs - LastCheckTimestampMsecs < 1000 );

		if ( bECUPageUpdate == TRUE )
		{
			// Update ECU page number
			SetFieldDec ( ECUPAGE_INDEX, EcuPage );
			bECUPageUpdate = FALSE;

			for ( EcuIdx = 0;
			      EcuIdx < gNumOfECUs &&
			      EcuIdx < MAX_ECUS_PER_PAGE;
			      EcuIdx++ )
			{
				// initialize ECU IDs
				SetFieldHex ( ECU_ID+EcuIdx, GetEcuId ( EcuPageOffset+EcuIdx ) );

				// initialize initial OBDCONDs
				SetFieldDec ( INITIAL_OBDCOND+EcuIdx, gstEcuSIDData[EcuPageOffset+EcuIdx].pTest10_10_Ipt->Data[0] );

				// initialize initial IGNCTRs
				SetFieldDec ( INITIAL_IGNCTR+EcuIdx, gstEcuSIDData[EcuPageOffset+EcuIdx].pTest10_10_Ipt->Data[1] );

				if ( gbPlugIn == TRUE )
				{
					// initialize initial FEOCNTRs
					SetFieldDec ( INITIAL_FEOCNTR+EcuIdx, gstEcuSIDData[EcuPageOffset+EcuIdx].pFEOCNTR->Data[0] );
				}

				// if INF 8/B not supported, print 0 (INF8/B will never update)
				if ( IptID == 0 )
				{
					SetFieldDec ( CURRENT_OBDCOND+EcuIdx, 0 );
					SetFieldDec ( CURRENT_IGNCTR+EcuIdx,  0 );
				}
				else
				{
					SetFieldDec ( CURRENT_OBDCOND+EcuIdx, gstEcuSIDData[EcuPageOffset+EcuIdx].pIpt->Data[0] );
					SetFieldDec ( CURRENT_IGNCTR+EcuIdx,  gstEcuSIDData[EcuPageOffset+EcuIdx].pIpt->Data[1] );
				}
			}
		}

		LastCheckTimestampMsecs = CurrentTimestampMsecs;
	}

	Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "Error in Drive Cycle Test\n\n" );

	gotoxy ( 0, BOTTOM_ROW+2 );
	return FAIL;
}


/*******************************************************************************
**
**  Function:  DisplayDynamicTest10Results
**
**  Purpose:
**
*******************************************************************************/
void DisplayDynamicTest10Results ( void )
{
	IPT           Ipt;
	BYTE          EcuIdx;

	unsigned int  IptID = 0x00;  // support INF for IPT data, default 0 = no support


	// Initialize Array
	memset ( &Ipt,
	         0x00,
	         sizeof ( Ipt ) );

	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( IsIDSupported ( EcuIdx, INFREQUEST, INF_TYPE_IPT_GAS ) == TRUE )
		{
			IptID = INF_TYPE_IPT_GAS;
		}
		else if ( IsIDSupported ( EcuIdx, INFREQUEST, INF_TYPE_IPT_DIESEL ) == TRUE )
		{
			IptID = INF_TYPE_IPT_DIESEL;
		}

		// if IPT data is supported
		if ( IptID != 0 )
		{
			// ECU IDs
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %X supports INF $%X IPT data\n",
			      GetEcuId ( EcuIdx ),
			      IptID );

			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "         Initial    Current\n" );
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "OBDCOND  %-8d    %-8d\n",
			      gstEcuSIDData[EcuIdx].pTest10_10_Ipt->Data[0],
			      gstEcuSIDData[EcuIdx].pIpt->Data[0] );
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "IGNCTR   %-8d    %-8d\n",
			      gstEcuSIDData[EcuIdx].pTest10_10_Ipt->Data[1],
			      gstEcuSIDData[EcuIdx].pIpt->Data[1] );
		}

		// IPT data is not supported
		else
		{
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %X does not support IPT data\n",
			      GetEcuId ( EcuIdx ) );
		}

		IptID = 0;
	}
}


/*******************************************************************************
**
**  Function:  ReadIpt
**
**  Purpose:
**
*******************************************************************************/
BOOL ReadIpt ( const char * szTestSectionEnd, IPT Ipt[] )
{
	char         Buffer[256];
	char        *pSubString;
	int          CounterIndex;
	BYTE         EcuIdx;
	unsigned int EcuId = 0;


	// search for ECU ID
	while ( fgets ( Buffer, sizeof ( Buffer ), gLogFileHandle ) != 0 )
	{
		if ( substring( Buffer, szTestSectionEnd ) != 0 )     // end of Test XX section
		{
			return FALSE;
		}

		if ( (pSubString = substring ( Buffer, szECUID )) != 0 )
		{
			pSubString += sizeof ( szECUID );
			EcuId = strtoul ( pSubString, NULL, 16 );
			break;  // leave while (not end of file) loop
		}
	}

	// find EcuIdx
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( EcuId == GetEcuId ( EcuIdx ) )
		{
			break;  // leave ECU for loop
		}
	}

	if ( EcuIdx >= gNumOfECUs )
	{
		return FALSE;
	}

	// search for IPT INFOTYPE and SIZE
	while ( fgets ( Buffer, sizeof ( Buffer ), gLogFileHandle ) != 0 )
	{
		if ( substring ( Buffer, szTestSectionEnd ) != 0 )     // end of Test XX section
		{
			return FALSE;
		}

		if ( (pSubString = substring ( Buffer, szINF_SIZE )) != 0 )
		{
			pSubString += sizeof ( szINF_SIZE );
			Ipt[EcuIdx].ID = (BYTE) strtoul ( pSubString, NULL, 16 );
			pSubString += 3;
			Ipt[EcuIdx].NODI = (BYTE) strtod ( pSubString, NULL );
			break;  // leave while (not end of file) loop
		}
	}

	// read counters
	for ( CounterIndex = 0;
	      CounterIndex < Ipt[EcuIdx].NODI;
	      CounterIndex++ )
	{
		fgets ( Buffer, sizeof ( Buffer ), gLogFileHandle );

		if ( (Ipt[EcuIdx].ID == 0x08 && (pSubString = substring ( Buffer, szINF8[CounterIndex] )) != 0) ||
		     (Ipt[EcuIdx].ID == 0x0B && (pSubString = substring ( Buffer, szINFB[CounterIndex] )) != 0) )
		{
			pSubString = substring ( pSubString, "=" ) + 1;
			Ipt[EcuIdx].Data[CounterIndex] = (unsigned short)strtoul ( pSubString, NULL, 10 );
		}
		else
		{
			return FALSE;
		}
	}

	// Sid 9 Inf 8 data valid
	Ipt[EcuIdx].Flags |= VALIDDATA;

	return TRUE;
}


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


	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		// Initialize ECU Arrays
		if ( (gstEcuSIDData[EcuIdx].pTest10_10_Ipt = (IPT *) malloc ( sizeof ( IPT ) )) == NULL )
		{
			Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
			      "Unable to allocate memory (%d bytes) for gstEcuSIDData[%d].pTest10_10_Ipt.",
			      sizeof ( IPT ),
			      EcuIdx );
			exit ( FAIL );
		}
		memset ( gstEcuSIDData[EcuIdx].pTest10_10_Ipt,
		         0x00,
		         sizeof ( IPT ) );

		if ( (gstEcuSIDData[EcuIdx].pFEOCNTR = (INF *) malloc ( sizeof ( INF ) )) == NULL )
		{
			Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
			      "Unable to allocate memory (%d bytes) for gstEcuSIDData[%d].pFEOCNTR.",
			      sizeof ( INF ),
			      EcuIdx );
			exit ( FAIL );
		}
		memset ( gstEcuSIDData[EcuIdx].pFEOCNTR,
		         0x00,
		         sizeof ( INF ) );

		if ( (gstEcuSIDData[EcuIdx].pIpt = (IPT *) malloc ( sizeof ( IPT ) )) == NULL )
		{
			Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
			      "Unable to allocate memory (%d bytes) for gstEcuSIDData[%d].pIpt.",
			      sizeof ( IPT ),
			      EcuIdx );
			exit ( FAIL );
		}
		memset ( gstEcuSIDData[EcuIdx].pIpt,
		         0x00,
		         sizeof ( IPT ) );
	}

}


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


	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		// gstEcuSIDData[EcuIdx].pTest10_10_Ipt is used in Test11_PerformanceCounters,
		// only free its memory if in Test 9.19
		if ( geTestPhase == eTestNoFault3DriveCycle && gTestSubsection == 19 )
		{
			free ( gstEcuSIDData[EcuIdx].pTest10_10_Ipt );
		}

		free ( gstEcuSIDData[EcuIdx].pFEOCNTR );

		free ( gstEcuSIDData[EcuIdx].pIpt );
	}
}
