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


// Function Prototype
STATUS IsDTCStored ( unsigned long Flags );


/*******************************************************************************
**
**  Function:  VerifyStoredDTCData
**
**  Purpose:  Verify SID 3 DTC stored data
**
*******************************************************************************/
STATUS VerifyStoredDTCData ( void )
{
	REQ_MSG        stReqMsg;
	BYTE           EcuIdx;

	unsigned long  NumOfDTCs;
	unsigned long  DataIdx;
	unsigned long  InitialFailureCount = 0;


	InitialFailureCount = GetFailureCount ( );

	// Request SID 3 data
	stReqMsg.SID    = 0x03;
	stReqMsg.NumIds = 0;
	if ( RequestSid ( &stReqMsg, REQ_MSG_NORMAL ) == FAIL )
	{
		// If DTC stored or historical DTC or ISO15765 protocol, there must be a response
		if ( gbDTCStored == TRUE ||
		     gbDTCHistorical == TRUE ||
		     gstProtocolList[gProtocolIdx].eProtocol == ISO15765   ||
		     gstProtocolList[gProtocolIdx].eProtocol == ISO14230 )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "SID $03 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;
		}
	}

	// if a DTC is not supposed to be present (Test 5.12, 6.4, 10.8, 11.8)
	if ( gbDTCStored == FALSE && gbDTCHistorical == FALSE )
	{
		// Verify that SID $03 reports no DTCs stored
		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			// warn for ECUs which don't respond
			if ( gstResponse[EcuIdx].ConfDTCSize == 0 )
			{
				Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  No response to SID $03 request\n",
				      GetEcuId ( EcuIdx ) );
			}

			else
			{
				// If ISO15765 protocol, check that DTCs are $00
				if ( gstProtocolList[gProtocolIdx].eProtocol == ISO15765 )
				{
					if ( gstResponse[EcuIdx].ConfDTC[0] != 0x00 ||
						 gstResponse[EcuIdx].ConfDTC[1] != 0x00 )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  SID $03 indicates DTC stored\n",
						      GetEcuId ( EcuIdx ) );
						return FAIL;
					}
				}
				// If other protocol, check that DTCs are $00
				else
				{
					// response should contain multiple of 6 data bytes
					if ( gstResponse[EcuIdx].ConfDTCSize % 6 != 0 )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  SID $03 response size error\n",
						      GetEcuId ( EcuIdx ) );
						return FAIL;
					}

					// validation of all DTCs reported in response
					for ( DataIdx = 0;
					      DataIdx < (unsigned long)(gstResponse[EcuIdx].ConfDTCSize / 2);
					      DataIdx++ )
					{
						if ( gstResponse[EcuIdx].ConfDTC[DataIdx*2]     != 0x00 ||
						     gstResponse[EcuIdx].ConfDTC[(DataIdx*2)+1] != 0x00 )
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  SID $03 indicates DTC stored\n",
							      GetEcuId ( EcuIdx ) );
							return FAIL;
						}
					}
				}
			}
		}
	}

	// if a DTC is supposed to be present (Test 7.4, 8.4, 9.4)
	else
	{
		// Verify that SID 3 reports DTCs stored
		NumOfDTCs = 0;
		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			// warn for ECUs which don't respond
			if ( gstResponse[EcuIdx].ConfDTCSize == 0 )
			{
				Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  No response to SID $03 request\n",
				      GetEcuId ( EcuIdx ) );
			}

			else
			{
				// Print out all the DTCs
				for ( DataIdx = 0;
				      DataIdx < gstResponse[EcuIdx].ConfDTCSize;
				      DataIdx += 2 )
				{
					if ( gstResponse[EcuIdx].ConfDTC[DataIdx]     != 0 ||
					     gstResponse[EcuIdx].ConfDTC[DataIdx + 1] != 0 )
					{
						// Process based on the type of DTC
						switch ( gstResponse[EcuIdx].ConfDTC[DataIdx] & 0xC0 )
						{
							case 0x00:
							{
								Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
								      "ECU %X  Stored DTC P%02X%02X detected\n",
								      GetEcuId ( EcuIdx ),
								      gstResponse[EcuIdx].ConfDTC[DataIdx] & 0x3F,
								      gstResponse[EcuIdx].ConfDTC[DataIdx + 1] );
							}
							break;

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

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

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

		// If no stored DTCs found, then fail
		if ( NumOfDTCs == 0 )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "No ECU indicates SID $03 DTC stored\n" );
			return FAIL;
		}
	}

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

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

}


/*******************************************************************************
**
**  Function:  IsDTCStored
**
**  Purpose:  Check if SID 3 reports a DTC stored
**
*******************************************************************************/
STATUS IsDTCStored ( unsigned long Flags )
{
	REQ_MSG  stReqMsg;
	BYTE     EcuIdx;


	// Request SID 3 data
	stReqMsg.SID    = 0x03;
	stReqMsg.NumIds = 0;
	if ( RequestSid ( &stReqMsg, Flags ) != PASS )
	{
		// Indicate request failed
		return ERRORS;
	}

	// Check if SID 3 reports DTCs stored
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( gstResponse[EcuIdx].ConfDTCSize == 0 )
		{
			// If no data, ignore
		}
		// Check if there is at least one DTC
		else if ( gstResponse[EcuIdx].ConfDTC[0] != 0 ||
		          gstResponse[EcuIdx].ConfDTC[1] != 0 )
		{
			// Break the loop if a DTC is stored
			break;  // leave ECU for loop
		}
	}

	// If no stored DTCs, return FALSE
	if ( EcuIdx == gNumOfECUs )
	{
		return FAIL;
	}

	return PASS;
}
