/*************************************************************************\
* "$RCSfile: CCpuLoad.cpp,v $"                                            *
***************************************************************************
* Information by CVS or VSS:                                              *
*     Last check in on: $Date: 2011/10/17 10:12:20 $                      *
*     by                $Author: schluckebier $
*     Revision        : $Revision: 1.19 $                                 *
\*************************************************************************/
/*! \file CCpuLoad.cpp
*	\brief Implementation CCpuLoad class
**************************************************************************/

#include "VIC_PS.h"
#include "CCpuLoad.hpp"
#include "CRegion.h"
#include "CPSApp.hpp"
#include <clk.h>
#include <idl.h>

CCpuLoad *CCpuLoad::m_pThis = NULL;	//!< The one and only CCpuLoad instance
int CCpuLoad::m_FrameCounter = 0;	//!< Global frame counter
int CCpuLoad::m_DisplayCounter = 0;	//!< Global display frame counter

#pragma CODE_SECTION("p_intern")
/*************************************************************************/
void CCpuLoad::CpuLoadLoop()
/*************************************************************************/
/*! @brief This task replaces the DSP/BIOS idle task to calculate the CPU load.

	It runs whenever no other task is ready to run.\n
	This task has priority 1 while all other tasks should run at least at
	priority 2.
**************************************************************************/
{
	volatile int j;
	unsigned int t0, newTime, idleTime = 0;
	unsigned int tSecond = VIB_TimerCount();
	unsigned int second = 0;
	unsigned int timerFreq = VIB_TimerFreq();

	while (m_pThis)
	{
		// *** Sleep for 10 ms ***
#if 0
		static int ticsIdle = 0;
		int ticCounter = 0;
		t0 = VIB_TimerCount();
		do
		{
			for (j = 0; j < 1000; j++);
			ticCounter++;
			newTime = VIB_TimerCount();
		} while (newTime - t0 < 10 * 1000 * timerFreq);

		if (ticsIdle == 0)
		{	// Idle calibration
			ticsIdle = ticCounter;
		}
		idleTime += ticCounter * (10 * 1000 * timerFreq / ticsIdle);
#elif defined(_TMS320C6400_PLUS)
		// *** Power down C64+ ***
		unsigned int iTSCVal_A, iTSCVal_B;
		unsigned int TSC_diff;
		t0 = VIB_TimerCount();	// Timer value
		iTSCVal_A = TSCL;  // Time stamp counter (stops during power down)

		do
		{
			asm(" IDLE");
			newTime = VIB_TimerCount();
		} while (newTime - t0 < 10 * 1000 * timerFreq);
		iTSCVal_B = TSCL;
		// Difference between timer and TSC equals the amount of time spent in IDLE
		TSC_diff = iTSCVal_B - iTSCVal_A;
		TSC_diff = (TSC_diff / VIB_HWInfo.Freq) * timerFreq;
		idleTime += newTime - t0 - TSC_diff;
#else
		// *** Power down C64x ***
		t0 = VIB_TimerCount();
		do
		{
			unsigned int oldTime;
			_disable_interrupts();
			oldTime = VIB_TimerCount();
			ICR = ~IER;
			asm(" B Next");
			asm(" MVC CSR,B5");
			asm(" ADDK 0x4400,B5");	// Select PD1, wake by non-enabled interrupt
			asm(" MVC B5,CSR");		// Enter PD1
			asm(" NOP");
			asm(" NOP");
			asm("Next: NOP");
			asm(" NOP 6");

			newTime = VIB_TimerCount();
			idleTime += newTime - oldTime;

			_enable_interrupts();
		} while (newTime - t0 < 10 * 1000 * timerFreq);
#endif

		// Pass through idle loop to pump data to the Real-Time Analysis tools
		TSK_disable();
		IDL_run();
		TSK_enable();

		t0 = VIB_TimerCount() - tSecond;
		if (t0 > 1000000 * timerFreq)
		{
			int i;
			int CpuLoad;

			second++;
			VIB_SetLED(second & 1);

			// *** CPU-Load ***
			CpuLoad = 100 - (idleTime + t0 / 50) / (t0 / 100);
			CpuLoad = _max2(0, _min2(CpuLoad, 100));
			tSecond = VIB_TimerCount();
			idleTime = 0;

			for (i = 1; i < CPUMAXBUFFER; i++)
				m_CpuLoadBuffer[i - 1] = m_CpuLoadBuffer[i];
			m_CpuLoadBuffer[CPUMAXBUFFER - 1] = CpuLoad;

			// *** Frames per Second ***
			for (i = 1; i < FRAMEMAXBUFFER; i++)
				m_FrameCountBuffer[i - 1] = m_FrameCountBuffer[i];
			m_FrameCountBuffer[i - 1] = m_FrameCounter;
			m_FrameCounter = 0;
			for (i = 1; i < FRAMEMAXBUFFER; i++)
				m_DisplayCountBuffer[i - 1] = m_DisplayCountBuffer[i];
			m_DisplayCountBuffer[i - 1] = m_DisplayCounter;
			m_DisplayCounter = 0;
		}
	}
}

/*************************************************************************/
void CCpuLoad::CpuLoadTask()
/*************************************************************************/
/*! @brief Trampoline for CpuLoadLoop()
**************************************************************************/
{
	m_pThis->CpuLoadLoop();
}

/*************************************************************************/
CCpuLoad::CCpuLoad()
/*************************************************************************/
/*! @brief Initializes CPU load.
**************************************************************************/
{
	int i;
	TSK_Attrs ta = TSK_ATTRS;

	m_pThis = this;

	for (i = 0; i < CPUMAXBUFFER; i++)
		m_CpuLoadBuffer[i] = 0;
	for (i = 0; i < TEMPMAXBUFFER; i++)
		m_TemperatureBuffer[i] = -128;
	for (i = 0; i < FRAMEMAXBUFFER; i++)
		m_FrameCountBuffer[i] = 0;
	for (i = 0; i < FRAMEMAXBUFFER; i++)
		m_DisplayCountBuffer[i] = 0;

	ta.priority  = 1;
//	ta.stack     = 0;
	ta.stacksize = 4096;
	ta.stackseg  = 0;
//	ta.initstackflag = 1;
//	ta.environ   = 0;
	ta.name      = "CpuLoadTask";
//	ta.exitflag  = 0;
	TSK_create((Fxn)CpuLoadTask, &ta);
	TSK_sleep(20);	// Wait for idle calibration
}

/*************************************************************************/
CCpuLoad *CCpuLoad::CreateInstance()
/*************************************************************************/
/*! @brief Creates CCpuLoad instance.
	@return Pointer to CCpuLoad instance
**************************************************************************/
{
	if (m_pThis == NULL)
		m_pThis = new CCpuLoad();
	return m_pThis;
}

/*************************************************************************/
void CCpuLoad::DisposeInstance()
/*************************************************************************/
/*! @brief Disposes CCpuLoad instance.
**************************************************************************/
{
	if (m_pThis)
		delete m_pThis;
	m_pThis = NULL;
}

/*************************************************************************/
void CCpuLoad::DrawCpuLoad(CIMAGE *pImage, int xOff, int yOff)
/*************************************************************************/
/*! @brief Draws CPU load graph.
	@param pImage Pointer to a color image
	@param xOff x offset
	@param yOff y offset
**************************************************************************/
{
	char Text[16];
	CPSApp *pApp = CPSApp::GetInstance();
	COLOR rgbBack;
	
	if (xOff + CPUMAXBUFFER + 24 > pImage->Width)
		return;
	if (yOff + 20 > pImage->Height)
		return;
	
	rgbBack.R = rgbBack.B = 0;
	rgbBack.G = 40;
	GUI_FRect(pImage, xOff, YPOS + yOff - 100, CPUMAXBUFFER, 100, &rgbBack);
	for (int i = 0; i < (CPUMAXBUFFER - 1); i++)
	{
		GUI_Line(pImage, xOff + i,     YPOS - m_CpuLoadBuffer[i] + yOff,
						 xOff + i + 1, YPOS - m_CpuLoadBuffer[i + 1] + yOff, &rgbGreen);
	}

	sprintf(Text, "%d %%", m_CpuLoadBuffer[CPUMAXBUFFER - 1]);
	pApp->SetTP(TextStyle::SHADOW);
	GUI_TextOut(pImage, CPUMAXBUFFER + xOff, YPOS + yOff, "0 %");
	GUI_TextOut(pImage, CPUMAXBUFFER + xOff, 100 - m_CpuLoadBuffer[CPUMAXBUFFER - 1] + yOff + 8, Text);
	pApp->SetTP(TextStyle::NORMAL);
}

#define TEMP_MIN 0
#define TEMP_MAX 80

/*************************************************************************/
void CCpuLoad::DrawTemperature(CIMAGE *pImage, int xOff, int yOff)
/*************************************************************************/
/*! @brief Draws temperature graph.
	@param pImage Pointer to a color image
	@param xOff x offset
	@param yOff y offset
**************************************************************************/
{
	int i;
	char Text[16];
	static int TCount = TEMPCYC;
	CPSApp *pApp = CPSApp::GetInstance();
	COLOR rgbBack;

	if (xOff + TEMPMAXBUFFER + 24 > pImage->Width)
		return;
	if (yOff + 20 > pImage->Height)
		return;

	if (TCount == TEMPCYC)
	{
		unsigned short Temperature;

		TCount = 0;
		VIB_IICReadTemp(&Temperature);
		for (i = 1; i < TEMPMAXBUFFER; i++)
			m_TemperatureBuffer[i - 1] = m_TemperatureBuffer[i];
		m_TemperatureBuffer[TEMPMAXBUFFER - 1] = (short)Temperature / 256.0f;
	}
	else
	{
		TCount++;
	}

	rgbBack.R = rgbBack.B = 0;
	rgbBack.G = 40;
	GUI_FRect(pImage, xOff, YPOS + yOff - TEMP_MAX, TEMPMAXBUFFER, TEMP_MAX, &rgbBack);

	int tempPrev;
	for (i = 0; i <TEMPMAXBUFFER; i++)
	{
		const int temp = MAX(0, MIN(m_TemperatureBuffer[i], TEMP_MAX));

		if (i > 0 && m_TemperatureBuffer[i - 1] != -128)
		{
			GUI_Line(pImage, xOff + i - 1, YPOS - tempPrev + yOff,
							 xOff + i,     YPOS - temp + yOff, &rgbRed);
		}
		tempPrev = temp;
	}

	pApp->SetTP(TextStyle::SHADOW);
	sprintf(Text, "%u °C", TEMP_MIN);
	GUI_TextOut(pImage, TEMPMAXBUFFER + xOff, YPOS + yOff, Text);
	sprintf(Text, "%d °C", (int)m_TemperatureBuffer[TEMPMAXBUFFER - 1]);
	GUI_TextOut(pImage, TEMPMAXBUFFER + xOff, YPOS + yOff - 16 - m_TemperatureBuffer[TEMPMAXBUFFER - 1], Text);
	pApp->SetTP(TextStyle::NORMAL);
}

/*************************************************************************/
void CCpuLoad::DrawFrame(CIMAGE *pImage, int xOff, int yOff)
/*************************************************************************/
/*! @brief Draws Frame count graph.
	@param pImage Pointer to a color image
	@param xOff x offset
	@param yOff y offset
**************************************************************************/
{
	int i;
	int Fc0, Fc1;
	int Dc0, Dc1;
	int nMax = 0;
	CPSApp *pApp = CPSApp::GetInstance();
	char Text[16];
	COLOR rgbBack;
	
	if (xOff + FRAMEMAXBUFFER + 24 > pImage->Width)
		return;
	if (yOff + 20 > pImage->Height)
		return;
	
	GUI_TextOut(pImage, FRAMEMAXBUFFER + xOff, YPOS + yOff, "fps");
	for (i = 0; i < FRAMEMAXBUFFER; i++)
	{
		if (m_FrameCountBuffer[i] > nMax)
			nMax = m_FrameCountBuffer[i];
		if (m_DisplayCountBuffer[i] > nMax)
			nMax = m_DisplayCountBuffer[i];
	}
	nMax += 3;
	rgbBack.R = rgbBack.B = 0;
	rgbBack.G = 40;
	GUI_FRect(pImage, xOff, YPOS + yOff - 100, FRAMEMAXBUFFER, 100, &rgbBack);
	for (i = 0; i < (FRAMEMAXBUFFER - 1); i++)
	{
		Dc0 = (m_DisplayCountBuffer[i] * 100 / nMax);
		Dc1 = (m_DisplayCountBuffer[i + 1] * 100 / nMax);
		Fc0 = (m_FrameCountBuffer[i] * 100 / nMax);
		Fc1 = (m_FrameCountBuffer[i + 1] * 100 / nMax);

		GUI_Line(pImage, xOff + i,     YPOS - Dc0 + yOff,
						 xOff + i + 1, YPOS - Dc1 + yOff, &rgbGreen);
		GUI_Line(pImage, xOff + i,     YPOS - Fc0 + yOff,
						 xOff + i + 1, YPOS - Fc1 + yOff, &rgbYellow);
	}

	pApp->SetTP(TextStyle::SHADOW);
	GUI_SetTxtFrontCol(rgbYellow);
	sprintf(Text, "Cam:%d", m_FrameCountBuffer[FRAMEMAXBUFFER - 1]);
	GUI_TextOut(pImage, xOff, YPOS + yOff, Text);

	GUI_SetTxtFrontCol(rgbGreen);
	sprintf(Text, "Dis:%d", m_DisplayCountBuffer[FRAMEMAXBUFFER - 1]);
	GUI_TextOut(pImage, xOff + FRAMEMAXBUFFER / 2 + 4, YPOS + yOff, Text);
	pApp->SetTP(TextStyle::NORMAL);
}

/*************************************************************************/
