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


// Function Prototypes
STATUS VerifyIPD ( void );
STATUS GetIPD    ( BYTE  EcuIdx );
void   SaveIPD   ( BYTE  EcuIdx );
void   LogIPD    ( BYTE  EcuIdx );

STATUS LogINF12    ( void );
STATUS VerifyINF12 ( void );

STATUS StartLogFile  ( BOOL *pbTestReEntered );
STATUS RunDynamicTest10 ( unsigned long EngineStartTimestampMsecs );
void   DisplayDynamicTest10Results ( void );
void   AllocateMemoryForRunDynamicTest10 ( void );
void   FreeMemoryForRunDynamicTest10 ( 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 VerifyINF12Data ( BYTE EcuIdx );
extern STATUS VerifyINF82Data ( BYTE EcuIdx );
extern STATUS ReadVIN         ( void );
extern STATUS ReadCALIDs      ( void );
extern STATUS ReadCVNs        ( void );

extern STATUS VerifyDTCExtDataList     ( BYTE );
extern STATUS VerifyDTCExtData         ( BYTE );
extern STATUS VerifyDTCIMReadinessList ( void );


// Variables
INF *pTest10_10_FEOCNTR;

BOOL bIPDSupported = FALSE;

WORD DTCIUMPRListCount = 0;
DTC  DTCIUMPRList[MAX_DTCIUMPR_COUNT];

WORD DTCTestResultListCount = 0;
DTC  DTCTestResultList[MAX_DTCTESTRESULT_COUNT];

WORD DTCSMADListCount = 0;
DTC  DTCSMADList[MAX_DTCSMAD_COUNT];

WORD DTCIMReadinessListCount = 0;
DTC  DTCIMReadinessList[MAX_DTCIUMPR_COUNT];

extern unsigned short gProtocolSrchIdx;

extern DTCIUMPR  *DTCIUMPR_Test10_10;
extern FILE      *gLogFileHandle;
extern char      *gComplianceTestTypeStrings[];
extern char      *gDisplayStrings[];
extern char      *gVehicleTypeStrings[];
extern char      *gPwrTrnTypeStrings[];
extern char      *gEngineTypeStrings[];


const char szEcuId[]      =   "ECU ID";
const char szIpdSize[]    =   "IPD SIZE";
const char szIpdName[][8] = { "OBDCOND",
                              "IGNCNTR" };

extern const char szIumprData1[];
extern const char szIumprData2[];
extern const char szSmadData1[];
extern const char szSmadData2[];
extern const char szSmadData3[];


/********************************************************************************
**  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, COND_IDLE_ROW+1},
	{"- Cumulative time > or = 300 seconds with vehicle speed > 25MPH/40KPH.", 1, COND_SPEED_ROW},
	{"- Cumulative time since engine start > or = 600 seconds.", 1, COND_SPEED_ROW+1},

	{"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          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." );

	// 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;
	}

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

	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 $22 data
//*******************************************************************************
	gTestSubsection = 2;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify Diagnostic Data (SID $22 PIDs), 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.3 - Get VIN and create (or append) log file
//*******************************************************************************
	gTestSubsection = 3;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify Vehicle Information (SID $22 INFs), 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;
	}

	// Get list of DTCs supporting ExtData $91 (IUMPR), Physically Addressed
	if ( VerifyDTCExtDataList ( 0x91 ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	// Get list of DTCs supporting ExtData $92 (Test Result), Physically Addressed
	if ( VerifyDTCExtDataList ( 0x92 ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	// Get list of DTCs supporting ExtData $93 (SMAD), Physically Addressed
	if ( VerifyDTCExtDataList ( 0x93 ) != 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 (SID $22 PID Support), Engine Off)" );

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

	if ( RequestIDSupportData ( PF5REQUEST, 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 (SID $14), 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 (SID $22 PID $F501), 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 On-Board Monitoring Test Results (SID $19 DTCEDRN $92))" );

	// Read DTC ExtData $92 (Test Result), based on list read in Test 10.3
	if ( VerifyDTCExtData ( 0x92 ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Test Result Data error\n" );
		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 (SID $19 LEV $42 StatusMask $04), Engine Off)" );

	if ( VerifyPendingDTCData ( DTC_STATE_NO_FAULT ) != 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 (SID $19 LEV $42 StatusMask $08), Engine Off)" );

	if ( VerifyConfirmedDTCData ( DTC_STATE_NO_FAULT ) != 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 (DTC IUMPR, SMAD, IM Readiness), Engine Off)" );

	// Get list of DTCs supporting IM Readiness
	if ( VerifyDTCIMReadinessList ( ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		   "IM Readiness List error\n" );
		bSubTestFailed = TRUE;
	}

	// Read DTC ExtData $91 (IUMPR)
	if ( VerifyDTCExtData ( 0x91 ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "IUMPR Data error\n" );
		bSubTestFailed = TRUE;
	}

	// Read DTC ExtDaya $93 (SMAD)
	if ( VerifyDTCExtData ( 0x93 ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "SMAD Data error\n" );
		bSubTestFailed = TRUE;
	}

	// Get in-use performance tracking (SID $22 INF $F882)
	if ( VerifyIPD ( ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "IPD Data error\n" );
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE )
	{
		if ( Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT,
		           "Verify IUMPR, SMAD, IM Readiness, IPD unsuccessful." ) == 'N' )
		{
			Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
			return FAIL;
		}
		bSubTestFailed = TRUE;
		gbTestSubsectionFailed = FALSE;
	}
	else if ( bIPDSupported == FALSE &&
	          (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." );

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

	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;
	AllocateMemoryForRunDynamicTest10 ( );
	eRetVal = RunDynamicTest10 ( EngineStartTimestampMsecs );
	DisplayDynamicTest10Results ( );
	FreeMemoryForRunDynamicTest10 ( );
	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." );


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

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

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify Diagnostic Data (SID $22 PIDs), 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 (SID $22 INFs), Engine Running)" );

	// Get in-use performance tracking (SID $22 INF $F882)
	if ( VerifyIPD ( ) != PASS || gbTestSubsectionFailed == TRUE )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "IPD (INF $F882) data error\n" );
		bSubTestFailed = TRUE;
	}

	// Get IUMPR Data
	if ( VerifyDTCExtData ( 0x91 ) != PASS || gbTestSubsectionFailed == TRUE )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "IUMPR data error\n" );
		bSubTestFailed = TRUE;
	}


	// Get SMAD Data
	if ( VerifyDTCExtData ( 0x93 ) != PASS || gbTestSubsectionFailed == TRUE )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "SMAD data error\n" );
		bSubTestFailed = TRUE;
	}

	// if PHEV, get FEOCNTR Data
	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


	// Request SID $22 INF support data
	if ( RequestIDSupportData ( INFREQUEST, TRUE ) != PASS )
	{
		if ( Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT,
		           "Errors detected that may cause future failures." ) == 'N' )
		{
			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 );
			scanf_s ( "%s", Buffer, sizeof ( Buffer ) );
			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;
	}

	// done
	return PASS;
}


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


	bIPDSupported = FALSE;

	// check for support and set DID accordingly
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		// INF $F882 request
		if ( IsIDSupported ( EcuIdx, INFREQUEST, INF_TYPE_IPD_UDS ) == TRUE )
		{
			gstResponse[EcuIdx].bIPDSupported = TRUE;
			bIPDSupported = TRUE;


			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %-8X  supports INF $F882 IPD\n",
			      GetEcuId ( EcuIdx ) );
		}
	}


	if ( bIPDSupported == FALSE)
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "In-Use Performace Data (INF $F882) not supported by any ECU\n" );
		return PASS;
	}


	// Request the In-Use Performance Data
	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = INF_TYPE_IPD_UDS;
	if ( RequestSID ( &stReqMsg, REQ_MSG_NORMAL ) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "INF $%04X request\n",
		      stReqMsg.u.DID[0] );
		return FAIL;
	}


	// Check the responses
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( GetIPD ( EcuIdx ) != PASS )
		{
			eRetCode = FAIL;
		}

		if ( (geTestPhase == eTestNoFault3DriveCycle  && gTestSubsection == 16) ||
		     (geTestPhase == eTestInUseCounters       && gTestSubsection == 10) )
		{
			// save copy of OBDCOND
			gstResponse[EcuIdx].IPD_Test10_10[IPD_OBDCOND_INDEX] = gstResponse[EcuIdx].IPD[IPD_OBDCOND_INDEX];

			// save copy of IGNCNTR
			gstResponse[EcuIdx].IPD_Test10_10[IPD_IGNCNTR_INDEX] = gstResponse[EcuIdx].IPD[IPD_IGNCNTR_INDEX];
		}
	}

	return eRetCode;
}


//*******************************************************************************
//**
//**  Function:  GetIPD
//**
//**  Purpose:   Copy from gstResponse[EcuIdx].INF into common,
//**             protocol-independent, format.
//**
//*******************************************************************************/
STATUS GetIPD ( BYTE  EcuIdx )
{
	if ( gstResponse[EcuIdx].INFSize != 0 )
	{
		// If NOT test 9.16, 10.12 or 11.2, log failures
		if ( !((geTestPhase == eTestNoFault3DriveCycle  && gTestSubsection == 16) ||
		       (geTestPhase == eTestInUseCounters       && gTestSubsection == 12) ||
		       (geTestPhase == eTestPerformanceCounters && gTestSubsection == 2)) )
		{
			if ( VerifyINF82Data ( EcuIdx ) != PASS )
			{
				return FAIL;
			}
		}
		else
		{
			// save IPD to selected ECU
			SaveIPD ( EcuIdx );

			// Log IPD for selected ECU
			LogIPD ( EcuIdx );
		}
	}
	// if no IPD data to process, exit
	else
	{
		// clear IPD structure
		memset ( &gstResponse[EcuIdx].IPD,
		         0,
		         sizeof ( gstResponse[EcuIdx].IPD ) );

		gstResponse[EcuIdx].bIPDSupported = FALSE;

		// Log IPD for selected ECU
		LogIPD ( EcuIdx );
	}

	return PASS;
}


//*******************************************************************************
//**
//**  Function:  SaveIPD
//**
//**  Purpose:   Copy from gstResponse[EcuIdx].INF into common,
//**             protocol-independent, format.
//**
//*******************************************************************************/
void SaveIPD ( BYTE  EcuIdx )
{
	INF   *pINF;
	WORD  *pIPD = NULL;


	// point to IPD structure in gstResponse
	pINF = (INF *)&gstResponse[EcuIdx].INF[0];

	gstResponse[EcuIdx].bIPDSupported = TRUE;

	// save OBDCOND
	gstResponse[EcuIdx].IPD[IPD_OBDCOND_INDEX] = (pINF->Data[0] << 8) +
	                                              pINF->Data[1];

	// save IGNCNTR
	gstResponse[EcuIdx].IPD[IPD_IGNCNTR_INDEX] = (pINF->Data[2] << 8) +
	                                              pINF->Data[3];

	// indicate the data structure is valid
	gstResponse[EcuIdx].StatusFlags |= IPDDATAVALID;

	// if this is a test phase that needs it's data saved for later testing,
	// also save the data to that phase's copy of IPD
	if ( (geTestPhase == eTestInUseCounters       && gTestSubsection == 10) ||
	     (geTestPhase == eTestPerformanceCounters && gTestSubsection == 1)  ||
	     (geTestPhase == eTestPerformanceCounters && gTestSubsection == 7)  ||
	     (geTestPhase == eTestPerformanceCounters && gTestSubsection == 12) )
	{
		if ( geTestPhase == eTestInUseCounters       && gTestSubsection == 10 )
		{
			pIPD = (WORD *)&gstResponse[EcuIdx].IPD_Test10_10[0];
		}
		else if ( geTestPhase == eTestPerformanceCounters && gTestSubsection == 1 )
		{
			pIPD = (WORD *)&gstResponse[EcuIdx].IPD_Test11_CurrentDisplayData[0];
		}
		else if ( geTestPhase == eTestPerformanceCounters && gTestSubsection == 7 )
		{
			pIPD = (WORD *)&gstResponse[EcuIdx].IPD_Test11_7[0];
		}
		else if ( geTestPhase == eTestPerformanceCounters && gTestSubsection == 12 )
		{
			pIPD = (WORD *)&gstResponse[EcuIdx].IPD_Test11_12[0];
		}

		// save copy of OBDCOND
		pIPD[IPD_OBDCOND_INDEX] = gstResponse[EcuIdx].IPD[IPD_OBDCOND_INDEX];

		// save copy of IGNCNTR
		pIPD[IPD_IGNCNTR_INDEX] = gstResponse[EcuIdx].IPD[IPD_IGNCNTR_INDEX];
	}
}


//*******************************************************************************
//**
//**  Function:  LogIPD
//**
//**  Purpose:
//**
//*******************************************************************************/
void LogIPD ( BYTE EcuIdx )
{
	// if TEST 10.10
	if ( geTestPhase == eTestInUseCounters && gTestSubsection == 10 )
	{
		// log ECU ID
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "%-9s = 0x%X\n",
		      szEcuId,
		      GetEcuId ( EcuIdx ) );

		if (gstResponse[EcuIdx].bIPDSupported)
		{
			// log number of data items (datasize/2)
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "%-9s = 2\n",
			      szIpdSize );

			// log IPD names and values
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "%-9s = %u\n",
			      szIpdName[IPD_OBDCOND_INDEX],
			      gstResponse[EcuIdx].IPD[IPD_OBDCOND_INDEX] );

			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "%-9s = %u\n",
			      szIpdName[IPD_IGNCNTR_INDEX],
			      gstResponse[EcuIdx].IPD[IPD_IGNCNTR_INDEX] );
		}
		else
		{
			// log number of data items (datasize/2)
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "%-9s = 0\n",
			      szIpdSize );
		}
	}
	else if ( gstResponse[EcuIdx].bIPDSupported )
	{
		if (gstResponse[EcuIdx].bIPDSupported)
		{
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %-8X  INF $F882  %-7s = %u\n",
			      GetEcuId ( EcuIdx ),
			      szIpdName[IPD_OBDCOND_INDEX],
			      gstResponse[EcuIdx].IPD[IPD_OBDCOND_INDEX] );

			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %-8X  INF $F882  %-7s = %u\n",
			      GetEcuId ( EcuIdx ),
			      szIpdName[IPD_IGNCNTR_INDEX],
			      gstResponse[EcuIdx].IPD[IPD_IGNCNTR_INDEX] );
		}
	}

}




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

	unsigned short FEOCNTR;


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

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


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

		// point to INF 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->INFLSB != (INF_TYPE_FEOCNTR & 0x00FF) )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %-8X  Invalid response to INF $F812 request\n",
				      GetEcuId ( EcuIdx ) );
				eRetCode = FAIL;
			}
			else
			{
				// If the data is good, save it
				if ( VerifyINF12Data ( EcuIdx ) == PASS )
				{
					memcpy ( gstResponse[EcuIdx].pFEOCNTR,
					         &(gstResponse[EcuIdx].INF[0]),
					         sizeof ( INF ) );

					if ( geTestPhase == eTestPerformanceCounters && 
					     gTestSubsection == 10 )
					{
						memcpy ( gstResponse[EcuIdx].pTest10_10_FEOCNTR,
						         &(gstResponse[EcuIdx].INF[0]),
						         sizeof ( INF ) );
					}

					FEOCNTR = (pINF->Data[0] << 8) +
					           pINF->Data[1];

					// If NOT test 9.16, 10.12 or 11.2, log
					if ( ( geTestPhase != eTestNoFault3DriveCycle  || gTestSubsection != 16 ) &&
					     ( geTestPhase != eTestInUseCounters       || gTestSubsection != 12 ) &&
					     ( geTestPhase != eTestPerformanceCounters || gTestSubsection != 2 ) )
					{
						// log FEOCNTR
						Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %-8X  INF $F812  FEOCNTR = %d\n",
						      GetEcuId ( EcuIdx ),
						      FEOCNTR );
					}
				}
			}
		}
	}

	return eRetCode;
}


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


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

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


	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		// point to INF 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 )
		{
			VerifyINF12Data ( EcuIdx );
		}
	}

	return eRetCode;
}


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

	BYTE           SubTestStatusFlags  = SUB_TEST_NORMAL;
	BYTE           LastTestStatusFlags = SUB_TEST_INITIAL;  // set to force an update the first time
	BYTE           key;
	unsigned int   TestState;
	unsigned int   TimeStatusFlags = 0;
	BOOL           bErrorPrinted = FALSE;    // indicates whether or not the failure message has already been printed
	BYTE           LoopCount;
	BOOL           bLoop;
	BOOL           bFail;
	BOOL           bRunTimeSupport;
	unsigned short RunTimeSecs;
	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 int   EcuPageOffset = 0;
	unsigned int   EcuPage = 1;
	BOOL           bECUPageUpdate = FALSE;

	BOOL           bOBDTypeSupport;
	BYTE           PID1C;
	BOOL           bBAROSupport;
	unsigned int   Baro_InHg;
	BOOL           bAATEMPSupport;
	unsigned int   AAT_C;
	BOOL           bRPMSupport;
	BOOL           bUseRPM = FALSE;  // indicate use of RPM for Idle and Drive time calculations
	unsigned short RPM;
	BOOL           bVSSSupport;
	unsigned short SpeedMPH;
	unsigned short SpeedKPH;
	BOOL           bIPDSupported = FALSE;        // support INF for IPD, default FALSE = no support
	BOOL           bPermDTC;
	unsigned short OBDCOND = 0;
	unsigned short IGNCTR  = 0;


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

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

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

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

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

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

	if ( bRPMSupport == FALSE && bRunTimeSupport == FALSE )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "PIDs $F40C (RPM) and $F41F (RUNTM) not supported\n" );
		return FAIL;
	}

	//-------------------------------------------------------------------------
	// Request OBD_Type - SID $22 PID $F41C.  Needed to determine wether to use
	// Vehicle Speed (KPH/MPH) or Egine Speed (RPM) to determine idel and drive time.
	//-------------------------------------------------------------------------
	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = 0xF41C;
	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->PIDLSB == 0x1C )
				{
					PID1C = pPid->Data[0];
					break;  // leave ECU for loop
				}
			}
		}
	}

	if ( EcuIdx == gNumOfECUs )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "No response to PID $F41C (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   && gstUserInput.eFuelType == DIESEL) ||
	     (gstUserInput.eComplianceType == US_OBDII && gstUserInput.eFuelType == DIESEL && PID1C == 0x22) )
	{
		bUseRPM = TRUE;
	}
	else if ( gstUserInput.eComplianceType  == HD_EOBD ||
	          (gstUserInput.eComplianceType == US_OBDII &&
	           gstUserInput.eFuelType       == DIESEL &&
	           gstUserInput.eVehicleType    == MDDC) )
	{
		if ( bVSSSupport )
		{
			bUseRPM = FALSE;
		}
		else
		{
			bUseRPM = TRUE;
		}
	}
	else
	{
		bUseRPM = FALSE;
	}


	if ( IsIDSupported ( ALLECUS, INFREQUEST, INF_TYPE_IPD_UDS ) == TRUE )
	{
		bIPDSupported = TRUE;
	}
	else
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "IPD (INF $F882) not supported\n" );
	}


	//-------------------------------------------
	// Initialize Array
	//-------------------------------------------
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
//		gstResponse[EcuIdx].pIPT = (IPT *) malloc ( sizeof ( IPT ) );
		memset ( gstResponse[EcuIdx].IPD,
		         0x00,
		         sizeof ( WORD ) * 2);

//		gstResponse[EcuIdx].pFEOCNTR = (INF *) malloc ( sizeof ( INF ) );
		memset ( gstResponse[EcuIdx].pFEOCNTR,
		         0x00,
		         sizeof ( INF ) );
	}


	//-------------------------------------------
	// 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, gstResponse[EcuIdx].IPD_Test10_10[0] );

		// initialize initial IGNCTRs
		SetFieldDec ( INITIAL_IGNCTR+EcuIdx,  gstResponse[EcuIdx].IPD_Test10_10[1] );

		if ( gbPlugIn == TRUE )
		{
			// initialize initial FEOCNTRs
			SetFieldDec ( INITIAL_FEOCNTR+EcuIdx, (unsigned int)((gstResponse[EcuIdx].pFEOCNTR->Data[0] << 8) +
			                                                      gstResponse[EcuIdx].pFEOCNTR->Data[1]) );
		}

		OBDCondTimestampSecs[EcuIdx] = 0;

		// if INF $F882 not supported, print 0 (INF8/B will never update)
		if ( bIPDSupported == FALSE )
		{
			SetFieldDec ( CURRENT_OBDCOND+EcuIdx, gstResponse[EcuIdx].IPD[IPD_OBDCOND_INDEX] );
			SetFieldDec ( CURRENT_IGNCTR+EcuIdx,  gstResponse[EcuIdx].IPD[IPD_IGNCNTR_INDEX] );
		 }
	}
	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 $19 LEV $55
			//-------------------------------------------
			stReqMsg.SID     = 0x19;
			stReqMsg.NumIds  = 2;
			stReqMsg.u.ID[0] = 0x55;  // reportWWHOBDDTCWithPrmanentStatus
			stReqMsg.u.ID[1] = 0x33;  // FunctionalGroupIdentifier
			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 $19 LEV $55 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 $19 LEV $55 - DTC erased prematurely.\n" );

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


		//-------------------------------------------
		// request RPM - PID $F40C
		//-------------------------------------------
		if ( bRPMSupport == TRUE )
		{
			stReqMsg.SID      = 0x22;
			stReqMsg.NumIds   = 1;
			stReqMsg.u.DID[0] = 0xF40C;
			if ( (eRetCode = RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE )) == FAIL )
			{
				Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
				      "PID $F40C 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->PIDLSB == 0x0C )
						{
							RPM = (pPid->Data[0] << 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,
					      "PID $F40C missing response\n" );
					SubTestStatusFlags |= SUB_TEST_FAIL;
				}
			}
		}

		//-------------------------------------------
		// request Speed - PID $F40D
		//-------------------------------------------
		if ( bVSSSupport == TRUE  )
		{
			stReqMsg.SID      = 0x22;
			stReqMsg.NumIds   = 1;
			stReqMsg.u.DID[0] = 0xF40D;
			if ( (eRetCode = RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE )) == FAIL )
			{
				Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
				      "PID $F40D 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->PIDLSB == 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,
					      "PID $F40D missing response\n" );
					SubTestStatusFlags |= SUB_TEST_FAIL;
				}
			}
		}

		//-------------------------------------------
		// request engine RunTime - PID $F41F
		//-------------------------------------------
		if ( bRunTimeSupport == TRUE )
		{
			stReqMsg.SID      = 0x22;
			stReqMsg.NumIds   = 1;
			stReqMsg.u.DID[0] = 0xF41F;
			if ( (eRetCode = RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE )) == FAIL )
			{
				Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
				      "PID $F41F 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->PIDLSB == 0x1F )
						{
							RunTimeSecs = (pPid->Data[0] << 8) +
							               pPid->Data[1];    // 1 cnt = 1 sec
							break;  // leave ECU for loop
						}
					}
				}

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


		//-------------------------------------------
		// request Barometric Pressure - PID $F433
		//-------------------------------------------
		if ( bBAROSupport == TRUE )
		{
			stReqMsg.SID      = 0x22;
			stReqMsg.NumIds   = 1;
			stReqMsg.u.DID[0] = 0xF433;
			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->PIDLSB == 0x33 )
						{
							Baro_InHg = (unsigned int)((float)(pPid->Data[0]) * 0.295301);  // Convert from kPa to in Hg
						}
					}
				}
			}
		}


		//-------------------------------------------
		// request Ambient Air Temperature - PID $F446
		//-------------------------------------------
		if ( bAATEMPSupport == TRUE )
		{
			stReqMsg.SID      = 0x22;
			stReqMsg.NumIds   = 1;
			stReqMsg.u.DID[0] = 0xF446;
			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->PIDLSB == 0x46 )
						{
							AAT_C = pPid->Data[0] - 40;  // 1 C with -40 offset
						}
					}
				}
			}
		}


		//-------------------------------------------
		// Get SID $22 IPD
		//-------------------------------------------
		if ( bIPDSupported == TRUE )
		{
			stReqMsg.SID      = 0x22;
			stReqMsg.NumIds   = 1;
			stReqMsg.u.DID[0] = 0xF882;
			if ( (eRetCode = RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_IGNORE_NO_RESPONSE )) == FAIL )
			{
				Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
				      "INF $%04X request\n",
				      stReqMsg.u.DID[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 ( GetIPD ( EcuIdx ) == PASS )
					{
						if ( EcuIdx >= EcuPageOffset &&
						     EcuIdx < (EcuPageOffset + MAX_ECUS_PER_PAGE) )
						{
							SetFieldDec ( CURRENT_OBDCOND+(EcuIdx-EcuPageOffset), gstResponse[EcuIdx].IPD[IPD_OBDCOND_INDEX] );
							SetFieldDec ( CURRENT_IGNCTR +(EcuIdx-EcuPageOffset), gstResponse[EcuIdx].IPD[IPD_IGNCNTR_INDEX] );
						}

						// check when OBDCOND counters increment
//						if ( OBDCondTimestampSecs[EcuIdx] == 0 &&
//						     gstResponse[EcuIdx].IPD[IPD_OBDCOND_INDEX] > gstResponse[EcuIdx].IPD_Test10_10[IPD_OBDCOND_INDEX] )
						if ( gstResponse[EcuIdx].IPD[IPD_OBDCOND_INDEX] > gstResponse[EcuIdx].IPD_Test10_10[IPD_OBDCOND_INDEX] )
						{
							if ( OBDCondTimestampSecs[EcuIdx] == 0 )
							{
								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;
								}
								OBDCondTimestampSecs[EcuIdx] = RunTimeSecs;
							}
						}
					}
					else
					{
						if ( EcuIdx >= EcuPageOffset &&
						     EcuIdx < (EcuPageOffset + MAX_ECUS_PER_PAGE) )
						{
							SetFieldDec ( CURRENT_OBDCOND+(EcuIdx-EcuPageOffset), 0 );
							SetFieldDec ( CURRENT_IGNCTR +(EcuIdx-EcuPageOffset),  0 );
						}
					}
				}
			}
		}

		//-------------------------------------------
		// Get SID $22 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), (unsigned int)((gstResponse[EcuIdx].pFEOCNTR->Data[0] << 8) +
						                                                                      gstResponse[EcuIdx].pFEOCNTR->Data[1]) );
					}
				}
			}
		}


		//-------------------------------------------
		// 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;
		LoopCount = 0;
		while ( bLoop && LoopCount < 5 )
		{
			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 ( bIPDSupported == TRUE )
					{
						// check if any OBDCOND counters increment too soon or too late
						bFail = FALSE;
						for ( EcuIdx = 0;
						      EcuIdx < gNumOfECUs;
						      EcuIdx++ )
						{
							if ( (gstResponse[EcuIdx].StatusFlags & IPDDATAVALID) != 0 &&
							     OBDCondTimestampSecs[EcuIdx] != 0 )
							{
								if ( OBDCondTimestampSecs[EcuIdx] < (TestCompleteTimestampSecs - 20) )
								{
									Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
									      "ECU %-8X  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 %-8X  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 ( (gstResponse[EcuIdx].StatusFlags & IPDDATAVALID) != 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;
			}

			LoopCount++;
		} // 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, gstResponse[EcuPageOffset+EcuIdx].IPD_Test10_10[0] );

				// initialize initial IGNCTRs
				SetFieldDec ( INITIAL_IGNCTR+EcuIdx,  gstResponse[EcuPageOffset+EcuIdx].IPD_Test10_10[1] );

				if ( gbPlugIn == TRUE )
				{
					// initialize initial FEOCNTRs
					SetFieldDec ( INITIAL_FEOCNTR+EcuIdx, (unsigned int)((gstResponse[EcuIdx].pFEOCNTR->Data[0] << 8) +
					                                                      gstResponse[EcuIdx].pFEOCNTR->Data[1]) );
				}

				// if INF $F882 not supported, print 0 (will never update)
				if ( bIPDSupported == FALSE )
				{
					SetFieldDec ( CURRENT_OBDCOND+EcuIdx, 0 );
					SetFieldDec ( CURRENT_IGNCTR+EcuIdx,  0 );
				}
				else
				{
					SetFieldDec ( CURRENT_OBDCOND+EcuIdx, gstResponse[EcuPageOffset+EcuIdx].IPD[IPD_OBDCOND_INDEX] );
					SetFieldDec ( CURRENT_IGNCTR+EcuIdx,  gstResponse[EcuPageOffset+EcuIdx].IPD[IPD_IGNCNTR_INDEX] );
				}
			}
		}

		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 )
{
	BYTE          EcuIdx;
	WORD          DtcIdx;


	if ( geTestPhase == eTestNoFault3DriveCycle )
	{
		Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
		      "On exit from Test 9.19 the following were the conditions:\n" );
	}
	else
	{
		Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
		      "On exit from Test 10.12 the following were the conditions:\n" );
	}

	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		// if IPD is supported
		if ( gstResponse[EcuIdx].bIPDSupported == TRUE )
		{
			// ECU IDs
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %-8X  supports INF $%04X IPD\n",
			      GetEcuId ( EcuIdx ),
			      INF_TYPE_IPD_UDS );

			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "         Initial    Current\n" );
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "%-7s  %-7d    %-7d\n",
			      szIpdName[IPD_OBDCOND_INDEX],
			      gstResponse[EcuIdx].IPD_Test10_10[0],
			      gstResponse[EcuIdx].IPD[IPD_OBDCOND_INDEX] );
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "%-7s  %-7d    %-7d\n",
			      szIpdName[IPD_IGNCNTR_INDEX],
			      gstResponse[EcuIdx].IPD_Test10_10[1],
			      gstResponse[EcuIdx].IPD[IPD_IGNCNTR_INDEX] );
		}
		// IPD is not supported
		else
		{
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %-8X  does not support INF $%04X IPD\n",
			      GetEcuId ( EcuIdx ),
			      INF_TYPE_IPD_UDS );
		}

		// if IUMPR is supported
		if ( gstResponse[EcuIdx].bDTCIUMPRSupported == TRUE )
		{
			// ECU IDs
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %-8X  supports DTC IUMPR data\n",
			      GetEcuId ( EcuIdx ) );

			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "                        Initial    Current\n" );

			for ( DtcIdx = 0;
			      DtcIdx < gstResponse[EcuIdx].DTCIUMPRCount;
			      DtcIdx++ )
			{
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "DTC %c%02X%02X  %-11s  %-7d    %-7d\n",
				      DTCTypeCharacter[(gstResponse[EcuIdx].DTCIUMPRList[DtcIdx].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				      gstResponse[EcuIdx].DTCIUMPRList[DtcIdx].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				      gstResponse[EcuIdx].DTCIUMPRList[DtcIdx].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
				      szIumprData1,
				      gstResponse[EcuIdx].DTCIUMPR_Test10_10[DtcIdx].CompCounts,
				      gstResponse[EcuIdx].DTCIUMPRList[DtcIdx].CompCounts );

				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "DTC %c%02X%02X  %-11s  %-7d    %-7d\n",
				      DTCTypeCharacter[(gstResponse[EcuIdx].DTCIUMPRList[DtcIdx].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				      gstResponse[EcuIdx].DTCIUMPRList[DtcIdx].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				      gstResponse[EcuIdx].DTCIUMPRList[DtcIdx].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
				      szIumprData2,
				      gstResponse[EcuIdx].DTCIUMPR_Test10_10[DtcIdx].CondCounts,
				      gstResponse[EcuIdx].DTCIUMPRList[DtcIdx].CondCounts );
			}  // end for ( DtcIdx )
		}  // end if ( bIUMPRSupported )
		// IUMPR is not supported
		else
		{
			// if not Test 9
			if ( geTestPhase != eTestNoFault3DriveCycle )
			{
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %-8X  does not support DTC IUMPR data\n",
				      GetEcuId ( EcuIdx ) );
			}
		}

		// if SMAD is supported
		if ( gstResponse[EcuIdx].bDTCSMADSupported == TRUE )
		{
			// ECU IDs
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %-8X  supports DTC SMAD data\n",
			      GetEcuId ( EcuIdx ) );

			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "                             Initial    Current\n" );

			for ( DtcIdx = 0;
			      DtcIdx < gstResponse[EcuIdx].DTCSMADCount;
			      DtcIdx++ )
			{
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "DTC %c%02X%02X  %-16s  %-7d    %-7d\n",
				      DTCTypeCharacter[(gstResponse[EcuIdx].DTCSMADList[DtcIdx].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				      gstResponse[EcuIdx].DTCSMADList[DtcIdx].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				      gstResponse[EcuIdx].DTCSMADList[DtcIdx].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
				      szSmadData1,
				      gstResponse[EcuIdx].DTCSMAD_Test10_10[DtcIdx].NumCounts,
				      gstResponse[EcuIdx].DTCSMADList[DtcIdx].NumCounts );

				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "DTC %c%02X%02X  %-16s  %-7d    %-7d\n",
				      DTCTypeCharacter[(gstResponse[EcuIdx].DTCSMADList[DtcIdx].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				      gstResponse[EcuIdx].DTCSMADList[DtcIdx].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				      gstResponse[EcuIdx].DTCSMADList[DtcIdx].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
				      szSmadData2,
				      gstResponse[EcuIdx].DTCSMAD_Test10_10[DtcIdx].DenomCounts,
				      gstResponse[EcuIdx].DTCSMADList[DtcIdx].DenomCounts );

				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "DTC %c%02X%02X  %-16s  %-7d    %-7d\n",
				      DTCTypeCharacter[(gstResponse[EcuIdx].DTCSMADList[DtcIdx].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				      gstResponse[EcuIdx].DTCSMADList[DtcIdx].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				      gstResponse[EcuIdx].DTCSMADList[DtcIdx].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
				      szSmadData3,
				      gstResponse[EcuIdx].DTCSMAD_Test10_10[DtcIdx].ActRatio,
				      gstResponse[EcuIdx].DTCSMADList[DtcIdx].ActRatio );
			}  // end for ( DtcIdx )
		}  // end if ( bDTCSMADSupported )
		// SMAD is not supported
		else
		{
			// if not Test 9
			if ( geTestPhase != eTestNoFault3DriveCycle )
			{
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %-8X  does not support DTC SMAD data\n",
				      GetEcuId ( EcuIdx ) );
			}
		}
	}  // end for ( EcuIdx )
}


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


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

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

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

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

}


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


	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		// gstEcuSIDData[EcuIdx].DTCIUMPR_Test10_10, pTest10_10_FEOCNTR and
		// pIpt are used in Test11_PerformanceCounters,
		// only free the memory if exiting Test 9 dynamic
		if ( geTestPhase == eTestNoFault3DriveCycle )
		{
//			free ( gstResponse[EcuIdx].DTCIUMPR_Test10_10 );
			free ( gstResponse[EcuIdx].pTest10_10_FEOCNTR );
		}

		free ( gstResponse[EcuIdx].pFEOCNTR );
	}
}
