//----------------------------------------------------------------------------------
// <copyright file="lrps_command.c" company="LiquidRobotics">
//	Copyright (c) Liquid Robotics Corporation.  All rights reserved.
// </copyright>
//
// <summary>
// 	Implements a process status report
// </summary>
//
// <owner>Mike Cookson</owner>
//---------------------------------------------------------------------------------

#include "includes.h"
#include <string.h>
#include "lrstat.h"
#include "uart.h"
#include "timer.h"

// shorthand for the method call that outputs a string
#define MON_PUTS(putstr) OSuartPutStrWait(portID, putstr, 0, &err)

// outputs a number "num" to the console at "portID" left-justified in a field of "fieldLen" characters
#define putNum(portID, num, fieldLen) putJustified(portID, ltoa(num, buffer, 10), fieldLen, OS_TRUE)

// this is an array of data that must be captured from the TCB array for the report
// (only as many entries are used as there are tasks existing)
typedef struct
{
	char* name;					// name of the task
	uint8_t prio;				// priority of the task
	uint16_t id;				// ID of the task
	uint32_t ctxsw;				// number of context switches
	uint32_t stksize;			// stack size allocate for this task
	uint32_t stkused;			// stack bytes used at the last context switch
	uint16_t lrclocks;			// number of clock ticks allocated to the task in the last interval
} taskinfo_t;

static taskinfo_t taskinfo[OS_MAX_TASKS + OS_N_SYS_TASKS];


// Local Function Prototypes

static void putJustified(uint8_t portID, char* pstr, uint8_t fieldLen, BOOLEAN rightJustified);

/*PAGE*/
/*
 *******************************************************************************
 *                   		PROCESS STATUS REPORT
 *
 * Description:			This routine outputs a listing of the tasks and shows
 *						the priority, ID, name, number of context switches during
 *						the last interval, percentage of CPU used during the last
 *						interval, stack usage at the last context switch and
 *					    configured stack size.
 *
 * Arguments:			portiD indicates the UART to which the report should be
 *						sent
 *
 * Returns:				n/a	
 *
 * Notes:				This function relies on functions provided by LRSTAT.C,
 *						which is an alternate implementation of the statistics
 *						task.
 *******************************************************************************
 */
void LRPS_Command(uint8_t portID)
{
	OS_TCB* pTCB;				// points to a task control block being read
	taskinfo_t* pInfo;			// points to elements of taskinfo (captured task statistics)
	uint8_t err;				// error number returned from IO routines
	uint16_t cpu;				// captured CPU % used
	uint16_t run;				// captured number of ticks elapsed in the last interval
	char buffer[11];			// buffer for formatting numbers

	// capture the state of the system
	OS_CPU_SR cpu_sr;
	OS_ENTER_CRITICAL();

	cpu = OSCPUUsage;
	run = lrstatRunCtr;

	for (pTCB = OSTCBList, pInfo = taskinfo; pTCB; pTCB = pTCB->OSTCBNext, ++pInfo)
	{
		pInfo->name = (char*) pTCB->OSTCBTaskName;
		pInfo->prio = pTCB->OSTCBPrio;
		pInfo->id = pTCB->OSTCBId;
		pInfo->ctxsw = pTCB->OSTCBCtxSwCtr;
		pInfo->stksize = pTCB->OSTCBStkSize;
		pInfo->stkused = pTCB->OSTCBStkUsed;
		pInfo->lrclocks = TCBTOLRSTATDATA(pTCB).accumTimeSave;
	}

	OS_EXIT_CRITICAL();

	// now display the report on the UART associated with port ID
	MON_PUTS("CPU Used (%): ");
	putNum(portID, cpu, 0);
	MON_PUTS("\r\n\nPrio  ID     ");
	putJustified(portID, "Name", OS_TASK_NAME_SIZE, OS_FALSE);
	MON_PUTS("  CtxSw   CPU(%)  StkUsed  StkSize\r\n");

	uint8_t remaining = pInfo-taskinfo;
	
	for (pInfo = taskinfo; remaining--; ++pInfo)
	{
		putNum(portID, pInfo->prio, 4);
		putNum(portID, pInfo->id, 7);
		MON_PUTS("  ");
		putJustified(portID, pInfo->name, OS_TASK_NAME_SIZE, OS_FALSE);
		putNum(portID, pInfo->ctxsw, 7);

		if (run == 0)
			MON_PUTS("     ---");
		else
			putNum(portID, pInfo->lrclocks*1000L/run/10, 8);

		putNum(portID, pInfo->stkused, 9);
		putNum(portID, pInfo->stksize, 9);
		MON_PUTS("\r\n");
	}
}

/*PAGE*/
/*
 *******************************************************************************
 *                   	EMITS A STRING WITH JUSTFICATION
 *
 * Description:			This routine emits a string to the indicated port with
 *						right or left justification within a field.
 *
 * Arguments:			portID is the UART to which the string should be sent
 *
 *						pstr is the string to be emitted
 *
 *						fieldLen is the length (in characters) of the field within
 *						which to justify the string
 *
 *						rightJustified is TRUE if the string should be right
 *						justified or FALSE if the string should be left justified;
 *						right justification adds spaces before the string so that
 *						the total output is at least fieldLen, while left
 *						justifications adds spaces after the string.
 *
 * Returns:				n/a	
 *
 * Notes:
 *******************************************************************************
 */
static void putJustified(uint8_t portID, char* pstr, uint8_t fieldLen, BOOLEAN rightJustified)
{
	uint8_t len = strlen(pstr);
	uint8_t err;

	if (rightJustified)
		while(len < fieldLen--)
			OSuartPutCharWait(portID, ' ', 0);

	MON_PUTS(pstr);

	if (!rightJustified)
		while(len < fieldLen--)
			OSuartPutCharWait(portID, ' ', 0);
}
