/****************************************************************************/
/* Copyright 2009 - 2016 MBARI                                              */
/****************************************************************************/
/* Summary  : Main Routine for OASIS5 Daughter Board                        */
/* Filename : main.c                                                        */
/* Author   : Robert Herlien (rah)                                          */
/* Project  : OASIS5 Controller                                             */
/* Revision : 1.0                                                           */
/* Created  : 10/28/2015                                                    */
/*                                                                          */
/* MBARI provides this documentation and code "as is", with no warranty,    */
/* express or implied, of its quality or consistency. It is provided without*/
/* support and without obligation on the part of the Monterey Bay Aquarium  */
/* Research Institute to assist in its use, correction, modification, or    */
/* enhancement. This information should not be published or distributed to  */
/* third parties without specific written permission from MBARI.            */
/*                                                                          */
/****************************************************************************/
/* Modification History:                                                    */
/* 28oct2015 rah - created from main.c in FOCE SensorNode, in turn created  */
/*                 from BRS Chamber Controller main.c                       */
/* 07mar2016 rah - modified for OASIS5 Daughter Board                       */
/****************************************************************************/

#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "mcc.h"
#include "regs.h"
#include "dig_io.h"
#include "flash_mem.h"
#include "periph.h"
#include "serial.h"
#include "inst_port.h"
#include "config.h"
#include "buffer.h"
#include "sys_timer.h"
#include "misc.h"


/***************************** Modbus includes ******************************/
#include "mb.h"
#include "port.h"
#include "mbport.h"
/***************************** Modbus includes ******************************/

/****************************** Modbus defines ******************************/

#define MB_SLAVE_ID     1               /* Slave ID not used, does not matter */
#define MB_BAUD_RATE    500000          /* Baud rate does not matter          */


/****************************** Modbus variables ****************************/
USHORT mbTimeout = MB_TMOUT_50US;

static USHORT usRegInputStart = REG_INPUT_START;
static USHORT usRegInputBuf[REG_INPUT_NREGS];

static BOOL usGotModbusFrame = FALSE;

static ConfigData configData =
    { MB_SLAVE_ID, MB_BAUD_RATE, 1, BLINKY_OFF, FALSE, 0 };


/************************************************************************/
/* Function    : main                                                   */
/* Purpose     : Application main() function                            */
/* Inputs      : None                                                   */
/* Outputs     : Return value (unused)                                  */
/************************************************************************/
int main (void)
{
    /* Allow nested interrupts  */
   _NSTDIS = 0;

    /* init functions */
    SYSTEM_Initialize();        /* mcc.c, from Microchip Code Config    */

    periphInit();               /* Turn off unused peripherals          */
    digInit();                  /* Initialize GPIO                      */
    serInit();
    instPortInit();
    tmrInit();
    miscInit();

    SRbits.IPL = 0;             /* Turn on interrupts   */

    setBlinkyDebug(configData.blinkyDebug);
    digPwrVecSet(0);

    eMBInit(MB_RTU, (UCHAR)configData.id, 0, configData.baud, MB_PAR_NONE);

    eMBEnable();               /* Enable the Modbus Protocol Stack. */

    for(;;)
    {
        /* service various functions that need periodic polling */
        eMBPoll(); 
        tmrTask();
        blinkyTask(usGotModbusFrame);

        /* start polling instrument port tasks once modbus messages have started */
        if ( usGotModbusFrame )
            instPortTxTask();
    }
}


/****************************************************************************/
/*                             Modbus callbacks                             */
/****************************************************************************/
/*   Input Register callback (used just to return number of ModBus polls)   */
/****************************************************************************/
eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    int             iRegIndex;

    /* set the modbus frame received flag to true */
    usGotModbusFrame = TRUE;

    if( ( usAddress >= REG_INPUT_START )
        && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
    {
        iRegIndex = ( int )( usAddress - usRegInputStart );
        while( usNRegs > 0 )
        {
            *pucRegBuffer++ =
                ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );
            *pucRegBuffer++ =
                ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );
            iRegIndex++;
            usNRegs--;
        }
    }
    else
        eStatus = MB_ENOREG;

    return eStatus;
}


/****************************************************************************/
/*      Holding Register callback (main ModBus Register interface           */
/****************************************************************************/
eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
                 eMBRegisterMode eMode )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    USHORT          loByte;
    USHORT          hiByte;
    USHORT          reg;

    /* set the modbus frame received flag to true */
    usGotModbusFrame = TRUE;

    if ( usAddress > REG_HOLDING_MAX )
        return MB_ENOREG;

    /* read mode */
    if ( eMode == MB_REG_READ )
    {
        if ( ((usAddress + usNRegs) > INST_HOLDING_START) && 
             (usAddress <= INST_HOLDING_END) )
        {
            instSnapshotRxAvail();
        }

        while( usNRegs > 0 )
        {
            /* set the reg to zero just in case there is no representation */
            reg = 0;

            if ((usAddress >= MISC_START) && (usAddress <= MISC_END))
                reg = miscRegRead(usAddress, &configData);
            
            else if ((usAddress >= INST_HOLDING_START) && (usAddress <= INST_HOLDING_END))
                reg = instPortReadReg(usAddress);

            /* assign it to the reg buffer */
            *pucRegBuffer++ = (unsigned char)( reg >> 8 );
            *pucRegBuffer++ = (unsigned char)( reg & 0xFF );

            /* increment register index and address */
            --usNRegs;
            ++usAddress;
        }
    }

    /* write mode */
    if ( eMode == MB_REG_WRITE )
    {
        while( usNRegs > 0 )
        {
            /* get a register from the reg buffer */
            hiByte = *(pucRegBuffer++);
            hiByte <<= 8;
            loByte = *(pucRegBuffer++);

            reg = ((hiByte & 0xFF00) + (loByte & 0x00FF));

            if ((usAddress >= MISC_START) && (usAddress <= MISC_END))
                eStatus = miscRegWrite(usAddress, reg, &configData);
            
            else if ( (usAddress >= INST_HOLDING_START) && 
                      (usAddress <= INST_HOLDING_END) )
                instPortWriteReg(usAddress, reg);

            if ((usAddress == MB_TIMEOUT_REG) && (reg >= 35))
            {
                xMBPortTimersInit(reg);
                mbTimeout = reg;
            }

            /* increment register index and address */
            --usNRegs;
            ++usAddress;
        }

        /* If a write is ending then all Tx write 
           transactions are over, so clear all txWriteRequest registers. */
        instPortClearWriteReq();
    }

    return(eStatus);
}


int isBitSet(int bitNum, UCHAR* buff)
{
    return(buff[bitNum/8] & (1 << (bitNum%0x7)));
}

void bitSet(int bitNum, UCHAR* buff, int val)
{
    /* set of clear the bit depending on the val */
    if ( val ) 
        buff[(bitNum / 8)] |= (1 << (bitNum&0x7));
    else
        buff[(bitNum / 8)] &= ~(1 << (bitNum&0x7));

    return;
}


/****************************************************************************/
/*      Coil callback (Relays, Tx Enables, Blinky)                          */
/****************************************************************************/
eMBErrorCode
eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils,
               eMBRegisterMode eMode )
{
    int i, coil, val;
    
    /* set the modbus frame received flag to true */
    usGotModbusFrame = TRUE;

    /* only coils 1 - 12 are represented */
    if ( (usAddress < FIRST_COIL) || 
         (usAddress + usNCoils) > (TOTAL_COILS + FIRST_COIL) )
        return(MB_ENOREG);

    /* perform a coil write(s) */
    if ( eMode == MB_REG_WRITE )
    {
        for (i = 0; i < usNCoils; i++)
        {
            /* calculate the coil number */
            coil = i + usAddress;

            /* see if the bit is set for that coil */
            val = isBitSet(i, pucRegBuffer);

            /* take the appropriate action for that coil */
            switch ( coil )
            {
                /* instrument port power bits */
                case  1: digPwrSet(0, val); break;
                case  2: digPwrSet(1, val); break;
                case  3: digPwrSet(2, val); break;
                case  4: digPwrSet(3, val); break;
                case  5: digPwrSet(4, val); break;
                case  6: digPwrSet(5, val); break;
                /* instrument port xcvr bits */
                case  9: if (val) digTx0En(); else digTx0Dis(); break;
                case 10: if (val) digTx1En(); else digTx1Dis(); break;
                case 11: if (val) digTx2En(); else digTx2Dis(); break;
                case 12: if (val) digTx3En(); else digTx3Dis(); break;
                case 13: if (val) digTx4En(); else digTx4Dis(); break;
                case 24: usGotModbusFrame = FALSE;
                    setBlinkyDebug(configData.blinkyDebug);
                    break;
                default: break;
            }
        }

        return(MB_ENOERR);
    }


    /* perform a coil read(s) */
    if ( eMode == MB_REG_READ )
    {
        /* clear all of the coil bytes in the buffer */
        memset(pucRegBuffer, 0, ((usNCoils / 8) + 1));

        for (i = 0; i < usNCoils; ++i)
        {
            /* calculate the coil number */
            coil = i + usAddress;
            
            switch ( coil )
            {
                /* instrument port power bits */
                case  1: bitSet(i, pucRegBuffer, digPwrIsOn(0)); break;
                case  2: bitSet(i, pucRegBuffer, digPwrIsOn(1)); break;
                case  3: bitSet(i, pucRegBuffer, digPwrIsOn(2)); break;
                case  4: bitSet(i, pucRegBuffer, digPwrIsOn(3)); break;
                case  5: bitSet(i, pucRegBuffer, digPwrIsOn(4)); break;
                case  6: bitSet(i, pucRegBuffer, digPwrIsOn(5)); break;

                /* instrument port xcvr bits */
                case  9: bitSet(i, pucRegBuffer, digTx0IsEnabled()); break;
                case 10: bitSet(i, pucRegBuffer, digTx1IsEnabled()); break;
                case 11: bitSet(i, pucRegBuffer, digTx2IsEnabled()); break;
                case 12: bitSet(i, pucRegBuffer, digTx3IsEnabled()); break;
                case 13: bitSet(i, pucRegBuffer, digTx4IsEnabled()); break;
                default: break;
            }
        }

        return(MB_ENOERR);
    }

    return(MB_EIO);
}

/****************************************************************************/
/*      Discrete Input Callback (not used)                                  */
/****************************************************************************/
eMBErrorCode
eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
    /* set the modbus frame received flag to true */
    usGotModbusFrame = TRUE;

    return(MB_ENOREG);
}
