/*******************************************************************************
********************************************************************************
**
**  Copyright(c) 2022, Alliance for Automotive Innovation
**  Used only under license from the Alliance for Automotive Innovation. All Rights Reserved.
**
**  Project:  J1699-5
**  FileName: Test12_ZEV.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:  012/05/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 <conio.h>    // MS-DOS console input and output declarations
#include <windows.h>  // Windows API declarations
#include "j2534.h"    // j1699 project j2534 declarations
#include "j1699.h"    // j1699 project general declarations


// Function Prototypes
STATUS CheckDoIPChannel ( void );

extern STATUS ReadProtocolID      ( void );
extern void   InitOBDProtocolList ( void );
extern STATUS StartDoIPChannel    ( void );


/*******************************************************************************
**
**  Function:  Test5_NoDTC
**
**  Purpose:   Run test 5 with no malfunction, no DTC set
**
*******************************************************************************/
STATUS Test12_ZEV ( void )
{
	BOOL          bSubTestFailed = FALSE;
	BOOL          bLoopDone = FALSE;


//*******************************************************************************
//  Test 12.1 - Determine the OBD protocol to use
//*******************************************************************************
	gTestSubsection = 1;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Determine Protocol, PSA Inactive)" );


	Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
	      "Perform whatever steps are required for the vehicle to be\n"
	      "in the PSA Inactive state." );


	gbIgnoreUnsupported = TRUE;
	// Determine the OBD protocol to use
	if ( DetermineOBDProtocol ( ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Protocol determination unsuccessful.\n" );
		Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
		return FAIL;
	}

	Log ( BLANK, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "\n" );

	Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "Reading Protocol ID\n" );
	if ( ReadProtocolID ( ) != PASS )
	{
		bSubTestFailed = TRUE;
	}
	gbIgnoreUnsupported = FALSE;

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 12.2 - Clear DTCs (SID $14), PSA Off
//*******************************************************************************
	gTestSubsection = 2;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Clear DTCs (SID $14), PSA Inactive)" );

	// Propulsion System Active should not be ON, Ready, Active
	gbPSA = FALSE;

	// Clear Codes Functionally Addressed
	if ( ClearCodes ( ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 12.3 - Verify Datastream Data, Request Current Powertrain Diagnostic Data, PSA OFF
//*******************************************************************************
	gTestSubsection = 3;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify Diagnostic Data (SID $22 PIDs), PSA Inactive)" );

	// check PID Data Physically Addressed
	gbPhysicalAddressing = TRUE;
	if ( VerifyPIDSupportAndData ( FALSE ) != PASS )
	{
		bSubTestFailed = TRUE;
	}
	gbPhysicalAddressing = FALSE;

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 12.4 - PSA ON
//*******************************************************************************
	gTestSubsection = 4;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(PSA Active)" );

	ClearKeyboardBuffer ( );

	Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
	      "Start vehicle and ensure Propulsion System is in READY State." );

	if ( CheckDoIPChannel ( ) == FAIL )
	{
		return FAIL;
	}

	// Propulsion System Active should be ON, Ready, Active
	gbEngineRunning = TRUE;
	gbPSA = TRUE;

//	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
//	{
//		bSubTestFailed = TRUE;
//	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 12.5 - Clear DTCs (SID $14), PSA On
//*******************************************************************************
	gTestSubsection = 5;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Clear DTCs (SID $14), PSA Active)" );

	// Clear Codes Functionally Addressed
	if ( ClearCodes ( ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

//	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
//	{
//		bSubTestFailed = TRUE;
//	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 12.6 - Verify diagnostic support and data, Engine Running
//*******************************************************************************
	gTestSubsection = 6;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify Diagnostic Data (SID $22 PIDs), PSA Active)" );

	// check PID Data Physically Addressed
	gbPhysicalAddressing = TRUE;
	if ( VerifyPIDSupportAndData ( FALSE ) != PASS )
	{
		bSubTestFailed = TRUE;
	}
	gbPhysicalAddressing = FALSE;

//	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
//	{
//		bSubTestFailed = TRUE;
//	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
// Test 12.7 - Verify vehicle information support and data, Engine Running
//*******************************************************************************
	gTestSubsection = 7;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify Vehicle Information (SID $22 INFs), PSA Active)" );

	// check INF Data Physically Addressed
	gbPhysicalAddressing = TRUE;
	if ( VerifyINFSupportAndData ( ) != PASS )
	{
		bSubTestFailed = TRUE;
	}
	gbPhysicalAddressing = FALSE;
	// check INF Data Physically Addressed

//	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
//	{
//		bSubTestFailed = TRUE;
//	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
// Test 12.8 - Verify vehicle information, Induce Circuit Fault
//*******************************************************************************
	gTestSubsection = 8;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Induce Circuit Fault)" );

	ClearKeyboardBuffer ( );

	Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
	      "With ignition off and engine off, disconnect a sensor that is tested\n"
	      "continuously (e.g., TP, AAT, etc.) but will not prevent PSA=True, a fault that\n"
	      "will generate a warning indication and a single DTC with the PSA=True in a short\n"
	      "period of time (i.e., < 10 s) for only one ECU.\n"
	      "If a DTC that becomes confirmed in two driving cycles cannot be tested, it is\n"
	      "acceptable to use a fault that sets in one driving cycle. If this is the case,\n"
	      "a pending DTC, a confirmed DTC, and warning indication will be set on the first\n"
	      "driving cycle.\n\n"
	      "Start vehicle (propulsion system active=TRUE), and remain for one minute, or\n"
	      "whatever time it takes to set a pending DTC." );

	Log ( BLANK, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "\n\n" );

	if ( CheckDoIPChannel ( ) == FAIL )
	{
		return FAIL;
	}
	
	gbDTCPending = TRUE;

	// check Pending DTCs Functionally Addressed
	if ( VerifyPendingDTCData ( DTC_STATE_ZEV_PEND_FAULT ) != PASS )
	{
		bSubTestFailed = TRUE;
	}


	// check Confirmed DTCs Functionally Addressed
	if ( VerifyConfirmedDTCData ( DTC_STATE_ZEV_PEND_FAULT ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
// Test 12.9 - Verify vehicle information, Continue to Induce Fault
//*******************************************************************************
	gTestSubsection = 9;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Continue to Induce Fault)" );

	ClearKeyboardBuffer ( );

	Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
	      "Turn key OFF for 30 seconds or longer, as appropriate for the ECU,\n"
	      "and keep the failure present.\n",

	      "NOTE: You may first start the engine and then induce a failure\n"
	      "in order to avoid starting issues.");

	Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
	      "Activate propulsion system.\n\n"

	      "Let idle for 1 minute or whatever time it takes to set\n"
	      "a confirmed DTC and illuminate the MIL.\n\n"

	      "NOTE: Some powertrain control systems have engine controls that can\n"
	      "start and stop the engine without regard to ignition position." );

	Log ( BLANK, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "\n\n" );

	if ( CheckDoIPChannel ( ) == FAIL )
	{
		return FAIL;
	}


	// Set flag to indicate a stored DTC should be present
	gbDTCStored = TRUE;

	// check Pending DTCs Functionally Addressed
	if ( VerifyPendingDTCData ( DTC_STATE_ZEV_CONF_FAULT ) != PASS )
	{
		bSubTestFailed = TRUE;
	}


	// check Confirmed DTCs Functionally Addressed
	if ( VerifyConfirmedDTCData ( DTC_STATE_ZEV_CONF_FAULT ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
	           "Is there a warning indication on the instrument panel?" ) != 'Y' )
	{
		Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "MIL should be illuminated.\n" );
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
// Test 12.10 - Verify vehicle information, With Fault Repaired
//*******************************************************************************
	gTestSubsection = 10;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	// Prompt user to fix fault and perform first two drive cycles
	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Repair Fault and Complete One Drive Cycle, MIL Illuminated)" );

	ClearKeyboardBuffer ( );

	printf ( "\n\n\n" );
	Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
	      "Turn key OFF for 30 seconds or longer, as appropriate for the ECU." );

	printf ( "\n\n\n" );
	Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
	      "Reconnect sensor" );

	printf ( "\n\n\n" );
	Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
	      "Activate propulsion system for 1 minute or whatever time\n"
	      "it takes to run the monitor and detect that there is no malfunction.\n\n"

	      "Note: Some powertrain control systems have engine controls that can start and\n"
	      "stop the engine without regard to ignition position." );

	printf ( "\n\n\n" );
	Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
	     "Turn key OFF for 30 seconds or longer, as appropriate for the ECU\n"
	     "(this completes one driving cycle)." );

	printf ( "\n\n\n" );
	Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
	      "Activate propulsion system for 1 minute or whatever time\n"
	      "it takes to run the monitor and detect that there is no malfunction.\n\n"

	      "Note: Some powertrain control systems have engine controls that can start and\n"
	      "stop the engine without regard to ignition position." );

	// Set flag to indicate engine should be running
	gbEngineRunning = TRUE;
	// Set flag to indicate a pending DTC should not be present
	gbDTCPending = FALSE;
	// Set flag to indicate a stored DTC should not be present
	gbDTCStored = TRUE;

	Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );


//*******************************************************************************
//  Test 12.11 - Check for a pending DTC, Engine Running
//*******************************************************************************
	gTestSubsection = 11;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify No Pending DTCs (SID $19 LEV $42 StatusMask $04), PSA Active)" );

	// Flush the STDIN stream of any user input above
	ClearKeyboardBuffer ( );

	if ( CheckDoIPChannel ( ) == FAIL )
	{
		return FAIL;
	}

	// Verify pending DTC data
	Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
	      "Checking for a pending DTC...(press any key to stop test)\n"
	      "NOTE: Stopping while Pending DTC is present is a failure.\n" );
	do
	{
		if ( IsDTCPending ( (REQ_MSG_NORMAL|REQ_MSG_ALLOW_NO_RESPONSE) ) == FAIL )
		{
			bLoopDone = TRUE;
			break;  // leave while (not keyboard hit) loop
		}
		Sleep ( 500 );
	}
	while ( !_kbhit ( ) );


	if ( bLoopDone == FALSE )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "User abort\n" );
	}
	else
	{
		Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTOFF, ENTER_PROMPT,
		      "No Pending DTC reported" );
	}


	// check Confirmed DTCs Functionally Addressed
	if ( VerifyConfirmedDTCData ( DTC_STATE_ZEV_REPAIR_1DC_FAULT ) != PASS )
	{
		bSubTestFailed = TRUE;
	}


	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 12.x - ZEV Tests
//*******************************************************************************
	Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTOFF, ENTER_PROMPT,
	      "*** Further ZEV Tests NOT CURRENTLY IMPLEMENTED ***" );

	if ( gbTestSectionFailed == TRUE )
	{
		return FAIL;
	}

	return PASS;
}


STATUS CheckDoIPChannel ( void )
{
	if ( gstProtocolList[gProtocolIdx].eProtocol == DOIP ||
	      gstProtocolList[gProtocolIdx].eProtocol == DOIP_NDIS )
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Checking DoIP Channel (in case vehicle socket closed)..." );
		if ( StartDoIPChannel ( ) != PASS )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "Start DoIP Channel unsuccessful.\n" );
			Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
			return FAIL;
		}
	}
	return PASS;
}
