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


// Function Prototypes
BOOL   IsDidInRequest ( REQ_MSG SidReqList, WORD DID );
STATUS VerifyReverseGroupDiagnosticSupport ( IDTYPE IdType );
STATUS VerifySingleAndGroupPidRequest ( IDTYPE IdType );

extern STATUS DetermineVariablePIDSize ( void );
extern STATUS VerifyIDSupportData ( IDTYPE IdType );


// Variables
BYTE gReverseOrderStateArray[MAX_ECUS];


/*******************************************************************************
**
**  Function:  VerifyReverseOrderSupport
**
**  Purpose:   Verify DID reverse order support
**
*******************************************************************************/
STATUS VerifyReverseOrderSupport ( IDTYPE IdType )
{
	REQ_MSG  stReqMsg;
	BYTE     EcuIdx;

	WORD     BaseId;
	WORD     IdIdx;
	WORD     FullId;
	BYTE     SupSize;
	DWORD    InitialFailureCount = 0;
	BOOL     bTestFailed = FALSE;


	// Save the original response data for comparison
	CopyIDSupport ( IdType );
;

	InitialFailureCount = GetFailureCount ( );

	// Ignore unsupported responses
	// Support info is cleared on receive of first response to reverse order request
	gbIgnoreUnsupported = TRUE;

	// reverse order request started
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		gReverseOrderStateArray[EcuIdx] = REVERSE_ORDER_REQUESTED;
	}

	if ( IdType == PIDREQUEST )
	{
		BaseId = 0xF400;
		SupSize = gstResponse[EcuIdx].PIDSupportSize;
	}
	else if ( IdType == PF5REQUEST )
	{
		BaseId = 0xF500;
		SupSize = gstResponse[EcuIdx].PF5SupportSize;
	}
	else if ( IdType == MIDREQUEST )
	{
		BaseId = 0xF600;
		SupSize = gstResponse[EcuIdx].MIDSupportSize;
	}
	else
	{
		BaseId = 0xF800;
		SupSize = gstResponse[EcuIdx].INFSupportSize;
	}

	// Request SID $22 DID support data in reverse order
	for ( IdIdx  = 0xE0;
	      IdIdx <= 0xE0;
	      IdIdx -= 0x20 )
	{
		FullId = BaseId+IdIdx;
		
		stReqMsg.SID      = 0x22;
		stReqMsg.NumIds   = 1;
		stReqMsg.u.DID[0] = FullId;
		if ( RequestSID ( &stReqMsg, REQ_MSG_NORMAL|REQ_MSG_IGNORE_NO_RESPONSE ) == FAIL )
		{
			// There must be a response to DID 0x00
			if ( IdIdx == 0x00 )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "No response to DID $%04X request\n",
				      FullId );
				bTestFailed = TRUE;
			}
			else
			{
				for ( EcuIdx = 0;
				      EcuIdx < gNumOfECUs;
				      EcuIdx++ )
				{
					// Check if DID is supported
					if ( SupSize != 0 &&
					     (gstResponse[EcuIdx].CompareSupport[(IdIdx - 1) >> 5].SupBits[((IdIdx - 1) >> 3) & 0x03] & (0x80 >> ((IdIdx - 1) & 0x07))) != 0x00 )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  No response to DID $%04X request\n",
						      GetEcuId ( EcuIdx ),
						      FullId );
						bTestFailed = TRUE;
					}
				}
			}
		}
		else if ( IdIdx != 0x00 )
		{
			for ( EcuIdx = 0;
			      EcuIdx < gNumOfECUs;
			      EcuIdx++ )
			{
				// Check if data received and DID is not supported
				if ( SupSize != 0 &&
				     (gstResponse[EcuIdx].CompareSupport[(IdIdx - 1) >> 5].SupBits[((IdIdx - 1) >> 3) & 0x03] & (0x80 >> ((IdIdx - 1) & 0x07))) == 0 )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X  Unsupported DID $%04X response\n",
					      GetEcuId ( EcuIdx ),
					      FullId );
					bTestFailed = TRUE;
				}
			}
		}
	}


	if ( VerifyIDSupportData ( IdType ) == FAIL )
	{
		bTestFailed = TRUE;
	}


	// No longer ignore unsupported response
	gbIgnoreUnsupported = FALSE;

	// reverse order request finished
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		gReverseOrderStateArray[EcuIdx] = NOT_REVERSE_ORDER;
	}

	// Verify that all SID $22 DID reverse/individual support data compares
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( CompareIDSupport ( EcuIdx, IdType, FALSE ) ==FAIL )
		{
			bTestFailed = TRUE;
		}
	}


	if ( IdType == PIDREQUEST )
	{
		// Determine size of PIDs $06, $07, $08, $09
		if ( DetermineVariablePIDSize ( ) != PASS )
		{
			bTestFailed = TRUE;
		}
	}

	// Warn the user that this could take a while
	Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
	      "Requesting DIDs $%04X - $%04X\n"
	      "Please wait (press any key to abort)...\n",
	      BaseId+0xFF,
	      BaseId );
	ClearKeyboardBuffer ( );

	// Request SID $22 DID data in reverse
	for ( IdIdx  = 0xFF;
	      IdIdx  > 0x00;
	      IdIdx -= 0x01 )
	{
		// Bypass DID request if it is a DID-Support DID (already done above)
		if ( (IdIdx & 0x001F) != 0 )
		{
			FullId = BaseId+IdIdx;
		
			printf ( "INFORMATION: Checking DID $%04X\r", FullId );
			stReqMsg.SID      = 0x22;
			stReqMsg.NumIds   = 1;
			stReqMsg.u.DID[0] = FullId;
			RequestSID ( &stReqMsg, REQ_MSG_NORMAL|REQ_MSG_IGNORE_NO_RESPONSE );
			for ( EcuIdx = 0;
			      EcuIdx < gNumOfECUs;
			      EcuIdx++ )
			{
				// Check if DID is supported
				if ( IsIDSupported ( EcuIdx, IdType, IdIdx ) == TRUE )
				{
					// if received a nak
					if ( gstResponse[EcuIdx].bNAKReceived )
					{
						// nak $31 (ID not supported)
						if ( gstResponse[EcuIdx].NAKCode == NAK_ID_NOT_SUPPORTED )
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  DID $%04X  supported but received NAK $31 (Unsupported DID request)! (Reverse Order)\n",
							      GetEcuId ( EcuIdx ),
							      FullId );
						}
						// other nak codes handles in RequestSID.Process___Msg
					}

					// Check if no data received
					if (  (IdType == PIDREQUEST && gstResponse[EcuIdx].PIDSize == 0) ||
					      (IdType == PF5REQUEST && gstResponse[EcuIdx].PIDSize == 0) ||
					      (IdType == MIDREQUEST && gstResponse[EcuIdx].MIDSize == 0) ||
					      (IdType == INFREQUEST && gstResponse[EcuIdx].INFSize == 0) )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  DID $%04X  no data! (Reverse Order)\n",
						      GetEcuId ( EcuIdx ),
						      FullId );
					}
				}  // end Did is supported
				// Otherwise DID is not supported
				else
				{
					// if received a nak
					if ( gstResponse[EcuIdx].bNAKReceived )
					{
						// nak $31 (ID not supported)
						if ( gstResponse[EcuIdx].NAKCode == NAK_ID_NOT_SUPPORTED )
						{
							// Only 01, 02 and 41 are allowed to NAK $31
							// when not supported for functional addressing
							if ( gbPhysicalAddressing != TRUE &&
							     FullId != 0xF401 &&
							     FullId != 0xF402 &&
							     FullId != 0xF441 )
							{
								Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
								     "ECU %X  DID $%04X  NAK $31 for functionally addressed request of unsupported DID! (Reverse Order)\n",
								      GetEcuId ( EcuIdx ),
								      FullId );
							}
						}
						// nak other than $31 is not allowed
						else
						{
							// all non-supported physically addressed requests
							// are required to NAK $31 when not supported
							if ( gbPhysicalAddressing == TRUE )

							{
								Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
								     "ECU %X  DID $%04X  NAK $31 was not received for request of unsupported PID! (Reverse Order)\n",
								      GetEcuId ( EcuIdx ),
								      FullId );
							}
						}
					}
					// otherwise a nak was not received
					else
					{
						// all non-supported physically addressed requests
						// are required to NAK $31 when not supported
						if ( gbPhysicalAddressing == TRUE )
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							     "ECU %X  DID $%04X  NAK $31 was not received for request of unsupported PID! (Reverse Order)\n",
							      GetEcuId ( EcuIdx ),
							      FullId );
						}
					}

					// Check if data received
					if ( (IdType == PIDREQUEST && gstResponse[EcuIdx].PIDSize != 0) ||
					     (IdType == PF5REQUEST && gstResponse[EcuIdx].PIDSize != 0) ||
					     (IdType == MIDREQUEST && gstResponse[EcuIdx].MIDSize != 0) ||
					     (IdType == INFREQUEST && gstResponse[EcuIdx].INFSize != 0) )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  Unsupported DID $%04X response! (Reverse Order)\n",
						      GetEcuId ( EcuIdx ),
						      FullId );
					}
				}  // end DID is not supported
			}  // end for ( EcuIdx )
		}  // end if not DID Support DID

		// Abort test if user presses a key
		if ( _kbhit ( ) )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "User abort\n" );
			ClearKeyboardBuffer ( );

			RestoreIDSupport ( IdType );

			return FAIL;
		}
	}  //end for ( IdIdx )
	
	if ( bTestFailed == TRUE )
	{
		RestoreIDSupport ( IdType );
	}

	printf ( "\n" );

	// Make sure reverse group DID support responses match reverse single DID support responses
	if ( VerifyReverseGroupDiagnosticSupport ( IdType ) == FAIL )
	{
		bTestFailed = TRUE;
	}

	// Make sure the single requested DIDs responses match group requested DIDs response.
	if ( VerifySingleAndGroupPidRequest ( IdType ) == FAIL )
	{
		bTestFailed = TRUE;
	}

	// Link active test to verify communication remained active for ALL protocols
	if ( VerifyECUCommunication ( ) != PASS )
	{
		bTestFailed = TRUE;
	}

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

}


/*******************************************************************************
**
**  Function:  VerifyReverseGroupDiagnosticSupport
**
**  Purpose:   Verify DID group support (ISO15765 only)
**
*******************************************************************************/
STATUS VerifyReverseGroupDiagnosticSupport ( IDTYPE IdType )
{
#define GROUP_DID_REQUEST_SIZE 3
	BYTE     EcuIdx;

	WORD     BaseId;
	REQ_MSG  GroupIdReqMsg;                             // group requests
	BOOL     bTestFailed = FALSE;


	// Save the original response data for comparison
	CopyIDSupport ( IdType );

	// Ignore unsupported responses
	// Support info is cleared on receive of first response to reverse order request
	gbIgnoreUnsupported = TRUE;

	// reverse order request started
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		gReverseOrderStateArray[EcuIdx] = REVERSE_ORDER_REQUESTED;
		if ( IdType == PIDREQUEST )
		{
			gstResponse[EcuIdx].PIDSupportSize = 0;
			memset ( &gstResponse[EcuIdx].PIDSupport[0],
			         0x00,
			         sizeof ( ID_SUPPORT ) * MAX_NUM_SUP_BYTES );
		}
		else if ( IdType == PF5REQUEST )
		{
			gstResponse[EcuIdx].PF5SupportSize = 0;
			memset ( &gstResponse[EcuIdx].PF5Support[0],
			         0x00,
			         sizeof ( ID_SUPPORT ) * MAX_NUM_SUP_BYTES );
		}
		else if ( IdType == MIDREQUEST )
		{
			gstResponse[EcuIdx].MIDSupportSize = 0;
			memset ( &gstResponse[EcuIdx].MIDSupport[0],
			         0x00,
			         sizeof ( ID_SUPPORT ) * MAX_NUM_SUP_BYTES );
		}
		else
		{
			gstResponse[EcuIdx].INFSupportSize = 0;
			memset ( &gstResponse[EcuIdx].INFSupport[0],
			         0x00,
			         sizeof ( ID_SUPPORT ) * MAX_NUM_SUP_BYTES );
		}
	}

	if ( IdType == PIDREQUEST )
	{
		BaseId = 0xF400;
	}
	else if ( IdType == PF5REQUEST )
	{
		BaseId = 0xF500;
	}
	else if ( IdType == MIDREQUEST )
	{
		BaseId = 0xF600;
	}
	else
	{
		BaseId = 0xF800;
	}

	// Request SID $22 DID support data as a reverse (high to low) group
	GroupIdReqMsg.SID    = 0x22;
	GroupIdReqMsg.NumIds = GROUP_DID_REQUEST_SIZE-1;
	GroupIdReqMsg.u.DID[0] = BaseId+0xE0;
	GroupIdReqMsg.u.DID[1] = BaseId+0xC0;
	if ( RequestSID ( &GroupIdReqMsg, REQ_MSG_ALLOW_NO_RESPONSE|REQ_MSG_IGNORE_NO_RESPONSE ) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "SID $22 reverse group DIDs $%04X-$%04X support request failed\n",
		      BaseId+0xE0,
		      BaseId+0xC0 );
		bTestFailed = TRUE;
	}

	// Request SID 22 DID support data as a reverse (high to low) group
	GroupIdReqMsg.SID    = 0x22;
	GroupIdReqMsg.NumIds = GROUP_DID_REQUEST_SIZE;
	GroupIdReqMsg.u.DID[0] = BaseId+0xA0;
	GroupIdReqMsg.u.DID[1] = BaseId+0x80;
	GroupIdReqMsg.u.DID[2] = BaseId+0x60;
	if ( RequestSID ( &GroupIdReqMsg,  REQ_MSG_ALLOW_NO_RESPONSE|REQ_MSG_IGNORE_NO_RESPONSE ) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "SID $22 reverse group DIDs $%04X-$%04X support request failed\n",
		      BaseId+0xA0,
		      BaseId+0x60 );
		bTestFailed = TRUE;
	}

	// Request DID support data as a group
	GroupIdReqMsg.SID    = 0x22;
	GroupIdReqMsg.NumIds = GROUP_DID_REQUEST_SIZE;
	GroupIdReqMsg.u.DID[0] = BaseId+0x40;
	GroupIdReqMsg.u.DID[1] = BaseId+0x20;
	GroupIdReqMsg.u.DID[2] = BaseId+0x00;
	if ( RequestSID ( &GroupIdReqMsg, REQ_MSG_NORMAL ) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "SID $22 reverse group DIDs $%04X-$%04X support request failed\n",
		      BaseId+0x40,
		      BaseId+0x00 );
		bTestFailed = TRUE;
	}


	// No longer ignore unsupported response
	gbIgnoreUnsupported = FALSE;

	// reverse order request finished
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		gReverseOrderStateArray[EcuIdx] = NOT_REVERSE_ORDER;
	}


	// Verify that all DID reverse/individual group support data compares
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( CompareIDSupport ( EcuIdx, IdType, FALSE ) ==FAIL )
		{
			bTestFailed = TRUE;
		}
	}

	// Restore the original response data for comparison
	RestoreIDSupport ( IdType );

	if ( bTestFailed == TRUE )
	{
		return FAIL;
	}

	return PASS;
}


/*******************************************************************************
**
**  Function:  VerifyReverseGroupDiagnosticSupport -
**
**  Purpose:   Verify SID $22 DID group support
**
*******************************************************************************/
STATUS VerifySingleAndGroupPidRequest ( IDTYPE IdType )
{
#define GROUP_DID_REQUEST_SIZE 3
	BYTE     EcuIdx;

	WORD     BaseId;
	WORD     IdIdx;
	WORD     ByteIdx;
	WORD     DidCount = 0;                          // temp. count of DIDs for current ECU
	WORD     MaxDidCount = 0;                       // largest count of DIDs for any ECU
	WORD     DidSize;

	WORD     NumberOfDidsRequested = 0;
	WORD     DesiredResponseSize = 0;               // expected response size for current ECU
	WORD     EcuDIDSize[GROUP_DID_REQUEST_SIZE];    // the size for each DID the ECU supports
	WORD     SingleDIDSize[MAX_ECUS][GROUP_DID_REQUEST_SIZE];  // for each ECU, the size for each DID in the group
	REQ_MSG  EcuSpecificSidReq[MAX_ECUS];           // the last 6 DIDs for each ECU
	REQ_MSG  SingleIdReqMsg;                        // single requests
	REQ_MSG  GroupIdReqMsg;                         // group requests
	BOOL     bTestFailed = FALSE;


	if ( IdType == PIDREQUEST )
	{
		BaseId = 0xF400;
	}
	else if ( IdType == PF5REQUEST )
	{
		BaseId = 0xF500;
	}
	else if ( IdType == MIDREQUEST )
	{
		BaseId = 0xF600;
	}
	else
	{
		BaseId = 0xF800;
	}

	// find the list of the last three DIDs supported by each ECU
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		for ( IdIdx = (BaseId+0xFF), DidCount = 0;
		      IdIdx > BaseId &&
		      DidCount < GROUP_DID_REQUEST_SIZE;
		      IdIdx-- )
		{
			// Don't use DID supported DIDs
			if ( (IdIdx & 0x001F) != 0 )
			{
				// If DID is supported, add it to the list
				if ( IsIDSupported ( EcuIdx, IdType, IdIdx ) == TRUE )
				{
					EcuSpecificSidReq[EcuIdx].u.DID[DidCount] = IdIdx;
					DidCount++;
				}
			}
		}
		EcuSpecificSidReq[EcuIdx].NumIds = (BYTE)DidCount;
		if ( EcuSpecificSidReq[EcuIdx].NumIds > MaxDidCount)
		{
			MaxDidCount = EcuSpecificSidReq[EcuIdx].NumIds;
		}
	}


	// combine the list of DIDs from each ECU into one list with at least one DID from each ECU
	GroupIdReqMsg.SID    = 0x22;
	GroupIdReqMsg.NumIds = 0;
	IdIdx  = 0;
	EcuIdx = 0;
	do
	{
		// add one DID at a time to the actual request
		if ( IdIdx < EcuSpecificSidReq[EcuIdx].NumIds )
		{
			// still some DIDs in the specific ECU list
			if ( IsDidInRequest ( GroupIdReqMsg, EcuSpecificSidReq[EcuIdx].u.DID[IdIdx] ) == FALSE )
			{
				// not already in the list, so add it
				GroupIdReqMsg.u.DID[GroupIdReqMsg.NumIds] = EcuSpecificSidReq[EcuIdx].u.DID[IdIdx];
				GroupIdReqMsg.NumIds++;
			}
		}

		// point to next ECU
		EcuIdx++;

		if ( EcuIdx >= gNumOfECUs )
		{
			// at the end of the ECU list, start over with next DID for each ECU
			EcuIdx = 0;
			IdIdx++;
		}

	} while ( GroupIdReqMsg.NumIds < MaxDidCount && IdIdx < MaxDidCount );


	if ( GroupIdReqMsg.NumIds != 0 )
	{
		// request the DIDs individually, to get the size information
		for ( DidCount = 0;
		      DidCount < GroupIdReqMsg.NumIds;
		      DidCount++ )
		{
			SingleIdReqMsg.SID      = 0x22;
			SingleIdReqMsg.NumIds   = 1;
			SingleIdReqMsg.u.DID[0] = GroupIdReqMsg.u.DID[DidCount];

			if ( RequestSID ( &SingleIdReqMsg, REQ_MSG_NORMAL ) == FAIL )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "Single DID $%04X request failed\n",
				      SingleIdReqMsg.u.DID[0] );
				bTestFailed = TRUE;
			}

			// save the response DID size
			for ( EcuIdx = 0;
			      EcuIdx < gNumOfECUs;
			      EcuIdx++ )
			{
				if ( IdType == PIDREQUEST || IdType == PF5REQUEST )
				{
					SingleDIDSize[EcuIdx][DidCount] = gstResponse[EcuIdx].PIDSize;
				}
				else if ( IdType == MIDREQUEST )
				{
					SingleDIDSize[EcuIdx][DidCount] = gstResponse[EcuIdx].MIDSize;
				}
				else
				{
					SingleDIDSize[EcuIdx][DidCount] = gstResponse[EcuIdx].INFSize;
				}
			}
		}


		// request the DIDs as a group and check the responses
		if ( RequestSID ( &GroupIdReqMsg, REQ_MSG_NORMAL ) == FAIL )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "SID $22 group DID request failed\n" );
			bTestFailed = TRUE;
		}
		else
		{
			for ( EcuIdx = 0;
			      EcuIdx < gNumOfECUs;
			      EcuIdx++ )
			{
				// rebuild ECU specific DID list, based on final group request
				for ( IdIdx = 0, DidCount = 0, DesiredResponseSize = 0;
				      IdIdx < GROUP_DID_REQUEST_SIZE;
				      IdIdx++ )
				{
					if ( IsIDSupported ( EcuIdx, IdType, GroupIdReqMsg.u.DID[IdIdx] ) == TRUE )
					{
						EcuSpecificSidReq[EcuIdx].u.DID[DidCount] = GroupIdReqMsg.u.DID[IdIdx];
						EcuDIDSize[DidCount] = SingleDIDSize[EcuIdx][IdIdx];
						DesiredResponseSize += SingleDIDSize[EcuIdx][IdIdx];
						DidCount++;
					}
				}
				EcuSpecificSidReq[EcuIdx].NumIds = (BYTE)DidCount;

				// check the response size
				if ( IdType == PIDREQUEST || IdType == PF5REQUEST )
				{
					DidSize = gstResponse[EcuIdx].PIDSize;
				}
				else if ( IdType == MIDREQUEST )
				{
					DidSize = gstResponse[EcuIdx].MIDSize;
				}
				else
				{
					DidSize = gstResponse[EcuIdx].INFSize;
				}

				if ( DidSize != DesiredResponseSize )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X  SID $22 group/single DID size mismatch (Response = %d bytes  Expected = %d bytes)\n",
					      GetEcuId ( EcuIdx ),
					      DidSize,
					      DesiredResponseSize );
					bTestFailed = TRUE;
				}
				else if (DidSize != 0 )
				{
					// size is good, check message content
					NumberOfDidsRequested = EcuSpecificSidReq[EcuIdx].NumIds;

					for ( ByteIdx = 0;
					      NumberOfDidsRequested > 0 && ByteIdx < DidSize;
					      NumberOfDidsRequested-- )
					{
						for ( IdIdx = 0;
						      IdIdx < EcuSpecificSidReq[EcuIdx].NumIds;
						      IdIdx++ )
						{
							WORD DidTemp;
							if ( IdType == PIDREQUEST || IdType == PF5REQUEST )
							{
								DidTemp = (gstResponse[EcuIdx].PID[ByteIdx] << 8) + gstResponse[EcuIdx].PID[ByteIdx+1];
							}
							else if ( IdType == MIDREQUEST )
							{
								DidTemp = (gstResponse[EcuIdx].MID[ByteIdx] << 8) + gstResponse[EcuIdx].MID[ByteIdx+1];
							}
							else
							{
								DidTemp = (gstResponse[EcuIdx].INF[ByteIdx] << 8) + gstResponse[EcuIdx].INF[ByteIdx+1];
							}

							if ( DidTemp == EcuSpecificSidReq[EcuIdx].u.DID[IdIdx] )
							{
								break;  // leave ID for loop
							}
						}

						if ( IdIdx < EcuSpecificSidReq[EcuIdx].NumIds )
						{
							// found the DID, get the offset to the next DID
							ByteIdx += EcuDIDSize[IdIdx];
						}
						else
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  group/single DID mismatch (DID $%04X)\n",
							      GetEcuId ( EcuIdx ),
							      EcuSpecificSidReq[EcuIdx].u.DID[IdIdx] );
							bTestFailed = TRUE;
							break;  // leave byte for loop
						}
					} // end for (each DID)
				} // end else (size is good)
			} // end for (each ECU)
		}
	}

	if ( bTestFailed == TRUE )
	{
		return FAIL;
	}

	return PASS;
}


/*******************************************************************************
**
**  Function:  IsPidInRequest
**
**  Purpose:  Check if the designated DID is already in the request
**
*******************************************************************************/
BOOL IsDidInRequest ( REQ_MSG SidReqList, WORD Did )
{
	WORD  DidIdx;


	for ( DidIdx = 0;
	      DidIdx < SidReqList.NumIds;
	      DidIdx++ )
	{
		if ( SidReqList.u.DID[DidIdx] == Did )
		{
			return TRUE;
		}
	}

	return FALSE;
}
