/*******************************************************************************
********************************************************************************
**
**  Copyright(c) 2022, Alliance for Automotive Innovation
**  Used only under license from the Alliance for Automotive Innovation. All Rights Reserved.
**
**  Project:  J1699-5
**  FileName: VerifyDTCFFData.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 <windows.h>  // Windows API declarations
#include "j2534.h"    // j1699 project j2534 declarations
#include "j1699.h"    // j1699 project general declarations

#include "PIDTable.h" // look-up table for PID name, scaling, offset , etc


// Funtion prototypes
STATUS RequestFFIDData        ( BOOL *pbFFSupOnPendFault );
STATUS VerifyFFDTCinDTCList   ( void );
STATUS VerifyFFDTCinFFDTCList ( void );
STATUS DisplayFFData          ( BYTE  EcuIdx );

extern void VerifyPidData   ( BYTE EcuIdx, PID *pPid );


// Variable prototypes
extern unsigned char DTCTypeCharacter[];
extern unsigned int  gVariablePIDSize;

/*******************************************************************************
**
**  Function:  VerifyDTCFFData
**
**  Purpose:   Verify DTC Freeze Frame support and data
**
*******************************************************************************/
STATUS VerifyDTCFFData ( void )
{
	REQ_MSG   stReqMsg;
	BYTE      EcuIdx;
	STATUS    eRetCode = PASS;

	unsigned short  DTCIdx;
	unsigned short  DTCSize;
	DTCASVR        *pDTC;
	
	BYTE            EcuIdx2;
	unsigned short  FFDTCIdx;
	unsigned short  FrameIdx;
	DTCASTR        *pFF;

	BYTE  FFDTCList[MAX_RESP_BUF_SIZE];  // array of the different Freeze Frame DTCs
	BYTE  FFDTCListSize;                 // the size (in bytes) of FFDTCList

	unsigned long  InitialFailureCount = 0;


	// Initialize FFDTC list
	memset ( &FFDTCList[0],
	         0,
	         MAX_RESP_BUF_SIZE );

	FFDTCListSize = 0;

	InitialFailureCount = GetFailureCount ( );

	// Request SID $19 LEV $04 support data
	if ( gbPhysicalAddressing == FALSE )
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Request FFDTCs (SID $19 LEV $04) Functionally Addressed\n" );
	}
	else
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Request FFDTCs (SID $19 LEV $04) Physically Addressed\n" );
	}


	// Check for freeze frame DTCs
	// Request SID $19 LEV $04
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		gstResponse[EcuIdx].pFFDTCList->Size = 0;

		if ( (DTCSize = gstResponse[EcuIdx].pDTCList->Size) != 0 )
		{
			for ( DTCIdx = 0;
			      DTCIdx < DTCSize;
			      DTCIdx += sizeof ( DTCASVR ) )
			{
				pDTC = (DTCASVR *) &gstResponse[EcuIdx].pDTCList->Dtc[DTCIdx];

				// if functional addressing, only request DTC once
				if ( gbPhysicalAddressing == FALSE )
				{
					for ( FFDTCIdx = 0;
					      FFDTCIdx < FFDTCListSize;
					      FFDTCIdx += sizeof ( DTCASTR ) )
					{
						pFF = (DTCASTR*)&FFDTCList[FFDTCIdx];

						// is the DTC has already in the list
						if ( pFF->Dtc.HighByte == pDTC->Record.Dtc.HighByte &&
						     pFF->Dtc.MidByte  == pDTC->Record.Dtc.MidByte  &&
						     pFF->Dtc.LowByte  == pDTC->Record.Dtc.LowByte )
						{
							break;
						}
					}

					// if the DTC hasn't been requested yet, add it to the list
					if ( FFDTCIdx >= FFDTCListSize )
					{
						memcpy ( &FFDTCList[FFDTCListSize],
						         &pDTC->Record.Dtc,
						         sizeof ( DTCASTR ) );
						FFDTCListSize += sizeof ( DTCASTR );
					}
					// else the DTC has already been requested, skip it
					else
					{
						continue;
					}
				}


				for ( FrameIdx = 0;
				      FrameIdx < 2;
				      FrameIdx++ )
				{
					if ( gbPhysicalAddressing == TRUE )
					{
						gbAllECUResetCompleted = FALSE;
					}
					// Request SID $19 LEV $04 data
					stReqMsg.SID     = 0x19;
					stReqMsg.NumIds  = 5;
					stReqMsg.u.ID[0] = 0x04;  // LEV: ReportDTCSnapshotRecordByDTCNumber
					stReqMsg.u.ID[1] = pDTC->Record.Dtc.HighByte;      // DTCMaskRecord.DTCHighByte
					stReqMsg.u.ID[2] = pDTC->Record.Dtc.MidByte;       // DTCMaskRecord.DTCMiddleByte
					stReqMsg.u.ID[3] = pDTC->Record.Dtc.LowByte;       // DTCMaskRecord.DTCLowByte
					stReqMsg.u.ID[4] = (FrameIdx == 0 ? 0x00 : 0xF0);  // DTCMaskRecord.DTCSnapshotRecordNumber
					                                                   // (first (00) or most recent (F0) freeze frame )
					if ( (gbPhysicalAddressing == TRUE &&
					      RequestSID_PhysicallyAddressed_Single ( &stReqMsg, REQ_MSG_ALLOW_NO_RESPONSE, gstResponse[EcuIdx].ReqId ) != PASS) ||
					     (gbPhysicalAddressing == FALSE &&
					      RequestSID ( &stReqMsg, REQ_MSG_ALLOW_NO_RESPONSE ) != PASS) )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "SID $19 LEV $04 request error\n" );
						eRetCode = FAIL;
						continue;
					}


					for ( EcuIdx2 = (gbPhysicalAddressing == TRUE ? EcuIdx : 0);
					      EcuIdx2 < (gbPhysicalAddressing == TRUE ? (EcuIdx+1) : gNumOfECUs);
					      EcuIdx2++ )
					{
						// Skip if no FFID data or no DTCs stored
						if ( gstResponse[EcuIdx2].FFSize != 0 &&
						     (gstResponse[EcuIdx2].FF[0] != 0 ||
						      gstResponse[EcuIdx2].FF[1] != 0) )
						{
							pFF = (DTCASTR*)&gstResponse[EcuIdx2].FF[0];

							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  SID $19 LEV $04  FFDTC %c%02X%02X %02X %s frame stored\n",
							      GetEcuId ( EcuIdx2 ),
							      DTCTypeCharacter[(pFF->Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
							      pFF->Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
							      pFF->Dtc.MidByte,                                   // 4th and 5th (0-F) characters
							      pFF->Dtc.LowByte,                                   // 6th and 7th (0-F) characters
							      (FrameIdx == 0 ? "first" : "most recent") );

							// if this is the first frame request, save the DTC
							if ( FrameIdx == 0 )
							{
								memcpy ( &gstResponse[EcuIdx2].pFFDTCList->Dtc[gstResponse[EcuIdx2].pFFDTCList->Size],
								         &gstResponse[EcuIdx2].FF[0],
								         sizeof ( DTC ) );
								gstResponse[EcuIdx2].pFFDTCList->Dtc[gstResponse[EcuIdx2].pFFDTCList->Size++] = pFF->Dtc.HighByte;
								gstResponse[EcuIdx2].pFFDTCList->Dtc[gstResponse[EcuIdx2].pFFDTCList->Size++] = pFF->Dtc.MidByte;
								gstResponse[EcuIdx2].pFFDTCList->Dtc[gstResponse[EcuIdx2].pFFDTCList->Size++] = pFF->Dtc.LowByte;
							}

							DisplayFFData ( EcuIdx2 );
						}  // end if ( Freeze Frame Data )
						else
						{
							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  SID $19 LEV $04  FFDTC %c%02X%02X %02X no data reported\n",
							      GetEcuId ( EcuIdx2 ),
							      DTCTypeCharacter[(pDTC->Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
							      pDTC->Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
							      pDTC->Record.Dtc.MidByte,                                   // 4th and 5th (0-F) characters
							      pDTC->Record.Dtc.LowByte  );                                // 6th and 7th (0-F) characters
						}
					}  // end for ( EcuIdx2 )
				}  // end for ( FrameIdx )
			}  // end for ( DTCIdx )
		}  // end if ( DTCSize != 0 )  (ECU had pending DTC(s))
	}  // end for ( EcuIdx )


	if ( InitialFailureCount != GetFailureCount ( ) )
	{
		// There could have been early/late responses that weren't treated as FAIL
		// or the test failed earlier
		return FAIL;
	}
	else
	{
		return eRetCode;
	}

}


/*******************************************************************************
**
**  Function: RequestFFIDData
**
**  Purpose: Verify that SID $19 LEV $03 returns stored DTC.
**           Assuming SID $19 LEV $03 was supported by the controller,
**           data should be returned.
**
*******************************************************************************/
STATUS RequestFFIDData ( BOOL          *pbFFSupOnPendFault )
{
	REQ_MSG   stReqMsg;
	BYTE      EcuIdx;
	STATUS    eRetCode = PASS;

	BYTE      FFDTCCount = 0;         // Count of ECUs which respond to Freeze Frame Request with data
	BYTE      FFDTCSupportCount = 0;  // Count of ECUs which respond to Freeze Frame Request
	DTCASSR  *pFF;


	if ( gbPhysicalAddressing == FALSE )
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Request FFIDs (SID $19 LEV $03) Functionally Addressed\n" );
	}
	else
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Request FFIDs (SID $19 LEV $03) Physically Addressed\n" );
	}


	// Request SID $19 LEV $03 data
	stReqMsg.SID     = 0x19;
	stReqMsg.NumIds  = 1;
	stReqMsg.u.ID[0] = 0x03;  // Subfunction (LEV): ReportSnapshotIdentification
	gbIgnoreUnsupported = TRUE;
	if ( (gbPhysicalAddressing == FALSE &&
	      RequestSID ( &stReqMsg, REQ_MSG_ALLOW_NO_RESPONSE ) != PASS) ||
	     (gbPhysicalAddressing == TRUE  &&
	      RequestSID_PhysicallyAddressed_All ( &stReqMsg, REQ_MSG_ALLOW_NO_RESPONSE ) != PASS) )
	{
		gbIgnoreUnsupported = FALSE;
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "SID $19 LEV $03 request error\n" );
		return FAIL;
	}
	gbIgnoreUnsupported = FALSE;

	// Check for freeze frame DTCs
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		// Clear Freeze Frame Array entry for this ECU
//		pFreezeFrameDTC[EcuIdx] = 0;

		// If there was a response by this ECU, Increment the count of ECUs which support Freeze Frame DTC
		if ( gstResponse[EcuIdx].bResponseReceived == TRUE )
		{
			FFDTCSupportCount++;
		}

		// Skip if no FFID data or no DTCs stored
		if ( gstResponse[EcuIdx].FFSize != 0 &&
		     (gstResponse[EcuIdx].FF[0] != 0 ||
		      gstResponse[EcuIdx].FF[1] != 0) )
		{
//			// Save the DTC
//			memcpy ( pFreezeFrameDTC,
//			         &gstResponse[EcuIdx].FF,
//			         gstResponse[EcuIdx].FFSize );

			pFF = (DTCASSR*)&gstResponse[EcuIdx].FF[0];
			FFDTCCount++;

			// Check if a DTC is not supposed to be present
			if ( gbDTCStored     == FALSE &&
			     gbDTCPending    == FALSE &&
			     gbDTCHistorical == FALSE )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  SID $19 LEV $03  FFDTC %c%02X%02X %02X stored\n",
				      GetEcuId ( EcuIdx ),
				      DTCTypeCharacter[(pFF->Dtc.HighByte & 0xC0 ) >> 6],  // 1st character (P, C, B, U)
				      pFF->Dtc.HighByte & 0x3F,                            // 2nd (0,1,2,3) and 3rd (0-F) characters
				      pFF->Dtc.MidByte,                                    // 4th and 5th (0-F) characters
				      pFF->Dtc.LowByte );                                  // 6th and 7th (0-F) characters

				eRetCode = FAIL;
			}

			else if ( gbDTCPending == TRUE  &&
			          gbDTCStored  == FALSE )
			{
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  SID $19 LEV $03  FFDTC %c%02X%02X %02X stored on pending fault\n",
				      GetEcuId ( EcuIdx ),
				      DTCTypeCharacter[(pFF->Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				      pFF->Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				      pFF->Dtc.MidByte,                                    // 4th and 5th (0-F) characters
				      pFF->Dtc.LowByte );                                  // 6th and 7th (0-F) characters
				*pbFFSupOnPendFault = TRUE;
			}
			else
			{
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  SID $19 LEV $03 FFDTC %c%02X%02X %02X stored\n",
				      GetEcuId ( EcuIdx ),
				      DTCTypeCharacter[(pFF->Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				      pFF->Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				      pFF->Dtc.MidByte,                                    // 4th and 5th (0-F) characters
				      pFF->Dtc.LowByte );                                  // 6th and 7th (0-F) characters
				*pbFFSupOnPendFault = TRUE;
			}
		}
		else
		{
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %X  SID $19 LEV $03 indicates no FFDTC stored\n",
			      GetEcuId ( EcuIdx ) );
		}
	}

	// Test 5.11.1 - If all ECUs report Freeze Frame DTCs stored, it is a Failure
	if ( geTestPhase == eTestNoDTC && gTestSubsection == 11 &&
	     FFDTCCount != 0 &&
	     FFDTCCount == FFDTCSupportCount )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "All ECUs reported Freeze Frame DTCs stored\n" );
		eRetCode = FAIL;
	}

	return eRetCode;
}


/*******************************************************************************
**
**  Function:  VerifyFFDTCinDTCList
**
**  Purpose:   Verify the DTC in the freeze frame is in the list of stored DTCs.
**
*******************************************************************************/
STATUS VerifyFFDTCinDTCList ( void )
{
	STATUS         eRetCode = PASS;

	BYTE           FFEcuIdx;
	unsigned long  FFDTCIdx;
	unsigned long  FFDTC;
	unsigned long  FFDTCCount = 0;
	DTCASSR  *pFFDTC;

	BYTE           DTCEcuIdx;
	unsigned long  DTCIdx;
	unsigned long  StrdDTC;
	unsigned long  StrdDTCCount = 0;
	DTCASVR  *pDTC;

	unsigned long  MtchdDTCCount = 0;
	BOOL           bMtchdDTC = FALSE;


	// Count all DTCs for all ECUs
	for ( DTCEcuIdx = 0;
	      DTCEcuIdx < gNumOfECUs;
	      DTCEcuIdx++ )
	{
		// Count all Stored DTCs
		if ( gstResponse[DTCEcuIdx].pDTCList->Size != 0 )
		{
			for ( DTCIdx = 0;
			      DTCIdx < gstResponse[DTCEcuIdx].pDTCList->Size;
			      DTCIdx += sizeof ( DTCASVR ) )
			{
				pDTC = (DTCASVR *)(&gstResponse[DTCEcuIdx].pDTCList->Dtc[DTCIdx]);
				StrdDTC = (pDTC->Record.Dtc.HighByte << 16) + (pDTC->Record.Dtc.MidByte << 8) + pDTC->Record.Dtc.LowByte;
				if ( StrdDTC != 0x0000 )
				{
					StrdDTCCount++;
				}
			}
		}

		// Count all Freeze Frames
		for ( FFDTCIdx = 0;
		      FFDTCIdx < gstResponse[DTCEcuIdx].FFSize;
		      FFDTCIdx += sizeof ( DTCASSR ) )
		{
			pFFDTC = (DTCASSR *)(&gstResponse[DTCEcuIdx].FF[0]);
			FFDTC = (pFFDTC->Dtc.HighByte << 16) + (pFFDTC->Dtc.MidByte << 8) + pFFDTC->Dtc.LowByte;
			if ( FFDTC != 0x0000  )
			{
				FFDTCCount++;
			}
		}
	}


	// Verify each Freeze DTCs is in the list of Confirmed or Pending DTCs
	for ( FFEcuIdx = 0;
	      FFEcuIdx < gNumOfECUs;
	      FFEcuIdx++ )
	{
		for ( FFDTCIdx = 0;
		      FFDTCIdx < gstResponse[FFEcuIdx].FFSize;
		      FFDTCIdx += sizeof ( DTCASSR ) )
		{
			bMtchdDTC = FALSE;

			pFFDTC = (DTCASSR *)(&gstResponse[FFEcuIdx].FF[FFDTCIdx]);
			FFDTC = (pFFDTC->Dtc.HighByte << 16) + (pFFDTC->Dtc.MidByte << 8) + pFFDTC->Dtc.LowByte;
			if ( FFDTC != 0x0000 )
			{
				for ( DTCEcuIdx = 0;
				      DTCEcuIdx < gNumOfECUs;
				      DTCEcuIdx++ )
				{
					for ( DTCIdx = 0;
					      DTCIdx < gstResponse[DTCEcuIdx].pDTCList->Size;
					      DTCIdx += sizeof ( DTCASVR ) )
					{
						pDTC = (DTCASVR*)(&gstResponse[DTCEcuIdx].pDTCList->Dtc[DTCIdx]);
						StrdDTC = (pDTC->Record.Dtc.HighByte << 16) + (pDTC->Record.Dtc.MidByte << 8) + pDTC->Record.Dtc.LowByte;
						if ( StrdDTC != 0x0000 )
						{
							if ( FFDTC == StrdDTC )
							{
								bMtchdDTC = TRUE;
								MtchdDTCCount++;
								break;  // leave DTC index for loop
							}
						}
					}  // end for DTC Index < DTC Size
	
					if ( bMtchdDTC == TRUE )
					{
						break;  // leave DTC ECU for loop
					}
				}  // end for DTC ECU
	
				if ( !bMtchdDTC )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X  Freeze Frame DTC %c%02X%02X %02X not found in list of stored or pending DTCs on any ECU\n",
					      GetEcuId (FFEcuIdx),
					      DTCTypeCharacter[(pFFDTC->Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
					      pFFDTC->Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
					      pFFDTC->Dtc.MidByte,                                   // 4th and 5th (0-F) characters
					      pFFDTC->Dtc.LowByte );                                 // 6th and 7th (0-F) characters
					eRetCode = FAIL;
				}
			}  // end FFDTC != 0x0000
		}  // end for FFDTC index < FFSize
	}  // end for FF ECU


	if ( StrdDTCCount !=0  &&
	     MtchdDTCCount == 0 )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Stored DTC with no Freeze Frame DTC on any ECU\n" );
		eRetCode = FAIL;
	}


	return eRetCode;
}


/*******************************************************************************
**
**  Function:  VerifyFFDTCinFFDTCList
**
**  Purpose:   Verify the DTC in the freeze frame is in the list of previous freeze frame DTCs.
**
*******************************************************************************/
STATUS VerifyFFDTCinFFDTCList ( void )
{
	STATUS         eRetCode = PASS;

	BYTE           FFEcuIdx;
	unsigned long  FFDTCIdx;
	unsigned long  FFDTC;
	unsigned long  FFDTCCount = 0;
	DTCASSR   *pFFDTC;

	unsigned long  DTCIdx;
	unsigned long  StrdDTC;
	unsigned long  StrdDTCCount = 0;
	DTCASSR   *pDTC;

	unsigned long  MtchdDTCCount = 0;
	BOOL           bMtchdDTC = FALSE;


	// Verify each Freeze Frame DTC is in the list of previous freeze frame DTCs
	for ( FFEcuIdx = 0;
	      FFEcuIdx < gNumOfECUs;
	      FFEcuIdx++ )
	{
		for ( FFDTCIdx = 0;
		      FFDTCIdx < gstResponse[FFEcuIdx].FFSize;
		      FFDTCIdx += sizeof ( DTCASSR ) )
		{
			bMtchdDTC = FALSE;

			pFFDTC = (DTCASSR *)(&gstResponse[FFEcuIdx].FF[FFDTCIdx]);
			FFDTC = (pFFDTC->Dtc.HighByte << 16) + (pFFDTC->Dtc.MidByte << 8) + pFFDTC->Dtc.LowByte;
			if ( FFDTC != 0x000000 )
			{
				for ( DTCIdx = 0;
				      DTCIdx < gstResponse[FFEcuIdx].pFFDTCList->Size;
				      DTCIdx += sizeof ( DTCASSR ) )
				{
					pDTC = (DTCASSR*)(&gstResponse[FFEcuIdx].pFFDTCList->Dtc[DTCIdx]);
					StrdDTC = (pDTC->Dtc.HighByte << 16) + (pDTC->Dtc.MidByte << 8) + pDTC->Dtc.LowByte;
					if ( StrdDTC != 0x000000 )
					{
						if ( FFDTC == StrdDTC )
						{
							bMtchdDTC = TRUE;
							MtchdDTCCount++;
							break;  // leave DTC index for loop
						}
					}
				}  // end for DTC Index < DTC Size
	
				if ( bMtchdDTC == TRUE )
				{
					break;  // leave DTC ECU for loop
				}
			}  // end FFDTC != 0x0000
	
			if ( !bMtchdDTC && FFDTC != 0x000000 )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  Freeze Frame DTC %c%02X%02X %02X not found in list of previous Freeze Frame DTCs on this ECU\n",
				      GetEcuId (FFEcuIdx),
				      DTCTypeCharacter[(pFFDTC->Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				      pFFDTC->Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				      pFFDTC->Dtc.MidByte,                                   // 4th and 5th (0-F) characters
				      pFFDTC->Dtc.LowByte );                                 // 6th and 7th (0-F) characters
				eRetCode = FAIL;
			}

		}  // end for FFDTC index < FFSize
	}  // end for FF ECU

	return eRetCode;
}


/*******************************************************************************
**
**  Function:  DisplayFFData
**
**  Purpose:   Display the freeze frame data.
**
*******************************************************************************/
STATUS DisplayFFData ( BYTE  EcuIdx )
{
	STATUS           eRetCode = PASS;

	DTCASSR         *pFFDTC;
	unsigned long    FFDTC;

	WORD             FFPid;         // ID    of current PID in Freeze Frame data list
	unsigned long    FFPidIdx;      // index of current PID in Freeze Frame data List
	unsigned long    FFDataOffset;  // byte offset into Freeze Frame data list
	unsigned long    FFPidCnt;      // the number of PIDs in Freeze Frame data list
	PID             *pPid;          // pointer to current PID in Freeze Frame data list

	unsigned long    PidIdx;        // index into PidPrintTable
	unsigned short   PidSize = 0;   // size (in bytes) (from PidPrintTable) of current PID in Freeze Frame data list
	WORD             TableInc = 0;  // number of table entries for current PID

	// Verify Freeze Frame data is valid
	pFFDTC = (DTCASSR *)(&gstResponse[EcuIdx].FF[0]);
	FFDTC = (pFFDTC->Dtc.HighByte << 16) + (pFFDTC->Dtc.MidByte << 8) + pFFDTC->Dtc.LowByte;
	FFPidCnt = gstResponse[EcuIdx].FF[5];  // get the number of data items in freeze frame

	if ( FFDTC != 0x000000 )
	{
		for ( FFDataOffset = 0, FFPidIdx = 0;
		      FFPidIdx < FFPidCnt;
		      FFDataOffset += PidSize, FFPidIdx++ )
		{
			// point to next Freeze Frame DID data
			pPid = (PID *)(&gstResponse[EcuIdx].FF[sizeof ( DTCASTR ) + sizeof ( SNAPSHOTRECORD ) + FFDataOffset]);

			FFPid = (pPid->PIDMSB << 8) + pPid->PIDLSB;
			
			if ( FFPid == 0xF400 ||
			     FFPid == 0xF420 ||
			     FFPid == 0xF440 ||
			     FFPid == 0xF460 ||
			     FFPid == 0xF480 ||
			     FFPid == 0xF4A0 ||
			     FFPid == 0xF4C0 ||
			     FFPid == 0xF4E0 ||
			     FFPid == 0xF500 ||
			     FFPid == 0xF520 ||
			     FFPid == 0xF540 ||
			     FFPid == 0xF560 ||
			     FFPid == 0xF580 ||
			     FFPid == 0xF5A0 ||
			     FFPid == 0xF5C0 ||
			     FFPid == 0xF5E0 )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  Freeze Frame DTC %c%02X%02X %02X PID $%4X is a DID-Support DID (not allowed in Freeze Frame Data)\n",
				      GetEcuId (EcuIdx),
				      DTCTypeCharacter[(pFFDTC->Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				      pFFDTC->Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				      pFFDTC->Dtc.MidByte,                                   // 4th and 5th (0-F) characters
				      pFFDTC->Dtc.LowByte,                                   // 6th and 7th (0-F) characters
				      FFPid );
				PidSize = 2 + 4;
				eRetCode = FAIL;
				continue;
			}

			// Find DID in lookup table
			for ( PidIdx =0;
			      PidIdx < PidPrintTableSize;
			      PidIdx += TableInc )
			{
				if ( FFPid == PidPrintTable[PidIdx].PID )
				{
					gbIgnoreFailureLogging = TRUE;
					VerifyPidData ( EcuIdx, pPid );
					gbIgnoreFailureLogging = FALSE;
					if ( PidPrintTable[PidIdx].TotalByteCount == VARIABLESIZE )
					{
						PidSize = 2 + gVariablePIDSize;
					}
					else
					{
						PidSize = 2 + PidPrintTable[PidIdx].TotalByteCount;
					}
					break;
				}
				TableInc = PidPrintTable[PidIdx].ElementCount;
			}
			if ( PidIdx == PidPrintTableSize)
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  Freeze Frame DTC %c%02X%02X %02X PID $%4X is not defined\n",
				      GetEcuId (EcuIdx),
				      DTCTypeCharacter[(pFFDTC->Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				      pFFDTC->Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				      pFFDTC->Dtc.MidByte,                                   // 4th and 5th (0-F) characters
				      pFFDTC->Dtc.LowByte,                                   // 6th and 7th (0-F) characters
				      FFPid );
				// the size of the DID is unknown, unable to determine offset to next DID
				eRetCode = FAIL;
				break;
			}
		}  // end for (DTC Index < DTC Size_
	}  // end if ( FFDTC != 0x0000 )

	return eRetCode;
}
