/****************************************************************************/
/*
 * MODULE              JN-AN-1162 JenNet-IP Smart Home
 *
 * DESCRIPTION         OccupancyStatus MIB Implementation
 */
/****************************************************************************/
/*
 * This software is owned by NXP B.V. and/or its supplier and is protected
 * under applicable copyright laws. All rights are reserved. We grant You,
 * and any third parties, a license to use this software solely and
 * exclusively on NXP products [NXP Microcontrollers such as JN5168, JN5164].
 * You, and any third parties must reproduce the copyright and warranty notice
 * and any other legend of ownership on each copy or partial copy of the
 * software.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Copyright NXP B.V. 2014. All rights reserved
 */
/****************************************************************************/

/****************************************************************************/
/***        Include files                                                 ***/
/****************************************************************************/
/* Standard includes */
#include <string.h>
/* SDK includes */
#include <jendefs.h>
/* Hardware includes */
#include <AppHardwareApi.h>
#include <PeripheralRegs.h>
/* Stack includes */
#include <Api.h>
#include <AppApi.h>
#include <JIP.h>
#include <6LP.h>
#include <AccessFunctions.h>
/* JenOS includes */
#include <dbg.h>
#include <dbg_uart.h>
#include <os.h>
#include <pdm.h>
/* Application device includes */
#include "DeviceDefs.h"
#include "Node.h"
#include "Address.h"
#include "MibSensor.h"
#include "MibOccupancyStatus.h"
#include "MibOccupancyConfig.h"
#include "MibOccupancyControl.h"
#include "DriverOccupancy.h"

/****************************************************************************/
/***        Macro Definitions                                             ***/
/****************************************************************************/
/* Data access */
#define PS_MIB_OCCUPANCY_CONFIG   ((tsMibOccupancyConfig *)  psMibOccupancyStatus->pvMibOccupancyConfig)
#define PS_MIB_OCCUPANCY_CONTROL  ((tsMibOccupancyControl *) psMibOccupancyStatus->pvMibOccupancyControl)

/****************************************************************************/
/***        Type Definitions                                              ***/
/****************************************************************************/

/****************************************************************************/
/***        Local Function Prototypes                                     ***/
/****************************************************************************/

/****************************************************************************/
/***        Exported Variables                                            ***/
/****************************************************************************/

/****************************************************************************/
/***        Local Variables                                               ***/
/****************************************************************************/
PRIVATE tsMibOccupancyStatus  *psMibOccupancyStatus;

/****************************************************************************/
/***        Exported Functions                                            ***/
/****************************************************************************/

/****************************************************************************
 *
 * NAME: MibOccupancyStatus_vInit
 *
 * DESCRIPTION:
 * Initialises data
 *
 ****************************************************************************/
PUBLIC void MibOccupancyStatus_vInit(thJIP_Mib         		 hMibOccupancyStatusInit,
                               		 tsMibOccupancyStatus  *psMibOccupancyStatusInit,
                               		 void  				   *pvMibOccupancyConfigInit,
                               		 void 				   *pvMibOccupancyControlInit)
{
    /* Debug */
    DBG_vPrintf(DEBUG_MIB_OCCUPANCY_STATUS, "\n%sMibOccupancyStatus_vInit() {%d}", acDebugIndent, sizeof(tsMibOccupancyStatus));

    /* Valid data pointer ? */
    if (psMibOccupancyStatusInit != (tsMibOccupancyStatus *) NULL)
    {
        /* Take copy of pointer to data */
        psMibOccupancyStatus = psMibOccupancyStatusInit;
        /* Take a copy of the MIB handle */
        psMibOccupancyStatus->hMib = hMibOccupancyStatusInit;

        /* Take a copy of the other MIB pointers */
		psMibOccupancyStatus->pvMibOccupancyConfig  = pvMibOccupancyConfigInit;
		psMibOccupancyStatus->pvMibOccupancyControl = pvMibOccupancyControlInit;

        /* Note the sensor type */
        psMibOccupancyStatus->sTemp.u8SensorType = DriverOccupancy_u8Type();
    }

    /* Initialise driver */
	DriverOccupancy_vInit();

	/* Valid Occupancy Status LED ? */
	#if DEVICE_OUTPUT_MASK_OCCUPANCY_STATUS
	{
		/* Configure as output */
		vAHI_DioSetDirection(0, DEVICE_OUTPUT_MASK_OCCUPANCY_STATUS);
		/* Turn LED off (by turning output on) */
		vAHI_DioSetOutput(DEVICE_OUTPUT_MASK_OCCUPANCY_STATUS, 0);
    }
    #endif
	/* Valid Occupancy Sensor LED ? */
	#if OCCUPANCY_SENSOR_LED
	{
		/* Configure as output */
		vAHI_DioSetDirection(0, OCCUPANCY_SENSOR_LED_MASK);
		/* Turn LED off (by turning output on) */
		vAHI_DioSetOutput(OCCUPANCY_SENSOR_LED_MASK, 0);
    }
    #endif
}

/****************************************************************************
 *
 * NAME: MibOccupancyStatus_vRegister
 *
 * DESCRIPTION:
 * Registers MIB
 *
 ****************************************************************************/
PUBLIC void MibOccupancyStatus_vRegister(void)
{
    teJIP_Status eStatus;

    /* Debug */
    DBG_vPrintf(DEBUG_MIB_OCCUPANCY_STATUS, "\n%sMibOccupancyStatus_vRegister()", acDebugIndent);
    Node_vDebugIndent(DEBUG_MIB_OCCUPANCY_STATUS);

	/* Start refresh timer */
	psMibOccupancyStatus->u16StateRefreshTimer = PS_MIB_OCCUPANCY_CONFIG->sPerm.u16StateRefresh;

    /* Register MIB */
    eStatus = eJIP_RegisterMib(psMibOccupancyStatus->hMib);
    /* Debug */
    DBG_vPrintf(DEBUG_MIB_OCCUPANCY_STATUS, "\n%seJIP_RegisterMib(OccupancyStatus) = %d", acDebugIndent, eStatus);
    Node_vDebugOutdent(DEBUG_MIB_OCCUPANCY_STATUS);
}

/****************************************************************************
 *
 * NAME: MibOccupancyStatus_vTick
 *
 * DESCRIPTION:
 * Timer function
 *
 ****************************************************************************/
PUBLIC void MibOccupancyStatus_vTick(void)
{
	/* Do we need to transmit the occupancy status ? */
	if (psMibOccupancyStatus->bTxState)
	{
		/* Clear flag */
		psMibOccupancyStatus->bTxState = FALSE;
		/* Restart refresh timer */
		psMibOccupancyStatus->u16StateRefreshTimer = PS_MIB_OCCUPANCY_CONFIG->sPerm.u16StateRefresh;
		/* Is the network running and up ? */
		if (Node_u32StackState() == NODE_STACK_STATE_RUNNING && Node_bUp())
		{
			/* Is a valid MIB specified to transmit to ? */
			if (PS_MIB_OCCUPANCY_CONFIG->sPerm.u32StateMibId != 0)
			{
				teJIP_Status eResult;
				ts6LP_SockAddr s6LP_SockAddr;

				/* Form address */
				s6LP_SockAddr.sin6_family   = E_6LP_PF_INET6;
				s6LP_SockAddr.sin6_flowinfo = 0;
				s6LP_SockAddr.sin6_port     = JIP_DEFAULT_PORT;
				s6LP_SockAddr.sin6_scope_id = 0;
				memcpy(&s6LP_SockAddr.sin6_addr, &PS_MIB_OCCUPANCY_CONFIG->sPerm.sStateAddress, sizeof(in6_addr));
				/* Transmit update */
				eResult = eJIP_Remote_Mib_Set(&s6LP_SockAddr, 0, PS_MIB_OCCUPANCY_CONFIG->sPerm.u32StateMibId, PS_MIB_OCCUPANCY_CONFIG->sPerm.u8StateVarIdx, E_JIP_VAR_TYPE_UINT8, &psMibOccupancyStatus->sTemp.u8Occupancy, 1);
				/* Debug */
				DBG_vPrintf(DEBUG_MIB_OCCUPANCY_STATUS, "\n%sMibOccupancyStatus_vTick() { eJIP_Remote_Mib_Set(0x%x, %d, %d) = %d }",
					acDebugIndent,
					PS_MIB_OCCUPANCY_CONFIG->sPerm.u32StateMibId, PS_MIB_OCCUPANCY_CONFIG->sPerm.u8StateVarIdx, psMibOccupancyStatus->sTemp.u8Occupancy,
					eResult);

				/* UART ? */
				#ifdef UART_H_INCLUDED
				{
					/* Initialise UART */
					UART_vChar(' ');
					UART_vChar('O');
					UART_vChar('=');
					UART_vChar('0'+eResult);
					UART_vChar(' ');
				}
				#endif
			}
		}
	}
	/* Are there any variable notifications pending ? */
	else if ((psMibOccupancyStatus->u32NotifyChanged & VAR_MASK_OCCUPANCY_STATUS) != 0)
	{
		/* Use common function to output notifications */
		Node_vJipNotifyChanged(psMibOccupancyStatus->hMib, &psMibOccupancyStatus->u32NotifyChanged, VAR_MASK_OCCUPANCY_STATUS, VAR_COUNT_OCCUPANCY_STATUS);
	}
}

/****************************************************************************
 *
 * NAME: MibOccupancyStatus_vAppTimer100ms
 *
 * DESCRIPTION:
 * Timer function
 *
 ****************************************************************************/
PUBLIC void MibOccupancyStatus_vAppTimer100ms(void)
{
	bool_t bRead; /* Reading from driver */

	/* Is the occupancy sensor enabled and sensitivity is set ? */
	if (VAR_VAL_OCCUPANCY_CONTROL_MODE_OFF != PS_MIB_OCCUPANCY_CONTROL->sPerm.u8Mode &&
	    0 								   <  PS_MIB_OCCUPANCY_CONFIG->sPerm.u8Sensitivity)
	{
		/* Take occupancy reading */
		bRead = DriverOccupancy_bRead();
		/* Occupied ? */
		if (bRead)
		{
			/* Turn LED on (by turning output off) */
			#if OCCUPANCY_SENSOR_LED_MASK
				vAHI_DioSetOutput(0, OCCUPANCY_SENSOR_LED_MASK);
			#endif
		}
		/* Unoccupied ? */
		else
		{
			/* Turn LED off (by turning output on) */
			#if OCCUPANCY_SENSOR_LED_MASK
				vAHI_DioSetOutput(OCCUPANCY_SENSOR_LED_MASK, 0);
			#endif
		}
		/* Are we currently in the occupied state ? */
		if (0 != psMibOccupancyStatus->sTemp.u8Occupancy)
		{
			/* Reading indicates unoccupied ? */
			if (FALSE == bRead)
			{
				/* Have we just become unoccupied ? */
				if (TRUE == psMibOccupancyStatus->bRead)
				{
					/* Restart the unoccupied timer */
					psMibOccupancyStatus->u32UnoccupiedTimer = 0;
				}
				else
				{
					/* Increment the unoccupied timer */
					psMibOccupancyStatus->u32UnoccupiedTimer += DEVICE_WAKE_TIMER_PERIOD_10MS;
				}
				/* Have we been unoccupied for the required delay ? */
				if (psMibOccupancyStatus->u32UnoccupiedTimer >= PS_MIB_OCCUPANCY_CONFIG->sPerm.u32UnoccupiedDelay)
				{
				    /* Debug */
				    DBG_vPrintf(DEBUG_MIB_OCCUPANCY_STATUS, "\n%sMibOccupancyStatus_vAppTimer100ms(Unoccupied)", acDebugIndent);
					/* Move to the unoccupied state */
					psMibOccupancyStatus->sTemp.u8Occupancy = 0;
					/* Turn LED off (by turning output on) */
					#if DEVICE_OUTPUT_MASK_OCCUPANCY_STATUS
						vAHI_DioSetOutput(DEVICE_OUTPUT_MASK_OCCUPANCY_STATUS, 0);
					#endif
					/* Transmit occupied state */
					MibOccupancyStatus_vQueueTxState(TRUE);
					/* Need to notify for the occupancy variable */
					psMibOccupancyStatus->u32NotifyChanged |= (1 << VAR_IX_OCCUPANCY_STATUS_OCCUPANCY);
				}
			}
		}
		/* Currently in the unoccupied state ? */
		else
		{
			/* Reading indicates occupied */
			if (TRUE == bRead)
			{
				bool_t bGoOccupied = FALSE;

				/* Occupied events and delay are enabled ? */
				if (PS_MIB_OCCUPANCY_CONFIG->sPerm.u8OccupiedEvents > 0 &&
					PS_MIB_OCCUPANCY_CONFIG->sPerm.u16OccupiedDelay > 0)
				{
					/* Have we just become occupied ? */
					if (FALSE == psMibOccupancyStatus->bRead)
					{
						/* Not yet counting events ? */
						if (0 == psMibOccupancyStatus->u8OccupiedEvents)
						{
							/* Reset the occupied timer */
							psMibOccupancyStatus->u16OccupiedTimer = 0;
						}
						/* Increment the event count */
						psMibOccupancyStatus->u8OccupiedEvents++;
						/* Have we reached the required number of events ? */
						if (psMibOccupancyStatus->u8OccupiedEvents >= PS_MIB_OCCUPANCY_CONFIG->sPerm.u8OccupiedEvents)
						{
							/* Move to the occupied state */
							bGoOccupied = TRUE;
						}
					}
					/* Still not occupied ? */
					if (bGoOccupied == FALSE)
					{
						/* Increment the occupied timer */
						psMibOccupancyStatus->u16OccupiedTimer += DEVICE_WAKE_TIMER_PERIOD_10MS;
						/* Have we exceeded the occupancy timer ? */
						if (psMibOccupancyStatus->u16OccupiedTimer >= PS_MIB_OCCUPANCY_CONFIG->sPerm.u16OccupiedDelay)
						{
							/* Reset the occupied timer and events */
							psMibOccupancyStatus->u16OccupiedTimer = 0;
							psMibOccupancyStatus->u8OccupiedEvents = 0;
						}
					}
				}
				/* Occupied delay only is enabled ? */
				else if (PS_MIB_OCCUPANCY_CONFIG->sPerm.u16OccupiedDelay > 0)
				{
					/* Have we just become occupied ? */
					if (FALSE == psMibOccupancyStatus->bRead)
					{
						/* Reset the occupied timer and events */
						psMibOccupancyStatus->u8OccupiedEvents = 0;
						psMibOccupancyStatus->u16OccupiedTimer = 0;
					}
					else
					{
						/* Increment the occupied timer */
						psMibOccupancyStatus->u16OccupiedTimer += DEVICE_WAKE_TIMER_PERIOD_10MS;
					}
					/* Have we been occupied for the required delay ? */
					if (psMibOccupancyStatus->u16OccupiedTimer >= PS_MIB_OCCUPANCY_CONFIG->sPerm.u16OccupiedDelay)
					{
						/* Move to the occupied state */
						bGoOccupied = TRUE;
					}
				}
				/* Occupied events only is enabled ? */
				else if (PS_MIB_OCCUPANCY_CONFIG->sPerm.u8OccupiedEvents > 0)
				{
					/* Have we just become occupied ? */
					if (FALSE == psMibOccupancyStatus->bRead)
					{
						/* Increment the event count */
						psMibOccupancyStatus->u8OccupiedEvents++;
						/* Have we reached the required number of events ? */
						if (psMibOccupancyStatus->u8OccupiedEvents >= PS_MIB_OCCUPANCY_CONFIG->sPerm.u8OccupiedEvents)
						{
							/* Move to the occupied state */
							bGoOccupied = TRUE;
						}
					}
				}
				/* No event or delays limiting occupied transition ? */
				else
				{
					/* Have we just become occupied ? */
					if (FALSE == psMibOccupancyStatus->bRead)
					{
						/* Move to the occupied state */
						bGoOccupied = TRUE;
					}
				}
				/* Need to go to occupied state ? */
				if (bGoOccupied)
				{
				    /* Debug */
				    DBG_vPrintf(DEBUG_MIB_OCCUPANCY_STATUS, "\n%sMibOccupancyStatus_vAppTimer100ms(Occupied)", acDebugIndent);
					/* Move to the occupied state */
					psMibOccupancyStatus->sTemp.u8Occupancy = 1;
					/* Turn LED on (by turning output off) */
					#if DEVICE_OUTPUT_MASK_OCCUPANCY_STATUS
						vAHI_DioSetOutput(0, DEVICE_OUTPUT_MASK_OCCUPANCY_STATUS);
					#endif
					/* Transmit occupied state */
					MibOccupancyStatus_vQueueTxState(TRUE);
					/* Need to notify for the occupancy variable */
					psMibOccupancyStatus->u32NotifyChanged |= (1 << VAR_IX_OCCUPANCY_STATUS_OCCUPANCY);
					/* Reset the occupied timer and events */
					psMibOccupancyStatus->u8OccupiedEvents = 0;
					psMibOccupancyStatus->u16OccupiedTimer = 0;
				}
			}
		}
		/* Note reading for comparision with the next reading */
		psMibOccupancyStatus->bRead = bRead;
		/* Plenty of time on refresh timer ? */
		if (psMibOccupancyStatus->u16StateRefreshTimer > DEVICE_WAKE_TIMER_PERIOD_10MS)
		{
			/* Decrement refresh timer */
			psMibOccupancyStatus->u16StateRefreshTimer -= DEVICE_WAKE_TIMER_PERIOD_10MS;
		}
		/* Refresh timer about to expire ? */
		else if (psMibOccupancyStatus->u16StateRefreshTimer > 0)
		{
			/* Debug */
			DBG_vPrintf(DEBUG_MIB_OCCUPANCY_STATUS, "\n%sMibOccupancyStatus_vAppTimer100ms(Refresh)", acDebugIndent);
			/* Zero refresh timer */
			psMibOccupancyStatus->u16StateRefreshTimer = 0;
			/* Transmit occupied state */
			MibOccupancyStatus_vQueueTxState(FALSE);
		}
	}
	/* Occupancy sensor is disabled ? */
	else
	{
		/* Reset timers and events */
		psMibOccupancyStatus->u32UnoccupiedTimer = 0;
		psMibOccupancyStatus->u16OccupiedTimer = 0;
		psMibOccupancyStatus->u8OccupiedEvents = 0;
		/* Update last reading to unoccupied */
		psMibOccupancyStatus->bRead = FALSE;
		/* Are we currently in the occupied state ? */
		if (0 != psMibOccupancyStatus->sTemp.u8Occupancy)
		{
			/* Move to the unoccupied state */
			psMibOccupancyStatus->sTemp.u8Occupancy = 0;
			/* Turn LED off (by turning output on) */
			#if DEVICE_OUTPUT_MASK_OCCUPANCY_STATUS
				vAHI_DioSetOutput(DEVICE_OUTPUT_MASK_OCCUPANCY_STATUS, 0);
			#endif
			/* Need to notify for the occupancy variable */
			psMibOccupancyStatus->u32NotifyChanged |= (1 << VAR_IX_OCCUPANCY_STATUS_OCCUPANCY);
		}
	}
}

/****************************************************************************
 *
 * NAME: MibOccupancyStatus_vQueueTxState
 *
 * DESCRIPTION:
 * Queues a occupancy state transmission
 *
 ****************************************************************************/
PUBLIC void MibOccupancyStatus_vQueueTxState(bool_t bImmediate)
{
	/* Is the network up and we have a valid MIB to transmit to ? */
	if (Node_bUp() && PS_MIB_OCCUPANCY_CONFIG->sPerm.u32StateMibId != 0)
	{
		/* End device build ? */
		#ifdef MK_BLD_NODE_TYPE_END_DEVICE
		{
			/* Debug */
			DBG_vPrintf(DEBUG_MIB_OCCUPANCY_STATUS, "\n%sMibOccupancyStatus_vQueueTxState() { bTxState = TRUE }", acDebugIndent);
			/* Is the stack not running ? */
			if (Node_u32StackState() != NODE_STACK_STATE_RUNNING)
			{
				/* Immediate transmission ? */
				if (bImmediate)
				{
					/* Resume running the stack */
					Node_v6lpResume();
				}
			}
			/* Flag bulb control transmission */
			psMibOccupancyStatus->bTxState = TRUE;
		}
		/* Coordinator or router build ? */
		#else
		{
			/* Stack is running ? */
			if (Node_u32StackState() == NODE_STACK_STATE_RUNNING)
			{
				/* Debug */
				DBG_vPrintf(DEBUG_MIB_OCCUPANCY_STATUS, "\n%sMibOccupancyStatus_vQueueTxState() { bTxState = TRUE }", acDebugIndent);
				/* Flag bulb control transmission */
				psMibOccupancyStatus->bTxState = TRUE;
			}
		}
		#endif
	}

	/* Debug */
	Node_vDebugOutdent(DEBUG_MIB_OCCUPANCY_STATUS);
}

/****************************************************************************/
/***        END OF FILE                                                   ***/
/****************************************************************************/
