/*******************************************************************************
********************************************************************************
**
**  Copyright(c) 2022, Alliance for Automotive Innovation
**  Used only under license from the Alliance for Automotive Innovation. All Rights Reserved.
**
**  Project:  J1699-5
**  FileName: Test11_PerformanceCounters.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
BOOL   VerifyIMReadiness      ( void);
BOOL   IsIM_ReadinessComplete ( PID *pPid );
STATUS EvaluateINF14          ( void );

STATUS VerifyAndEvaluateIPTData  ( BOOL bDisplayErrorMsg );
BOOL   ReadDataFromLogFile    ( const char *szTestSectionStart,
                                const char *szTestSectionEnd );
BOOL   ReadIPD                ( const char *szTestSectionEnd );
BOOL   CheckIPD               ( BYTE        EcuIdx );
STATUS EvaluateIPD            ( BYTE        EcuIdx,
                                BOOL        bDisplayErrorMsg );
BOOL   ReadDTCIUMPRData       ( const char* szTestSectionEnd );
STATUS EvaluateDTCIUMPRData   ( BYTE        EcuIdx,
                                BOOL        bDisplayErrorMsg );
STATUS EvaluateDTCSMADData    ( BYTE        EcuIdx,
                                BOOL        bDisplayErrorMsg );

BOOL   ReadDTCSMADData        ( const char *szTestSectionEnd );

STATUS ReadMAD ( void );

STATUS RunDynamicTest11       ( BOOL          bDisplayCARBTimers,
                                unsigned long EngineStartTimestampMsecs );
void   PrintECUData           ( void );


extern STATUS VerifyINF14Data  ( BYTE  EcuIdx );
extern STATUS VerifyINF79Data  ( BYTE EcuIdx );
extern STATUS GetIPD           ( BYTE  EcuIdx );
extern STATUS VerifyIPD        ( void );
extern STATUS VerifyDSCC       ( void );

extern STATUS VerifyDTCExtData ( BYTE  ExtDataID );
extern STATUS GetDTCExtData    ( BYTE  ExtDataID,
                                 BYTE  EcuIdx,
                                 DTC  *pDtc );

extern BOOL SaveDTCIUMPRData   ( BYTE  EcuIdx,
                                 WORD *pIndex );
extern BOOL SaveDTCSMADData    ( BYTE  EcuIdx,
                                 WORD *pIndex );
extern void SaveIPD            ( BYTE  EcuIdx );

extern void   LogLastTransaction    ( void );     // copies last transaction from ring buffer to log file
extern void   DumpTransactionBuffer ( void );  // copies transaction ring buffer to log file
extern void   SaveTransactionStart  ( void );   // marks the start of a new transaction in the ring buffer


// Variable Definitions
#define IMREADINESS_SIZE        17  // Number of IM Readiness Entries displayed

typedef enum
{
	COMPLETE=0,
	INCOMPLETE=1,
	NOTSUPPORTED=2,
	DISABLED=3,
	INVALID=4
}IM_Status;
//IM_Status **Test11IMStatus;
IM_Status   Test11IMStatus[MAX_ECUS][IMREADINESS_SIZE];
IM_Status   eCurrentIMStatus[IMREADINESS_SIZE];

const char *szIM_Status[] =
{
	"Complete",
	"Incomplete",
	"Not Supported",
	"Disabled",
	"Invalid"
};

PID *pTest11_PIDF501 = NULL;

//WORD     **pIPD_Test11_5 = NULL;
//WORD     **pIPD_Test11_11 = NULL;
//WORD     **pIPD_Test11_CurrentDisplayData = NULL;

//DTCIUMPR **pDTCIUMPR_Test11_5 = NULL;
//DTCIUMPR **pDTCIUMPR_Test11_11 = NULL;
//DTCIUMPR **pDTCIUMPR_Test11_CurrentDisplayData = NULL;


typedef struct
{
	BOOL bCatBank1;
	BOOL bCatBank2;
	BOOL bO2Bank1;
	BOOL bO2Bank2;
	BOOL bSO2Bank1;
	BOOL bSO2Bank2;
	BOOL bNCAT;
	BOOL bNADS;
	BOOL bAFRIBank1;
	BOOL bAFRIBank2;
	BOOL bPFBank1;
	BOOL bPFBank2;
} BANK_SUPPORT;
static BANK_SUPPORT BankSupport[MAX_ECUS];

const char *szTest_Status[] =
{
	"Normal",
	"FAILURE"
};

//******************************************************************************
// Dynamic Test 11.x screen
//******************************************************************************
#define COL1            0
#define COL2            9
#define COL3            17
#define COL4            25
#define COL5            33
#define COL6            40
#define COL7            49
#define COL8            57
#define COL9            65
#define COL10           73

#define COL_TEST_STATUS (COL1+13)
#define COL_PROC_ECU    (COL6+16)
#define COL_PROC_DTC    (COL1+16)
#define COL_LST_RD_CNT  (COL8+17)
#define COL_ECUPG       (COL1+8)
#define COL_ECU         (COL1+12)
#define COL_ECU_STATUS  (COL1+12)
#define COL_DATPG       (COL1+9)
#define SPACE           9

#define TEST_STATUS_ROW  0
#define PROC_STATUS_ROW  1
#define ECU_PG_ROW       2
#define ECU_NUM_ROW      2
#define ECU_ID_ROW       3
#define ECU_STATUS_ROW   4
#define DAT_PG_ROW       6
#define STATUS_ROW       7
#define CARB_CYCLE_ROW  21
#define PRESS_ESC_ROW   22

#define STRING_WIDTH    13
#define NUMERIC_WIDTH   5
#define HEX_WIDTH       4
#define ECU_WIDTH       8

#define IUMPR_1_COL_LEN 13
#define IUMPR_2_COL_LEN 16


StaticTextElement  _string_elements_11[] =
{
	// Test Status
	{"Test Status:",     COL1,                 TEST_STATUS_ROW},  // 0
	{"Processing ECU:",  COL6,                 TEST_STATUS_ROW},  // 1
	{"Processing DTC:",  COL1,                 PROC_STATUS_ROW},  // 2
	{"List Read Count:", COL8,                 PROC_STATUS_ROW},  // 3

	// ECU List
	{"ECU Pg:",          COL1,                  ECU_PG_ROW},      // 4
	{"ECU List:",        COL1,                  ECU_ID_ROW},      // 5
	{"ECU Status:",      COL1,                  ECU_STATUS_ROW},  // 6
	{"Data Pg:",         COL1,                  DAT_PG_ROW},      // 7
	{"1",                COL_ECU + (0 * SPACE), ECU_NUM_ROW},     // 8
	{"2",                COL_ECU + (1 * SPACE), ECU_NUM_ROW},     // 9
	{"3",                COL_ECU + (2 * SPACE), ECU_NUM_ROW},     // 10
	{"4",                COL_ECU + (3 * SPACE), ECU_NUM_ROW},     // 11
	{"5",                COL_ECU + (4 * SPACE), ECU_NUM_ROW},     // 12
	{"6",                COL_ECU + (5 * SPACE), ECU_NUM_ROW},     // 13
	{"7",                COL_ECU + (6 * SPACE), ECU_NUM_ROW},     // 14
	{"8",                COL_ECU + (7 * SPACE), ECU_NUM_ROW},     // 15

	{"Press ESC to Exit and return later, F to FAIL and continue testing,",  COL1, PRESS_ESC_ROW},   // 16
	{"1-8 to select new ECU, PGUP/PGDN to change page of ECUs, or",          COL1, PRESS_ESC_ROW+1}, // 17
	{"L/R Arrow to view previous/next Data page",                            COL1, PRESS_ESC_ROW+2}  // 18
};
const int _num_string_elements_11 = sizeof ( _string_elements_11 )/sizeof ( _string_elements_11[0] );


StaticTextElement  _string_elements_imr[] =
{
	// IM Readiness
	{"I/M STATUS",             COL1, STATUS_ROW+0},  // 0
	{"MISFIRE",                COL1, STATUS_ROW+1},  // 1
	{"FUEL",                   COL1, STATUS_ROW+2},  // 2
	{"CCM",                    COL1, STATUS_ROW+3},  // 3
	{"COOL",                   COL1, STATUS_ROW+4},  // 4
	{"G/D SPECIFIC",           COL1, STATUS_ROW+5},  // 5, see _string_elements11_Gas and _string_elements11_Deisel respectively for STATUS_ROW+5
	{"G/D SPECIFIC",           COL1, STATUS_ROW+6},  // 6, see _string_elements11_Gas and _string_elements11_Deisel respectively for STATUS_ROW+6
	{"G/D SPECIFIC",           COL1, STATUS_ROW+7},  // 7, see _string_elements11_Gas and _string_elements11_Deisel respectively for STATUS_ROW+7
	{"G/D SPECIFIC",           COL1, STATUS_ROW+8},  // 8, see _string_elements11_Gas and _string_elements11_Deisel respectively for STATUS_ROW+8
	{"PF",                     COL1, STATUS_ROW+9},  // 9
	{"EGS",                    COL1, STATUS_ROW+10}, // 10
	{"CV",                     COL1, STATUS_ROW+11}, // 11
	{"EGR",                    COL1, STATUS_ROW+12}, // 12
	{"I/M STATUS (continued)", COL6, STATUS_ROW+0},  // 13
	{"VVT",                    COL6, STATUS_ROW+1},  // 14
	{"DOR",                    COL6, STATUS_ROW+2},  // 15
	{"CSER",                   COL6, STATUS_ROW+3},  // 16
	{"G/D SPECIFIC",           COL6, STATUS_ROW+4},  // 17, see _string_elements11_Gas and _string_elements11_Deisel respectively for STATUS_ROW+16
	{"OTH",                    COL6, STATUS_ROW+5},  // 18
};
const int _num_string_elements_imr = sizeof ( _string_elements_imr )/sizeof ( _string_elements_imr[0] );

StaticTextElement  _string_elements11_Gas[] =
{
	// column 1
	{"CAT",       COL1, STATUS_ROW+5},
	{"HCAT",      COL1, STATUS_ROW+6},
	{"EVAP",      COL1, STATUS_ROW+7},
	{"AIR",       COL1, STATUS_ROW+8},

	{"reserved",  COL6, STATUS_ROW+4},
};
const int _num_string_elements11_Gas = sizeof ( _string_elements11_Gas )/sizeof ( _string_elements11_Gas[0] );

StaticTextElement  _string_elements11_Diesel[] =
{
	// column 1
	{"HCCAT",     COL1, STATUS_ROW+5},
	{"NCAT",      COL1, STATUS_ROW+6},
	{"reserved",  COL1, STATUS_ROW+7},
	{"BP",        COL1, STATUS_ROW+8},

	{"NAC",       COL6, STATUS_ROW+4},
};
const int _num_string_elements11_Diesel = sizeof ( _string_elements11_Diesel )/sizeof ( _string_elements11_Diesel[0] );

#define IMSTATUS_ELEMENT_INDEX            0
#define IMSTATUSCONT_ELEMENT_INDEX        13
#define IMSTATUS_GD_ELEMENT1_START_INDEX  5
#define IMSTATUS_GD_ELEMENT1_END_INDEX    8
#define IMSTATUS_GD_ELEMENT2_INDEX        17


StaticTextElement  _string_elements_ipt1[] =
{
	// counters and column headings
	{"RATE BASED COUNTERS",  COL6, STATUS_ROW+7},   // 0
	{"OBDCOND",              COL6, STATUS_ROW+8},   // 1
	{"IGNCNTR",              COL6, STATUS_ROW+9},   // 2

	{"RGID IUMPR/SMAD Status:",   COL6, STATUS_ROW+11},  // 3
};
const int _num_string_elements_ipt1 = sizeof ( _string_elements_ipt1 )/sizeof ( _string_elements_ipt1[0] );

#define IPDSTATUS_ELEMENT_INDEX            0


StaticTextElement  _string_elements_dtcext[] =
{
	// Readiness Group, DTCIUMPR, DTCSMAD Heading columns
	{"RGID",            COL1, STATUS_ROW+0},  // 0
	{"IUMPR Done",      COL3, STATUS_ROW+0},  // 1
	{"SMAD Done",       COL5, STATUS_ROW+0},  // 2
	{"Status",          COL7, STATUS_ROW+0},  // 3
};
const int _num_string_elements_dtcext = sizeof ( _string_elements_dtcext )/sizeof ( _string_elements_dtcext[0] );


StaticTextElement  _string_elements_rg1[] =
{
	// Readiness Group
	{"No Group",         COL1, STATUS_ROW+1},   // 0
	{"Catalyst",         COL1, STATUS_ROW+2},   // 1
	{"Heated Catalyst",  COL1, STATUS_ROW+3},   // 2
	{"Misfire",          COL1, STATUS_ROW+4},   // 3
	{"Evap",             COL1, STATUS_ROW+5},   // 4
	{"Sec Air",          COL1, STATUS_ROW+6},   // 5
	{"Fuel",             COL1, STATUS_ROW+7},   // 6
	{"EGS",              COL1, STATUS_ROW+8},   // 7
	{"EGR",              COL1, STATUS_ROW+9},   // 8
	{"PCV",              COL1, STATUS_ROW+10},  // 9
	{"Cooling",          COL1, STATUS_ROW+11},  // 10
	{"Cold Start",       COL1, STATUS_ROW+12},  // 11
};
const int _num_string_elements_rg1 = sizeof ( _string_elements_rg1 )/sizeof ( _string_elements_rg1[0] );

StaticTextElement  _string_elements_rg2[] =
{
	// Readiness Group
	{"VVT",              COL1, STATUS_ROW+1},  // 0
	{"DOR",              COL1, STATUS_ROW+2},  // 1
	{"CCM",              COL1, STATUS_ROW+3},  // 2
	{"Other",            COL1, STATUS_ROW+4},  // 3
	{"NMHC",             COL1, STATUS_ROW+5},  // 4
	{"NOx",              COL1, STATUS_ROW+6},  // 5
	{"Boost",            COL1, STATUS_ROW+7},  // 6
	{"NOx Absorber",     COL1, STATUS_ROW+8},  // 7
	{"PM",               COL1, STATUS_ROW+9},  // 8
};
const int _num_string_elements_rg2 = sizeof ( _string_elements_rg2 )/sizeof ( _string_elements_rg2[0] );


StaticTextElement  _string_elements_timers[] =
{
	{"Cont. Idle (Sec.):",  COL1, CARB_CYCLE_ROW},
	{"At Speed (Sec.):",      30, CARB_CYCLE_ROW},
	{"Run Time (Sec.):",      56, CARB_CYCLE_ROW}
};
const int _num_string_elements_timers = sizeof ( _string_elements_timers )/sizeof ( _string_elements_timers[0] );


DynamicValueElement  _dynamic_elements_11[] =
{
	// Test Status
	{COL_TEST_STATUS,       TEST_STATUS_ROW, 7},            // 0  Test Status
	{COL4,                  TEST_STATUS_ROW, STRING_WIDTH}, // 1  Processing ECU
	{COL2,                  PROC_STATUS_ROW, STRING_WIDTH}, // 2  Processing DTC
	{COL_LST_RD_CNT,        PROC_STATUS_ROW, STRING_WIDTH}, // 3  List Read Count

	// ECU Page
	{COL_ECUPG,             ECU_PG_ROW,      1},            // 4  Curent ECU Page
	{COL_ECU_STATUS,        ECU_STATUS_ROW,  1},            // 5  Curent ECU Status
	{COL_DATPG,             DAT_PG_ROW,      1},            // 6  Curent Data Page

	// ECU ID columns
	{COL_ECU + (0 * SPACE), ECU_ID_ROW,      ECU_WIDTH},    // 7  ECU 1
	{COL_ECU + (1 * SPACE), ECU_ID_ROW,      ECU_WIDTH},    // 8  ECU 2
	{COL_ECU + (2 * SPACE), ECU_ID_ROW,      ECU_WIDTH},    // 9  ECU 3
	{COL_ECU + (3 * SPACE), ECU_ID_ROW,      ECU_WIDTH},    // 10 ECU 4
	{COL_ECU + (4 * SPACE), ECU_ID_ROW,      ECU_WIDTH},    // 11 ECU 5
	{COL_ECU + (5 * SPACE), ECU_ID_ROW,      ECU_WIDTH},    // 12 ECU 6
	{COL_ECU + (6 * SPACE), ECU_ID_ROW,      ECU_WIDTH},    // 13 ECU 7
	{COL_ECU + (7 * SPACE), ECU_ID_ROW,      ECU_WIDTH},    // 14 ECU 8

	// ECU status columns
	{COL_ECU + (0 * SPACE), ECU_STATUS_ROW,  HEX_WIDTH},    // 15 ECU Status 1
	{COL_ECU + (1 * SPACE), ECU_STATUS_ROW,  HEX_WIDTH},    // 16 ECU Status 2
	{COL_ECU + (2 * SPACE), ECU_STATUS_ROW,  HEX_WIDTH},    // 17 ECU Status 3
	{COL_ECU + (3 * SPACE), ECU_STATUS_ROW,  HEX_WIDTH},    // 18 ECU Status 4
	{COL_ECU + (4 * SPACE), ECU_STATUS_ROW,  HEX_WIDTH},    // 19 ECU Status 5
	{COL_ECU + (5 * SPACE), ECU_STATUS_ROW,  HEX_WIDTH},    // 20 ECU Status 6
	{COL_ECU + (6 * SPACE), ECU_STATUS_ROW,  HEX_WIDTH},    // 21 ECU Status 7
	{COL_ECU + (7 * SPACE), ECU_STATUS_ROW,  HEX_WIDTH},    // 22 ECU Status 8
};
const int _num_dynamic_elements_11 = sizeof ( _dynamic_elements_11 )/sizeof ( _dynamic_elements_11[0] );

// Indexes common to dynamic elements Page1 and Page2+
#define TESTSTATUS_INDEX            0
#define ECU_PAGE_INDEX              4
#define ECU_STATUS_INDEX            15
#define DATA_PAGE_INDEX             6
#define ECU_ID_INDEX                7

// Normal / FAILURE                 18
// blank / DTC Detected             19


// dynamic IM Readiness elements (Data Page 1 only)
DynamicValueElement  _dynamic_elements_imr[] =
{
	// IM Readiness status columns                Gas      / Diesel
	{COL2, STATUS_ROW+1,  STRING_WIDTH},   // 0   MISFIRE  / MISFIRE
	{COL2, STATUS_ROW+2,  STRING_WIDTH},   // 1   FUEL     / FUEL
	{COL2, STATUS_ROW+3,  STRING_WIDTH},   // 2   CCM      / CCM
	{COL2, STATUS_ROW+4,  STRING_WIDTH},   // 3   COOL     / COOL
	{COL2, STATUS_ROW+5,  STRING_WIDTH},   // 4   CAT      / HCCAT
	{COL2, STATUS_ROW+6,  STRING_WIDTH},   // 5   HCAT     / NCAT
	{COL2, STATUS_ROW+7,  STRING_WIDTH},   // 6   EVAP     / reserved
	{COL2, STATUS_ROW+8,  STRING_WIDTH},   // 7   AIR      / BP
	{COL2, STATUS_ROW+9,  STRING_WIDTH},   // 8   PF       / PF
	{COL2, STATUS_ROW+10, STRING_WIDTH},   // 9   EGS      / EGS
	{COL2, STATUS_ROW+11, STRING_WIDTH},   // 10  CV       / CV
	{COL2, STATUS_ROW+12, STRING_WIDTH},   // 11  EGR      / EGR
	{COL7, STATUS_ROW+1,  STRING_WIDTH},   // 12  VVT      / VVT
	{COL7, STATUS_ROW+2,  STRING_WIDTH},   // 13  DOR      / DOR
	{COL7, STATUS_ROW+3,  STRING_WIDTH},   // 14  CSER     / CSER
	{COL7, STATUS_ROW+4,  STRING_WIDTH},   // 15  reserved / NAC
	{COL7, STATUS_ROW+5,  STRING_WIDTH},   // 16  OTH      / OTH
};
const int _num_dynamic_elements_imr = sizeof ( _dynamic_elements_imr )/sizeof ( _dynamic_elements_imr[0] );

// Indexes for dynamic IM Readiness elements (Data Page 1 only)
#define IM_READINESS_INDEX          0
// MISFIRE     / MISFIRE            0
// FUEL        / FUEL               1
// CCM         / CCM                2
// COOL        / COOL               3
// CAT         / HCCAT              4
// HCAT        / NCAT               5
// EVAP        / reserved           6
// AIR         / BP                 7
// PF          / PF                 8
// EGS         / EGS                9
// CV          / CV                 10
// EGR         / EGR                11
// VVT         / VVT                12
// DOR         / DOR                13
// CSER        / CSER               14
// reserved    / NAC                15
// OTH         / OTH                16


// dynamic IUMPR elements (Data Page 1 only)
DynamicValueElement  _dynamic_elements_ipt1[] =
{
	// IPD current value columns
	{COL8, STATUS_ROW+8,  NUMERIC_WIDTH},    // 0   OBD Monitoring Conditions
	{COL8, STATUS_ROW+9,  NUMERIC_WIDTH},    // 1   Ignition Counter

	// DTCIUMPR DTC column
	{COL9, STATUS_ROW+11,  STRING_WIDTH},   // 2   IUMPR/SMAD Status
};
const int _num_dynamic_elements_ipt1 = sizeof ( _dynamic_elements_ipt1 )/sizeof ( _dynamic_elements_ipt1[0] );

// Indexes for dynamic IUMPR elements (Data Page 1 only)
#define OBD_MONITOR_COND_INDEX      0
#define IGNITION_COUNTER_INDEX      1

#define IUMPR_SMAD_STATUS_INDEX     2


// dynamic IUMPR/SMAD elements                   (Data Page 2)      (Data Page 3)
DynamicValueElement  _dynamic_elements_ipt[] =
{
	// DTCIUMPR count column
	{COL3, STATUS_ROW+1,  NUMERIC_WIDTH},  // 0   No Group           VVT
	{COL3, STATUS_ROW+2,  NUMERIC_WIDTH},  // 1   Catalyst           DOR
	{COL3, STATUS_ROW+3,  NUMERIC_WIDTH},  // 2   Heated Catalyst    CCM
	{COL3, STATUS_ROW+4,  NUMERIC_WIDTH},  // 3   Misfire            Other
	{COL3, STATUS_ROW+5,  NUMERIC_WIDTH},  // 4   Evap               NMHC
	{COL3, STATUS_ROW+6,  NUMERIC_WIDTH},  // 5   Sec Air            NOx
	{COL3, STATUS_ROW+7,  NUMERIC_WIDTH},  // 6   Fuel               Boost
	{COL3, STATUS_ROW+8,  NUMERIC_WIDTH},  // 7   EGS                NOx Absorber
	{COL3, STATUS_ROW+9,  NUMERIC_WIDTH},  // 8   EGR                PM
	{COL3, STATUS_ROW+10, NUMERIC_WIDTH},  // 9   PCV
	{COL3, STATUS_ROW+11, NUMERIC_WIDTH},  // 10  Cooling
	{COL3, STATUS_ROW+12, NUMERIC_WIDTH},  // 11  Cold Start

	// DTCIUMPR total column
	{COL4, STATUS_ROW+1,  NUMERIC_WIDTH},  // 12  No Group           VVT
	{COL4, STATUS_ROW+2,  NUMERIC_WIDTH},  // 13  Catalyst           DOR
	{COL4, STATUS_ROW+3,  NUMERIC_WIDTH},  // 14  Heated Catalyst    CCM
	{COL4, STATUS_ROW+4,  NUMERIC_WIDTH},  // 15  Misfire            Other
	{COL4, STATUS_ROW+5,  NUMERIC_WIDTH},  // 16  Evap               NMHC
	{COL4, STATUS_ROW+6,  NUMERIC_WIDTH},  // 17  Sec Air            NOx
	{COL4, STATUS_ROW+7,  NUMERIC_WIDTH},  // 18  Fuel               Boost
	{COL4, STATUS_ROW+8,  NUMERIC_WIDTH},  // 19  EGS                NOx Absorber
	{COL4, STATUS_ROW+9,  NUMERIC_WIDTH},  // 20  EGR                PM
	{COL4, STATUS_ROW+10, NUMERIC_WIDTH},  // 21  PCV
	{COL4, STATUS_ROW+11, NUMERIC_WIDTH},  // 22  Cooling
	{COL4, STATUS_ROW+12, NUMERIC_WIDTH},  // 23  Cold Start

	// DTCSMAD count column
	{COL5, STATUS_ROW+1,  NUMERIC_WIDTH},  // 24  No Group           VVT
	{COL5, STATUS_ROW+2,  NUMERIC_WIDTH},  // 25  Catalyst           DOR
	{COL5, STATUS_ROW+3,  NUMERIC_WIDTH},  // 26  Heated Catalyst    CCM
	{COL5, STATUS_ROW+4,  NUMERIC_WIDTH},  // 27  Misfire            Other
	{COL5, STATUS_ROW+5,  NUMERIC_WIDTH},  // 28  Evap               NMHC
	{COL5, STATUS_ROW+6,  NUMERIC_WIDTH},  // 29  Sec Air            NOx
	{COL5, STATUS_ROW+7,  NUMERIC_WIDTH},  // 30  Fuel               Boost
	{COL5, STATUS_ROW+8,  NUMERIC_WIDTH},  // 31  EGS                NOx Absorber
	{COL5, STATUS_ROW+9,  NUMERIC_WIDTH},  // 32  EGR                PM
	{COL5, STATUS_ROW+10, NUMERIC_WIDTH},  // 33  PCV
	{COL5, STATUS_ROW+11, NUMERIC_WIDTH},  // 34  Cooling
	{COL5, STATUS_ROW+12, NUMERIC_WIDTH},  // 35  Cold Start

	// DTCSMAD total column
	{COL6, STATUS_ROW+1,  NUMERIC_WIDTH},  // 36  No Group           VVT
	{COL6, STATUS_ROW+2,  NUMERIC_WIDTH},  // 37  Catalyst           DOR
	{COL6, STATUS_ROW+3,  NUMERIC_WIDTH},  // 38  Heated Catalyst    CCM
	{COL6, STATUS_ROW+4,  NUMERIC_WIDTH},  // 39  Misfire            Other
	{COL6, STATUS_ROW+5,  NUMERIC_WIDTH},  // 40  Evap               NMHC
	{COL6, STATUS_ROW+6,  NUMERIC_WIDTH},  // 41  Sec Air            NOx
	{COL6, STATUS_ROW+7,  NUMERIC_WIDTH},  // 42  Fuel               Boost
	{COL6, STATUS_ROW+8,  NUMERIC_WIDTH},  // 43  EGS                NOx Absorber
	{COL6, STATUS_ROW+9,  NUMERIC_WIDTH},  // 44  EGR                PM
	{COL6, STATUS_ROW+10, NUMERIC_WIDTH},  // 45  PCV
	{COL6, STATUS_ROW+11, NUMERIC_WIDTH},  // 46  Cooling
	{COL6, STATUS_ROW+12, NUMERIC_WIDTH},  // 47  Cold Start

	// RGID Status column
	{COL7, STATUS_ROW+1,  NUMERIC_WIDTH},  // 48  No Group           VVT
	{COL7, STATUS_ROW+2,  NUMERIC_WIDTH},  // 49  Catalyst           DOR
	{COL7, STATUS_ROW+3,  NUMERIC_WIDTH},  // 50  Heated Catalyst    CCM
	{COL7, STATUS_ROW+4,  NUMERIC_WIDTH},  // 51  Misfire            Other
	{COL7, STATUS_ROW+5,  NUMERIC_WIDTH},  // 52  Evap               NMHC
	{COL7, STATUS_ROW+6,  NUMERIC_WIDTH},  // 53  Sec Air            NOx
	{COL7, STATUS_ROW+7,  NUMERIC_WIDTH},  // 54  Fuel               Boost
	{COL7, STATUS_ROW+8,  NUMERIC_WIDTH},  // 55  EGS                NOx Absorber
	{COL7, STATUS_ROW+9,  NUMERIC_WIDTH},  // 56  EGR                PM
	{COL7, STATUS_ROW+10, NUMERIC_WIDTH},  // 57  PCV
	{COL7, STATUS_ROW+11, NUMERIC_WIDTH},  // 58  Cooling
	{COL7, STATUS_ROW+12, NUMERIC_WIDTH},  // 59  Cold Start
};
const int _num_dynamic_elements_ipt = sizeof ( _dynamic_elements_ipt )/sizeof ( _dynamic_elements_ipt[0] );

// Indexes for dynamic IUMPR elements (Data Page 2&3)
#define IUMPR_CNT_COL_INDEX        0

#define IUMPR_TOT_COL_INDEX       12

#define SMAD_CNT_COL_INDEX        24

#define SMAD_TOT_COL_INDEX        36

#define RGID_STAT_INDEX           48


// dynamic timer elements (Data Page 1, 2, 3)
DynamicValueElement  _dynamic_elements_timers[] =
{
	{20, CARB_CYCLE_ROW,   NUMERIC_WIDTH},  // 0 CARB Idle Timer
	{47, CARB_CYCLE_ROW,   NUMERIC_WIDTH},  // 1 CARB At Speed Timer
	{73, CARB_CYCLE_ROW,   NUMERIC_WIDTH},  // 2 CARB Run Time
};
const int _num_dynamic_elements_timers = sizeof ( _dynamic_elements_timers )/sizeof ( _dynamic_elements_timers[0] );

// Indexes for dynamic timer elements (Data Page 1 only)
#define CARB_TIMER_INDEX            0
#define IDLE_TIMER_INDEX            0
#define SPEED_25_MPH_TIMER_INDEX    1
#define TOTAL_DRIVE_TIMER_INDEX     2


// Data Page limits
#define MAX_DATA_PAGE_1             IUMPR_1_COL_LEN      // Maximum number of DTC IUMPR on page 1
#define MAX_DATA_PER_PAGE           2 * IUMPR_2_COL_LEN  // Maximum number of DTC IUMPR on pages other than 1
#define MAX_DATA_PAGE               3                    // 


// IPT (IPD and IUMPR) Status Flags
#define NUM_BITS_PER_FLAGARRAY_MEMBER    32             // The number of bitflags in each member of a flag array
// The number of DWORDs needed to contain the bitflags for the Max number of DTCs
// with 2 bits each (numerator and denominator) for an ECU
#define IPT_STATUS_FLAG_BYTES    2

// Flags[0]  IPD and IUMPR
#define OBDCOND_NOT_DONE          0xFFFFFFFE
#define IGNCNTR_NOT_DONE          0xFFFFFFFD

#define DTCRG00_IUMPR_NOT_DONE    0xFFFFFFFE
#define DTCRG01_IUMPR_NOT_DONE    0xFFFFFFFD
#define DTCRG02_IUMPR_NOT_DONE    0xFFFFFFFB
#define DTCRG03_IUMPR_NOT_DONE    0xFFFFFFF7
#define DTCRG04_IUMPR_NOT_DONE    0xFFFFFFEF
#define DTCRG05_IUMPR_NOT_DONE    0xFFFFFFDF
#define DTCRG06_IUMPR_NOT_DONE    0xFFFFFFBF
#define DTCRG07_IUMPR_NOT_DONE    0xFFFFFF7F
#define DTCRG08_IUMPR_NOT_DONE    0xFFFFFEFF
#define DTCRG09_IUMPR_NOT_DONE    0xFFFFFDFF
#define DTCRG0A_IUMPR_NOT_DONE    0xFFFFFBFF
#define DTCRG0B_IUMPR_NOT_DONE    0xFFFFF7FF
#define DTCRG0C_IUMPR_NOT_DONE    0xFFFFEFFF
#define DTCRG0D_IUMPR_NOT_DONE    0xFFFFDFFF
#define DTCRG0E_IUMPR_NOT_DONE    0xFFFFBFFF
#define DTCRG0F_IUMPR_NOT_DONE    0xFFFF7FFF
#define DTCRG10_IUMPR_NOT_DONE    0xFFFEFFFF
#define DTCRG11_IUMPR_NOT_DONE    0xFFFDFFFF
#define DTCRG12_IUMPR_NOT_DONE    0xFFFBFFFF
#define DTCRG13_IUMPR_NOT_DONE    0xFFF7FFFF
#define DTCRG14_IUMPR_NOT_DONE    0xFFEFFFFF
#define DTCRG_IUMPR_ALL_DONE      0x001FFFFF

// Flags[1]  SMAD
#define DTCRG00_SMAD_NOT_DONE     0xFFFFFFFE
#define DTCRG01_SMAD_NOT_DONE     0xFFFFFFFD
#define DTCRG02_SMAD_NOT_DONE     0xFFFFFFFB
#define DTCRG03_SMAD_NOT_DONE     0xFFFFFFF7
#define DTCRG04_SMAD_NOT_DONE     0xFFFFFFEF
#define DTCRG05_SMAD_NOT_DONE     0xFFFFFFDF
#define DTCRG06_SMAD_NOT_DONE     0xFFFFFFBF
#define DTCRG07_SMAD_NOT_DONE     0xFFFFFF7F
#define DTCRG08_SMAD_NOT_DONE     0xFFFFFEFF
#define DTCRG09_SMAD_NOT_DONE     0xFFFFFDFF
#define DTCRG0A_SMAD_NOT_DONE     0xFFFFFBFF
#define DTCRG0B_SMAD_NOT_DONE     0xFFFFF7FF
#define DTCRG0C_SMAD_NOT_DONE     0xFFFFEFFF
#define DTCRG0D_SMAD_NOT_DONE     0xFFFFDFFF
#define DTCRG0E_SMAD_NOT_DONE     0xFFFFBFFF
#define DTCRG0F_SMAD_NOT_DONE     0xFFFF7FFF
#define DTCRG10_SMAD_NOT_DONE     0xFFFEFFFF
#define DTCRG11_SMAD_NOT_DONE     0xFFFDFFFF
#define DTCRG12_SMAD_NOT_DONE     0xFFFBFFFF
#define DTCRG13_SMAD_NOT_DONE     0xFFF7FFFF
#define DTCRG14_SMAD_NOT_DONE     0xFFEFFFFF
#define DTCRG_SMAD_ALL_DONE       0x001FFFFF


// 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 SetFieldDec(index,val)     (update_screen_dec  ( _dynamic_elements_11,     _num_dynamic_elements_11,     index, val ))
#define SetFieldHex(index,val)     (update_screen_hex  ( _dynamic_elements_11,     _num_dynamic_elements_11,     index, val ))
#define SetFieldText(index,val)    (update_screen_text ( _dynamic_elements_11,     _num_dynamic_elements_11,     index, val ))

#define SetFieldDec_IM(index,val)  (update_screen_dec  ( _dynamic_elements_imr,    _num_dynamic_elements_imr,    index, val ))
#define SetFieldHex_IM(index,val)  (update_screen_hex  ( _dynamic_elements_imr,    _num_dynamic_elements_imr,    index, val ))
#define SetFieldText_IM(index,val) (update_screen_text ( _dynamic_elements_imr,    _num_dynamic_elements_imr,    index, val ))

#define SetFieldDec_1(index,val)   (update_screen_dec  ( _dynamic_elements_ipt1, _num_dynamic_elements_ipt1, index, val ))
#define SetFieldHex_1(index,val)   (update_screen_hex  ( _dynamic_elements_ipt1, _num_dynamic_elements_ipt1, index, val ))
#define SetFieldText_1(index,val)  (update_screen_text ( _dynamic_elements_ipt1, _num_dynamic_elements_ipt1, index, val ))

#define SetFieldDec_2(index,val)   (update_screen_dec  ( _dynamic_elements_ipt, _num_dynamic_elements_ipt, index, val ))
#define SetFieldHex_2(index,val)   (update_screen_hex  ( _dynamic_elements_ipt, _num_dynamic_elements_ipt, index, val ))
#define SetFieldText_2(index,val)  (update_screen_text ( _dynamic_elements_ipt, _num_dynamic_elements_ipt, index, val ))

#define SetFieldDec_T(index,val)   (update_screen_dec  ( _dynamic_elements_timers, _num_dynamic_elements_timers, index, val ))
#define SetFieldHex_T(index,val)   (update_screen_hex  ( _dynamic_elements_timers, _num_dynamic_elements_timers, index, val ))
#define SetFieldText_T(index,val)  (update_screen_text ( _dynamic_elements_timers, _num_dynamic_elements_timers, index, val ))


#define NORMAL_TEXT        -1
#define HIGHLIGHTED_TEXT    8
#define RED_TEXT            1


// DTC Ext Data request state
#define UNDEFINED     0x00
#define IUMPR_REQ     0x01
#define SMAD_REQ      0x02


// ReadDataFromLogFile specific states
#define TESTSECTION      0x01
#define RGSECTION        0x02
#define IUMPRSECTION     0x03
#define SMADSECTION      0x04
#define IPDSECTION       0x05

// ReadDTC RG/IUMPT/SMAD Data specific states
#define ECUID            0x01
#define LISTSIZE         0x02
#define LISTDATA         0x03

// Read Data c0mmon states
#define ENDOFSEARCH      0x08
#define SEARCHSUCCEEDED  0x20
#define SEARCHFAILED     0x80


extern FILE         *gLogFileHandle;

extern const char szEcuId[];
extern const char szIpdListTitle[];
extern const char szIpdSize[];
extern const char szIpdName[][8];
extern const char szIumprListTitle[];
extern const char szIumprSize[];
extern const char szIumprName[];
extern const char szIumprData1[];
extern const char szIumprData2[];
extern const char szSmadListTitle[];
extern const char szSmadSize[];
extern const char szSmadName[];
extern const char szSmadData1[];
extern const char szSmadData2[];
extern const char szSmadData3[];
extern const char szRGListTitle[];
extern const char szRGSize[];
extern const char szRGId[];
extern const char szRGName[];

extern BYTE DTCTypeCharacterSize;

/*******************************************************************************
**
**  Function:  Test11_PerformanceCounters
**
**  Purpose:   Run Test 11 to verify performance counters
**
*******************************************************************************/
STATUS Test11_PerformanceCounters ( BOOL *pbTestReEntered )
{
	STATUS         eRetVal;

	unsigned long  EngineStartTimestampMsecs = 0;
	BOOL           bRunTest11_2;
	BOOL           bSubTestFailed = FALSE;


	// initialize arrays
	memset ( BankSupport,
	         FALSE,
	         sizeof ( BankSupport ) );


//*******************************************************************************
//  Test 11.1 - Verify performance counters
//*******************************************************************************
	gTestSubsection = 1;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify performance counters)" );

	if ( gbEngineRunning == FALSE )
	{
		Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
		      "Turn ignition to crank position and start engine." );
		gbEngineRunning = TRUE;
		EngineStartTimestampMsecs = GetTickCount ( );
	}

	// Verify still connected
	if ( VerifyECUCommunication ( ) == FAIL )
	{
		bSubTestFailed = TRUE;

		// lost connection, probably due to crank - so re-establish connection
		DisconnectOBDProtocol ( );

		if ( ConnectOBDProtocol ( ) == FAIL )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "Connect Protocol unsuccessful\n" );
			Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
			return FAIL;
		}
	}


	bRunTest11_2 = FALSE;


	// if this test has not been reenter, the data is already in the appropriate array,
	// otherwise read the data from the file
	if ( *pbTestReEntered == TRUE )
	{
		// Retrieve IPT and IUMPR from the first run of test 10.10
		if ( ReadDataFromLogFile ( "TEST: **** Test 10.10", "RESULTS: **** Test 10.10" ) == FALSE )
		{
			bSubTestFailed = TRUE;
			gbTestSubsectionFailed = FALSE;
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "Errors finding Test 10.10 IPD, DTCIUMPR, DTCSMAD, RG data in log file.\n" );
		}
	}


	if ( VerifyIMReadiness () == FALSE )
	{
		bRunTest11_2 = TRUE;
	}

	// Read DTC ExtData $91 (IUMPR)
	if ( VerifyDTCExtData ( 0x91 ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	ReadMAD ( );
	// Read DTC ExtData $93 (SMAD)
	if ( VerifyDTCExtData ( 0x93 ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	eRetVal = VerifyAndEvaluateIPTData ( FALSE );
	// if conditions require running of test 11.2
	if ( (eRetVal & FAIL) == FAIL )
	{
		bRunTest11_2 = TRUE;
	}
	// if conditions indicate that test 11.2 may not finish normally
	if ( (eRetVal & ERRORS) == ERRORS )
	{
		bSubTestFailed = TRUE;
	}


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

	if ( bSubTestFailed == TRUE || gbTestSubsectionFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT,
		           "Errors detected that may cause the next test,\n"
		           "Test 11.2 (Complete Drive Cycle and Clear IM Readiness Bits),\n"
		           "to fail even when the test exits normally." ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 11.2 - Manufacture Specific Drive Cycle
//*******************************************************************************
	gTestSubsection = 2;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Manufacture Specific Drive Cycle)" );

	if ( bRunTest11_2 == TRUE )
	{
		// tester-present message should already be active

		Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "\n"
		      "Drive the vehicle in the manufacturer-specified manner to complete all the\n"
		      "OBD monitors required to set all the supported I/M Readiness bits to a \"Ready\"\n"
		      "condition. The vehicle may have to \"soak\" with the ignition off (engine off).\n"
		      "Allow vehicle to soak according to the manufacturer-specified conditions in\n"
		      "order to run any engine-off diagnostics and/or prepare the vehicle for any \n"
		      "engine-running diagnostics on the next drive cycle that requires an engine-off\n"
		      "soak period.\n\n" );

		if ( Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
		           "Would you like to use the J1699 software as a monitor to view\n"
		           "the status of the I/M Readiness bits?\n"
		           "(Enter 'N' to exit the software if you are going to turn the vehicle off\n"
		           "or don't need a monitor. You can return to this point, at any time,\n"
		           "by re-starting the J1699 software and selecting 'Dynamic Tests'.)" ) != 'Y' )
		{
			Log ( SUBSECTION_INCOMPLETE_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
			ABORT_RETURN;
		}


		// stop tester-present message
		StopPeriodicMsg ( TRUE );
		Sleep ( gRequestDelayTimeMsecs );


		eRetVal = RunDynamicTest11 ( *pbTestReEntered, EngineStartTimestampMsecs );


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


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

		if ( eRetVal == ABORT || gbTestSectionAborted == TRUE )
		{
			if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
			{
				Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" );
			}
			Log ( SUBSECTION_INCOMPLETE_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
			ABORT_RETURN;
		}
		else if ( eRetVal == FAIL || 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, "" );
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 11.3 -
//*******************************************************************************
	gTestSubsection = 3;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Turn engine off to allow drive cycle data to be collected without it changing during data collection process.))" );

	Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
	      "Pull over to a safe spot and turn the ignition off for at least 30 sec.\n"
	      "Keep tool connected to the J1962 connector.  " );

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

	Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );

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


//*******************************************************************************
//  Test 11.4 - Re-establish communication, ignition on, engine off
//*******************************************************************************
	gTestSubsection = 4;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Re-establish communication, ignition on, engine off)" );

	if ( DetermineOBDProtocol ( ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Protocol determination unsuccessful.\n" );
		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 11.5 - Verify Diagnostic Data, Engine Off
//*******************************************************************************
	gTestSubsection = 5;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify Diagnostic Data (SID $22 PIDs), Engine Off)" );

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

	if ( VerifyDSCC ( ) != 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 11.6 - Verify Monitor Test Results, Engine Off
//*******************************************************************************
	gTestSubsection = 6;
	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 )
	{
		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 11.7 - Verify Vehicle Information, Engine Off
//*******************************************************************************
	gTestSubsection = 7;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify Vehicle Information (Sevice $22 INFs), Engine Off)" );

	// Read DTC ExtData $91 (IUMPR)
	if ( VerifyDTCExtData ( 0x91 ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	// Read DTC ExtDaya $93 (SMAD)
	if ( VerifyDTCExtData ( 0x93 ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( VerifyAndEvaluateIPTData ( TRUE ) != PASS )
	{
		bSubTestFailed = TRUE;
		gbTestSubsectionFailed = FALSE;
	}

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

	if ( bSubTestFailed == TRUE || gbTestSubsectionFailed == 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 11.8 - Verify Diagnostic Data, Engine Off
//*******************************************************************************
	gTestSubsection = 8;
	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 11.9 - Verify 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 11.10 - Verify Pending DTCs, Engine Off
//*******************************************************************************
	gTestSubsection = 10;
	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 11.11 - Clear DTCs, Engine Off
//*******************************************************************************
	gTestSubsection = 11;
	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 11.12 - Verify Vehicle Information, Engine Off
//*******************************************************************************
	gTestSubsection = 12;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify Vehicle Information (DTC IUMPR, SMAD, IM Readiness), Engine Off)" );

	// Read DTC ExtData $91 (IUMPR)
	if ( VerifyDTCExtData ( 0x91 ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	// Read DTC ExtDaya $93 (SMAD)
	if ( VerifyDTCExtData ( 0x93 ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( VerifyAndEvaluateIPTData ( TRUE ) != PASS )
	{
		bSubTestFailed = TRUE;
		gbTestSubsectionFailed = FALSE;
	}

	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:  VerifyIMReadiness
**
**  Purpose:
**
**  NOTE:
**
*******************************************************************************/
BOOL VerifyIMReadiness ( void )
{
	REQ_MSG        stReqMsg;
	BYTE           EcuIdx;

	PID           *pPid;


	// check if need to run test 11.2
	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = 0xF501;
	if ( RequestSID ( &stReqMsg, REQ_MSG_NORMAL ) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "PID $F501 request unsuccessful\n" );
		Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
		return FALSE;
	}

	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( IsIDSupported ( EcuIdx, PF5REQUEST,0xF501 ) == TRUE )
		{
			pPid = (PID *)&gstResponse[EcuIdx].PID[0];

			// save for EvaluateIPData ( )
			pTest11_PIDF501[EcuIdx].PIDLSB  = pPid->PIDLSB;
			pTest11_PIDF501[EcuIdx].Data[0] = pPid->Data[0];
			pTest11_PIDF501[EcuIdx].Data[1] = pPid->Data[1];
			pTest11_PIDF501[EcuIdx].Data[2] = pPid->Data[2];
			pTest11_PIDF501[EcuIdx].Data[3] = pPid->Data[3];
			pTest11_PIDF501[EcuIdx].Data[4] = pPid->Data[4];
			pTest11_PIDF501[EcuIdx].Data[5] = pPid->Data[5];

			if ( IsIM_ReadinessComplete ( pPid ) == FALSE )
			{
				return FALSE;
			}
		}
	}

	return TRUE;
}


/*******************************************************************************
**
**  Function:  IsIM_ReadinessComplete
**
**  Purpose:
**
**  NOTE:      supported bits :: 1 -> supported,    0 -> not supported
**
**               status  bits :: 1 -> not complete, 0 -> complete (or not applicable)
**
*******************************************************************************/
BOOL IsIM_ReadinessComplete ( PID * pPid )
{
	if ( pPid->PIDLSB != 1 )
	{
		return FALSE;
	}

	//   supported bits    status bits
	if ( (pPid->Data[1] & (pPid->Data[1] >> 4)) != 0 ||
	     (pPid->Data[2] & (pPid->Data[2] >> 4)) != 0 ||
	     (pPid->Data[3] & (pPid->Data[3] >> 4)) != 0 ||
	     (pPid->Data[4] & (pPid->Data[4] >> 4)) != 0 ||
	     (pPid->Data[5] & (pPid->Data[5] >> 4)) != 0 )

	{
		return FALSE;
	}

	return TRUE;
}


/*******************************************************************************
**
**  Function:  VerifyAndEvaluateIPTData
**
**  Purpose:
**
*******************************************************************************/
STATUS VerifyAndEvaluateIPTData ( BOOL bDisplayErrorMsg )
{
	BYTE           EcuIdx;
	STATUS         eRetCode;
	STATUS         eRetVal = PASS;


	// collect current In-Use Performance data
	if ( VerifyIPD ( ) == PASS )
	{
		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			if ( gstResponse[EcuIdx].bIPDSupported == TRUE )
			{
				if ( (eRetCode = EvaluateIPD ( EcuIdx, TRUE )) != PASS )
				{
					eRetVal |= eRetCode;
				}
			}
		}
	
	}
	else
	{
		eRetVal = FAIL;
	}

	// VerifyDTCExtData $91 and $93 are run just before each call to this routine
	// check current IUMPR and SMAD data
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( gstResponse[EcuIdx].bDTCIUMPRSupported == TRUE )
		{
			// if Test 11.1
			if ( geTestPhase == eTestPerformanceCounters && gTestSubsection == 1 )
			{
//				WORD  Index;
				// Initialize pTest11CurrentDisplayData
//				SaveDTCIUMPRData ( EcuIdx, &Index );

				// initialize current Test 11 IUMPR
				memcpy ( &gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData,
				         &gstResponse[EcuIdx].DTCIUMPR_RGList,
				         sizeof ( DTCIUMPR ) * gstResponse[EcuIdx].DTCIUMPR_RGTotalCount );
			}

			if ( (eRetCode = EvaluateDTCIUMPRData ( EcuIdx, bDisplayErrorMsg )) != PASS )
			{
				eRetVal |= eRetCode;
			}
		}

		if ( gstResponse[EcuIdx].bDTCSMADSupported == TRUE )
		{
			// if Test 11.1
			if ( geTestPhase == eTestPerformanceCounters && gTestSubsection == 1 )
			{
//				WORD  Index;
				// Initialize pTest11CurrentDisplayData
//				SaveDTCSMADData ( EcuIdx, &Index );

				// initialize current Test 11 SMAD
				memcpy ( &gstResponse[EcuIdx].DTCSMAD_Test11_CurrentDisplayData,
				         &gstResponse[EcuIdx].DTCSMAD_RGList,
				         sizeof ( DTCSMAD ) * gstResponse[EcuIdx].DTCSMAD_RGTotalCount );
			}

			if ( (eRetCode = EvaluateDTCSMADData ( EcuIdx, bDisplayErrorMsg )) != PASS )
			{
				eRetVal |= eRetCode;
			}
		}
	}

	return eRetVal;
}


/*******************************************************************************
**
**  Function:  EvaluateIPD
**
**  Purpose:
**
*******************************************************************************/
STATUS EvaluateIPD ( BYTE    EcuIdx,
                     BOOL    bDisplayErrorMsg )
{
	STATUS  eRetCode = PASS;

	WORD    Index;
	int     count;


	// by default all IPD items completed
	gstResponse[EcuIdx].IPDStatusFlags = 0xFF;

	// if IPD (OBDCOND and IGNCNTR) not supported, then cannot do comparsions
	if ( gstResponse[EcuIdx].bIPDSupported == TRUE )
	{
		// IGNCNTR must be >= OBDCOND
		if ( gstResponse[EcuIdx].IPD[IPD_IGNCNTR_INDEX] < gstResponse[EcuIdx].IPD[IPD_OBDCOND_INDEX] )
		{
			if ( bDisplayErrorMsg == TRUE )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %-8X  IGNCNTR less than OBDCOND\n",
				      GetEcuId ( EcuIdx ) );
			}
			eRetCode |= FAIL;
			gstResponse[EcuIdx].IPDStatusFlags &= IGNCNTR_NOT_DONE;
		}

		// OBDCOND must be >= other monitor condition counters
		for ( Index = 0;
		      Index < gstResponse[EcuIdx].DTCIUMPR_RGTotalCount;
		      Index ++ )
		{
			if ( gstResponse[EcuIdx].IPD[IPD_OBDCOND_INDEX] < gstResponse[EcuIdx].DTCIUMPR_RGList[Index].CondCounts )
			{
				if ( bDisplayErrorMsg == TRUE )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %-8X  OBDCOND (%d) not greater than IUMPR DTC %c%02X%02X %02X Denominator (%d)\n",
					      GetEcuId ( EcuIdx ),
					      gstResponse[EcuIdx].IPD[IPD_OBDCOND_INDEX],
					      DTCTypeCharacter[(gstResponse[EcuIdx].DTCIUMPR_RGList[Index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
					      gstResponse[EcuIdx].DTCIUMPR_RGList[Index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
					      gstResponse[EcuIdx].DTCIUMPR_RGList[Index].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
					      gstResponse[EcuIdx].DTCIUMPR_RGList[Index].Record.Dtc.LowByte,
					      gstResponse[EcuIdx].DTCIUMPR_RGList[Index].CondCounts );
				}
				eRetCode |= FAIL;
				gstResponse[EcuIdx].IPDStatusFlags &= OBDCOND_NOT_DONE;
			}
		}

		// Test 11.2 or 11.7
		if ( gTestSubsection == 2 ||
		     gTestSubsection == 7 )
		{
			if ( (gstResponse[EcuIdx].StatusFlags & IPDDATAVALID) != 0 )
			{
				// IGNCNTR must be greater than the value in test 10.10
				count = gstResponse[EcuIdx].IPD[IPD_IGNCNTR_INDEX] - gstResponse[EcuIdx].IPD_Test10_10[IPD_IGNCNTR_INDEX];
				if ( count < 1 )
				{
					if ( bDisplayErrorMsg == TRUE )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %-8X  IGNCNTR incremented less than 1 since Test 10.10\n",
						      GetEcuId ( EcuIdx ) );
					}
					eRetCode |= FAIL;
					gstResponse[EcuIdx].IPDStatusFlags &= IGNCNTR_NOT_DONE;
				}

				// OBDCOND must have incremented by at least 1 since test 10.10
				count = gstResponse[EcuIdx].IPD[IPD_OBDCOND_INDEX] - gstResponse[EcuIdx].IPD_Test10_10[IPD_OBDCOND_INDEX];
				if ( count < 1 )
				{
					if ( bDisplayErrorMsg == TRUE )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %-8X  OBDCOND incremented less than 1 since Test 10.10\n",
						      GetEcuId ( EcuIdx ) );
					}
					eRetCode |= FAIL;
					gstResponse[EcuIdx].IPDStatusFlags &= OBDCOND_NOT_DONE;
				}
			}  // end if ( IPDDATAVALID )
		}  // end if Test 11.2 or 11.7


		// Test 11.11
		else if ( gTestSubsection == 12 )
		{
			// IGNCNTR and OBDCOND must have the same values as recorded in step 11.7
			if ( gstResponse[EcuIdx].IPD_Test11_12[IPD_IGNCNTR_INDEX]  != gstResponse[EcuIdx].IPD_Test11_7[IPD_IGNCNTR_INDEX] )
			{
				if ( bDisplayErrorMsg == TRUE )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %-8X  IGNCNTR different from value in test 11.5\n",
					      GetEcuId ( EcuIdx ) );
				}
				eRetCode |= FAIL;
			}
	
			if ( gstResponse[EcuIdx].IPD_Test11_12[IPD_OBDCOND_INDEX]  != gstResponse[EcuIdx].IPD_Test11_7[IPD_OBDCOND_INDEX] )
			{
				if ( bDisplayErrorMsg == TRUE )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %-8X  OBDCOND different from value in test 11.5\n",
					      GetEcuId ( EcuIdx ) );
				}
				eRetCode |= FAIL;
			}
		}  // end if ( gTestSubsection == 12 )  // Test 11.12
	}  // end if ( bIPDSupported )

	return eRetCode;
}


/*******************************************************************************
**
**  Function:  EvaluateDTCIUMPRData
**
**  Purpose:
**
*******************************************************************************/
STATUS EvaluateDTCIUMPRData ( BYTE    EcuIdx,
                              BOOL    bDisplayErrorMsg )
{
	STATUS  eRetCode = PASS;

	BOOL    bValueSupported = FALSE;
	WORD    Index;
	int     count1;
	int     count2;

	DWORD   PrevFlags;
	DWORD   FlagMask;


	// Save previous status flags state
	PrevFlags = gstResponse[EcuIdx].DTCReadinessGroupStatusFlags[0];

	// by default all IUMPR RG items completed
	gstResponse[EcuIdx].DTCReadinessGroupStatusFlags[0] = 0xFFFFFFFF;

	// if DTCIUMPR not supported, then cannot do comparsions
	if ( gstResponse[EcuIdx].bDTCIUMPRSupported == TRUE )
	{
		// Test 11.12
		if ( gTestSubsection == 12 )
		{
			// OBD condition and completion counters must continue to be non-zero if they were non-zero in test 11.7
			for ( Index = 0;
			      Index < gstResponse[EcuIdx].DTCIUMPR_RGTotalCount;
			      Index++ )
			{
				if ( gstResponse[EcuIdx].DTCIUMPR_Test11_7[Index].CompCounts  != 0 &&
				     gstResponse[EcuIdx].DTCIUMPR_Test11_12[Index].CompCounts == 0 )
				{
					if ( bDisplayErrorMsg == TRUE )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %-8X  IUMPR DTC %c%02X%02X %02X Completion Counter which was non-zero in test 11.7 is now zero\n",
						      GetEcuId ( EcuIdx ),
						      DTCTypeCharacter[(gstResponse[EcuIdx].DTCIUMPR_Test11_7[Index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
						      gstResponse[EcuIdx].DTCIUMPR_Test11_7[Index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
						      gstResponse[EcuIdx].DTCIUMPR_Test11_7[Index].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
						      gstResponse[EcuIdx].DTCIUMPR_Test11_7[Index].Record.Dtc.LowByte );
					}
					eRetCode |= FAIL;
				}

				if ( gstResponse[EcuIdx].DTCIUMPR_Test11_7[Index].CondCounts  != 0 &&
				     gstResponse[EcuIdx].DTCIUMPR_Test11_12[Index].CondCounts == 0 )
				{
					if ( bDisplayErrorMsg == TRUE )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %-8X  IUMPR DTC %c%02X%02X %02X Condition Counter which was non-zero in test 11.7 is now zero\n",
						      GetEcuId ( EcuIdx ),
						      DTCTypeCharacter[(gstResponse[EcuIdx].DTCIUMPR_Test11_7[Index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
						      gstResponse[EcuIdx].DTCIUMPR_Test11_7[Index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
						      gstResponse[EcuIdx].DTCIUMPR_Test11_7[Index].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
						      gstResponse[EcuIdx].DTCIUMPR_Test11_7[Index].Record.Dtc.LowByte );
					}
					eRetCode |= FAIL;
				}
			}  // end for ( gstResponse[EcuIdx].DTCIUMPR_RGTotalCount )
		}  // end if Test 11.12
	
		// Test 11.1, 11.2 or 11.7
		else
		{
			// Clear RG inc'd counts
			memset ( &gstResponse[EcuIdx].DTCIUMPR_RGIncdCount[0],
			         0,
			         sizeof ( gstResponse[EcuIdx].DTCIUMPR_RGIncdCount[0] ) * MAX_READINESS_GROUPS );

			for ( Index = 0;
			      Index < gstResponse[EcuIdx].DTCIUMPR_RGTotalCount;
			      Index ++ )
			{
				bValueSupported = FALSE;

				// Check for change
				if ( gTestSubsection == 7 )
				{
					// Check if DTCIUMPR value is supported
					if ( gstResponse[EcuIdx].DTCIUMPR_Test11_7[Index].CompCounts  > 0 ||
					     gstResponse[EcuIdx].DTCIUMPR_Test11_7[Index].CondCounts  > 0 ||
					     gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].CompCounts > 0 ||
					     gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].CondCounts > 0 )
					{
						bValueSupported = TRUE;
					}
					count1 = gstResponse[EcuIdx].DTCIUMPR_Test11_7[Index].CompCounts - gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].CompCounts;
					count2 = gstResponse[EcuIdx].DTCIUMPR_Test11_7[Index].CondCounts - gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].CondCounts;
				}
				else
				{
					// Check if DTCIUMPR value is supported
					if ( gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[Index].CompCounts > 0 ||
					     gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[Index].CondCounts > 0 ||
					     gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].CompCounts > 0 ||
					     gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].CondCounts > 0 )
					{
						bValueSupported = TRUE;
					}
					count1 = gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[Index].CompCounts - gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].CompCounts;
					count2 = gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[Index].CondCounts - gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].CondCounts;
				}

				if ( count1 == 0 && count2 == 0 &&
				     bValueSupported == FALSE )
				{
					if ( gTestSubsection == 1 ||
					     gTestSubsection == 7 )
					{
						if ( bDisplayErrorMsg == TRUE )
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %-8X  IUMPR DTC %c%02X%02X %02X  has not changed since Test 10.10\n",
							      GetEcuId ( EcuIdx ),
							      DTCTypeCharacter[(gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[Index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
							      gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[Index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
							      gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[Index].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
							      gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[Index].Record.Dtc.LowByte );
						}

						eRetCode |= FAIL;
					}
				}
				else
				{
					if ( (count1 == 0 || count2 == 0) &&
					     bValueSupported == TRUE )
					{
						if ( gTestSubsection == 1 ||
						     gTestSubsection == 7 )
						{
							if ( bDisplayErrorMsg == TRUE )
							{
								Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
								      "ECU %-8X  IUMPR DTC %c%02X%02X %02X  has not changed since Test 10.10\n",
								      GetEcuId ( EcuIdx ),
							      DTCTypeCharacter[(gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[Index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
							      gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[Index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
							      gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[Index].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
							      gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[Index].Record.Dtc.LowByte );
							}
							eRetCode |= FAIL;
						}
					}

					else if ( count1 != 0 && count2 != 0 &&
					     bValueSupported == TRUE )
					{
						gstResponse[EcuIdx].DTCIUMPR_RGIncdCount[gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[Index].RGID]++;
					}
				}
			}  // end for ( Index )

			// update IUMPR RG completion flags
			for ( Index = 0, FlagMask = 0xFFFFFFFE;
			      Index < MAX_READINESS_GROUPS;
			      Index ++, FlagMask = (FlagMask << 1) + 1 )
			{
				if ( gstResponse[EcuIdx].DTCIUMPR_RGListCount[Index] != 0 &&
				     (((Index == 0x11 ||
				        Index == 0x13) &&
				       gstResponse[EcuIdx].DTCIUMPR_RGIncdCount[Index] < 1) ||
				      gstResponse[EcuIdx].DTCIUMPR_RGIncdCount[Index] < gstResponse[EcuIdx].DTCIUMPR_RGListCount[Index]) )
				{
					gstResponse[EcuIdx].DTCReadinessGroupStatusFlags[0] &= FlagMask;
				}
			}

			// if Test 11.1 or 11.7, return fail (incomplete) if not all counters have incremented
			if ( gTestSubsection == 1 ||
			     gTestSubsection == 7 )
			{
			     if ( (gstResponse[EcuIdx].DTCReadinessGroupStatusFlags[0] & DTCRG_IUMPR_ALL_DONE) != DTCRG_IUMPR_ALL_DONE )
				{
					eRetCode |= FAIL;
				}
			}
				// else if Test 11.2, return fail (no change) if the counters increment status hasn't changed
			else if ( gstResponse[EcuIdx].DTCReadinessGroupStatusFlags[0] == PrevFlags )
			{
				eRetCode |= FAIL;
			}
		}  // end else if Test 11.1, 2 or 7
	}  // end if (bDTCIUMPRSupported)

	return eRetCode;
}


/*******************************************************************************
**
**  Function:  EvaluateDTCSMADData
**
**  Purpose:
**
*******************************************************************************/
STATUS EvaluateDTCSMADData ( BYTE    EcuIdx,
                             BOOL    bDisplayErrorMsg )
{
	STATUS  eRetCode = PASS;

	BOOL   bValueSupported = FALSE;
	WORD   Index;
	int    count;

	DWORD  PrevFlags;
	DWORD  FlagMask;


	// Save previous status flags state
	PrevFlags = gstResponse[EcuIdx].DTCReadinessGroupStatusFlags[1];

	// by default all SMAD RG items completed
	gstResponse[EcuIdx].DTCReadinessGroupStatusFlags[1] = 0xFFFFFFFF;

	// if DTCSMAD not supported, then cannot do comparsions
	if ( gstResponse[EcuIdx].bDTCSMADSupported == TRUE )
	{
		// Test 11.12
		if ( gTestSubsection == 12 )
		{
			// SMAD data must continue to be non-zero if they were non-zero in test 11.7
			for ( Index = 0;
			      Index < gstResponse[EcuIdx].DTCSMADCount;
			      Index++ )
			{
				if ( gstResponse[EcuIdx].DTCSMAD_Test11_7[Index].NumCounts  != 0.0 &&
				     gstResponse[EcuIdx].DTCSMAD_Test11_12[Index].NumCounts == 0.0 )
				{
					if ( bDisplayErrorMsg == TRUE )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %-8X  SMAD DTC %c%02X%02X %02X Monitor Activity Numerator Counts which was non-zero in test 11.7 is now zero\n",
						      GetEcuId ( EcuIdx ),
						      DTCTypeCharacter[(gstResponse[EcuIdx].DTCSMAD_Test11_7[Index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
						      gstResponse[EcuIdx].DTCSMAD_Test11_7[Index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
						      gstResponse[EcuIdx].DTCSMAD_Test11_7[Index].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
						      gstResponse[EcuIdx].DTCSMAD_Test11_7[Index].Record.Dtc.LowByte );
					}
					eRetCode |= FAIL;
				}

				if ( gstResponse[EcuIdx].DTCSMAD_Test11_7[Index].ActRatio  != 0.0 &&
				     gstResponse[EcuIdx].DTCSMAD_Test11_12[Index].ActRatio == 0.0 )
				{
					if ( bDisplayErrorMsg == TRUE )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %-8X  SMAD DTC %c%02X%02X %02X Stored Monitor Activity Ratio which was non-zero in test 11.7 is now zero\n",
						      GetEcuId ( EcuIdx ),
						      DTCTypeCharacter[(gstResponse[EcuIdx].DTCSMAD_Test11_7[Index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
						      gstResponse[EcuIdx].DTCSMAD_Test11_7[Index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
						      gstResponse[EcuIdx].DTCSMAD_Test11_7[Index].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
						      gstResponse[EcuIdx].DTCSMAD_Test11_7[Index].Record.Dtc.LowByte );
					}
					eRetCode |= FAIL;
				}
			}
		}  // end if Test 11.12
	
		// Test 11.1, 11.2 or 11.7
		else
		{
			memset ( &gstResponse[EcuIdx].DTCSMAD_RGIncdCount[0],
			         0,
			         sizeof ( gstResponse[EcuIdx].DTCSMAD_RGIncdCount[0] ) * MAX_READINESS_GROUPS );
			for ( Index = 0;
			      Index < gstResponse[EcuIdx].DTCSMAD_RGTotalCount;
			      Index ++ )
			{
				bValueSupported = FALSE;

				// Check for change
				count = gstResponse[EcuIdx].DTCSMAD_RGList[Index].NumCounts - gstResponse[EcuIdx].DTCSMAD_Test10_10[Index].NumCounts;
				if ( gstResponse[EcuIdx].DTCSMAD_RGList[Index].ActRatio == 0.0 &&
				     count < 0 )
				{
					if ( gTestSubsection == 1 ||
					     gTestSubsection == 7 )
					{
						if ( bDisplayErrorMsg == TRUE )
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %-8X  SMAD DTC %c%02X%02X %02X  has decremented since Test 10.10\n",
							      GetEcuId ( EcuIdx ),
							      DTCTypeCharacter[(gstResponse[EcuIdx].DTCSMAD_Test11_CurrentDisplayData[Index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
							      gstResponse[EcuIdx].DTCSMAD_Test11_CurrentDisplayData[Index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
							      gstResponse[EcuIdx].DTCSMAD_Test11_CurrentDisplayData[Index].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
							      gstResponse[EcuIdx].DTCSMAD_Test11_CurrentDisplayData[Index].Record.Dtc.LowByte );
						}

						eRetCode |= FAIL;
					}
				}
				else if ( gstResponse[EcuIdx].DTCSMAD_RGList[Index].ActRatio == 0 &&
				          count != 0 )
				{
					gstResponse[EcuIdx].DTCSMAD_RGIncdCount[gstResponse[EcuIdx].DTCSMAD_Test11_CurrentDisplayData[Index].RGID]++;
				}
			}  // end for ( DTCSMADIndex

			// update SMAD RG completion flags
			for ( Index = 0, FlagMask = 0xFFFFFFFE;
			      Index < MAX_READINESS_GROUPS;
			      Index ++, FlagMask = (FlagMask << 1) + 1 )
			{
				if ( gstResponse[EcuIdx].DTCSMAD_RGListCount[Index] != 0 &&
				     (((Index == 0x11 ||
				        Index == 0x13) &&
				       gstResponse[EcuIdx].DTCSMAD_RGIncdCount[Index] < 1) ||
					   gstResponse[EcuIdx].DTCSMAD_RGIncdCount[Index] < gstResponse[EcuIdx].DTCSMAD_RGListCount[Index]) )
				{
					gstResponse[EcuIdx].DTCReadinessGroupStatusFlags[1] &= FlagMask;
				}
			}

			// if Test 11.1 or 11.7, return fail (incomplete) if not all counters have incremented
			if ( gTestSubsection == 1 ||
			     gTestSubsection == 7 )
			{
				if ( (gstResponse[EcuIdx].DTCReadinessGroupStatusFlags[0] & DTCRG_SMAD_ALL_DONE) != DTCRG_SMAD_ALL_DONE )
				{
					eRetCode |= FAIL;
				}
			}
			// else if Test 11.2, return fail (no change) if the counters increment status hasn't changed
			else if ( gstResponse[EcuIdx].DTCReadinessGroupStatusFlags[1] == PrevFlags )
			{
				eRetCode |= FAIL;
			}
		}  // end else Test 11.1, 2 or 7
	}  // end if (bDTCSMADSupported)

	return eRetCode;
}


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


	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = 0xF814;
	if ( IsIDSupported ( ALLECUS, INFREQUEST, INF_TYPE_EVAP_DIST ) == TRUE )
	{
		if ( RequestSID ( &stReqMsg, REQ_MSG_NORMAL ) == FAIL )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "INF $F814 request\n" );
			return FAIL;
		}

		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			if ( IsIDSupported ( EcuIdx, INFREQUEST, INF_TYPE_EVAP_DIST ) == TRUE &&
			     VerifyINF14Data ( EcuIdx ) == FAIL )
			{
				eRetCode = FAIL;
			}
		}
	}

	return eRetCode;
}


/*******************************************************************************
**
**  Function:  SavePIDF501Data
**
**  Purpose:
**
*******************************************************************************/
void SaveIMReadiness ( BYTE EcuIdx )
{
	PID  *pPid;


	pPid = (PID *)&gstResponse[EcuIdx].PID[0];

	// if Misfire supported
	if ( pPid->Data[1] & 0x01 )
	{
		Test11IMStatus[EcuIdx][0] = (pPid->Data[1] & 0x10) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][0] = NOTSUPPORTED;
	}

	// if Fuel supported
	if ( pPid->Data[1] & 0x02 )
	{
		Test11IMStatus[EcuIdx][1] = (pPid->Data[1] & 0x20) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][1] = NOTSUPPORTED;
	}

	// if CCM supported
	if ( pPid->Data[1] & 0x04 )
	{
		Test11IMStatus[EcuIdx][2] = (pPid->Data[1] & 0x40) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][2] = NOTSUPPORTED;
	}

	// if Cool supported
	if ( pPid->Data[1] & 0x08 )
	{
		Test11IMStatus[EcuIdx][3] = (pPid->Data[1] & 0x80) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][3] = NOTSUPPORTED;
	}

	// if Catalyst (CAT) (Gasoline) or NMHC Catalyst (HCCAT) (Diesel) supported
	if ( pPid->Data[2] & 0x01 || pPid->Data[2] & 0x02 )
	{
		Test11IMStatus[EcuIdx][4] = ((pPid->Data[2] & 0x10 ) || (pPid->Data[2] & 0x20)) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][4] = NOTSUPPORTED;
	}

	// if Heated Catalyst (HCAT) (Gasoline) or NOx/SCR Aftertreatment (NCAT) (Diesel) supported
	if ( pPid->Data[2] & 0x04 || pPid->Data[2] & 0x08 )
	{
		Test11IMStatus[EcuIdx][5] = ((pPid->Data[2] & 0x40) || (pPid->Data[2] & 0x80)) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][5] = NOTSUPPORTED;
	}

	// if Evaportive Emissions (Gasoline) supported
	if ( pPid->Data[3] & 0x01 )
	{
		Test11IMStatus[EcuIdx][6] = (pPid->Data[3] & 0x10) ? INCOMPLETE :COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][6] = NOTSUPPORTED;
	}

	// if Secondary Air (Air) (Gasoline) or Boost Pressure (BP) (Diesel) supported
	if ( pPid->Data[3] & 0x02 || pPid->Data[3] & 0x04 )
	{
		Test11IMStatus[EcuIdx][7] = ((pPid->Data[3] & 0x20) || (pPid->Data[3] & 0x40)) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][7] = NOTSUPPORTED;
	}

	// if Particulate Filter (PF) supported
	if ( pPid->Data[3] & 0x08 )
	{
		Test11IMStatus[EcuIdx][8] = (pPid->Data[3] & 0x80) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][8] = NOTSUPPORTED;
	}

	// if Exhaust Gas Sensor (EGS) supported
	if ( pPid->Data[4] & 0x01 )
	{
		// if completed
		Test11IMStatus[EcuIdx][9] = (pPid->Data[4] & 0x10) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][9] = NOTSUPPORTED;
	}

	// if crankcase ventilation (CV) supported
	if ( pPid->Data[4] & 0x02 )
	{
		Test11IMStatus[EcuIdx][10] = (pPid->Data[4] & 0x20) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][10] = NOTSUPPORTED;
	}

	// if Exhaust Gas Recirculation (EGR) supported
	if ( pPid->Data[4] & 0x04 )
	{
		Test11IMStatus[EcuIdx][11] = (pPid->Data[4] & 0x40) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][11] = NOTSUPPORTED;
	}

	// if Variable Valve Timing (VVT) supported
	if ( pPid->Data[4] & 0x08 )
	{
		Test11IMStatus[EcuIdx][12] = (pPid->Data[4] & 0x80) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][12] = NOTSUPPORTED;
	}

	// if Direct Ozone Reduction (DOR) supported
	if ( pPid->Data[5] & 0x01 )
	{
		// if completed
		Test11IMStatus[EcuIdx][13] = (pPid->Data[5] & 0x10) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][13] = NOTSUPPORTED;
	}

	// if Cold Start Emission Reduction (CSER) supported
	if ( pPid->Data[5] & 0x02 )
	{
		Test11IMStatus[EcuIdx][14] = (pPid->Data[5] & 0x20) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][14] = NOTSUPPORTED;
	}

	// if NOx Adsorber (NAC) supported
	if ( pPid->Data[5] & 0x04 )
	{
		Test11IMStatus[EcuIdx][15] = (pPid->Data[5] & 0x40) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][15] = NOTSUPPORTED;
	}

	// if Other (OTH) supported
	if ( pPid->Data[5] & 0x08 )
	{
		Test11IMStatus[EcuIdx][16] = (pPid->Data[5] & 0x80) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][16] = NOTSUPPORTED;
	}
}


/*******************************************************************************
**
**  Function:  SavePIDF501Data
**
**  Purpose:
**
*******************************************************************************/
BOOL SavePIDF501Data ( BYTE EcuIdx,
                       BOOL bInitialSave )
{
	PID  *pPid;
	BOOL  bUpdated = FALSE;


	pPid = (PID *)&gstResponse[EcuIdx].PID[0];

	if ( pPid->PIDLSB == 0x01 )
	{
		// note any differences
		if ( bInitialSave == TRUE ||
		     gstResponse[EcuIdx].PIDF501.Data[0] != pPid->Data[0] ||
		     gstResponse[EcuIdx].PIDF501.Data[1] != pPid->Data[1] ||
		     gstResponse[EcuIdx].PIDF501.Data[2] != pPid->Data[2] ||
		     gstResponse[EcuIdx].PIDF501.Data[3] != pPid->Data[3] ||
		     gstResponse[EcuIdx].PIDF501.Data[4] != pPid->Data[4] ||
		     gstResponse[EcuIdx].PIDF501.Data[5] != pPid->Data[5] )
		{
			gstResponse[EcuIdx].PIDF501.Data[0] = pPid->Data[0];
			gstResponse[EcuIdx].PIDF501.Data[1] = pPid->Data[1];
			gstResponse[EcuIdx].PIDF501.Data[2] = pPid->Data[2];
			gstResponse[EcuIdx].PIDF501.Data[3] = pPid->Data[3];
			gstResponse[EcuIdx].PIDF501.Data[4] = pPid->Data[4];
			gstResponse[EcuIdx].PIDF501.Data[5] = pPid->Data[5];

			SaveIMReadiness ( EcuIdx );

			bUpdated = TRUE;
		}  // end if F501 changed
	}  //  end if ( pPid->PIDLSB == 1 )

	return bUpdated;
}


/*******************************************************************************
**
**  Function:  ReadIPD
**
**  Purpose:
**
*******************************************************************************/
BOOL ReadIPD ( const char *szTestSectionEnd )
{
	char   buf[256];
	char  *p;
	BYTE   count;
	BYTE   NODI = 0;
	BYTE   EcuIdx;
	DWORD  EcuId = 0;


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

		// if found ECU ID, p points to start of ECU ID
		if ( (p = substring ( buf, szEcuId )) != 0 )
		{
			// point to number beyond '='
			p = substring ( p, "=" ) + 1;
			// convert the text to hexadecimal number
			EcuId = strtoul ( p, NULL, 16 );
			break;
		}
	}

	// find matching ECU ID in existing ECU data
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++)
	{
		if ( EcuId == GetEcuId ( EcuIdx ) )
		{
			break;
		}
	}

	// if didn't find a matching ECU ID, something is wrong, stop
	if ( EcuIdx >= gNumOfECUs )
	{
		return FALSE;
	}


	// clear ECU's IPD support by default
	gstResponse[EcuIdx].bIPDSupported = FALSE;

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

		// if found IPD SIZE, p points to start of IPD SIZE
		if ( (p = substring ( buf, szIpdSize )) != 0 )
		{
			// point to number beyond '='
			p = substring ( p, "=" ) + 1;
			NODI = (BYTE)strtod ( p, NULL );
			break;
		}
	}

	// if NODI equals 0, there is no data and IPD is not supported
	if ( NODI == 0 )
	{
		return TRUE;
	}

	// read counters
	for ( count = 0;
	      count < NODI;
	      count++)
	{
		if ( fgets ( buf, sizeof ( buf ), gLogFileHandle ) == 0 )
		{
			return FALSE;
		}
		// stop if reached end of section
		if ( substring ( buf, szTestSectionEnd ) != 0 )     // end of Test XX section
		{
			return FALSE;
		}

		// if found IPD, copy it to appropriate ECU's data
		if ( (p = substring ( buf, szIpdName[count] )) != 0 )
		{
			// point to number beyond '='
			p = substring ( p, "=" ) + 1;
			gstResponse[EcuIdx].IPD_Test10_10[count] = (unsigned short)strtoul ( p, NULL, 10 );
		}
		else
		{
			return FALSE;
		}
	}

	// IPD valid
	gstResponse[EcuIdx].bIPDSupported = TRUE;

	return TRUE;
}


/*******************************************************************************
**
**  Function:  CheckIPD
**
**  Purpose:
**
*******************************************************************************/
BOOL CheckIPD ( BYTE  EcuIdx )
{
	int index;
	BOOL bUpdated = FALSE;


	// copy current counter values for active ECUs
	for ( index = 0;
	      index < 2;
	      index++ )
	{
		// note changes
		if ( gstResponse[EcuIdx].IPD_Test11_CurrentDisplayData[index] != gstResponse[EcuIdx].IPD[index] )
		{
			// save current value of rate based counters
			gstResponse[EcuIdx].IPD_Test11_CurrentDisplayData[index] = gstResponse[EcuIdx].IPD[index];

			bUpdated = TRUE;
		}
	}

	return bUpdated;
}


/*******************************************************************************
**
**  Function:  ReadDTCRGData
**
**  Purpose:
**
*******************************************************************************/
BOOL ReadDTCRGData ( const char *szTestSectionEnd )
{
	char   buf[256];
	char  *p;

	WORD   RGcount = 0;
	WORD   Index;

	char   Type;
	WORD   Dtc;
	BYTE   FailureType;
	BYTE   RG_Id;

	BYTE   EcuIdx;
	DWORD  EcuId = 0;

	BYTE   SearchState = ECUID;


	// search for Readiness Group data
	while ( (SearchState & 0x0F) != ENDOFSEARCH &&
	        fgets ( buf, sizeof ( buf ), gLogFileHandle ) != 0 )
	{
		// stop if reached end of section
		if (substring ( buf, szTestSectionEnd ) != 0 )     // end of Test XX section
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
			      "Reading Test 10.10 Readiness Group data from logfile: Unexpected Test Section End (SearchState = %d).\n",
			      SearchState );
			SearchState = ENDOFSEARCH;
			SearchState |= SEARCHFAILED;
;
		}

		switch ( SearchState & 0x0F )
		{
			case ECUID:
			{
				// if found ECU ID, p points to start of ECU ID
				if ((p = substring ( buf, szEcuId )) != 0 )
				{
					// point to number beyond '='
					p = substring ( p, "=" ) + 1;
					// convert the text to hexadecimal number
					EcuId = strtoul ( p, NULL, 16 );
					
					// find matching ECU ID in existing ECU data
					for ( EcuIdx = 0;
					      EcuIdx < gNumOfECUs;
					      EcuIdx++)
					{
						if ( EcuId == GetEcuId(EcuIdx) )
						{
							break;
						}
					}
				
					// if didn't find a matching ECU ID, something is wrong, stop
					if ( EcuIdx >= gNumOfECUs )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
						      "Reading Test 10.10 Readiness Group data from logfile: ECU %X not a currently responding ECU.\n",
						      EcuId );
						SearchState = ENDOFSEARCH;
						SearchState |= SEARCHFAILED;
					}
					else
					{
						// clear ECU's Readiness Group support by default
						gstResponse[EcuIdx].bDTCReadinessGroupSupported = FALSE;
						SearchState = LISTSIZE;
					}
				}
			}
			break;

			case LISTSIZE:
			{
				// if found Readiness Group SIZE, p points to start of Readiness Group SIZE
				if ( (p = substring ( buf, szRGSize )) != 0)
				{
					// point to number beyond '='
					p = substring ( p, "=" ) + 1;
					gstResponse[EcuIdx].DTCReadinessGroupCount = (unsigned int)strtod ( p, NULL );

					// if gstResponse[EcuIdx].DTCReadinessGroupCount equals 0, there is no data and Readiness Group is not supported
					if ( gstResponse[EcuIdx].DTCReadinessGroupCount == 0 )
					{
						SearchState = ENDOFSEARCH;
						SearchState |= SEARCHSUCCEEDED;
					}
					else
					{
						SearchState = LISTDATA;
					}
				}
			}
			break;

			case LISTDATA:
			{
				// if line contains "DTC", p points to start of "DTC"
				if ( (p = substring ( buf, szRGName )) != 0 )
				{
					// point to beyond "DTC ", the DTC Number
					p += 4;
					// convert the text to hexadecimal DTC Number
					Type = *p++;
					Dtc = (WORD)strtoul ( p, NULL, 16 );
					for ( Index = 0;
					      Index < DTCTypeCharacterSize;
					      Index++ )
					{
						if ( DTCTypeCharacter[Index] == Type )
						{
							Dtc += (Index<<14);
							break;
						}
					}

					// point to Failure type (skip past the DTC Number and space seperating Failure Type)
					p += 5;
					FailureType = (BYTE)(strtoul ( p, NULL, 16 ));

					// if line contains "RGID", p points to start of "RGID"
					if ( (p = substring( buf, szRGId )) != 0 )
					{
						// point to number beyond '$'
						p = substring ( p, "$" ) + 1;
						RG_Id = (unsigned char)strtoul ( p, NULL, 16 );
					}
					else
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
						      "Reading Test 10.10 Readiness Group data from logfile: ECU %X  RG DTC %c%02X%02X %02X missing \"%s\".\n",
						       GetEcuId(EcuIdx),
						       DTCTypeCharacter[((BYTE)((Dtc & 0xFF00)>>8) & 0xC0) >> 6],  // 1st character (P, C, B, U)
						       (BYTE)((Dtc & 0xFF00)>>8) & 0x3F,                          // 2nd (0,1,2,3) and 3rd (0-F) characters
						       (BYTE)(Dtc & 0x00FF),                                      // 4th and 5th characters (0-F)
						       FailureType,                                               // Failure Type Byte
						       szRGId );
						SearchState = ENDOFSEARCH;
						SearchState |= SEARCHFAILED;
						break;
					}

					gstResponse[EcuIdx].DTCReadinessGroupList[RGcount].Record.Dtc.HighByte = (BYTE)((Dtc & 0xFF00)>>8);
					gstResponse[EcuIdx].DTCReadinessGroupList[RGcount].Record.Dtc.MidByte  = (BYTE)(Dtc & 0x00FF);
					gstResponse[EcuIdx].DTCReadinessGroupList[RGcount].Record.Dtc.LowByte  = FailureType;

					gstResponse[EcuIdx].DTCReadinessGroupList[RGcount].RGID  = RG_Id;
					gstResponse[EcuIdx].DTCReadinessGroupListCount[RG_Id]++;

					if ( ++RGcount >= gstResponse[EcuIdx].DTCReadinessGroupCount )
					{
						SearchState = ENDOFSEARCH;
						SearchState |= SEARCHSUCCEEDED;
					}
				}  // end if ( line contains "DTC" )
				else
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
					      "Reading Test 10.10 Readiness Group data from logfile: ECU %X  entry %d missing \"%s\".\n",
					       GetEcuId(EcuIdx),
					       RGcount+1,
					       szRGName );
					SearchState = ENDOFSEARCH;
					SearchState |= SEARCHFAILED;
				}
			}
			break;

			case ENDOFSEARCH:
			default:

			break;
		}  // end switch (SearchState)
	}  // end while (fgets != 0)

	if ( feof (gLogFileHandle) )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
		      "Reading Test 10.10 Readiness Group data from logfile: ECU %X  unexpected ENDOFFILE reading RG DTC entry %d.\n",
		      GetEcuId(EcuIdx),
		      RGcount+1 );
	}
	else
	{
		if ( SearchState & SEARCHSUCCEEDED )
		{
			// Readiness Group data valid
			gstResponse[EcuIdx].bDTCReadinessGroupSupported = TRUE;
			return TRUE;
		}
		else
		{
			return FALSE;
		}
	}

	return FALSE;
}




/*******************************************************************************
**
**  Function:  ReadDTCIUMPRData
**
**  Purpose:
**
*******************************************************************************/
BOOL ReadDTCIUMPRData ( const char *szTestSectionEnd )
{
	char   buf[256];
	char  *p;

	WORD   IUMPRcount = 0;
	WORD   Index;

	char   Type;
	WORD   Dtc;
	BYTE   FailureType;
	WORD   Numerator;
	WORD   Denominator;
	BYTE   RGId;

	BYTE   EcuIdx;
	DWORD  EcuId = 0;

	BYTE   SearchState = ECUID;


	// search for ECU ID
	while ( (SearchState & 0x0F) != ENDOFSEARCH &&
	        fgets ( buf, sizeof ( buf ), gLogFileHandle ) != 0 )
	{
		// stop if reached end of section
		if (substring ( buf, szTestSectionEnd ) != 0 )     // end of Test XX section
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
			      "Reading Test 10.10 IUMPR data from logfile: Unexpected Test Section End (SearchState = %d).\n",
			      SearchState );
			SearchState = ENDOFSEARCH;
			SearchState |= SEARCHFAILED;
		}

		switch ( SearchState & 0x0F )
		{
			case ECUID:
			{
				// if found ECU ID, p points to start of ECU ID
				if ((p = substring ( buf, szEcuId )) != 0 )
				{
					// point to number beyond '='
					p = substring ( p, "=" ) + 1;
					// convert the text to hexadecimal number
					EcuId = strtoul ( p, NULL, 16 );

					// find matching ECU ID in existing ECU data
					for ( EcuIdx = 0;
					      EcuIdx < gNumOfECUs;
					      EcuIdx++)
					{
						if ( EcuId == GetEcuId(EcuIdx) )
						{
							break;
						}
					}

					// if didn't find a matching ECU ID, something is wrong, stop
					if ( EcuIdx >= gNumOfECUs )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
						      "Reading Test 10.10 IUMPR data from logfile: ECU %X not a currently responding ECU.\n",
						      EcuId );
						SearchState = ENDOFSEARCH;
						SearchState |= SEARCHFAILED;
					}
					else
					{
						// clear ECU's IUMPR support by default
						gstResponse[EcuIdx].bDTCIUMPRSupported = FALSE;
						SearchState = LISTSIZE;
					}
				}
			}
			break;

			case LISTSIZE:
			{
				// search for IUMPR SIZE
				// if found IUMPR SIZE, p points to start of IUMPR SIZE
				if ( (p = substring ( buf, szIumprSize )) != 0)
				{
					// point to number beyond '='
					p = substring ( p, "=" ) + 1;
					gstResponse[EcuIdx].DTCIUMPR_RGTotalCount = (unsigned int)strtod ( p, NULL );

					// if gstResponse[EcuIdx].DTCIUMPR_RGTotalCount equals 0, there is no data and IUMPR is not supported
					if ( gstResponse[EcuIdx].DTCIUMPR_RGTotalCount == 0 )
					{
						SearchState = ENDOFSEARCH;
						SearchState |= SEARCHSUCCEEDED;
					}
					else
					{
						SearchState = LISTDATA;
					}
				}
			}
			break;

			case LISTDATA:
			{
				// if line contains "DTC", p points to start of "DTC"
				if ( (p = substring ( buf, szIumprName )) != 0 )
				{
					// point to beyond "DTC ", the DTC Number
					p += 4;
					// convert the text to hexadecimal DTC Number
					Type = *p++;
					Dtc = (WORD)strtoul ( p, NULL, 16 );
					for ( Index = 0;
					      Index < DTCTypeCharacterSize;
					      Index++ )
					{
						if ( DTCTypeCharacter[Index] == Type )
						{
							Dtc += (Index<<14);
							break;
						}
					}
		
					// point to Failure type (skip past the DTC Number and space seperating Failure Type)
					p += 5;
					FailureType = (BYTE)(strtoul ( p, NULL, 16 ));

					// Need to check RGID first because $0E, $10 and $14
					// are not monitored for change, therefore are not requested
					// and have no other data
					// if line contains "RGID", p points to start of "RGID"
					if ( (p = substring ( buf, szRGId )) != 0 )
					{
						// point to number beyond '$'
						p = substring ( p, "$" ) + 1;
						RGId = (BYTE)strtoul ( p, NULL, 16 );
					}
					else
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
						      "Reading Test 10.10 IUMPR data from logfile: ECU %X  IUMPR DTCs entry %d missing \"%s\".\n",
						      GetEcuId(EcuIdx),
						      IUMPRcount+1,
						      szRGId );
						SearchState = ENDOFSEARCH;
						SearchState |= SEARCHSUCCEEDED;
						break;
					}

					Numerator = 0;
					Denominator = 0;

					// DTCs with RG == $0E (CCM), $10 (NMHC) and $14 (PM)
					// are not monitored for changes, therefore have no other data to be read
					if ( RGId != 0x0E &&  // CCM
					     RGId != 0x10 &&  // NMHC
					     RGId != 0x14 )   // PM
					{
						// if line contains "N", p points to start of "N"
						if ( (p = substring ( buf, szIumprData1 )) != 0 )
						{
							// point to number beyond '='
							p = substring ( p, "=" ) + 1;
							Numerator = (unsigned short)strtoul ( p, NULL, 10 );
						}
						else
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
							      "Reading Test 10.10 IUMPR data from logfile: ECU %X  IUMPR DTCs entry %d missing \"%s\".\n",
							      GetEcuId(EcuIdx),
							      IUMPRcount+1,
							      szIumprData1 );
							SearchState = ENDOFSEARCH;
							SearchState |= SEARCHSUCCEEDED;
							break;
						}

						// if line contains "D", p points to start of "D"
						if ( (p = substring( buf, szIumprData2 )) != 0 )
						{
							// point to number beyond '='
							p = substring ( p, "=" ) + 1;
							Denominator = (unsigned short)strtoul ( p, NULL, 10 );
						}
						else
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
							      "Reading Test 10.10 IUMPR data from logfile: ECU %X  IUMPR DTCs entry %d missing \"%s\".\n",
							      GetEcuId(EcuIdx),
							      IUMPRcount+1,
							      szIumprData2 );
							SearchState = ENDOFSEARCH;
							SearchState |= SEARCHSUCCEEDED;
							break;
						}
					}  // end if ( RGId not a skipped type )

					// Save Test 10.10 data
					gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount].Record.Dtc.HighByte = (BYTE)((Dtc & 0xFF00)>>8);
					gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount].Record.Dtc.MidByte  = (BYTE)(Dtc & 0x00FF);
					gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount].Record.Dtc.LowByte  = FailureType;
					gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount].CompCounts = Numerator;
					gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount].CondCounts = Denominator;
					gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount].RGID       = RGId;

					// also make a copy into this ECU's DTCIUMPR_RGList because this is a test reentry and
					// original requests were skipped, need to be filled for future requests to work
					memcpy ( &gstResponse[EcuIdx].DTCIUMPR_RGList[IUMPRcount],
					         &gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount],
					         sizeof ( DTCIUMPR ) );

					gstResponse[EcuIdx].DTCIUMPR_RGListCount[RGId]++;

					if ( ++IUMPRcount >= gstResponse[EcuIdx].DTCIUMPR_RGTotalCount )
					{
						SearchState = ENDOFSEARCH;
						SearchState |= SEARCHSUCCEEDED;
					}
				}  // end if ( line contains "DTC" )
			}  // end case LISTSIZE
			break;

			case ENDOFSEARCH:
			default:

			break;
		}  // end switch (SearchState)
	}  // end while (fgets != 0)

	if ( feof (gLogFileHandle) )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
		      "Reading Test 10.10 IUMPR data from logfile: ECU %X  unexpected ENDOFFILE reading DTC IUMPR entry %d.\n",
		      GetEcuId(EcuIdx),
		      IUMPRcount+1 );
	}
	else
	{
		if ( SearchState & SEARCHSUCCEEDED )
		{
			// IUMPR data valid
			gstResponse[EcuIdx].bDTCIUMPRSupported = TRUE;
			return TRUE;
		}
		else
		{
			return FALSE;
		}
	}

	return FALSE;
}


/*******************************************************************************
**
**  Function:  ReadDTCSMADData
**
**  Purpose:
**
*******************************************************************************/
BOOL ReadDTCSMADData ( const char *szTestSectionEnd )
{
	char   buf[256];
	char  *p;

	WORD   SMADcount = 0;
	WORD   Index;

	char   Type;
	WORD   Dtc;
	BYTE   FailureType;
	BYTE   Man;
	float  Smar;
	BYTE   RGId;

	BYTE   EcuIdx;
	DWORD  EcuId = 0;

	BYTE   SearchState = ECUID;


	// search for SMAD data
	while ( (SearchState & 0x0F) != ENDOFSEARCH &&
	        fgets ( buf, sizeof ( buf ), gLogFileHandle ) != 0 )
	{
		// stop if reached end of section
		if (substring ( buf, szTestSectionEnd ) != 0 )     // end of Test XX section
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
			      "Reading Test 10.10 SMAD data from logfile: Unexpected Test Section End (SearchState = %d).\n",
			      SearchState );
			SearchState = ENDOFSEARCH;
			SearchState |= SEARCHFAILED;
		}

		switch ( SearchState & 0x0F )
		{
			case ECUID:
			{
				// if found ECU ID, p points to start of ECU ID
				if ((p = substring ( buf, szEcuId )) != 0 )
				{
					// point to number beyond '='
					p = substring ( p, "=" ) + 1;
					// convert the text to hexadecimal number
					EcuId = strtoul ( p, NULL, 16 );

					// find matching ECU ID in existing ECU data
					for ( EcuIdx = 0;
					      EcuIdx < gNumOfECUs;
					      EcuIdx++)
					{
						if ( EcuId == GetEcuId(EcuIdx) )
						{
							break;
						}
					}

					// if didn't find a matching ECU ID, something is wrong, stop
					if ( EcuIdx >= gNumOfECUs )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
						      "Reading Test 10.10 IUMPR data from logfile: ECU %X not a currently responding ECU.\n",
						      EcuId );
						SearchState = ENDOFSEARCH;
						SearchState |= SEARCHFAILED;
					}
					else
					{
						// clear ECU's SMAD support by default
						gstResponse[EcuIdx].bDTCSMADSupported = FALSE;
						SearchState = LISTSIZE;
					}
				}
			}
			break;

			case LISTSIZE:
			{
				// if found SMAD SIZE, p points to start of SMAD SIZE
				if ( (p = substring ( buf, szSmadSize )) != 0)
				{
					// point to number beyond '='
					p = substring ( p, "=" ) + 1;
					gstResponse[EcuIdx].DTCSMAD_RGTotalCount = (unsigned int)strtod ( p, NULL );

					// if gstResponse[EcuIdx].DTCSMAD_RGTotalCount equals 0, there is no data and SMAD is not supported
					if ( gstResponse[EcuIdx].DTCSMAD_RGTotalCount == 0 )
					{
						SearchState = ENDOFSEARCH;
						SearchState |= SEARCHSUCCEEDED;
					}
					else
					{
						SearchState = LISTDATA;
					}
				}
			}
			break;

			case LISTDATA:
			{
				// if line contains "DTC", p points to start of "DTC"
				if ( (p = substring ( buf, szSmadName )) != 0 )
				{
					// point to beyond "DTC ", the DTC Number
					p += 4;
					// convert the text to hexadecimal DTC Number
					Type = *p++;
					Dtc = (WORD)strtoul ( p, NULL, 16 );
					for ( Index = 0;
					      Index < DTCTypeCharacterSize;
					      Index++ )
					{
						if ( DTCTypeCharacter[Index] == Type )
						{
							Dtc += (Index<<14);
						}
					}

					// point to Failure type (skip past the DTC Number and space seperating Failure Type)
					p += 5;
					FailureType = (BYTE)(strtoul ( p, NULL, 16 ));

					// Need to check RGID first because $0E, $10 and $14
					// are not monitored for change, therefore are not requested
					// and have no other data
					// if line contains "RGID", p points to start of "RGID"
					if ( (p = substring ( buf, szRGId )) != 0 )
					{
						// point to number beyond '='
						p = substring ( p, "$" ) + 1;
						RGId = (BYTE)strtoul ( p, NULL, 16 );
					}
					else
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
						      "Reading Test 10.10 SMAD data from logfile: ECU %X  SMAD DTCs entry %d missing \"%s\".\n",
						      GetEcuId(EcuIdx),
						      SMADcount+1,
						      szRGId );
						SearchState = ENDOFSEARCH;
						SearchState |= SEARCHSUCCEEDED;
						break;
					}

					Man = 0;
					Smar = 0;

					// DTCs with RG == $0E (CCM), $10 (NMHC) and $14 (PM)
					// are not monitored for changes, therefore have no other data to be read
					if ( RGId != 0x0E &&  // CCM
					     RGId != 0x10 &&  // NMHC
					     RGId != 0x14 )   // PM
					{
						// if line contains "MAN", p points to start of "MAN"
						if ( (p = substring( buf, szSmadData1 )) != 0 )
						{
							// point to number beyond '='
							p = substring ( p, "$" ) + 1;
							Man = (unsigned char)strtoul ( p, NULL, 16 );
						}
						else
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
							      "Reading Test 10.10 SMAD data from logfile: ECU %X  SMAD DTCs entry %d missing \"%s\".\n",
							      GetEcuId(EcuIdx),
							      SMADcount+1,
							      szSmadData1 );
							SearchState = ENDOFSEARCH;
							SearchState |= SEARCHFAILED;
							break;
						}

						// if line contains "SMAR", p points to start of "SMAR"
						if ( (p = substring( buf, szSmadData3 )) != 0 )
						{
							// point to number beyond '='
							p = substring ( p, "(" ) + 1;
							Smar = strtof ( p, NULL );
						}
						else
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
							      "Reading Test 10.10 SMAD data from logfile: ECU %X  SMAD DTCs entry %d missing \"%s\".\n",
							      GetEcuId(EcuIdx),
							      SMADcount+1,
							      szSmadData2 );
							SearchState = ENDOFSEARCH;
							SearchState |= SEARCHFAILED;
							break;
						}
					}  // end  if ( RGId not a skipped type )

					// Save Test 10.10 data
					gstResponse[EcuIdx].DTCSMAD_Test10_10[SMADcount].Record.Dtc.HighByte = (BYTE)((Dtc & 0xFF00)>>8);
					gstResponse[EcuIdx].DTCSMAD_Test10_10[SMADcount].Record.Dtc.MidByte  = (BYTE)(Dtc & 0x00FF);
					gstResponse[EcuIdx].DTCSMAD_Test10_10[SMADcount].Record.Dtc.LowByte  = FailureType;
					gstResponse[EcuIdx].DTCSMAD_Test10_10[SMADcount].NumCounts = Man;
					gstResponse[EcuIdx].DTCSMAD_Test10_10[SMADcount].ActRatio  = Smar;
					gstResponse[EcuIdx].DTCSMAD_Test10_10[SMADcount].RGID      = RGId;

					// also make a copy into DTCSMAD_RGList because this is a test reentry and
					// original requests were skipped, need to be filled for future requests to work
					memcpy ( &gstResponse[EcuIdx].DTCSMAD_RGList[SMADcount],
					         &gstResponse[EcuIdx].DTCSMAD_Test10_10[SMADcount],
					         sizeof (DTCSMAD) );

					gstResponse[EcuIdx].DTCSMAD_RGListCount[RGId]++;

					if ( ++SMADcount >= gstResponse[EcuIdx].DTCSMAD_RGTotalCount )
					{
						SearchState = ENDOFSEARCH;
						SearchState |= SEARCHSUCCEEDED;
					}
				}  // end if ( line contains "DTC" )
			}  // end case LISTSIZE
			break;

			case ENDOFSEARCH:
			default:

			break;
		}  // end switch (SearchState)
	}  // end while (fgets != 0)

	if ( feof (gLogFileHandle) )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
		      "Reading Test 10.10 IUMPR data from logfile: ECU %X  unexpected ENDOFFILE reading DTC SMAD entry %d.\n",
		      GetEcuId(EcuIdx),
		      SMADcount+1 );
	}
	else
	{
		if ( SearchState & SEARCHSUCCEEDED )
		{
			// IUMPR data valid
			gstResponse[EcuIdx].bDTCSMADSupported = TRUE;
			return TRUE;
		}
		else
		{
			return FALSE;
		}
	}

	return FALSE;
}


/*******************************************************************************
**
**  Function:  ReadDataFromLogFile
**
**  Purpose:   To read data of an earlier test from the log file.
**             The data is expected to be in the following order
**                 IPD   data for each ECU
**                 IUMPR data for each ECU
**
*******************************************************************************/
BOOL ReadDataFromLogFile ( const char *szTestSectionStart,
                           const char *szTestSectionEnd )
{
	BYTE  EcuIdx;

	char  buf[256];

	BYTE  SearchState = TESTSECTION;


	// log file should be open
	if ( gLogFileHandle == NULL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
		      "Log File not open\n" );
		return FALSE;
	}

	// search from beginning of file
	fseek ( gLogFileHandle, 0, SEEK_SET );

	// read each line from the file until end
	while ( (SearchState & 0x0F) != ENDOFSEARCH &&
	        fgets ( buf, sizeof ( buf ), gLogFileHandle ) != 0 )
	{
		// stop if reached end of section
		if (substring ( buf, szTestSectionEnd ) != 0 )     // end of Test XX section
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
			      "Reading Test 10.10 data from logfile: Unexpected Test Section End (SearchState = %d).\n",
			      SearchState );
			SearchState = ENDOFSEARCH;
			SearchState |= SEARCHFAILED;
		}

		switch ( SearchState & 0x0F )
		{
			case TESTSECTION:
			{
				// if the start of the test section has been found,
				if ( substring ( buf, szTestSectionStart ) != 0 )
				{
					// find Readiness Group List
					SearchState = RGSECTION;
				}
			}
			break;

			case RGSECTION:
			{
				// if the Readiness Group List has been found, save the data into the appropriate ECU
				if ( substring ( buf, szRGListTitle ) != 0 )
				{
					// read the Readiness Group values for all ECUs
					for ( EcuIdx = 0;
					      EcuIdx < gNumOfECUs;
					      EcuIdx++ )
					{
						if ( ReadDTCRGData ( szTestSectionEnd ) == FALSE )
						{
							SearchState = ENDOFSEARCH;
							SearchState |= SEARCHFAILED;
							break;
						}
					}

					if ( (SearchState & SEARCHFAILED) == 0 )
					{
						// find IUMPR List
						SearchState = IUMPRSECTION;
					}
				}
			}
			break;

			case IUMPRSECTION:
			{
				// if the IUMPR List has been found, save the data into the appropriate ECU
				if ( substring ( buf, szIumprListTitle ) != 0 )
				{
					// read the IUMPR values for all ECUs
					for ( EcuIdx = 0;
					      EcuIdx < gNumOfECUs;
					      EcuIdx++ )
					{
						if ( ReadDTCIUMPRData ( szTestSectionEnd ) == FALSE )
						{
							SearchState = ENDOFSEARCH;
							SearchState |= SEARCHFAILED;
							break;
						}
					}

					if ( (SearchState & SEARCHFAILED) == 0 )
					{
						// find SMAD List
						SearchState = SMADSECTION;
					}
				}
			}
			break;

			case SMADSECTION:
			{
				// if the SMAD List has been found, save the data into the appropriate ECU
				if ( substring ( buf, szSmadListTitle ) != 0 )
				{
					// read the SMAD values for all ECUs
					for ( EcuIdx = 0;
					      EcuIdx < gNumOfECUs;
					      EcuIdx++ )
					{
						if ( ReadDTCSMADData ( szTestSectionEnd ) == FALSE )
						{
							SearchState = ENDOFSEARCH;
							SearchState |= SEARCHFAILED;
							break;
						}
					}

					if ( (SearchState & SEARCHFAILED) == 0 )
					{
						// find IPD List
						SearchState = IPDSECTION;
					}
				}
			}
			break;

			case IPDSECTION:
			{
				// if the IPD List has been found, save the data into the appropriate ECU
				if ( substring ( buf, szIpdListTitle ) != 0 )
				{
					// read the IPD values for all ECUs
					for ( EcuIdx = 0;
					      EcuIdx < gNumOfECUs;
					      EcuIdx++ )
					{
						if ( ReadIPD ( szTestSectionEnd ) == FALSE )
						{
							SearchState = ENDOFSEARCH;
							SearchState |= SEARCHFAILED;
							break;
						}
					}

					if ( (SearchState & SEARCHFAILED) == 0 )
					{
						// no more data to find
						SearchState = ENDOFSEARCH;
						SearchState |= SEARCHSUCCEEDED;
					}
				}
			}
			// fall through to ENDOFSEARCH

			case ENDOFSEARCH:
			default:

			break;
		}  // end switch ( SearchState )
	}  // end while ( fgets ( ) != 0 )

	if ( feof (gLogFileHandle) )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
		      "Reading Test 10.10 data from logfile: unexpected ENDOFFILE (SearchState = %d).\n",
		      SearchState );
	}
	else
	{
		fseek ( gLogFileHandle, 0, SEEK_END );
		if ( (SearchState & SEARCHFAILED) != 0x00 )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
			      "Incomplete Test 10.10 data in logfile.\n" );
			return FALSE;
		}
		return TRUE;
	}

	return FALSE;
}


/*******************************************************************************
**
**  Function:  CheckIUMPRData
**
**  Purpose:
**
*******************************************************************************/
BOOL CheckIUMPRData ( BYTE  EcuIdx,
                      BOOL  bUpdateValue )
{
	WORD index;
	BOOL bUpdated = FALSE;


	// copy current counter values for active ECUs
	for ( index = 0;
	      index < gstResponse[EcuIdx].DTCIUMPR_RGTotalCount;
	      index++ )
	{
		// note changes to the numerator and denominator
		if ( gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[index].CompCounts != gstResponse[EcuIdx].DTCIUMPR_RGList[index].CompCounts )
		{
			if ( bUpdateValue == TRUE )
			{
				// save current value of rate based counters
				gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[index].CompCounts = gstResponse[EcuIdx].DTCIUMPR_RGList[index].CompCounts;
			}

			bUpdated = TRUE;
		}

		if ( gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[index].CondCounts != gstResponse[EcuIdx].DTCIUMPR_RGList[index].CondCounts )
		{
			if ( bUpdateValue == TRUE )
			{
				// save current value of rate based counters
				gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[index].CondCounts = gstResponse[EcuIdx].DTCIUMPR_RGList[index].CondCounts;
			}

			bUpdated = TRUE;
		}
	}

	return bUpdated;
}


/*******************************************************************************
**
**  Function:  CheckSMADData
**
**  Purpose:
**
*******************************************************************************/
BOOL CheckSMADData ( BYTE  EcuIdx,
                     BOOL  bUpdateValue )
{
	WORD index;
	BOOL bUpdated = FALSE;


	// copy current counter values for active ECUs
	for ( index = 0;
	      index < gstResponse[EcuIdx].DTCSMAD_RGTotalCount;
	      index++ )
	{
		// note changes to the numerator and denominator
		if ( gstResponse[EcuIdx].DTCSMAD_Test11_CurrentDisplayData[index].NumCounts != gstResponse[EcuIdx].DTCSMAD_RGList[index].NumCounts )
		{
			if ( bUpdateValue == TRUE )
			{
				// save current value of rate based counters
				gstResponse[EcuIdx].DTCSMAD_Test11_CurrentDisplayData[index].NumCounts = gstResponse[EcuIdx].DTCSMAD_RGList[index].NumCounts;
			}

			bUpdated = TRUE;
		}
	}

	return bUpdated;
}


/*******************************************************************************
**
**  Function:  UpdateIPDDisplay
**
**  Purpose:
**
*******************************************************************************/
void UpdateIPDDisplay ( BYTE         EcuIdx )
{
	// if IPD is supported
	if ( gstResponse[EcuIdx].bIPDSupported == TRUE )
	{
		// update OBDCOND counter
		if ( (gstResponse[EcuIdx].IPDStatusFlags & 0x01) == 0 )
		{
			setrgb ( HIGHLIGHTED_TEXT );
		}
		SetFieldDec_1 ( OBD_MONITOR_COND_INDEX, gstResponse[EcuIdx].IPD_Test11_CurrentDisplayData[0] );
		setrgb ( NORMAL_TEXT );

		// update IGN counter
		if ( (gstResponse[EcuIdx].IPDStatusFlags & 0x01) == 0 )
		{
			setrgb ( HIGHLIGHTED_TEXT );
		}
		SetFieldDec_1 ( IGNITION_COUNTER_INDEX, gstResponse[EcuIdx].IPD_Test11_CurrentDisplayData[1] );
		setrgb ( NORMAL_TEXT );
	}
	else
	{
		SetFieldText_1 ( OBD_MONITOR_COND_INDEX, "Not Supported" );

		SetFieldText_1 ( IGNITION_COUNTER_INDEX, "Not Supported" );
	}
}


/*******************************************************************************
**
**  Function:  UpdateDTCExtDisplay
**
**  Purpose:
**
*******************************************************************************/
void UpdateDTCExtDisplay ( BYTE   EcuIdx,
                           BYTE   DataPage )
{
	WORD  Index;
	BYTE  RGIndex;
	char  String[13];


	// If Data Page 1, update DTC ExtData status display
	if ( DataPage == 1 )
	{
		// if IUMPR is supported
		if ( gstResponse[EcuIdx].bDTCIUMPRSupported == TRUE ||
		     gstResponse[EcuIdx].bDTCSMADSupported  == TRUE )
		{
			if ( (gstResponse[EcuIdx].StatusFlags & IUMPRDONE) != 0 &&
			     (gstResponse[EcuIdx].StatusFlags & SMADDONE)  != 0 )
			{
				SetFieldText_1 ( IUMPR_SMAD_STATUS_INDEX, "Complete     " );
			}
			else
			{
				setrgb ( HIGHLIGHTED_TEXT );
				SetFieldText_1 ( IUMPR_SMAD_STATUS_INDEX, "Incomplete   " );
				setrgb ( NORMAL_TEXT );
			}
		}  // end if ( bDTCIUMPRSupported || bDTCSMADSupported)
		else
		{
			SetFieldText_1 ( IUMPR_SMAD_STATUS_INDEX, "Not Supported" );
		}
	}  // end if ( DataPage == 1 )

	// If Data Page 2, updata RGID $00-$0B,
	// else Data Page 3, update RGID $0C-$14,
	// IUMPR ,SMAD count display
	else
	{
		for ( Index = 0, RGIndex = (DataPage == 2 ? 0x00 : 0x0C);
		      Index < (DataPage == 2 ? 12 : 9);
		      Index++, RGIndex++ )
		{
			// if IUMPR is supported
			if ( gstResponse[EcuIdx].bDTCIUMPRSupported == TRUE )
			{
				// RG == $0E (CCM), $10 (NMHC) and $14 (PM)
				// are not monitored
				if ( RGIndex == 0x0E ||  // CCM
				     RGIndex == 0x10 ||  // NMHC
				     RGIndex == 0x14 )   // PM
				{
					SetFieldText_2 ( IUMPR_CNT_COL_INDEX+Index, "Not Monitored" );
				}
				else
				{
					// write current number completed and total number of DTCIUMPR for this RGID
					sprintf_s ( &String[0], 13,
					            "%4d of %4d",
					            gstResponse[EcuIdx].DTCIUMPR_RGIncdCount[RGIndex],
					            gstResponse[EcuIdx].DTCIUMPR_RGListCount[RGIndex] );
					SetFieldText_2 ( IUMPR_CNT_COL_INDEX+Index, &String[0] );
				}
			}  // end if ( bDTCIUMPRSupported )
			else
			{
				SetFieldText_2 ( IUMPR_CNT_COL_INDEX, "Not Supported" );
			}

			// if SMAD is supported
			if ( gstResponse[EcuIdx].bDTCSMADSupported == TRUE )
			{
				// RG == $0E (CCM), $10 (NMHC) and $14 (PM)DTCSMAD_RGIncdCount
				// are not monitored
				if ( RGIndex == 0x0E ||  // CCM
				     RGIndex == 0x10 ||  // NMHC
				     RGIndex == 0x14 )   // PM
				{
					SetFieldText_2 ( SMAD_CNT_COL_INDEX+Index, "Not Monitored" );
				}
				else
				{
					// write current number completed and total number of DTCSMAD for this RGID
					sprintf_s ( &String[0], 13,
					            "%4d of %4d",
					            gstResponse[EcuIdx].DTCSMAD_RGIncdCount[RGIndex],
					            gstResponse[EcuIdx].DTCSMAD_RGListCount[RGIndex] );
					SetFieldText_2 ( SMAD_CNT_COL_INDEX+Index, &String[0] );
				}
			}  // end if ( bDTCSMADSupported )
			else
			{
				SetFieldText_2 ( SMAD_CNT_COL_INDEX+Index, "Not Supported" );
			}

			// write RGID Status
			// RG == $0E (CCM), $10 (NMHC) and $14 (PM)
			// are not monitored, therefore alway considered complete
			if ( RGIndex == 0x0E ||  // CCM
			     RGIndex == 0x10 ||  // NMHC
			     RGIndex == 0x14 ||  // PM
			     (gstResponse[EcuIdx].DTCIUMPR_RGIncdCount[RGIndex] == gstResponse[EcuIdx].DTCIUMPR_RGListCount[RGIndex] &&
			      gstResponse[EcuIdx].DTCSMAD_RGIncdCount[RGIndex]  == gstResponse[EcuIdx].DTCSMAD_RGListCount[RGIndex]) )
			{
				SetFieldText_2 ( RGID_STAT_INDEX+Index, "Complete  " );
			}
			else
			{
				setrgb ( HIGHLIGHTED_TEXT );
				SetFieldText_2 (  RGID_STAT_INDEX+Index, "Incomplete" );
				setrgb ( NORMAL_TEXT );
			}
		}  // end for ( index )
	}  // end if ( DataPage == 2 )
}

/*******************************************************************************
**
**  Function:  UpdatePID01Display
**
**  Purpose:
**
*******************************************************************************/
void UpdatePID01Display ( BYTE EcuIdx )
{
	int index;


	for ( index = 0;
	      index < IMREADINESS_SIZE;
	      index++ )
	{
		eCurrentIMStatus[index] = Test11IMStatus[EcuIdx][index];
		if ( Test11IMStatus[EcuIdx][index] == INCOMPLETE )
		{
			setrgb ( HIGHLIGHTED_TEXT );
		}
		else if ( Test11IMStatus[EcuIdx][index] == DISABLED )
		{
			setrgb ( RED_TEXT );
		}
		SetFieldText_IM ( IM_READINESS_INDEX+index, szIM_Status[Test11IMStatus[EcuIdx][index]] );
		setrgb ( NORMAL_TEXT );
	}
}


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


	for ( EcuIdx=0;
	      EcuIdx < gNumOfECUs &&
	      EcuIdx < MAX_ECUS_PER_PAGE;
	      EcuIdx++ )
	{
		SetFieldHex ( ECU_ID_INDEX+EcuIdx, GetEcuId ( EcuPageOffset+EcuIdx ) );

		// If I/M Readiness not supported for the current ECU, mark it as done
		if ( IsIDSupported ( EcuPageOffset+EcuIdx, PF5REQUEST, 0xF501 ) == FALSE )
		{
			gstResponse[EcuPageOffset+EcuIdx].StatusFlags |= IMREADYDONE;
		}

		// If IPD not supported for the current ECU, mark it as done
		if ( gstResponse[EcuPageOffset+EcuIdx].bIPDSupported      == FALSE )
		{
			gstResponse[EcuPageOffset+EcuIdx].StatusFlags |= IPDDONE;
		}

		// If IUMPR not supported for the current ECU, mark it as done
		if ( gstResponse[EcuPageOffset+EcuIdx].bDTCIUMPRSupported == FALSE )
		{
			gstResponse[EcuPageOffset+EcuIdx].StatusFlags |= IUMPRDONE;
		}

		// If SMAD not supported for the current ECU, mark it as done
		if ( gstResponse[EcuPageOffset+EcuIdx].bDTCSMADSupported == FALSE )
		{
			gstResponse[EcuPageOffset+EcuIdx].StatusFlags |= SMADDONE;
		}

		// If I/M Readiness and IPD are done for the current ECU, mark the ECU as done
		if ( (gstResponse[EcuPageOffset+EcuIdx].StatusFlags & IMREADYDONE) != 0 &&
		     (gstResponse[EcuPageOffset+EcuIdx].StatusFlags & IPDDONE)     != 0 &&
		     (gstResponse[EcuPageOffset+EcuIdx].StatusFlags & IUMPRDONE)   != 0 &&
		     (gstResponse[EcuPageOffset+EcuIdx].StatusFlags & SMADDONE)    != 0 )
		{
			gstResponse[EcuPageOffset+EcuIdx].StatusFlags |= ECUDONE;
		}

		// If I/M Readiness, IPD and IUMPR are done for the curnent ECU, mark the ECU as done
		if ( (gstResponse[EcuPageOffset+EcuIdx].StatusFlags & ECUDONE) != 0 )
		{
			SetFieldText ( ECU_STATUS_INDEX+EcuIdx, "Done" );
		}
		else
		{
			SetFieldText ( ECU_STATUS_INDEX+EcuIdx, "    " );
		}
	}
}


/*******************************************************************************
**
**  Function:  SelectECU
**
**  Purpose:
**
*******************************************************************************/
void SelectECU ( int NewIndex,
                 int OldIndex ,
                 int Offset )
{
	int EcuIdx;


	// un-highlite previous selection
	setrgb ( NORMAL_TEXT );
	EcuIdx = ECU_ID_INDEX + OldIndex;
	if ( ECU_ID_INDEX <= EcuIdx &&
	     EcuIdx <= (ECU_ID_INDEX+MAX_ECUS_PER_PAGE) )
	{
		SetFieldHex ( EcuIdx, GetEcuId ( Offset+OldIndex ) );
	}

	// highlite new selection
	setrgb ( HIGHLIGHTED_TEXT );
	EcuIdx = ECU_ID_INDEX + NewIndex;
	if ( ECU_ID_INDEX <= EcuIdx &&
	     EcuIdx <= (ECU_ID_INDEX+MAX_ECUS_PER_PAGE) )
	{
		SetFieldHex ( EcuIdx, GetEcuId ( Offset+NewIndex ) );
	}

	// restore screen attributes
	setrgb ( NORMAL_TEXT );
}


/*******************************************************************************
**
**  Function:  UpdateTestStatus
**
**  Purpose:
**
*******************************************************************************/
void UpdateTestStatus ( BYTE SubTestStatusFlags )
{
	if ( (SubTestStatusFlags & SUB_TEST_FAIL) == SUB_TEST_FAIL )
	{
		setrgb ( HIGHLIGHTED_TEXT );
		SetFieldText ( TESTSTATUS_INDEX, "FAILURE" );
		setrgb ( NORMAL_TEXT );
	}
	else
	{
		SetFieldText ( TESTSTATUS_INDEX, "Normal" );
	}

	if ( (SubTestStatusFlags & SUB_TEST_DTC) == SUB_TEST_DTC )
	{
		setrgb ( HIGHLIGHTED_TEXT );
		SetFieldText ( TESTSTATUS_INDEX + 1, "DTC Detected" );
		setrgb ( NORMAL_TEXT );
	}
	else
	{
		SetFieldText ( TESTSTATUS_INDEX + 1, "             " );
	}
}


/*******************************************************************************
**
**  Function:  ResetDisplay
**
**  Purpose:
**
*******************************************************************************/
void ResetDisplay ( BYTE EcuPageOffset,
                    BYTE CurrentEcuIdx,
                    BYTE EcuPage,
                    BYTE DataPage )
{
	BYTE   EcuIdx = EcuPageOffset + CurrentEcuIdx;

	unsigned int    LastDataPage = 3;
	char   String[8];


	// clear display and print static ECU elements
	init_screen ( _string_elements_11, _num_string_elements_11 );

	// display ECU ID and Status
	UpdateECUDisplay ( EcuPageOffset );

	SelectECU ( CurrentEcuIdx, -1, EcuPageOffset );

	// display ECU page number
	SetFieldDec ( ECU_PAGE_INDEX, EcuPage );

	// Update Data page number
	sprintf_s ( String, 8,
	            "%d of %d",
	            DataPage,
	            LastDataPage );
	SetFieldText ( DATA_PAGE_INDEX, String );

	// print static IM Readiness or IUMPR elements
	if ( DataPage == 1 )
	{
		place_screen_text ( _string_elements_imr, _num_string_elements_imr );
		place_screen_text ( _string_elements_ipt1, _num_string_elements_ipt1 );
	}
	else
	{
		place_screen_text ( _string_elements_dtcext, _num_string_elements_dtcext );
		if ( DataPage == 2 )
		{
			place_screen_text ( _string_elements_rg1, _num_string_elements_rg1 );
		}
		else
		{
			place_screen_text ( _string_elements_rg2, _num_string_elements_rg2 );
		}
	}

	EvaluateIPD          ( EcuIdx, FALSE );
	EvaluateDTCIUMPRData ( EcuIdx, FALSE );
	EvaluateDTCSMADData  ( EcuIdx, FALSE );
	if ( DataPage == 1 )
	{
		UpdatePID01Display ( EcuIdx );

		UpdateIPDDisplay ( EcuIdx );
	}
	UpdateDTCExtDisplay ( EcuIdx, DataPage );
}


/*******************************************************************************
**
**  Function:  RunDynamicTest11
**
**  Purpose:
**
*******************************************************************************/
STATUS RunDynamicTest11 ( BOOL          bDisplayCARBTimers,
                          unsigned long EngineStartTimestampMsecs )
{
	REQ_MSG        stReqMsg;
	PID           *pPid;
	BYTE           EcuIdx;
	STATUS         eRetVal = PASS;
	STATUS         eRetCode = PASS;

	BYTE           CurrentEcuIdx;
	BYTE           EcuPageOffset;
	BYTE           EcuPage;
	BOOL           bECUPageUpdate = FALSE;

	BYTE           DataPage;
	BOOL           bDataPageUpdate = FALSE;
	WORD           OffsetIncrement = 0;

	BOOL           bIPDSupported = FALSE;

	BOOL           bIUMPRSupported = FALSE;
	WORD           IUMPRIndex;

	BOOL           bSMADSupported = FALSE;
	WORD           SMADIndex;
	WORD           SMADPassCount = 0;

	WORD           DTCExtTotalCount = 0;
	WORD           DTCExtIndex = 0;
	WORD           DTCEXTPassCount = 0;
	DTC           *pDtc = NULL;
	BYTE           DTCExtState = UNDEFINED;
	BYTE           DTCECUIndex = 0;
	WORD           DTCIndex = 0;
	
	BOOL           bECUDataUpdate = FALSE;

	BYTE           EcuDoneCount = 0;

	BYTE           SubTestStatusFlags  = SUB_TEST_NORMAL;
	BYTE           LastTestStatusFlags = SUB_TEST_INITIAL; // set to force an update the first time

	BOOL           bLogMessage;
	BOOL           bIUMPRUpdate = FALSE;
	BOOL           bECUStatusUpdate = FALSE;

	BOOL           b1Sec = FALSE;
	unsigned long  LastCheckTimestampMsecs = 0;
	unsigned long  CurrentTimestampMsecs;

	// variables for CARB Timers
	unsigned short TestStartTimestampSecs;     // time the test started (in seconds)
	unsigned short LastCheckTimestampSecs = 0;
	unsigned short CheckDTCsTimerSecs = 0;     // # of seconds until DTCs are checked
	unsigned short IdleTimeSecs = 0;           // time at idle (in seconds, stops at 30)
	unsigned short AtSpeedTimeSecs = 0;        // time at speeds > 25mph (in seconds, stops at 300)
	unsigned short RPM = 0;                    // engine RPM (used if RUNTM is not supported)
	unsigned short SpeedMPH = 0;               // vehicle speed in MPH
	unsigned short SpeedKPH = 0;               // vehicle speed in KPH
	unsigned short RunTimeSecs = 0;            // engine run time (in seconds)
	BOOL           bRunTimeSupport;            // 1 = RUNTM PID supported
	BYTE           TestState = 0xFF;
	BYTE           TimeStatusFlags = 0;
	BOOL           bLoop;


	TestStartTimestampSecs = (unsigned short)(EngineStartTimestampMsecs / 1000);


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

	if ( IsIDSupported ( ALLECUS, PIDREQUEST, 0xF40C ) == FALSE &&
	     bRunTimeSupport == FALSE )
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "PIDs $F40C (RPM) and $F41F (RUNTM) not supported\n" );
		bDisplayCARBTimers = FALSE;
	}

	// check for SID $22 OBDCOND and IGNCNTR support
	if ( IsIDSupported ( ALLECUS, INFREQUEST, INF_TYPE_IPD_UDS ) == TRUE )
	{
		bIPDSupported = TRUE;
	}

	// check for DTC IUMPR support
	for ( EcuIdx=0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( gstResponse[EcuIdx].DTCIUMPR_RGTotalCount != 0 )
		{
			bIUMPRSupported = TRUE;
			DTCExtState = IUMPR_REQ;

			DTCExtTotalCount += gstResponse[EcuIdx].DTCIUMPR_RGTotalCount;
		}

		if ( gstResponse[EcuIdx].DTCSMAD_RGTotalCount != 0 )
		{
			bSMADSupported = TRUE;
			if ( DTCExtState != IUMPR_REQ )
			{
				DTCExtState = SMAD_REQ;
			}

			DTCExtTotalCount += gstResponse[EcuIdx].DTCSMAD_RGTotalCount;
		}
	}


	// save PID $F501
	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = 0xF501;
	if ( RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE ) == FAIL )
	{
		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			gstResponse[EcuIdx].PIDF501.Data[0] = 0xFF;
			gstResponse[EcuIdx].PIDF501.Data[1] = 0xFF;
			gstResponse[EcuIdx].PIDF501.Data[2] = 0xFF;
			gstResponse[EcuIdx].PIDF501.Data[3] = 0xFF;
			gstResponse[EcuIdx].PIDF501.Data[4] = 0xFF;
			gstResponse[EcuIdx].PIDF501.Data[5] = 0xFF;
		}

		Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
		      "PID $F501 request\n" );
	}
	else
	{
		for ( EcuIdx = 0;
		      EcuIdx < gUserNumOfECUs;
		      EcuIdx++ )
		{
			if ( gstResponse[EcuIdx].PIDSize > 0 )
			{
				SavePIDF501Data ( EcuIdx, TRUE );
			}
		}
	}


	// initialize Test 11 IPD, IUMPR and SMAD display data
	for ( EcuIdx=0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		// initialize current Test 11 IPD
		memcpy ( &gstResponse[EcuIdx].IPD_Test11_CurrentDisplayData,
		         &gstResponse[EcuIdx].IPD_Test10_10,
		         sizeof ( BYTE ) * 2 );

		// initialize current Test 11 IUMPR
		memcpy ( &gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData,
		         &gstResponse[EcuIdx].DTCIUMPR_Test10_10,
		         sizeof ( DTCIUMPR ) * gstResponse[EcuIdx].DTCIUMPR_RGTotalCount );

		// initialize current Test 11 SMAD
		memcpy ( &gstResponse[EcuIdx].DTCSMAD_Test11_CurrentDisplayData,
		         &gstResponse[EcuIdx].DTCSMAD_Test10_10,
		         sizeof ( DTCIUMPR ) * gstResponse[EcuIdx].DTCSMAD_RGTotalCount );
	}  // end for ( EcuIdx )


	gbSuspendLogOutput = TRUE;
	gbSuspendScreenOutput = TRUE;


	// set static text elements based on Fueltype
	if ( gstUserInput.eFuelType == GASOLINE )
	{
		for ( BYTE index = 0;
		           index < 4;
		           index++ )
		{
			memcpy ( &_string_elements_imr[IMSTATUS_GD_ELEMENT1_START_INDEX + index],
			         &_string_elements11_Gas[index],
			         sizeof ( _string_elements11_Gas[index] ) );
		}
		memcpy ( &_string_elements_imr[IMSTATUS_GD_ELEMENT2_INDEX],
		         &_string_elements11_Gas[4],
		         sizeof ( _string_elements11_Gas[4] ) );
	}
	else
	{
		for ( BYTE index = 0;
		           index < 5;
		           index++ )
		{
			memcpy ( &_string_elements_imr[IMSTATUS_GD_ELEMENT1_START_INDEX + index],
			         &_string_elements11_Diesel[index],
			         sizeof ( _string_elements11_Diesel[index] ) );
		}
		memcpy ( &_string_elements_imr[IMSTATUS_GD_ELEMENT2_INDEX],
		         &_string_elements11_Diesel[4],
		         sizeof ( _string_elements11_Diesel[4] ) );
	}


	// initialize screen
	// ECU Page and Offset
	EcuPageOffset = 0;
	EcuPage = 1;

	CurrentEcuIdx = 0;

	// Data Page and Offset
	DataPage = 1;

	ResetDisplay ( EcuPageOffset, CurrentEcuIdx, EcuPage, DataPage );


	// display CARB Timers, if required
	if ( bDisplayCARBTimers == TRUE )
	{
		// add CARB Timer display text to the screen
		place_screen_text ( _string_elements_timers, _num_string_elements_timers );
		SetFieldDec_T ( IDLE_TIMER_INDEX, IdleTimeSecs );
		SetFieldDec_T ( SPEED_25_MPH_TIMER_INDEX, AtSpeedTimeSecs );
		SetFieldDec_T ( TOTAL_DRIVE_TIMER_INDEX, RunTimeSecs );
	}


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

	CurrentTimestampMsecs = LastCheckTimestampMsecs = GetTickCount ( );

	IUMPRIndex = 0;
	SMADIndex  = 0;

//	unsigned long  LastTxTimestampMsecs = GetTickCount();
	//-------------------------------------------
	// loop until test completes
	//-------------------------------------------
	for (;;)
	{
		//-------------------------------------------
		// Things to do each loop:
		// Check for key press, act accordingly
		// Request next IPD
		// Request next IUMPR or SMAD data
		// Update CurrentTimestamp
		//-------------------------------------------
		if ( _kbhit ( ) != 0 )
		{
			unsigned char c = _getch ( );
			if ( c == 27 )                    // ESC key
			{
				eRetCode = ABORT;
			}

			else if ( c == 'F' || c == 'f' )   // "FAIL" key
			{
				eRetCode = FAIL;
			}

			else if ( c >= '1' && c <= '8' )   // new ECU index
			{
				EcuIdx = (c - '1');         // zero-based index
				if ( EcuPageOffset+EcuIdx < gNumOfECUs &&
				     EcuIdx != CurrentEcuIdx )
				{
					CurrentEcuIdx = EcuIdx;
					bECUPageUpdate = TRUE;
				}
			}

			else if ( c == 73 )  //PGUP
			{
				if ( EcuPage < MAX_ECU_PAGE &&
				     EcuPageOffset+MAX_ECUS_PER_PAGE < gNumOfECUs )
				{
					EcuPageOffset += MAX_ECUS_PER_PAGE;
					EcuPage++;
					CurrentEcuIdx = 0;
					bECUPageUpdate = TRUE;
				}
			}

			else if ( c == 81 )  //PGDN
			{
				if ( EcuPage > 1 )
				{
					EcuPageOffset -= MAX_ECUS_PER_PAGE;
					EcuPage--;
					CurrentEcuIdx = 0;
					bECUPageUpdate = TRUE;
				}
			}

			else if ( c == 224 )  // Extended Range
			{
				c = _getch ( );
				if ( c == 77 )  //Right Arrow
				{
					if ( DataPage < MAX_DATA_PAGE )
					{
						DataPage++;
						bDataPageUpdate = TRUE;
					}
				}

				else if ( c == 75 )  //Left Arrow
				{
					if ( DataPage > 1 )
					{
						DataPage--;
						bDataPageUpdate = TRUE;
					}
				}
			}
		}  // end if kbhit


		//-------------------------------------------
		// Update screen, if a key press requires it
		//-------------------------------------------
		if ( bECUPageUpdate  == TRUE ||
		     bDataPageUpdate == TRUE)
		{
			// if ECU view changed and Data Page wasn't already == 1, reset
			if ( bECUPageUpdate  == TRUE &&
			     DataPage != 1 )
			{
				// reset to Data Page 1
				DataPage = 1;
			}

			ResetDisplay ( EcuPageOffset, CurrentEcuIdx, EcuPage, DataPage );

			UpdateTestStatus ( SubTestStatusFlags );

			// display CARB Timers, if required
			if ( bDisplayCARBTimers == TRUE )
			{
				// add CARB Timer display text to the screen
				place_screen_text ( _string_elements_timers, _num_string_elements_timers );
				SetFieldDec_T ( IDLE_TIMER_INDEX, IdleTimeSecs );
				SetFieldDec_T ( SPEED_25_MPH_TIMER_INDEX, AtSpeedTimeSecs );
				SetFieldDec_T ( TOTAL_DRIVE_TIMER_INDEX, RunTimeSecs );
			}

			bDataPageUpdate = FALSE;
			bECUPageUpdate  = FALSE;
		}  // end if page update true


		//-------------------------------------------
		// Update Other test status: ECU and DTC Ext data counters
		//-------------------------------------------
		{
		short x;
		short y;

		// save current cursor position
		get_cursor_pos ( &x, &y );

		// print currently requested ECU
		gotoxy ( COL_PROC_ECU, TEST_STATUS_ROW );
		printf ( "%X",
		         GetEcuId ( DTCECUIndex ) );       // ECU ID of currently requested DTC

		if ( bIUMPRSupported == TRUE ||
		     bSMADSupported  == TRUE )
		{
			// print currently requested DTC and place in list
			gotoxy ( COL_PROC_DTC, PROC_STATUS_ROW );
			if ( DTCExtState == IUMPR_REQ )
			{
				pDtc = &gstResponse[DTCECUIndex].DTCIUMPR_RGList[IUMPRIndex].Record.Dtc;
			}
			else
			{
				pDtc = &gstResponse[DTCECUIndex].DTCSMADList[SMADIndex].Record.Dtc;
			}
			printf ( "%c%02X%02X %02X %02X  %4d of %4d (%3.1f %%)",
			         DTCTypeCharacter[(pDtc->HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
			         pDtc->HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
			         pDtc->MidByte,                                   // 4th and 5th characters (0-F)
			         pDtc->LowByte,                                   // Failure Type Byte
			         DTCExtState == IUMPR_REQ ? 0x91 : 0x93,          // Ext Data
			         DTCExtIndex + 1,                                 // current count of requested DTCs
			         DTCExtTotalCount,                                // total count of DTCs to be requested
			         ((float)(DTCExtIndex + 1)/(float)(DTCExtTotalCount) * (float)100) );   // percentage of DTCs requested for this pass

			if ( ++DTCExtIndex >= DTCExtTotalCount )
			{
				DTCExtIndex = 0;
				DTCEXTPassCount++;
			}
			// print current DTC list pass count
			gotoxy ( COL_LST_RD_CNT, PROC_STATUS_ROW );
			// highlight as incomplete if haven't completed 1st read through all of the IUMPR
			if ( DTCEXTPassCount < 1 )
			{
				setrgb ( HIGHLIGHTED_TEXT );
			}
			printf ( "%3d", DTCEXTPassCount );

			// reset to normal text
			setrgb ( NORMAL_TEXT );
		}

		// reset to save cursor psotion
		gotoxy ( x, y );
		}

		//-------------------------------------------
		// Get next IUMPR
		//-------------------------------------------
		if ( DTCExtState == IUMPR_REQ )
		{
			if ( gstResponse[DTCECUIndex].DTCIUMPR_RGTotalCount != 0 )
			{
				bLogMessage = FALSE;

				gbAllECUResetCompleted = FALSE;
				if ( (eRetVal = GetDTCExtData ( 0x91, DTCECUIndex, &gstResponse[DTCECUIndex].DTCIUMPR_RGList[IUMPRIndex].Record.Dtc ))!= PASS )
				{
					// early/late message, note it and keep going
					SubTestStatusFlags |= SUB_TEST_FAIL;
					bLogMessage = TRUE;
				}

				// Save the responses
				if ( gstResponse[DTCECUIndex].bDTCIUMPRSupported == TRUE &&
				     gstResponse[DTCECUIndex].bResponseReceived  == TRUE &&
				     (gstResponse[DTCECUIndex].DTCIUMPRCurrent.Record.Dtc.HighByte != 0x00 ||
				      gstResponse[DTCECUIndex].DTCIUMPRCurrent.Record.Dtc.MidByte  != 0x00 ||
				      gstResponse[DTCECUIndex].DTCIUMPRCurrent.Record.Dtc.LowByte  != 0x00) )
				{
					WORD Index;
					SaveDTCIUMPRData ( DTCECUIndex, &Index );

					if ( CheckIUMPRData ( DTCECUIndex, TRUE ) == TRUE )
					{
						bLogMessage = TRUE;
					}
				}  //end  if ECU received data

				if ( bLogMessage == TRUE )
				{
					LogLastTransaction ( );
				}

				if ( ++IUMPRIndex >= gstResponse[DTCECUIndex].DTCIUMPR_RGTotalCount )
				{
					IUMPRIndex = 0;
					DTCExtState = SMAD_REQ;
				}
			}  // end if ( gstResponse[DTCECUIndex].DTCIUMPR_RGTotalCount != 0)
			// Otherwise this ECU doesn't support IUMPR, try SMAD
			else
			{
				IUMPRIndex = 0;
				DTCExtState = SMAD_REQ;
			}
		}  // end if ( DTCExtState == IUMPR_REQ )


		//-------------------------------------------
		// Get next SMAD
		//-------------------------------------------
		if ( DTCExtState == SMAD_REQ )
		{
			if ( gstResponse[DTCECUIndex].DTCSMAD_RGTotalCount != 0 )
			{
				bLogMessage = FALSE;

				gbAllECUResetCompleted = FALSE;
				if ( (eRetVal = GetDTCExtData ( 0x93, DTCECUIndex, &gstResponse[DTCECUIndex].DTCSMAD_RGList[SMADIndex].Record.Dtc ))!= PASS )
				{
					// early/late message, note it and keep going
					SubTestStatusFlags |= SUB_TEST_FAIL;
					bLogMessage = TRUE;
				}

				// Save the response
				if ( gstResponse[DTCECUIndex].bDTCSMADSupported == TRUE &&
				     gstResponse[DTCECUIndex].bResponseReceived  == TRUE &&
				     (gstResponse[DTCECUIndex].DTCSMADCurrent.Record.Dtc.HighByte != 0x00 ||
				      gstResponse[DTCECUIndex].DTCSMADCurrent.Record.Dtc.MidByte  != 0x00 ||
				      gstResponse[DTCECUIndex].DTCSMADCurrent.Record.Dtc.LowByte  != 0x00) )
				{
					WORD Index;
					SaveDTCSMADData ( DTCECUIndex, &Index );

					if ( CheckSMADData ( DTCECUIndex, TRUE ) == TRUE )
					{
						bLogMessage = TRUE;
					}
				}  //end if ECU received data

				if ( bLogMessage == TRUE )
				{
					LogLastTransaction ( );
				}

				if ( ++SMADIndex >= gstResponse[DTCECUIndex].DTCSMAD_RGTotalCount )
				{
					SMADIndex = 0;
					if ( ++DTCECUIndex >= gNumOfECUs )
					{
						DTCECUIndex = 0;
					}
					DTCExtState = IUMPR_REQ;
				}
			}  // end if ( DTCSMAD_RGTotalCount != 0 )
			// Otherwise this ECU doesn't support SMAD, try IUMPR
			else
			{
				SMADIndex = 0;
				if ( ++DTCECUIndex >= gNumOfECUs )
				{
					DTCECUIndex = 0;
				}
				DTCExtState = IUMPR_REQ;
			}
		}  // end if ( DTCEExtState == IUMPR_REQ )


		//-------------------------------------------
		// Update current time
		//-------------------------------------------
		CurrentTimestampMsecs = GetTickCount ( );


		//-------------------------------------------
		// END things to do each loop
		//-------------------------------------------


		//-------------------------------------------
		// Things to do once a second:
		// Update LastCheckTimestamp
		// Request PID $F501, update screen if changed
		// Request OBDCOND and IGNCNTR, update screen if changed
		// Check IUMPR data, update screen if changed
		// Request PID $0C (RPM), PID $0D (Speed), and PID $1F (Runtime), determine test state
		//-------------------------------------------
		if ( (CurrentTimestampMsecs - LastCheckTimestampMsecs) >= 1000 );
		{
			LastCheckTimestampMsecs = CurrentTimestampMsecs;
			CheckDTCsTimerSecs--;

			// Alternate requesting PID $F501 and INF $F882
			if ( b1Sec )
			{
				//-------------------------------------------
				// request and update PID $F501
				//-------------------------------------------
				stReqMsg.SID      = 0x22;
				stReqMsg.NumIds   = 1;
				stReqMsg.u.DID[0] = 0xF501;
				if ( (eRetVal = RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE )) == FAIL )
				{
					Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
					      "PID $F501 request\n" );
					DumpTransactionBuffer ( );
				}
				else
				{
					bLogMessage = FALSE;

					if ( eRetVal == ERRORS )
					{
						// early/late message, note it and keep going
						SubTestStatusFlags |= SUB_TEST_FAIL;
						bLogMessage = TRUE;
					}

					for ( EcuIdx = 0;
					      EcuIdx < gNumOfECUs;
					      EcuIdx++ )
					{
						if ( gstResponse[EcuIdx].PIDSize > 0 )
						{
							if ( SavePIDF501Data ( EcuIdx, FALSE ) == TRUE )
							{
								bLogMessage = TRUE;

								pPid = (PID *)&gstResponse[EcuIdx].PID[0];
								if ( IsIM_ReadinessComplete ( pPid ) == TRUE &&
								     (gstResponse[EcuIdx].StatusFlags & IMREADYDONE) == 0 )
								{
									gstResponse[EcuIdx].StatusFlags |= IMREADYDONE;
									bECUStatusUpdate = TRUE;
								}

								if ( EcuIdx == EcuPageOffset+CurrentEcuIdx &&
								     DataPage == 1 )
								{
									UpdatePID01Display ( EcuIdx );
								}
							}
						}
					}  // end for ( EcuIdx )

					if ( bLogMessage == TRUE )
					{
						LogLastTransaction ( );
					}
				}  // end if ( eRetVal != FAIL )
			}  // end if ( b1Sec )
			else
			{
				//-------------------------------------------
				// Get OBDCOND and IGNCNTR
				//-------------------------------------------
				if ( bIPDSupported == TRUE )
				{
					stReqMsg.SID      = 0x22;
					stReqMsg.NumIds   = 1;
					stReqMsg.u.DID[0] = 0xF882;
					if ( (eRetVal = RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE )) == FAIL )
					{
						Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
						      "SID $22 IPD request\n" );
						DumpTransactionBuffer ( );
					}
					else
					{
						bLogMessage = FALSE;

						if ( eRetVal == ERRORS )
						{
							// early/late message, note it and keep going
							SubTestStatusFlags |= SUB_TEST_FAIL;
							bLogMessage = TRUE;
						}

						for ( EcuIdx = 0;
						      EcuIdx < gNumOfECUs;
						      EcuIdx++ )
						{
							gstResponse[EcuIdx].IPDStatusFlags = 0xFF;   // by default all items completed
							if ( GetIPD ( EcuIdx ) == PASS )
							{
								if ( CheckIPD ( EcuIdx ) == TRUE )
								{
									bLogMessage = TRUE;

									eRetVal = EvaluateIPD ( EcuIdx, FALSE );
									if ( (eRetVal & FAIL) == PASS &&
									     (gstResponse[EcuIdx].StatusFlags & IPDDONE) == 0 )
									{
										gstResponse[EcuIdx].StatusFlags |= IPDDONE;
										bECUStatusUpdate = TRUE;
									}

									if ( EcuIdx == EcuPageOffset+CurrentEcuIdx &&
									     DataPage == 1 )
									{
										UpdateIPDDisplay ( EcuIdx );
									}
								}
							}
						}  // end for ( EcuIdx )

						if ( bLogMessage == TRUE )
						{
							LogLastTransaction ( );
						}
					}  // end if ( eRetVal != FAIL )
				}  // end if ( bIPDSupported )
			}  // end if ( !b1Sec )


			//-------------------------------------------
			// Check if need to update IPD or DTC ExtData on-screen
			//-------------------------------------------
			// if something changed, update the display because EVAP from one ECU
			// could affect another ECU's display
			//-------------------------------------------
			for ( EcuIdx = 0;
			      EcuIdx < gNumOfECUs;
			      EcuIdx++ )
			{
				eRetVal = EvaluateIPD ( EcuIdx, FALSE );
				if ( (eRetVal & FAIL) == PASS &&
				     (gstResponse[EcuIdx].StatusFlags & IPDDONE) == 0 )
				{
					gstResponse[EcuIdx].StatusFlags |= IPDDONE;
					bECUStatusUpdate = TRUE;
				}

				eRetVal = EvaluateDTCIUMPRData ( EcuIdx, FALSE );
				if ( (eRetVal & FAIL) == PASS )
				{
					if ( (gstResponse[EcuIdx].DTCReadinessGroupStatusFlags[0] & DTCRG_IUMPR_ALL_DONE) == DTCRG_IUMPR_ALL_DONE )
					{
						gstResponse[EcuIdx].StatusFlags |= IUMPRDONE;
					}
					bECUStatusUpdate = TRUE;
				}

				eRetVal = EvaluateDTCSMADData ( EcuIdx, FALSE );
				if ( (eRetVal & FAIL) == PASS )
				{
					if ( (gstResponse[EcuIdx].DTCReadinessGroupStatusFlags[1] & DTCRG_SMAD_ALL_DONE) == DTCRG_SMAD_ALL_DONE )
					{
						gstResponse[EcuIdx].StatusFlags |= SMADDONE;
					}
					bECUStatusUpdate = TRUE;
				}

				if ( bECUStatusUpdate &&
				     EcuIdx == EcuPageOffset + CurrentEcuIdx )
				{
					// if data page 1, also update IPD, other counters can affect the status of OBDCOND
					if ( DataPage == 1 )
					{
						UpdateIPDDisplay ( EcuIdx );
					}
					UpdateDTCExtDisplay ( EcuIdx, DataPage );
				}
			}  // end for ( EcuIdx )


			//-------------------------------------------
			// Check if need to update ECU Status on-screen
			//-------------------------------------------
			if ( bECUStatusUpdate == TRUE )
			{
				// display ECU ID and Status
				UpdateECUDisplay ( EcuPageOffset );
				SelectECU ( CurrentEcuIdx, -1, EcuPageOffset );
				bECUStatusUpdate = FALSE;
			}


			//-------------------------------------------
			// Display Timers
			//-------------------------------------------
			if ( bDisplayCARBTimers == TRUE )
			{
				// Alternate requesting PID $F41F or $F40C and INF $F882
				if ( b1Sec )
				{
					//-------------------------------------------
					// if RunTime supported,
					// request engine RunTime (PID $F41F)
					//-------------------------------------------
					if ( bRunTimeSupport == TRUE )
					{
						stReqMsg.SID      = 0x22;
						stReqMsg.NumIds   = 1;
						stReqMsg.u.DID[0] = 0xF41F;
						eRetVal = RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE );
						if ( eRetVal == FAIL )
						{
							Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
							      "PID $F41F request\n" );
							DumpTransactionBuffer ( );
						}
						else
						{
							bLogMessage = FALSE;

							if ( eRetVal != PASS )
							{
								// cover the case where a response was early/late
								SubTestStatusFlags |= SUB_TEST_FAIL;
								bLogMessage = TRUE;
							}

							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];
										RunTimeSecs = (RunTimeSecs << 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;
								bLogMessage = TRUE;
							}

							if ( bLogMessage == TRUE )
							{
								LogLastTransaction ( );
							}
						}  // end if ( eRetVal != FAIL )
					}  // end if ( bRunTimeSupport )

					//-------------------------------------------
					// if RunTime is not supported,
					// get RunTime from system clock,
					// request RPM (PID $F40C)
					//-------------------------------------------
					else
					{
						RunTimeSecs = (unsigned short)(GetTickCount ( ) / 1000) - TestStartTimestampSecs;

						stReqMsg.SID      = 0x22;
						stReqMsg.NumIds   = 1;
						stReqMsg.u.DID[0] = 0xF40C;
						eRetVal = RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE );
						if ( eRetVal == FAIL )
						{
							Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
							      "PID $F40C request\n" );
							DumpTransactionBuffer ( );
						}
						else
						{
							bLogMessage = FALSE;
						
							if ( eRetVal != PASS )
							{
								// cover the case where a response was early/late
								SubTestStatusFlags |= SUB_TEST_FAIL;
								bLogMessage = TRUE;
							}

							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];
										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,
								      "PID $F40C missing response\n" );
								SubTestStatusFlags |= SUB_TEST_FAIL;
								bLogMessage = TRUE;
							}

							if ( bLogMessage == TRUE )
							{
								LogLastTransaction ( );
							}
						}  // end if ( eRetVal != FAIL )
					}    // end if ( !bRunTimeSupport )

					// Display RunTime
					SetFieldDec_T ( TOTAL_DRIVE_TIMER_INDEX, RunTimeSecs );
				}  // end if ( b1Sec )

				else if ( (TimeStatusFlags & ATSPEED_TIME) == 0 )
				{
					//-------------------------------------------
					// request Speed - PID $F40D
					//-------------------------------------------
					stReqMsg.SID      = 0x22;
					stReqMsg.NumIds   = 1;
					stReqMsg.u.DID[0] = 0xF40D;
					eRetVal = RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE );
					if ( eRetVal == FAIL )
					{
						Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
						      "PID $F40D request\n" );
						DumpTransactionBuffer ( );
					}
					else
					{
						bLogMessage = FALSE;

						if ( eRetVal != PASS )
						{
							// cover the case where a response was early/late
							SubTestStatusFlags |= SUB_TEST_FAIL;
							bLogMessage = TRUE;
						}

						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
								}
							}
						}  // end for ( EcuIdx )

						if ( EcuIdx >= gNumOfECUs )
						{
							Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
							      "PID $F40D missing response\n" );
							SubTestStatusFlags |= SUB_TEST_FAIL;
							bLogMessage = TRUE;
						}

						if ( bLogMessage == TRUE )
						{
							LogLastTransaction ( );
						}
					}  // end if ( eRetVal != FAIL )
				}  // end if ( !b1Sec )


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

							if ( (bRunTimeSupport == TRUE) ? (RunTimeSecs > 0) : (RPM > 450) )
							{

								if ( SpeedMPH <= 1 )
								{
									TestState = 1;
									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

						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 )
								{
									TimeStatusFlags |= CUMULATIVE_TIME;
									SaveTransactionStart ( ); // treat as a seperate transaction to avoid duplication
									Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
									      "Idle Time = %d;  Speed Time = %d;  Run Time = %d  *RUN TIME DONE*\n",
									      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );
									LogLastTransaction ( );
								}

								if ( SpeedMPH <= 1 &&
								     IdleTimeSecs < 30 )
								{
									TestState = 1;
									IdleTimeSecs = 0;
									bLoop = TRUE;
								}
								else if ( SpeedMPH >= 25 &&
								          AtSpeedTimeSecs < 300 )
								{
									TestState = 2;
									bLoop = TRUE;
								}
								else if ( TimeStatusFlags == (IDLE_TIME | ATSPEED_TIME | CUMULATIVE_TIME) )
								{
									TestState = 3;
									bLoop = TRUE;
								}
								else
								{
									LastCheckTimestampSecs = RunTimeSecs;
								}
							}
						}
						break;

						case 1:     // 30 seconds continuous time at idle
						{
							if ( SpeedMPH <= 1 &&
							     (bRunTimeSupport == FALSE) ? (RPM > 450) : 1 )
							{
								// check idle time
								IdleTimeSecs = min ( IdleTimeSecs + (RunTimeSecs - LastCheckTimestampSecs), 30 );
								LastCheckTimestampSecs = RunTimeSecs;

								SetFieldDec_T ( IDLE_TIMER_INDEX, IdleTimeSecs );

								if ( IdleTimeSecs >= 30 )
								{
									TimeStatusFlags |= IDLE_TIME;
									TestState = 0;
									bLoop = TRUE;
									SaveTransactionStart ( ); // treat as a seperate transaction to avoid duplication
									Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
									      "Idle Time = %d;  Speed Time = %d;  Run Time = %d  *IDLE DONE*\n",
									      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );
									LogLastTransaction ( );
								}

								// check 600 seconds cumulative time
								if ( (TimeStatusFlags & CUMULATIVE_TIME) != CUMULATIVE_TIME &&
								     RunTimeSecs >= 600 )
								{
									TimeStatusFlags |= CUMULATIVE_TIME;
									SaveTransactionStart ( ); // treat as a seperate transaction to avoid duplication
									Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
									      "Idle Time = %d;  Speed Time = %d;  Run Time = %d  *RUN TIME DONE*\n",
									      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );
									LogLastTransaction ( );
								}
							}
							else
							{
								TestState = 0;
								bLoop = TRUE;
							}
						}
						break;

						case 2:     // 300 seconds cumulative time at SpeedMPH >= 25 MPH
						{
							if ( SpeedMPH >= 25 &&
							     (bRunTimeSupport == FALSE) ? (RPM > 450) : 1 )
							{
								// check at speed time
								AtSpeedTimeSecs = min ( AtSpeedTimeSecs + (RunTimeSecs - LastCheckTimestampSecs), 300 );
								LastCheckTimestampSecs = RunTimeSecs;

								SetFieldDec_T ( SPEED_25_MPH_TIMER_INDEX, AtSpeedTimeSecs );

								if ( AtSpeedTimeSecs >= 300 )
								{
									TimeStatusFlags |= ATSPEED_TIME;
									TestState = 0;
									bLoop = TRUE;
									SaveTransactionStart ( ); // treat as a seperate transaction to avoid duplication
									Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
									      "Idle Time = %d;  Speed Time = %d;  Run Time = %d  *AT SPEED DONE*\n",
									      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );
									LogLastTransaction ( );
								}

								// check 600 seconds cumulative time
								if ( (TimeStatusFlags & CUMULATIVE_TIME) != CUMULATIVE_TIME &&
								     RunTimeSecs >= 600 )
								{
									TimeStatusFlags |= CUMULATIVE_TIME;
									SaveTransactionStart ( ); // treat as a seperate transaction to avoid duplication
									Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
									      "Idle Time = %d;  Speed Time = %d;  Run Time = %d  *RUN TIME DONE*\n",
									      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );
									LogLastTransaction ( );
								}
							}
							else
							{
								TestState = 0;
								bLoop = TRUE;
							}
						}
						break;

						default:     // 'idle' and 'at speed' times done
						{

						}
						break;
					} // end switch ( TestState )
				} // end while ( bLoop )
			} // end if (bDisplayCARBTimers == TRUE)


			//-------------------------------------------
			// Check for DTCs
			//-------------------------------------------
			if ( CheckDTCsTimerSecs <= 0 )
			{
				// initially clear the DTC flag
				SubTestStatusFlags &= (~SUB_TEST_DTC);

				eRetVal = IsDTCPending ( (REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_ALLOW_NO_RESPONSE) );
				// log the transaction
				LogLastTransaction ( );
				if ( eRetVal == PASS )
				{
					// flag the pending DTCs
					SubTestStatusFlags |= SUB_TEST_DTC;
				}

				eRetVal = IsDTCStored ( (REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_ALLOW_NO_RESPONSE) );
				// log the transaction
				LogLastTransaction ( );
				if ( eRetVal == PASS )
				{
					// flag the stored DTCs
					SubTestStatusFlags |= SUB_TEST_DTC;
				}

#ifdef _DEBUG
				CheckDTCsTimerSecs = 2;
#else
				CheckDTCsTimerSecs = 30;
#endif
			}


			// update on-screen test status
			if ( gbTestSubsectionFailed )
			{
				// some function logged a FAILURE
				SubTestStatusFlags |= SUB_TEST_FAIL;
			}

			if ( SubTestStatusFlags != LastTestStatusFlags )
			{
				UpdateTestStatus ( SubTestStatusFlags );

				LastTestStatusFlags = SubTestStatusFlags;
			}

			//-------------------------------------------
			// Check if test is complete
			//-------------------------------------------
			EcuDoneCount = 0;
			for ( EcuIdx=0;
			      EcuIdx < gNumOfECUs;
			      EcuIdx++ )
			{
				if ( (gstResponse[EcuIdx].StatusFlags & ECUDONE) == 0 )
				{
					if ( (gstResponse[EcuIdx].StatusFlags & IMREADYDONE) != 0 &&
					     (gstResponse[EcuIdx].StatusFlags & IPDDONE)     != 0 &&
					     (gstResponse[EcuIdx].StatusFlags & IUMPRDONE)   != 0 &&
					     (gstResponse[EcuIdx].StatusFlags & SMADDONE)    != 0 )
					{
						gstResponse[EcuIdx].StatusFlags |= ECUDONE;
						if ( EcuIdx >= EcuPageOffset &&
						     EcuIdx < (EcuPageOffset+MAX_ECUS_PER_PAGE) )
						{
							SetFieldText ( ECU_STATUS_INDEX+(EcuIdx-EcuPageOffset), "Done" );
						}
						EcuDoneCount++;
					}
				}
				else
				{
					EcuDoneCount++;
				}
			}

			if ( EcuDoneCount == gNumOfECUs )
			{
				if ( (SubTestStatusFlags & SUB_TEST_FAIL) == SUB_TEST_FAIL )
				{
					eRetCode =  FAIL;
				}
				else
				{
					eRetCode =  PASS;
					break;  // leave infinite for loop
				}
			}


			if ( eRetCode != PASS )
			{
				break;  // leave infinite for loop
			}

			// Toggle 1 sec Flag
			b1Sec = ~b1Sec;
		}  // end if time since last check greater than 1 sec
	} // end for (;;)

	gotoxy ( 0, PRESS_ESC_ROW+3 );
	gbSuspendLogOutput = FALSE;
	gbSuspendScreenOutput = FALSE;
	if ( eRetCode == ABORT )
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Test 11 aborted by user\n\n" );
	}
	else if ( eRetCode == FAIL &&
	          (SubTestStatusFlags & SUB_TEST_FAIL) != SUB_TEST_FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Test 11 failed by user\n\n" );
	}

	PrintECUData ( );

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

	if ( eRetCode == ABORT )
	{
		ABORT_RETURN;
	}
	return eRetCode;
}


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

	char DTCstring[9];
	WORD index;
	WORD pindex;


	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "On exit from Test 11.2 the following were the conditions:\n\n" );

	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
		      "ECU ID: %02X    Status: %s\n",
		      GetEcuId ( EcuIdx ),
		      (gstResponse[EcuIdx].StatusFlags & ECUDONE) ? "Done" : "    " );


		// write IM Status
		pindex = IMSTATUS_ELEMENT_INDEX;
		Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
		      "%s\n",
		      _string_elements_imr[pindex++] );
		for ( index = 0;
		      index < IMREADINESS_SIZE;
		      index++, pindex++ )
		{
			// skip "I/M STATUS (continued)"
			if ( pindex == IMSTATUSCONT_ELEMENT_INDEX )
			{
				pindex++;
			}
			Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
			      "%-19s  %s\n",
			      _string_elements_imr[pindex].szLabel,
			      szIM_Status[Test11IMStatus[EcuIdx][index]] );
		}

		// write In-Use Performance Data (OBDCOND and IGNCNTR) Status
		pindex = IPDSTATUS_ELEMENT_INDEX;
		Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
		      "%-19s  Initial     Current\n", _string_elements_ipt1[pindex++].szLabel );

		if ( gstResponse[EcuIdx].bIPDSupported == FALSE )
		{
			// write rate based counter
			for ( index = 0;
			      index < 2;
			      index++, pindex++ )
			{
				Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
				      "%-19s  Not Supported\n",
				      _string_elements_ipt1[pindex].szLabel );
			}
		}
		else
		{
			// write rate based counter
			for ( index = 0;
			      index < 2;
			      index++, pindex++ )
			{
				Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
				      "%-19s  %-7d     %-7d\n",
				      _string_elements_ipt1[pindex].szLabel,
				      gstResponse[EcuIdx].IPD_Test10_10[index],
				      gstResponse[EcuIdx].IPD_Test11_CurrentDisplayData[index] );
			}
		}

		// write IUMPR Data Status
		Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
		      "%-19s  N    D      N    D\n",
		      "IUMPR DATA " );

		if ( gstResponse[EcuIdx].bDTCIUMPRSupported == FALSE )
		{
			Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
			      "%-19s  Not Supported\n",
			      "IUMPR" );
		}
		else
		{
			for ( index = 0;
			      index < (unsigned)(gstResponse[EcuIdx].DTCIUMPR_RGTotalCount);
			      index++ )
			{
				sprintf_s ( DTCstring, sizeof ( DTCstring ),
				            "%c%02X%02X %02X",
				            DTCTypeCharacter[(gstResponse[EcuIdx].DTCIUMPR_Test10_10[index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				            gstResponse[EcuIdx].DTCIUMPR_Test10_10[index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				            gstResponse[EcuIdx].DTCIUMPR_Test10_10[index].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
				            gstResponse[EcuIdx].DTCIUMPR_Test10_10[index].Record.Dtc.LowByte );                                 // Failure Type Byte

				Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
				      "%-19s  %-4d %-4d   %-4d %-4d\n",
				      DTCstring,
				      gstResponse[EcuIdx].DTCIUMPR_Test10_10[index].CompCounts,
				      gstResponse[EcuIdx].DTCIUMPR_Test10_10[index].CondCounts,
				      gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[index].CompCounts,
				      gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[index].CondCounts );
			}  // end for ( index )
		}  // end if ( gstResponse[EcuIdx].bDTCIUMPRSupported )

	// write SMAD Data Status
		Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
		      "%-19s  MAN  SMAR   MAN  SMAR\n",
		      "SMAD DATA" );

		if ( gstResponse[EcuIdx].bDTCSMADSupported == FALSE )
		{
			Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
			      "%-19s  Not Supported\n",
			      "SMAD" );
		}
		else
		{
			for ( index = 0;
			      index < (unsigned)(gstResponse[EcuIdx].DTCSMAD_RGTotalCount);
			      index++ )
			{
				sprintf_s ( DTCstring, sizeof ( DTCstring ),
				            "%c%02X%02X %02X",
				            DTCTypeCharacter[(gstResponse[EcuIdx].DTCSMAD_Test10_10[index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				            gstResponse[EcuIdx].DTCSMAD_Test10_10[index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				            gstResponse[EcuIdx].DTCSMAD_Test10_10[index].Record.Dtc.MidByte,                                   // 4th and 5th characters (0-F)
				            gstResponse[EcuIdx].DTCSMAD_Test10_10[index].Record.Dtc.LowByte );                                 // Failure Type Byte

				Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
				      "%-19s  %-4d %-.3f  %-4d %-.3f\n",
				      DTCstring,
				      gstResponse[EcuIdx].DTCSMAD_Test10_10[index].NumCounts,
				      gstResponse[EcuIdx].DTCSMAD_Test10_10[index].ActRatio,
				      gstResponse[EcuIdx].DTCSMAD_Test11_CurrentDisplayData[index].NumCounts,
				      gstResponse[EcuIdx].DTCSMAD_Test11_CurrentDisplayData[index].ActRatio );
			}  // end for ( index )
		}  // end if ( gstResponse[EcuIdx].bDTCSMADSupported )

		Log ( BLANK, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "\n" );
	}  // end for ( EcuIdx )

	Log ( BLANK, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "\n" );

	// move to end of file
//	fseek ( gLogFileHandle, 0, SEEK_END );
}


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

	STATUS   RetCode = PASS;


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

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

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

				if ( pINF->INFLSB == (0xF879 & 0x00FF) )
				{
					// Validate MAD data
					if ( VerifyINF79Data ( EcuIdx ) == FAIL )
					{
						RetCode = FAIL;
					}
				}
			}
			else
			{
				// No $F879 data
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  INF $F879 (MAD) no data\n",
				      GetEcuId ( EcuIdx ) );
				RetCode = FAIL;
			}
		}
	}  // end  if INFSupported

	return RetCode;
}


/*******************************************************************************
**
**  Function:  AllocateMemoryForTest11_PerformanceCounters
**
**  Purpose:
**
*******************************************************************************/
void AllocateMemoryForTest11_PerformanceCounters ( void )
{
	BYTE          EcuIdx;
	unsigned int  IMReadinessIndex;


	// Allocate memory for saving PID $F501
	if ( (pTest11_PIDF501 = (PID *) malloc ( sizeof ( PID ) * gNumOfECUsForAlloc )) == NULL )
	{
		Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
		      "Unable to allocate memory (%d bytes) for pTest11_PIDF501.",
		      sizeof ( PID ) * gNumOfECUsForAlloc );
		exit ( FAIL );
	}
	memset ( pTest11_PIDF501,
	         0x00,
	         sizeof ( PID ) * gNumOfECUsForAlloc );


//	if ( (pDTCIUMPR_Test11_5[EcuIdx] = (DTCIUMPR **) malloc ( sizeof ( DTCIUMPR *) * gNumOfECUsForAlloc )) == NULL )
//	{
//		Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
//		      "Unable to allocate memory (%d bytes) for pDTCIUMPR_Test11_5.",
//		      sizeof ( IPT ) * gNumOfECUsForAlloc );
//		exit ( FAIL );
//	}
//	memset ( pDTCIUMPR_Test11_5,
//	         0x00,
//	         sizeof ( IPT ) * gNumOfECUsForAlloc );
//
//	if ( (pDTCIUMPR_Test11_11 = (DTCIUMPR **) malloc ( sizeof ( DTCIUMPR *) * gNumOfECUsForAlloc )) == NULL )
//	{
//		Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
//		      "Unable to allocate memory (%d bytes) for pDTCIUMPR_Test11_11.",
//		      sizeof ( IPT ) * gNumOfECUsForAlloc );
//		exit ( FAIL );
//	}
//	memset ( pDTCIUMPR_Test11_11,
//	         0x00,
//	         sizeof ( IPT ) * gNumOfECUsForAlloc );
//
//	if ( (pTest11CurrentDisplayData = (DTCIUMPR **) malloc ( sizeof ( DTCIUMPR *) * gNumOfECUsForAlloc )) == NULL )
//	{
//		Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
//		      "Unable to allocate memory (%d bytes) for pTest11CurrentDisplayData.",
//		      sizeof ( IPT ) * gNumOfECUsForAlloc );
//		exit ( FAIL );
//	}
//	memset ( pTest11CurrentDisplayData,
//	         0x00,
//	         sizeof ( IPT ) * gNumOfECUsForAlloc );
//
//	for ( EcuIdx = 0;
//	      EcuIdx < gNumOfECUs;
//	      EcuIdx++ )
//	{
//		if ( (pDTCIUMPR_Test11_5[EcuIdx] = (DTCIUMPR *) malloc ( sizeof ( DTCIUMPR ) * MAX_DTCIUMPR_COUNT )) == NULL )
//		{
//			Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
//			      "Unable to allocate memory (%d bytes) for pDTCIUMPR_Test11_5.",
//			      sizeof ( IPT ) * gNumOfECUsForAlloc );
//			exit ( FAIL );
//		}
//		memset ( pDTCIUMPR_Test11_5,
//		         0x00,
//		         sizeof ( IPT ) * gNumOfECUsForAlloc );
//
//		if ( (pDTCIUMPR_Test11_11[EcuIdx] = (DTCIUMPR *) malloc ( sizeof ( DTCIUMPR ) * MAX_DTCIUMPR_COUNT )) == NULL )
//		{
//			Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
//			      "Unable to allocate memory (%d bytes) for pDTCIUMPR_Test11_11.",
//			      sizeof ( IPT ) * gNumOfECUsForAlloc );
//			exit ( FAIL );
//		}
//		memset ( pDTCIUMPR_Test11_11,
//		         0x00,
//		         sizeof ( IPT ) * gNumOfECUsForAlloc );
//
//		if ( (pTest11CurrentDisplayData[EcuIdx] = (DTCIUMPR *) malloc ( sizeof ( DTCIUMPR ) * MAX_DTCIUMPR_COUNT )) == NULL )
//		{
//			Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
//			      "Unable to allocate memory (%d bytes) for pTest11CurrentDisplayData.",
//			      sizeof ( IPT ) * gNumOfECUsForAlloc );
//			exit ( FAIL );
//		}
//		memset ( pTest11CurrentDisplayData,
//		         0x00,
//		         sizeof ( IPT ) * gNumOfECUsForAlloc );
//	}


//	if ( (Test11IMStatus = (enum IM_Status **) malloc( sizeof ( enum IM_Status ) * gNumOfECUsForAlloc )) == NULL )
//	{
//		Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
//		      "Unable to allocate memory (%d bytes) for Test11IMStatus.",
//		      sizeof ( enum IM_Status ) * gNumOfECUsForAlloc );
//		exit ( FAIL );
//	}
//
//	for ( EcuIdx = 0;
//	      EcuIdx < gNumOfECUs;
//	      EcuIdx++ )
//	{
//		if ( (Test11IMStatus[EcuIdx] = (enum IM_Status *) malloc ( sizeof ( enum IM_Status ) * IMREADINESS_SIZE )) == NULL )
//		{
//			Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
//			      "Unable to allocate memory (%d bytes) for Test11IMStatus[%d].",
//			      sizeof ( enum IM_Status ) * IMREADINESS_SIZE,
//			      EcuIdx );
//			exit ( FAIL );
//		}
//	}

	for ( IMReadinessIndex = 0;
	      IMReadinessIndex < IMREADINESS_SIZE;
	      IMReadinessIndex++ )
	{
		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUsForAlloc;
		      EcuIdx++ )
		{
			Test11IMStatus[EcuIdx][IMReadinessIndex] = NOTSUPPORTED;
		}
		eCurrentIMStatus[IMReadinessIndex] = INVALID;
	}
}


/*******************************************************************************
**
**  Function:  FreeMemoryForTest11_PerformanceCounters
**
**  Purpose:
**
*******************************************************************************/
void   FreeMemoryForTest11_PerformanceCounters ( void )
{
	BYTE        EcuIdx;
//	extern IPT *pLast_Ipt;


//	free ( pLast_Ipt );

	free ( pTest11_PIDF501 );

//	free ( pDTCIUMPR_Test11_5 );
//
//	free ( pDTCIUMPR_Test11_11 );
//
//	free ( pTest11CurrentDisplayData );

	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUsForAlloc;
	      EcuIdx++ )
	{
//		free ( Test11IMStatus[EcuIdx] );

		// free informantion leftover from Test 10.10
//		free ( gstResponse[EcuIdx].DTCIUMPR_Test10_10 );
		free ( gstResponse[EcuIdx].pTest10_10_FEOCNTR );
//		free ( gstResponse[EcuIdx].pIPT );
	}

//	free ( Test11IMStatus );
}
