/*******************************************************************************
********************************************************************************
**
**  Copyright(c) 2022, Alliance for Automotive Innovation
**  Used only under license from the Alliance for Automotive Innovation. All Rights Reserved.
**
**  Project:  J1699-5
**  FileName: VerifyPIDBurstSupport.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:  VerifyPIDBurstSupport
**
**  Purpose:   Verify PID diagnostic burst support
**
*******************************************************************************/
STATUS VerifyPIDBurstSupport ( void )
{
	REQ_MSG       stReqMsg;
	STATUS        eRetCode = PASS;

	unsigned long StartTimestampMsecs;
	unsigned long PrintTimestampMsecs = 0;
	unsigned long CurrentTimestampMsecs;
	unsigned int  ReqTotal = 0;       // the number of requests
	unsigned int  RespTotal = 0;      // the number of responses without fault(s)
	float         RespReq_float = 0;  // the proportion of responses to requests
	unsigned int  PID00EcuCount;
	unsigned int  PID10EcuCount;

	unsigned int ExpectedNumOfECUs;   // the number of ECUs expected to respond


	// Count number of ECUs responding to PID $F800
	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = 0xF800;
	if ( RequestSID ( &stReqMsg, REQ_MSG_ALLOW_NO_RESPONSE ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "PID $F800 burst support failed\n" );
		return FAIL;
	}

	PID00EcuCount = gNumOfECUsResp;

	// Count number of ECUs responding to PID $F810
	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = 0xF810;
	if ( RequestSID ( &stReqMsg, REQ_MSG_ALLOW_NO_RESPONSE ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "PID $F810 burst support failed\n" );
		return FAIL;
	}

	PID10EcuCount = gNumOfECUsResp;

	// If no ECU supports both PID 0 and PID 1, then cannot run test
	if ( PID00EcuCount == 0 &&
	     PID10EcuCount == 0 )
	{
		return PASS;
	}

	// Stop the tester present message before burst test
	// since we are using the message in the burst test
	if ( StopPeriodicMsg ( TRUE ) != PASS )
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "StopPeriodicMsg failed\n");
	}

	// Wait for possible race conditions.
	// RequestSID will flush the queue.
	Sleep ( gRequestDelayTimeMsecs );

	gbSuspendScreenOutput = TRUE;
	gbIgnoreUnsupported = TRUE;
	// Request PID $F800 and PID $F810 in alternating order for 30 seconds
	stReqMsg.u.DID[0] = 0xF810;
	StartTimestampMsecs = GetTickCount ( );
	while ( ((CurrentTimestampMsecs = GetTickCount ( )) - StartTimestampMsecs) < 30000 )
	{
		stReqMsg.SID    = 0x22;
		stReqMsg.NumIds = 1;
		if ( stReqMsg.u.DID[0] == 0xF800 )
		{
			stReqMsg.u.DID[0] = 0xF810;
		}
		else
		{
			stReqMsg.u.DID[0] = 0xF800;
		}

		// set the number of expected responses
		ExpectedNumOfECUs = (stReqMsg.u.DID[0] == 0xF800) ? PID00EcuCount : PID10EcuCount;

		// if no ECU supports this PID, skip to next PID
		if ( ExpectedNumOfECUs != 0 )
		{
			ReqTotal++;

			if ( RequestSID ( &stReqMsg, (REQ_MSG_ALLOW_NO_RESPONSE|REQ_MSG_RETURN_AFTER_ALL_RESPONSES) ) != PASS )
			{
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "PID $%04X request failed in burst test\n",
				      stReqMsg.u.DID[0] );
			}
			else if ( gNumOfECUsResp != ExpectedNumOfECUs )
			{
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "Missing PID $%04X response in burst test\n",
				      stReqMsg.u.DID[0] );
			}
			else
			{
				RespTotal++;
			}
		}

		if ( (CurrentTimestampMsecs - PrintTimestampMsecs) >= 1000 )
		{
			printf ( "INFORMATION:  Burst Time Remaining = %d sec  Requests = %d  Responses = %d  \r",
			         (30000 - (CurrentTimestampMsecs - StartTimestampMsecs))/1000,
			         ReqTotal,
			         RespTotal );
			PrintTimestampMsecs = GetTickCount ( );
		}
	}
	gbSuspendScreenOutput = FALSE;
	gbIgnoreUnsupported = FALSE;

	printf ( "\n" );

	// Restart periodic message when done
	if ( StartPeriodicMsg ( ) != PASS )
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "StartPeriodicMsg failed\n");
	}

	// Link active test to verify communication remained active for ALL protocols
	if ( VerifyECUCommunication ( ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "VerifyECUCommunication failed\n" );
	}

	// Calculate the number of replies that failed
	RespReq_float = (float)(ReqTotal - RespTotal)/(float)(ReqTotal);

	// Log total number of response messages received from burst test
	Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "Burst test result:  Total Number of Requests =  %d,  Total Number of Errors =  %d,  Error Percentage = %2.2f%%\n",
	      ReqTotal,
	      ReqTotal - RespTotal,
	      (float)(100) * RespReq_float );

	// Verify that less than 10% of requests had responses with failures
	if ( RespReq_float > (float)(.10) )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Burst test result exceeded maximum error of 10%%\n" );
		eRetCode = FAIL;
	}

	return eRetCode;
}
