/***************************************************************************//**
 *   @file   BerkeleyTCPServer.c
 *   @brief  Berekely TCP serveur implementation
 *   @author Kévin MARCHAND(Kevin.marchand@intelligence-electronique.com)
********************************************************************************
 * Copyright 2014(c) PLCD, Inc.
 *
 * Cette application utilise les API des sockets BSD (Berkeley), et démarre un
 * serveur TCP sur le port <b>PORTNUM</b>. Toutes les trame reçues sont transmises
 * à la fonction ProccessCmd() (voir user.c) pour être traitées. Le résultat de 
 * ProccessCmd() et retourné au client. Le nombre de connexions simultanées
 * est défines par <b>MAX_CLIENT</b>.
 *
********************************************************************************
 *   SVN Revision: 1
*******************************************************************************/

/******************************************************************************/
/***************************** Include Files **********************************/
/******************************************************************************/
#include "TCPIPStack/TCPIPConfig.h"
#include "UARTsComm.h"
#include "user.h"
#include "Process.h"
#include "MODBUSTCPServer.h"

#if defined(STACK_USE_BERKELEY_API)

#include "Include/TCPIPStack/TCPIP.h"

//! Numéro du port d'écoute
#define PORTNUM 9764
//! Nombre maximum de connexions simultanées acceptées par le serveur.
#define MAX_CLIENT 3

char TestModbus(char trame[BUFFER_SIZE], int longueur)
{
int             longueurTrameTheo;
int             longueurTrame;

if (longueur >= 250)
    return(0);

if (longueur > 7)
{
    longueurTrameTheo = trame[6];
    longueurTrame = longueur;

    if ((trame[7] == 0x10) && (longueurTrame > longueurTrameTheo+8))
    {
    return(1);
    }

    if ((trame[7] == 0x03) && (longueurTrame >= 0x0C))
    {
    return(1);
    }
}
return(0);
}


/***************************************************************************//**
 * @brief Machine d'état gérant les connexions TCP.
 * @pre La stack TCPIP doit être initialisée.
 * @param none.
 * @return none.
*******************************************************************************/
void BerkeleyTCPServer(void)
{
    static SOCKET bsdServerSocket;   
    static SOCKET ClientSock[MAX_CLIENT];
    struct sockaddr_in addr;
    struct sockaddr_in addRemote;
    int addrlen = sizeof(struct sockaddr_in);
    char bfr[BUFFER_SIZE];
    char reponse[BUFFER_SIZE];
    int length;
    int i;
    int w;

    static enum
    {
	    BSD_INIT = 0,
        BSD_CREATE_SOCKET,
        BSD_BIND,
        BSD_LISTEN,
        BSD_OPERATION
    } BSDServerState = BSD_INIT;

    switch(BSDServerState)
    {
	    case BSD_INIT:
        	// Initialize all client socket handles so that we don't process 
        	// them in the BSD_OPERATION state
        	for(i = 0; i < MAX_CLIENT; i++)
                    ClientSock[i] = INVALID_SOCKET;
        		
        	BSDServerState = BSD_CREATE_SOCKET;
        	// No break needed
	    
        case BSD_CREATE_SOCKET:
            // Create a socket for this server to listen and accept connections on
            bsdServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
            if(bsdServerSocket == INVALID_SOCKET){
                #if defined(STACK_USE_UART)
                    putrsUART((ROM char*)"BerkeleyTCPServer : INVALID_SOCKET\r\n");
                #endif
                return;
            }
            #if defined(STACK_USE_UART)
                    putrsUART((ROM char*)"BerkeleyTCPServer : BSD_CREATE_SOCKET\r\n");
                #endif
            BSDServerState = BSD_BIND;
            // No break needed

        case BSD_BIND:
            // Bind socket to a local port

            addr.sin_port = PAR.PortIP;
            addr.sin_addr.S_un.S_addr = IP_ADDR_ANY;
            if( bind( bsdServerSocket, (struct sockaddr*)&addr, addrlen ) == SOCKET_ERROR ){
                #if defined(STACK_USE_UART)
                    putrsUART((ROM char*)"BerkeleyTCPServer bind : SOCKET_ERROR\r\n");
                #endif
                return;
            }
            BSDServerState = BSD_LISTEN;
            // No break needed
         #if defined(STACK_USE_UART)
                    putrsUART((ROM char*)"BerkeleyTCPServer bind: LISTEN\r\n");
                #endif
      case BSD_LISTEN:
            if(listen(bsdServerSocket, MAX_CLIENT) == 0)
	            BSDServerState = BSD_OPERATION;

                    // No break.  If listen() returns SOCKET_ERROR it could be because
                    // MAX_CLIENT is set to too large of a backlog than can be handled
                    // by the underlying TCP socket count (TCP_PURPOSE_BERKELEY_SERVER
                    // type sockets in TCPIPConfig.h).  However, in this case, it is
                    // possible that some of the backlog is still handleable, in which
                    // case we should try to accept() connections anyway and proceed
                    // with normal operation.
         
      case BSD_OPERATION:
            for(i=0; i<MAX_CLIENT; i++)
            {
                EXP.EtatSocket[i] = ClientSock[i];
                
                if(ClientSock[i] == INVALID_SOCKET)
                    T100[timer_CloseSocket1+i] = 3000;
 
	            // Accept any pending connection requests, assuming we have a place to store the socket descriptor
                if(ClientSock[i] == INVALID_SOCKET)
                    ClientSock[i] = accept(bsdServerSocket, (struct sockaddr*)&addRemote, &addrlen);

                // If this socket is not connected then no need to process anything
                if(ClientSock[i] == INVALID_SOCKET)
                	continue;

	            // For all connected sockets, receive and send back the data
                length = recv( ClientSock[i], bfr, sizeof(bfr), 0);

                if (length > 0)
                    {
                        switch (i)
                        {
                            case 0 : T100[timer_CloseSocket1] = 3000; break;
                            case 1 : T100[timer_CloseSocket2] = 3000; break;
                            case 2 : T100[timer_CloseSocket3] = 3000; break;
                        }
                        memcpy(MODBUS_RX, bfr, length);
                        if (TestModbus(bfr, length))
                        {
                            memcpy(MODBUS_RX, bfr, sizeof(bfr));
                            w = MODBUSTCPServer();
                               
                            if (w > 0)
                                send(ClientSock[i], MODBUS_TX, w, 0);
                        }
                        else
                        {
                            MODBUS_TX[0] = MODBUS_RX[0];
                            MODBUS_TX[1] = MODBUS_RX[1];
                            MODBUS_TX[2] = MODBUS_RX[2];
                            MODBUS_TX[3] = MODBUS_RX[3];
                            MODBUS_TX[4] = MODBUS_RX[4];
                            MODBUS_TX[5] = 3;
                            MODBUS_TX[6] = MODBUS_RX[6];
                            MODBUS_TX[7] = MODBUS_RX[7]+0x80;
                            MODBUS_TX[8] = 01;
                            send(ClientSock[i], MODBUS_TX, 9, 0);
                        }
                }
                else if( length < 0 )
                {
                    closesocket( ClientSock[i] );
                    ClientSock[i] = INVALID_SOCKET;
                }

                if ((!T100[timer_CloseSocket1+i])
                    && (ClientSock[i] != INVALID_SOCKET))
                {
                    closesocket( ClientSock[i] );
                    ClientSock[i] = INVALID_SOCKET;
                }
            }
            break;

       default:
            return;
    }
    return;
}

#endif //#if defined(STACK_USE_BERKELEY_API)

