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

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


	// Count number of ECUs responding to SID 1 PID 0
	stReqMsg.SID    = 0x01;
	stReqMsg.NumIds = 1;
	stReqMsg.ID[0]  = 0x00;
	if ( RequestSid ( &stReqMsg, REQ_MSG_ALLOW_NO_RESPONSE ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "SID $01 PID $00 burst support failed\n" );
		return FAIL;
	}

	Pid00EcuCount = gNumOfECUsResp;

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

	Pid01EcuCount = gNumOfECUsResp;

	// If no ECU supports both PID 0 and PID 1, then cannot run test
	if ( Pid00EcuCount == 0 &&
	     Pid01EcuCount == 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 SID 1 PID 0x00 and PID 0x01 in alternating order for 30 seconds
	stReqMsg.ID[0] = 0x01;
	StartTimestampMsecs = GetTickCount ( );
	while ( ((CurrentTimestampMsecs = GetTickCount ( )) - StartTimestampMsecs) < 30000 )
	{
		stReqMsg.SID    = 0x01;
		stReqMsg.NumIds = 1;
		stReqMsg.ID[0]++;
		stReqMsg.ID[0] &= 0x01;

		// set the number of expected responses
		ExpectedNumOfECUs = (stReqMsg.ID[0] == 0) ? Pid00EcuCount : Pid01EcuCount;

		// 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,
				      "SID $01 PID $%02X request failed in burst test\n",
				      stReqMsg.ID[0] );
			}
			else if ( gNumOfECUsResp != ExpectedNumOfECUs )
			{
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "Expected %d, Received %d responses to SID $01 PID $%02X in burst test\n",
				      ExpectedNumOfECUs,
				      gNumOfECUsResp,
				      stReqMsg.ID[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;
}
