/*******************************************************************************
  TCP Performance Test example

  Summary:
    TCP example for Microchip TCP/IP Stack
    
  Description:
    -Establishes a connection and then sends out dummy packets 
     from const memory
    -Reference: None.  This is for testing only.
*******************************************************************************/

/*******************************************************************************
FileName:   tcp_performance_test.c
Copyright © 2012 released Microchip Technology Inc.  All rights
reserved.

Microchip licenses to you the right to use, modify, copy and distribute
Software only when embedded on a Microchip microcontroller or digital signal
controller that is integrated into your product or third party product
(pursuant to the sublicense terms in the accompanying license agreement).

You should refer to the license agreement accompanying this Software for
additional information regarding your rights and obligations.

SOFTWARE AND DOCUMENTATION ARE PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF
MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE.
IN NO EVENT SHALL MICROCHIP OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER
CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR
OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE OR
CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT OF
SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
(INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.
*******************************************************************************/

#include "tcpip/tcpip.h"


#include "common/helpers.h"


// The TCP port to listen on for TCP transmit tests
#define TX_PERFORMANCE_PORT	9762

// The TCP port to listen on for TCP receive tests
#define RX_PERFORMANCE_PORT	9763

// TCP performance simultaneous supported connections.
#define TCP_MAX_PERFORMANCE_CONNECTIONS     (1)

void TCPTXPerformanceTask(void);
void TCPRXPerformanceTask(void);

/*****************************************************************************
  Function:
	void TCPPerformanceTask(void)

  Summary:
	Tests the performance of the TCP module.

  Description:
	This function calls both TCPTXPerformanceTask and TCPRXPerformanceTask
	to perform the performance task functions.  Refer to the documentation
	for each of those functions for details.

  Precondition:
	TCP is initialized.

  Parameters:
	None

  Returns:
	None
  ***************************************************************************/
void TCPPerformanceTask(void)
{
	TCPTXPerformanceTask();
	TCPRXPerformanceTask();
}

/*****************************************************************************
  Function:
	void TCPTXPerformanceTask(void)

  Summary:
	Tests the transmit performance of the TCP module.

  Description:
	This function tests the transmit performance of the TCP module.  To use,
	open a telnet connection to the device on TX_PERFORMANCE_PORT (9762 by 
	default).  The board will rapidly transmit data and report its performance
	to the telnet client.
	
	TCP performance is affected by many factors, including round-trip time 
	and the TCP buffer size.  For faster results, increase the size of the 
	TX buffer size in tcp_config.h.
    Round-trip time is affected by the distance to the 
	device, so across the desk will be orders of magnitude faster than 
	across the Internet.
	
	This function is particularly useful after development to determine the
	impact of your application code on the stack's performance.  A before and
	after comparison will indicate if your application is unacceptably
	blocking the processor or taking too long to execute.

  Precondition:
	TCP is initialized.

  Parameters:
	None

  Returns:
	None
  ***************************************************************************/
void TCPTXPerformanceTask(void)
{
	static TCP_SOCKET MySocket = INVALID_SOCKET;
	static SYS_TICK dwTimeStart;
	static uint32_t dwBytesSent;
	static TCPIP_UINT32_VAL dwVLine;
	uint8_t vBuffer[10];
	static uint8_t vBytesPerSecond[12];
	uint16_t w;
	SYS_TICK dw;
	uint64_t qw;
	
	// Start the TCP server, listening on PERFORMANCE_PORT
	if(MySocket == INVALID_SOCKET)
	{
		MySocket = TCPOpenServer(IP_ADDRESS_TYPE_IPV4, TX_PERFORMANCE_PORT, 0);
	
        // Abort operation if no TCP socket could be opened.
        // If this ever happens, you need to update your tcp_config.h
		if(MySocket == INVALID_SOCKET)
			return;

		// Initialize cumulative line transmission counter
		dwVLine.Val = 0;
	}
	
	// See how many bytes we can write to the TX FIFO
	// If we can't fit a single line of data in, then 
	// lets just wait for now.
	w = TCPIsPutReady(MySocket);
	if(w < 12+27+5+32u)
		return;

	// Upon connection initialize timer and byte count variables
	if(TCPIsConnected(MySocket))
	{
		if(TCPWasReset(MySocket))
		{
			dwTimeStart = SYS_TICK_Get();
			dwBytesSent = 0;
			vBytesPerSecond[0] = 0;	// Initialize empty string right now
		}
	}

	vBuffer[0] = '0';
	vBuffer[1] = 'x';

	// Transmit as much data as the TX FIFO will allow
	while(w >= 10+29+12+32u)
	{
		dwVLine.Val = SYS_TICK_Get();
		
		// Convert line counter to ASCII hex string
		vBuffer[2] = btohexa_high(dwVLine.v[3]);
		vBuffer[3] = btohexa_low(dwVLine.v[3]);
		vBuffer[4] = btohexa_high(dwVLine.v[2]);
		vBuffer[5] = btohexa_low(dwVLine.v[2]);
		vBuffer[6] = btohexa_high(dwVLine.v[1]);
		vBuffer[7] = btohexa_low(dwVLine.v[1]);
		vBuffer[8] = btohexa_high(dwVLine.v[0]);
		vBuffer[9] = btohexa_low(dwVLine.v[0]);

		dwVLine.Val++;
	
		// Place all data in the TCP TX FIFO
		TCPPutArray(MySocket, vBuffer, sizeof(vBuffer));                                        // 10 B
		TCPPutString(MySocket, (const uint8_t*)": We are currently achieving ");                // 29 B
		TCPPutArray(MySocket, (const uint8_t*)"       ", 12-strlen((char*)vBytesPerSecond));     
		TCPPutString(MySocket, vBytesPerSecond);                                                // 12 B 
		TCPPutString(MySocket, (const uint8_t*)"00 bytes/second TX throughput.\r\n");           // 32 B

		w -= 10+29+12+32;
		dwBytesSent += 10+29+12+32;
	}
	
	// Send everything immediately
	TCPFlush(MySocket);

	// Calculate exact bytes/second, less truncation
	dw = SYS_TICK_Get() - dwTimeStart;
	if(dw > SYS_TICK_TicksPerSecondGet()/10)
	{
		qw = ((uint64_t)dwBytesSent)*(SYS_TICK_TicksPerSecondGet()/100) + (dw>>1);
		qw /= dw;
		ultoa((uint32_t)qw, vBytesPerSecond);
		dwTimeStart += dw;
		
		dwBytesSent = 0;
	}
}

/*****************************************************************************
  Function:
	void TCPRXPerformanceTask(void)

  Summary:
	Tests the receive performance of the TCP module.

  Description:
	This function tests the receive performance of the TCP module.  To use,
	open a telnet connection to the device on RX_PERFORMANCE_PORT (9763 by 
	default).  Then use your telnet utility to upload a large file to the
	device.  Each second the board will report back how many bytes were 
	received in the previous second.
	
	TCP performance is affected by many factors, including round-trip time 
	and the TCP buffer size.  For faster results, increase the size of the 
	RX buffer size in tcp_config.h.
    Round-trip time is affected by the distance to the 
	device, so across the desk will be orders of magnitude faster than 
	across the Internet.
	
	This function is particularly useful after development to determine the
	impact of your application code on the stack's performance.  A before and
	after comparison will indicate if your application is unacceptably
	blocking the processor or taking too long to execute.

  Precondition:
	TCP is initialized.

  Parameters:
	None

  Returns:
	None
  ***************************************************************************/
void TCPRXPerformanceTask(void)
{
	static TCP_SOCKET MySocket = INVALID_SOCKET;
	static SYS_TICK dwTimeStart;
	static uint32_t dwBytesRead;
	uint8_t vBuffer[12];
	uint16_t w, wGetLen;
	SYS_TICK dw;
	uint64_t qw;
	
	// Start the TCP server, listening on RX_PERFORMANCE_PORT
	if(MySocket == INVALID_SOCKET)
	{
		MySocket = TCPOpenServer(IP_ADDRESS_TYPE_IPV4, RX_PERFORMANCE_PORT, 0);
	
        // Abort operation if no TCP socket could be opened.
        // If this ever happens, you need to update your tcp_config.h
		if(MySocket == INVALID_SOCKET)
			return;

		dwTimeStart = SYS_TICK_Get();
		dwBytesRead = 0;
	}
	
	// Read all data out of the TCP RX FIFO
	w = TCPIsGetReady(MySocket);
	if(w == 0u)
		return;

	dwBytesRead += w;
	wGetLen = sizeof(vBuffer);
	while(w)
	{
		if(w < sizeof(vBuffer))
			wGetLen = w;
		TCPGetArray(MySocket, vBuffer, wGetLen);
		w -= wGetLen;
	}
	
	dw = SYS_TICK_Get() - dwTimeStart;
	if(dw > SYS_TICK_TicksPerSecondGet())
	{
		if(TCPIsPutReady(MySocket) < 40u)
			return;

		dwTimeStart = SYS_TICK_Get();

		// Calculate exact bytes/second, with rounding
		qw = (uint64_t)dwBytesRead * SYS_TICK_TicksPerSecondGet();
		qw += dw>>1;
		qw /= dw;
		ultoa((uint32_t)qw, vBuffer);
		TCPPutString(MySocket, vBuffer);
		TCPPutString(MySocket, (const uint8_t*)" bytes/second\r\n");
		
		dwBytesRead = 0;
	}
	
	
}

