//----------------------------------------------------------------------------------
// <copyright file="lrstat.c" company="LiquidRobotics">
//	Copyright (c) Liquid Robotics Corporation.  All rights reserved.
// </copyright>
//
// <summary>
// 	Implements an alternate statistics tasks customized for the AVR 2650
// </summary>
//
// <owner>Mike Cookson</owner>
//---------------------------------------------------------------------------------

#include "includes.h"
#include "lrstat.h"
#include "timer.h"

#if OS_TASK_STAT_EN > 0

uint16_t lrstatIdleCtr;						// time spent in Idle task during last time period
uint16_t lrstatRunCtr;						// total time accumulated during the last time period
uint16_t lrstatRunningAccumulated;			// time accumulating during this time period

// additional counters per task used for accumulated time during the last time
// period and accumulating in the current time period
LRStatTaskData_t lrstatTaskData[OS_MAX_TASKS + OS_N_SYS_TASKS];

/*PAGE*/
/*
 *******************************************************************************
 *                       INITIALIZATION OF THE STATISTICS TASK
 *
 * Description :	Initializes the statistics task: this function replaces the
 *					MicroC/OS function of the same name.  The client app calls
 *					this function if the statistics task should be executed.
 *
 * Arguments : 		n/a
 *
 * Returns:			n/a
 *
 * Notes:			
 *******************************************************************************
 */
void  OSStatInit (void)
{
#if OS_CRITICAL_METHOD == 3
    OS_CPU_SR  cpu_sr = 0;
#endif

    OSTimeDly(2);				// synch with timer tick (from original implementation)

    OS_ENTER_CRITICAL();

	// zero the counters
	lrstatIdleCtr = 0;
	lrstatRunCtr = 0;
	lrstatRunningAccumulated = 0;

	// zero the accumulated time per task
	uint8_t idx;
	for (idx = 0; idx < (OS_MAX_TASKS+OS_N_SYS_TASKS); ++idx)
	{
		lrstatTaskData[idx].accumTime = 0;
		lrstatTaskData[idx].accumTimeSave = 0;
	}

    OSIdleCtr    = 0L; 			// clear idle counter (from original implementation)
	OSIdleCtrMax = 0L;			// clear max counts in idle (from original implementation, not used)
    OSStatRdy    = OS_TRUE;		// mark the statistics task as ready

    OS_EXIT_CRITICAL();
}

/*PAGE*/
/*
 *******************************************************************************
 *               ALTERNATE IMPLEMENTATION OF STATISTICS TASK
 *
 * Description :	This is an alternate implementation of the MicroC/OS
 *					statistics task that uses timer/counter 1 to measure how
 *					long each task runs.  This was necessary because the
 *					application hook for system idle puts the system to sleep,
 *					so the time spent idle cannot be measured by the number
 *					of idle periods, which is the MicroC/OS default
 *					implementation.
 *
 * Arguments : 		p_arg is an argument to the task, but it is not used for
 *					anything
 *
 * Returns:			n/a
 *
 * Notes:			
 *******************************************************************************
 */
void  OS_TaskStat (void *p_arg)
{
    (void)p_arg;                                 // Prevent compiler warning for not using 'p_arg'

	// wait until the statistics task is running
	while(!OSStatRdy)
        OSTimeDly(2 * OS_TICKS_PER_SEC / 10);

	// initialize the number of passes when NOT generating statistics
	uint8_t period = LRSTAT_CALCULATION_PERIOD;

	// -- main task loop --

    for (;;) {

		// if it's time to generate statistics ...
		if (!period--)
		{
			period = LRSTAT_CALCULATION_PERIOD;		// reload the down-counter
			LRState_CalculateAccumulatedTime();		// run time calculations

			// update the CPU usage number
			if (lrstatRunCtr)
	        	OSCPUUsage = ((INT8U)(100L - lrstatIdleCtr * 100L / lrstatRunCtr));
			else
				OSCPUUsage = 0xFF;
		}

        OSTaskStatHook();							// Invoke user definable hook on each pass
#if (OS_TASK_STAT_STK_CHK_EN > 0) && (OS_TASK_CREATE_EXT_EN > 0)
        OS_TaskStatStkChk();                     	// Check the stacks for each task on each pass
#endif
        OSTimeDly(LRSTAT_DELAY);        			// delay to collect more data
    }
}

/*PAGE*/
/*
 *******************************************************************************
 *             ATTRIBUTE COLLECTED TIME FROM TIMER TO A TASK
 *
 * Description :	This is an alternate implementation of the MicroC/OS
 *					statistics task that uses timer/counter 1 to measure how
 *					long each task runs.  This was necessary because the
 *					application hook for system idle puts the system to sleep,
 *					so the time spent idle cannot be measured by the number
 *					of idle periods, which is the MicroC/OS default
 *					implementation.
 *
 * Arguments : 		ptcb points to the task control block to which accumulated
 *					time should be attributed
 *
 * Returns:			n/a
 *
 * Notes:			This method should be called from the context switch hook,
 *					passing in the TCB for the task that is reliquishing control
 *******************************************************************************
 */
void LRStat_AttributeTime(OS_TCB* ptcb)
{
	// get accumulated time from timer
	uint16_t accumTime = OStmrGetAccumulatedTime();

	// if the statistics task is ready. then add the accumulate time to the
	// running total and to the indicated task
	if (OSStatRdy)
	{
#if OS_CRITICAL_METHOD == 3
	    OS_CPU_SR  cpu_sr = 0;
#endif

    	OS_ENTER_CRITICAL();

		lrstatRunningAccumulated += accumTime;
		TCBTOLRSTATDATA(ptcb).accumTime += accumTime;

	    OS_EXIT_CRITICAL();
	}
}

/*PAGE*/
/*
 *******************************************************************************
 *             CALCULATE AND STORE ACCUMULATED TIME PER TASK
 *
 * Description :	This function copies the running totals for each task to
 *					the "last cycle" stored values.
 *
 * Arguments : 		n/a
 *
 * Returns:			n/a
 *
 * Notes:			The statistics task calls this method periodically; client
 *					users of this module should read data from the OS-provided
 *					variables and from lrstatTaskData[]
 *******************************************************************************
 */
uint16_t LRState_CalculateAccumulatedTime(void)
{
#if OS_CRITICAL_METHOD == 3                      /* Allocate storage for CPU status register           */
    OS_CPU_SR  cpu_sr = 0;
#endif
	OS_TCB* ptcb;

    OS_ENTER_CRITICAL();

	lrstatRunCtr = lrstatRunningAccumulated;
	lrstatRunningAccumulated = 0;

	for (ptcb = OSTCBList; ptcb; ptcb = ptcb->OSTCBNext)
	{
		TCBTOLRSTATDATA(ptcb).accumTimeSave = TCBTOLRSTATDATA(ptcb).accumTime;
		TCBTOLRSTATDATA(ptcb).accumTime = 0;
		ptcb->OSTCBCtxSwCtr = 0;

		if (ptcb->OSTCBPrio == OS_TASK_IDLE_PRIO)
			lrstatIdleCtr = TCBTOLRSTATDATA(ptcb).accumTimeSave;
	}

    OS_EXIT_CRITICAL();
	return lrstatRunCtr;
}

#endif
