/***************************************************************************//**
 *   @file   user.c
 *   @brief  Initialisation et traitement des commandes
 *   @author Kévin MARCHAND(Kevin.marchand@intelligence-electronique.com)
********************************************************************************
 * Copyright 2014(c) PLCD, Inc.
 *
 * Fonctions d'initialisation et de traitement des commandes.
 *
********************************************************************************
 *   SVN Revision: 1
*******************************************************************************/

/******************************************************************************/
/***************************** Include Files **********************************/
/******************************************************************************/

/* Device header file */
#if defined(__XC16__)
    #include <xc.h>
#elif defined(__C30__)
    #if defined(__dsPIC33E__)
    	#include <p33Exxxx.h>
    #elif defined(__dsPIC33F__)
    	#include <p33Fxxxx.h>
    #endif
#endif

#include <stdint.h>          /* For uint16_t definition                       */
#include <stdbool.h>         /* For true/false definition                     */
#include "user.h"            /* variables/params used by user.c               */
#include "UARTsComm.h"
#include "AD7190.h"
#include "GenericTypeDefs.h"
#include "HardwareProfile.h"
#include "MODBUSTCPServer.h"
#include "MemFlash.h"
#include "Include/TCPIPStack/Tick.h"
#include "SPIFRAM.h"

#include "B40/abcc.h"
#include "B40/ad_obj.h"
#include "B40/user_adi.h"

#include "usb/usb.h"
#include "usb/usb_device_cdc.h"
/******************************************************************************/
/************************ Variables Declarations ******************************/
/******************************************************************************/
extern WORD INPUT_REG[INPUT_REG_SIZE];

const char DELIMITER[] = {" =\0"};
const char DELIMITER_NULL[] = {"\0"};

BYTE USB_In_Buffer[CDC_DATA_IN_EP_SIZE];

DATA_Trame *USB_Mess_head;
DATA_Trame USB_Mess[MAX_USB_MESSAGE];
unsigned char USB_list_count;
/***************************************************************************//**
 * @brief Initialisations diverses pour l'application.
 * @details Active les interruptions.
 * @param none.
 *
 * @return none.
*******************************************************************************/
void InitApp(void)
{
    // Global interrupt enable.
    INTCON2bits.GIE = 1;

    // Activation des interruptions pour les entrées de comptage.
    IFS1bits.INT1IF = 0;
    IFS1bits.INT2IF = 0;

    IEC1bits.INT1IE = 1;
    IEC1bits.INT2IE = 1;

//  hours, minutes, seconds = 0;
    CRC_Init();

    //Initialise la liste chainé des message UART et USB
    USB_Mess_head = NULL;
    USB_list_count = 0;
 }

/***************************************************************************//**
 * @brief Ajoute un message dans la file d'attente USB.
 * @remarks Verifie l'etat de l'USB.
 * @param buff - buffer à ajouter à la file d'attente.
 * @param length - nombre d'octets du message.
 * @return none.
*******************************************************************************/
void putsUSB(char * buff, unsigned int length)
{
    if((USBDeviceState < CONFIGURED_STATE)||(USBSuspendControl==1)) return;
    memcpy( USB_Mess[USB_list_count].data, (const void*)buff, length);
    USB_Mess[USB_list_count].length = length;

    USB_enqMsg(&USB_Mess[USB_list_count]);
}

/***************************************************************************//**
 * @brief Ajoute un message dans la file d'attente USB.
 * @param toEnqueue - message à ajouter à la file d'attente.
 * @return none.
*******************************************************************************/
void USB_enqMsg(DATA_Trame * toEnqueue)
{
  DATA_Trame * USB_list_itor;

  if (toEnqueue != NULL)
  {
    if (USB_Mess_head == NULL)
      USB_Mess_head = toEnqueue;
    else
    {
      USB_list_itor = USB_Mess_head;
      while (USB_list_itor->next != NULL)
        USB_list_itor = USB_list_itor->next;
      USB_list_itor->next = toEnqueue;
    }
    toEnqueue->next = NULL;
    if(USB_list_count < MAX_USB_MESSAGE-1)
        USB_list_count++;
  }
}

/***************************************************************************//**
 * @brief Initialisation USB du mode CDC.
 * @remarks
 * @param none.
 * @return none.
*******************************************************************************/
void DeviceCDCInitialize()
{
    CDCInitEP();
    line_coding.bCharFormat = 0;
    line_coding.bDataBits = 8;
    line_coding.bParityType = 0;
    line_coding.dwDTERate = 9600;
}
/***************************************************************************//**
 * @brief Emission et Reception USB.
 *
 * @remarks Utilisation des fonctions de transmissions USB :
 *  - putrsUSBUSART() pour les chaines de caractères résidant en mémoire programme.
 *  - putsUSBUSART() pour les chaines de caractères résidant en RAM.
 *  - putUSBUSART() pour les données résidant en RAM.
 *
 * @param none.
 * @return none.
*******************************************************************************/
void DeviceCDCTasks()
{
    BYTE numBytesRead;
    char reponse[BUFFER_SIZE] = {0};

    //Emission USB
    if(USB_Mess_head != NULL && USBUSARTIsTxTrfReady() == true)
    {
        putUSBUSART(USB_Mess_head->data, USB_Mess_head->length);
        USB_Mess_head = USB_Mess_head->next;
        USB_list_count--;
    }
    //Reception USB
    if( USBUSARTIsTxTrfReady() == true)
    {
        numBytesRead = getsUSBUSART(USB_In_Buffer,sizeof(USB_In_Buffer)); //until the buffer is free.
        if(numBytesRead > 0)
        {
            USB_In_Buffer[numBytesRead] = 0;
            if(USB_In_Buffer[0] == ':'){
                ProccessCmd(&USB_In_Buffer[1],numBytesRead-1,&reponse[0]);
                if(strlen(reponse) > 0)// Si il y a une reponse on l'envoie
                    putsUSB(reponse,strlen(reponse));
            }
        }
    }
    CDCTxService();
}

/***************************************************************************//**
 * @brief Prend en charge le protocole de communication en traitant les commandes reçu.
 *
 * @param[in] trame - Pointeur sur la trame à traiter.
 * @param[in] length - Longueur de la trame.
 * @param[out] reponse - Réponse à la commande.
 *
 * @return none.
*******************************************************************************/
void ProccessCmd(char * trame, int length, char * reponse)
{
    char *cmd;
    char *token;
    char params[6][30];
    unsigned int nbParam = 0;
    unsigned long gaugeVal;
    BIT_PARITY newParity = -1;
    BYTE readBuff[BUFFER_SIZE];
    BYTE *lpreadBuff;
    unsigned int addrFram,nbBytes;

    memset(params, 0, sizeof(params));

    trame[length-1] = '\0';
    cmd = strtok((char *)trame,DELIMITER);                                      // Extraction de la commande
    if(cmd == NULL)return;

//    word = strtok(NULL, DELIMITER);                                           // Extraction du numéro de carte
//    if(word == NULL)return;
//    NoRecu = atoi(word);

    token = strtok(NULL, DELIMITER);                                            // Extraction du paramètre
    while(token != NULL && nbParam < 6){
        strcpy(&params[nbParam++][0], token);
        token = strtok(NULL, DELIMITER);                                        // Extraction du paramètre
    }

    memset(reponse, 0, sizeof(reponse));

//    Ecriture_Memoire_Flash_Ligne_Str(Fichier_CONFIG, 1, cmd);

    /***************LEDs***************/
    if(strcmp(cmd,"LED0") == 0){
        if(strcmp(params[0],"ON") == 0) mLED1_On();
        else if(strcmp(params[0],"OFF") == 0) mLED1_Off();        
        sprintf(reponse,":CMD LED0 OK\r\n");
    }else if(strcmp(cmd,"LED1") == 0){
        if(strcmp(params[0],"ON") == 0) mLED2_On();
        else if(strcmp(params[0],"OFF") == 0) mLED2_Off();
        sprintf(reponse,":CMD LED1 OK\r\n");
    /***************Gauge***************/
    }else if(strcmp(cmd,"GET_GAUGE") == 0){
        gaugeVal = (WORD)((INPUT_REG[2] & 0xFF) << 8 | (INPUT_REG[2] & 0xFF00) >> 8);
        gaugeVal = gaugeVal << 16;
        gaugeVal = gaugeVal | (WORD)((INPUT_REG[1] & 0xFF) << 8 | (INPUT_REG[1] & 0xFF00) >> 8);
        sprintf(reponse,":GAUGE=%06ld\r\n",gaugeVal);
    }else if(strcmp(cmd,"SETGAIN") == 0){
        
        // Selects unipolar operation and ADC's input range to +-Vref/1.
        if(atoi(params[0])==8)
            AD7190_RangeSetup(1, AD7190_CONF_GAIN_8);
        else if(atoi(params[0])==16)
            AD7190_RangeSetup(1, AD7190_CONF_GAIN_16);
        else if(atoi(params[0])==32)
            AD7190_RangeSetup(1, AD7190_CONF_GAIN_32);
        else if(atoi(params[0])==64)
            AD7190_RangeSetup(1, AD7190_CONF_GAIN_64);
        else if(atoi(params[0])==128)
            AD7190_RangeSetup(1, AD7190_CONF_GAIN_128);
        else
            AD7190_RangeSetup(1, AD7190_CONF_GAIN_1);

        // Calibrates channel AIN3(+) - AIN4(-).
        AD7190_Calibrate(AD7190_MODE_CAL_INT_ZERO, AD7190_CH_AIN3P_AIN4M);
         // Calibrates channel AIN3(+) - AIN4(-).
        AD7190_Calibrate(AD7190_MODE_CAL_INT_FULL, AD7190_CH_AIN3P_AIN4M);

        sprintf(reponse,":SET GAIN to %d  and Calibaration OK\r\n",atoi(params[0]));
    }
    /***************SORTIES***************/
    //! exemple OUT1 ON
    else if(strcmp(cmd,"OUT1") == 0){
        if(strcmp(params[0],"ON") == 0)OUT0_IO = 1;
        else if(strcmp(params[0],"OFF") == 0)OUT0_IO = 0;
        sprintf(reponse,":CMD OUT1 OK\r\n");
    }else if(strcmp(cmd,"OUT2") == 0){
        if(strcmp(params[0],"ON") == 0)OUT1_IO = 1;
        else if(strcmp(params[0],"OFF") == 0)OUT1_IO = 0;
        sprintf(reponse,":CMD OUT2 OK\r\n");
    }else if(strcmp(cmd,"OUT3") == 0){
        if(strcmp(params[0],"ON") == 0)OUT2_IO = 1;
        else if(strcmp(params[0],"OFF") == 0)OUT2_IO = 0;
        sprintf(reponse,":CMD OUT3 OK\r\n");
    }else if(strcmp(cmd,"OUT4") == 0){
        if(strcmp(params[0],"ON") == 0)OUT3_IO = 1;
        else if(strcmp(params[0],"OFF") == 0)OUT3_IO = 0;
        sprintf(reponse,":CMD OUT4 OK\r\n");
    }else if(strcmp(cmd,"OUT5") == 0){
        if(strcmp(params[0],"ON") == 0)OUT4_IO = 1;
        else if(strcmp(params[0],"OFF") == 0)OUT4_IO = 0;
        sprintf(reponse,":CMD OUT5 OK\r\n");
    }else if(strcmp(cmd,"OUT6") == 0){
        if(strcmp(params[0],"ON") == 0)OUT5_IO = 1;
        else if(strcmp(params[0],"OFF") == 0)OUT5_IO = 0;
        sprintf(reponse,":CMD OUT6 OK\r\n");
    }else if(strcmp(cmd,"OUT7") == 0){
        if(strcmp(params[0],"ON") == 0)OUT6_IO = 1;
        else if(strcmp(params[0],"OFF") == 0)OUT6_IO = 0;
        sprintf(reponse,":CMD OUT7 OK\r\n");
    }else if(strcmp(cmd,"OUT8") == 0){
        if(strcmp(params[0],"ON") == 0)OUT7_IO = 1;
        else if(strcmp(params[0],"OFF") == 0)OUT7_IO = 0;
        sprintf(reponse,":CMD OUT8 OK\r\n");

    /***************ENTREES***************/
    //! exemple IN1
    }else if(strcmp(cmd,"IN1") == 0){
        sprintf(reponse,":IN1=%d\r\n",!IN0_IO);
    }else if(strcmp(cmd,"IN2") == 0){
        sprintf(reponse,":IN2=%d\r\n",!IN1_IO);
    }else if(strcmp(cmd,"IN3") == 0){
        sprintf(reponse,":IN3=%d\r\n",!IN2_IO);
    }else if(strcmp(cmd,"IN4") == 0){
        sprintf(reponse,":IN4=%d\r\n",!IN3_IO);
    }else if(strcmp(cmd,"IN5") == 0){
        sprintf(reponse,":IN5=%d\r\n",!IN4_IO);
    }else if(strcmp(cmd,"IN6") == 0){
        sprintf(reponse,":IN6=%d\r\n",!IN5_IO);
    }else if(strcmp(cmd,"IN7") == 0){
        sprintf(reponse,":IN7=%d\r\n",!IN6_IO);
    }else if(strcmp(cmd,"IN8") == 0){
        sprintf(reponse,":IN8=%d\r\n",!IN7_IO);
    /***************UART CONFIG***************/
    //! exemple UART2 115200 1 N 8
    }else if(strcmp(cmd,"UART2") == 0){
        if(!UART2_SetBaudrate(atol(params[0])))
            sprintf(reponse,":Err: Baudrate incorrect ou identique\r\n");
        if(!UART2_SetStopBit(atoi(params[1])))
            sprintf(&reponse[strlen(reponse)],":Err: nb Stop bit incorrect\r\n");
        if(params[2][0]=='N' && atoi(params[3]) == 8)
            newParity = NO_PAR_8BIT;
        else if(params[2][0]=='E' && atoi(params[3]) == 8)
            newParity = EVEN_PAR_8BIT;
        else if(params[2][0]=='O' && atoi(params[3]) == 8)
            newParity = ODD_PAR_8BIT;
        else if(params[2][0]=='N' && atoi(params[3]) == 9)
            newParity = NO_PAR_9BIT;
        if(!UART2_SetParityNBits(newParity))
            sprintf(&reponse[strlen(reponse)],":Err: nb bits ou parité incorrect\r\n");
    /***************AD Continuous read (interruption mode)***************/
    }else if(strcmp(cmd,"STARTCREAD") == 0){
        AD7190_StartContinuousRead(atoi(params[0]));
    }else if(strcmp(cmd,"STOPCREAD") == 0){
        AD7190_StopContinuousRead();
    /***************Compteur de millisecondes ***************/
    }else if(strcmp(cmd,"GETMILLIS") == 0){
        sprintf(reponse,":CNTMILLIS=%010lu\r\n",GetCounterMillis());
    }else if(strcmp(cmd,"INITMILLIS") == 0){
        ResetCounterMillis();
    /***************FRAM READ***************/
    //! exemple GETFRAM 0000 10
    }else if(strcmp(cmd,"GETFRAM") == 0){
        addrFram = atoi(params[0]);
        nbBytes = atoi(params[1]);
        if(nbBytes > sizeof(readBuff)-8) nbBytes = sizeof(readBuff)-8;
        FRAMBeginRead(addrFram);
        //FRAMReadArray(addrFram, (BYTE*)&read[0], nbByte);
        lpreadBuff = &readBuff[0];
        while(nbBytes--)
        {
            *lpreadBuff++ = FRAMRead();
        }
        *lpreadBuff=0;
        sprintf(reponse,":FRAM=%s\r\n",&readBuff[0]);
    }else{
        sprintf(reponse,":Err: commande inconnu.\r\n");
    }
    //Sauvegarde de la commande dans la FRAM à l'adresse 0x0000 pour TEST !!
    FRAMBeginWrite(0x0000);
    FRAMWriteArray((BYTE*)cmd, strlen(cmd));
}



/***************************************************************************//**
 * @brief This function configures CRC module
 *
 * @return none.
*******************************************************************************/
void CRC_Init()
{
   CRCXORL = POLY;      // Generator Polynomial
   CRCCON2 = POLYLEN;   // Length of polynomial-1

   CRCCON1bits.CRCEN =1;
}

/***************************************************************************//**
 * @brief This function calculates CRC checksum for the data provide by the user,
 *        based on the polynomial set in the CRCXOR Register.
 * @details This input parameter is provided as a provision to allow continuation
 *          of previously computed checksum. In case the checksum is being calculated
 *          for a fresh set of data then the input value for prev_CRC should be '0'.
 * @pre CRC Module must be Initialized (see CRC_Init()).
 *
 * @param data - Pointer to the first data word for which CRC needs to be calculated.
 * @param Number_of_words - Total number of words for which CRC needs to be calculated.
 * @param prev_CRC - previous CRC result or 0 for new.
 *
 * @return Returns Two Byte CRC checksum based on the set polynomial.
*******************************************************************************/
unsigned int CRC_ChecksumWord(int Number_of_words, unsigned char* data)
{
#define POLY 0xa001;
 unsigned int crc = 0xFFFF;
 int i, cc;
 char car;

// Suppression du début de trame ethernet
// for (i=0; i<6; i++)
//     data++;
//Number_of_words = 7;

 while (Number_of_words--)
	{
        car = *data;
	crc ^= *data++;
	for (i = 0; i < 8; i++)
		{
		cc = crc & 1;
                crc = crc / 2;
		if (cc) crc ^= POLY
		}
	}
return(crc);
}

/***************************************************************************//**
 * @brief Mise à zéro du compteur de milliseconds
 *
 * @return none.
*******************************************************************************/
void ResetCounterMillis(void)
{
    TickInit();
}

/***************************************************************************//**
 * @brief Récupère la valeur du compteur de milliseconds
 *
 * @return Compteur de millisecondes.
*******************************************************************************/
DWORD GetCounterMillis(void)
{
    return TickConvertToMilliseconds(TickGet());
}

/***************************************************************************//**
 * @brief Mise à jour des données en fonction de la valeur des ADI
 * @return none.
*******************************************************************************/
void UpdateB40ADI()
{
    static UINT8 DigitalOutput, DigitalInput;
    UINT8_BITS tmpDigitalInput;

    if( ( ABCC_AnbState() != ABP_ANB_STATE_PROCESS_ACTIVE ) )
        return;

    if(DigitalOutput != ADI_DigitalOutput){
        OUT0_IO = ADI_DigitalOutput & 0x01;
        OUT1_IO = (ADI_DigitalOutput>>1) & 0x01;
        OUT2_IO = (ADI_DigitalOutput>>2) & 0x01;
        OUT3_IO = (ADI_DigitalOutput>>3) & 0x01;
        OUT4_IO = (ADI_DigitalOutput>>4) & 0x01;
        OUT5_IO = (ADI_DigitalOutput>>5) & 0x01;
        OUT6_IO = (ADI_DigitalOutput>>6) & 0x01;
        OUT7_IO = (ADI_DigitalOutput>>7) & 0x01;
        DigitalOutput = ADI_DigitalOutput;
    }

    tmpDigitalInput.bits.b0 = !IN0_IO;
    tmpDigitalInput.bits.b1 = !IN1_IO;
    tmpDigitalInput.bits.b2 = !IN2_IO;
    tmpDigitalInput.bits.b3 = !IN3_IO;
    tmpDigitalInput.bits.b4 = !IN4_IO;
    tmpDigitalInput.bits.b5 = !IN5_IO;
    tmpDigitalInput.bits.b6 = !IN6_IO;
    tmpDigitalInput.bits.b7 = !IN7_IO;

    if(tmpDigitalInput.Val != DigitalInput){
        DigitalInput = tmpDigitalInput.Val;
        ADI_DigitalInput = DigitalInput;
        AD_NewWrPd();
    }
}
