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


/*******************************************************************************
**
**  Function: VerifyMILData
**
**  Purpose:   Verify OBD MIL / DTC status
**
*******************************************************************************/
STATUS VerifyMILData ( void )
{
	REQ_MSG  stReqMsg;
	PID     *pPid;
	BYTE     EcuIdx;
	STATUS   eRetCode = PASS;

	BYTE     NumOfECUsWithDTCs = 0;
	BYTE     NumOfECUsWithMILAndDTCs = 0;


	// Request PID $F501 data
	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = 0xF501;

	Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "Request IM Readiness Data (SID $22 DID $F501) %s Addressed\n",
	      gbPhysicalAddressing ? "Physically" : "Functionally" );

	if ( gbPhysicalAddressing == FALSE && RequestSID ( &stReqMsg, REQ_MSG_NORMAL ) != PASS )
	{
		eRetCode = FAIL;
	}
	else if ( gbPhysicalAddressing == TRUE )
	{
		// if there is no support data, request it
		if ( gstResponse[0].PF5SupportSize == 0 )
		{
			// Read $F5xx PID support PIDs
			if ( RequestIDSupportData ( PF5REQUEST, FALSE ) != PASS )
			{
				if ( Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT,
				           "Problems verifying PID $F5xx supported data." ) == 'N' )
				{
					return FAIL;
				}
			}
		}

		gNumOfECUsResp = 0;

		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			if ( IsIDSupported ( EcuIdx, PF5REQUEST, 0xF501 ) == TRUE &&
			     RequestSID_PhysicallyAddressed_Single ( &stReqMsg, REQ_MSG_NORMAL, gstResponse[EcuIdx].ReqId ) != PASS )
			{
				eRetCode = FAIL;
			}
		}
	}

	if ( eRetCode == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "PID $F501 request failed\n" );
	}

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

			// Check for data
			if ( gstResponse[EcuIdx].PIDSize == 0 )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  Insufficient data returned for SID $22 request\n",
				      GetEcuId ( EcuIdx ) );
				eRetCode = FAIL;
			}
			else
			{
				// If MIL Bit is set
				if ( (pPid->Data[0] & 0x80) == 0x80 )
				{
					// if test 9.3, MIL must not be illuminated
					if ( geTestPhase == eTestNoFault3DriveCycle &&
					     gTestSubsection == 3 )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  MIL Illuminated\n",
						      GetEcuId ( EcuIdx ) );
						eRetCode = FAIL;
					}
					else
					{
						Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  MIL Illuminated\n",
						      GetEcuId ( EcuIdx ) );
					}
					
					if ( gbDTCStored == FALSE )
					{
						// if MIL bit is set or DTC_CNT is not 0
						if ( pPid->Data[0] != 0x00 )
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  MIL/DTC status bit(s) set\n",
							      GetEcuId ( EcuIdx ) );
							eRetCode = FAIL;
						}
					}
	
					// Count ECUs with at least one DTC
					if ( (pPid->Data[0] & 0x7F) != 0x00 )
					{
						NumOfECUsWithDTCs++;
		
						// Count ECUs with at least one DTC and MIL is lit
						if ( (pPid->Data[0] & 0x80) != 0x00 )
						{
							NumOfECUsWithMILAndDTCs++;
						}
					}
				}
				else
				{
					Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X  MIL NOT Illuminated\n",
					      GetEcuId ( EcuIdx ) );
				}
			}
		}
		else if ( gbIgnoreUnsupported == FALSE )
		{
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %X  PID $F501 not supported\n",
			      GetEcuId ( EcuIdx ) );
		}
	}

	// Check if a DTC is supposed to be present
	if ( gbDTCStored == TRUE )
	{
		// Check if any ECU reported a DTC
		if ( NumOfECUsWithDTCs == 0 )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "PID $F501 indicates no ECU has DTC stored\n" );
			eRetCode = FAIL;
		}

		if ( gbDTCHistorical == TRUE )
		{
			// If the DTC is historical, make sure MIL is off
			if ( NumOfECUsWithMILAndDTCs > 0 )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "PID $F501 indicates MIL light ON after 3 driving cycles with fault repaired\n" );
				eRetCode = FAIL;
			}
		}
		else
		{
			// If the DTC is not historical, make sure MIL is on
			if ( NumOfECUsWithMILAndDTCs == 0 )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "PID $F501 indicates MIL light OFF before 3 driving cycles with fault repaired\n" );
				eRetCode = FAIL;
			}
		}
	}

	// Test case 5.4.1, verify that at least 1 ECU responded but
	// not more than the total specified by the user.
	if ( gbEngineRunning == FALSE )
	{
		if ( ( gNumOfECUsResp == 0 ) || ( gNumOfECUsResp > gUserNumOfECUs ) )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "Number of PID $F501 responses in error (%d responses, 1 to %d expected\n",
			      gNumOfECUsResp,
			      gUserNumOfECUs );
			eRetCode = FAIL;
		}
	}

	return eRetCode;
}
