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

#include <stdio.h>    // C Library input and output declarations
#include <stdlib.h>   // C Library general function declarations
#include <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


// Variables
BYTE           gSid0ASupEcuCnt = 0;
unsigned long  gSid0ASupEcuIdArray[MAX_ECUS];


/*******************************************************************************
**
**  Function:  VerifyPermanentCodeSupport
**
**  Purpose:   Verify SID A support
**
*******************************************************************************/
STATUS VerifyPermanentCodeSupport ( void )
{
	REQ_MSG        stReqMsg;
	BYTE           EcuIdx;

	unsigned long  NumOfDTCs;
	unsigned long  DataIdx;
	unsigned long  InitialFailureCount = 0;
	BOOL           bTestFailed = FALSE;
	BYTE           EcuIdIdx;


	if ( gstProtocolList[gProtocolIdx].eProtocol != ISO15765 )
	{
		return PASS;
	}

	InitialFailureCount = GetFailureCount ( );

	// Count number of ECUs responding to SID A
	stReqMsg.SID    = 0x0A;
	stReqMsg.NumIds = 0;
	if ( RequestSid ( &stReqMsg, REQ_MSG_NORMAL|REQ_MSG_IGNORE_NO_RESPONSE ) == FAIL )
	{
		// An ECU supports Service 0A
		for ( EcuIdx = 0, EcuIdIdx = 0, gSid0ASupEcuCnt = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			if ( gstResponse[EcuIdx].bPermDTCSupported )
			{
				gSid0ASupEcuCnt++;
				gSid0ASupEcuIdArray[EcuIdIdx++] = GetEcuId ( EcuIdx );
			}
		}

		// Permanent code support required for MY 2010 and beyond
		if ( gModelYear >= 2010 &&
		     (gstUserInput.eComplianceType == US_OBDII || gstUserInput.eComplianceType == HD_OBD) )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "SID $0A request failed\n" );
			return FAIL;
		}
		else if ( InitialFailureCount != GetFailureCount ( ) )
		{
			// FAIL was due to actual problems
			return FAIL;
		}
		else
		{
			// FAIL was due to no response, but none was required
			return PASS;
		}
	}


	for ( EcuIdx = 0, EcuIdIdx = 0, gSid0ASupEcuCnt = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( gstResponse[EcuIdx].bPermDTCSupported )
		{
			gSid0ASupEcuCnt++;
			gSid0ASupEcuIdArray[EcuIdIdx++] = GetEcuId ( EcuIdx );
		}
	}


	// If a Permanent DTC is not supposed to be present (Test 5.4, 9.6, 9.22)
	if ( gbDTCPermanent == FALSE )
	{
		// Verify that SID A reports no DTCs stored
		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			// check the Permanent DTCs count
			if ( gstResponse[EcuIdx].PermDTC[0] != 0x00 ||
			     gstResponse[EcuIdx].PermDTC[1] != 0x00 )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  SID $0A reports Permanent DTC stored\n",
				      GetEcuId ( EcuIdx ) );
				bTestFailed = TRUE;
			}
		}
	}

	// If a Permanent DTC is supposed to be present (Test 8.6, 9.11, 9.15, 9.18)
	else
	{
		// Verify that SID A reports DTCs stored
		NumOfDTCs = 0;
		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			// Print out all the DTCs
			for ( DataIdx = 0;
			      DataIdx < gstResponse[EcuIdx].PermDTCSize;
			      DataIdx += 2 )
			{
				if ( gstResponse[EcuIdx].PermDTC[DataIdx]     != 0 ||
				     gstResponse[EcuIdx].PermDTC[DataIdx + 1] != 0 )
				{
					// Process based on the type of DTC
					switch ( gstResponse[EcuIdx].PermDTC[DataIdx] & 0xC0 )
					{
						case 0x00:
						{
							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  Permanent DTC P%02X%02X detected\n",
							      GetEcuId ( EcuIdx ),
							      gstResponse[EcuIdx].PermDTC[DataIdx] & 0x3F,
							      gstResponse[EcuIdx].PermDTC[DataIdx + 1] );
						}
						break;

						case 0x40:
						{
							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  Permanent DTC C%02X%02X detected\n",
							      GetEcuId ( EcuIdx ),
							      gstResponse[EcuIdx].PermDTC[DataIdx] & 0x3F,
							      gstResponse[EcuIdx].PermDTC[DataIdx + 1] );
						}
						break;

						case 0x80:
						{
							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  Permanent DTC B%02X%02X detected\n",
							      GetEcuId ( EcuIdx ),
							      gstResponse[EcuIdx].PermDTC[DataIdx] & 0x3F,
							      gstResponse[EcuIdx].PermDTC[DataIdx + 1] );
						}
						break;

						case 0xC0:
						{
							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  Permanent DTC U%02X%02X detected\n",
							      GetEcuId ( EcuIdx ),
							      gstResponse[EcuIdx].PermDTC[DataIdx] & 0x3F,
							      gstResponse[EcuIdx].PermDTC[DataIdx + 1] );
						}
						break;
					}
					NumOfDTCs++;
				}
			}
		}

		// If no stored DTCs found, then fail
		if ( NumOfDTCs == 0 )
		{
			if ( gModelYear >= 2010 &&
			     (gstUserInput.eComplianceType == US_OBDII || gstUserInput.eComplianceType == HD_OBD ||
			      gstUserInput.eComplianceType == CNOBD_NO_IUMPR) )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "All ECUs  SID $0A report no Permanent DTC stored\n" );
				bTestFailed = TRUE;
			}
			else
			{
				Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "All ECUs  SID $0A report no Permanent DTC stored\n" );
			}
		}
	}

	// if this is MY 2010 and beyond
	if ( gModelYear >= 2010 &&
	     (gstUserInput.eComplianceType == US_OBDII || gstUserInput.eComplianceType == HD_OBD) )
	{
		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			if ( (gstResponse[EcuIdx].bConfDTCSupported == TRUE ||
			      gstResponse[EcuIdx].bPendDTCSupported == TRUE)  &&
			     gstResponse[EcuIdx].bPermDTCSupported == FALSE )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  Supports SID $03 and SID $07 but not SID $0A\n",
				      GetEcuId ( EcuIdx ) );
				bTestFailed = TRUE;
			}
		}

	}

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