//=========================================================================
// Summary  : */
// Filename : Task.cc
// Author   : */
// Project  : */
// Revision : 1
// Created  : 2000/08/19
// Modified : 2000/08/19
//=========================================================================
// Description :
//=========================================================================
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/kernel.h>
#include <sys/sched.h>
#include <malloc.h>
#include <process.h>
#include <signal.h>
#include "Task.h"
#include "Syslog.h"
#include "VehicleConfigurator.h"

#define ServerProgramName "taskServer"

Task *Task::_theTask = 0;

Task::Task(const char *name)
{
  Boolean debug = False;
  char errorBuf[100];

  if (_theTask != 0) {
    // Oops, we've already created a Task! Only 1 allowed per process
    sprintf(errorBuf, "Task::Task() - instance (\"%s\") already exists!",
	    _theTask->name());

    throw Exception(errorBuf);
  }

  // Keep track of the most recently created Task for cleanup on signal/exit

  // Set up the Scheduling
  struct sched_param sched_param ;
  pid_t pid = getpid();

  int result = sched_getparam( pid, &sched_param );
  if(result == -1)
    {
      sprintf(errorBuf,"Task::Task() - failed to get scheduler info");
      throw Exception(errorBuf);
    }

  result = sched_setscheduler( pid, SCHED_FIFO, &sched_param );
  if(result == -1)
    {
      sprintf(errorBuf,"Task::Task() - failed to Set scheduler info");
      throw Exception(errorBuf);
    }






  _theTask = this;

  _name = strdup(name);

  _eventTriggers = 0;

  _mode = Nominal;
  _cmdPtr = 0;
  _maxCmdBytes = 0;

  dprintf("Task::Task() \"%s\" - _theTask=0x%x\n", name, _theTask);

  signal(SIGINT, Task::signalHandler);
  signal(SIGTERM, Task::signalHandler);
  signal(SIGQUIT, Task::signalHandler);

  atexit(Task::cleanup);

  // We don't wait for children to die, and we don't
  // want any zombies either...
  //  signal(SIGCHLD, SIG_IGN);

  _eventTriggers = new EventTriggers(serverPid());
}



Task::Task(const char *name, int priority, int sched_policy)
{
  Boolean debug = False;
  char errorBuf[100];

  if (_theTask != 0) {
    // Oops, we've already created a Task! Only 1 allowed per process
    sprintf(errorBuf, "Task::Task() - instance (\"%s\") already exists!",
	    _theTask->name());

    throw Exception(errorBuf);
  }


  // Set up the Scheduling
  struct sched_param sched_param ;
  pid_t pid = getpid();
  sched_param.sched_priority = priority;

  int result = sched_getparam( pid, &sched_param );
  if(result == -1)
    {
      sprintf(errorBuf,"Task::Task() - failed to get scheduler info");
      throw Exception(errorBuf);
    }

  result = sched_setscheduler( pid, sched_policy, &sched_param );
  if(result == -1)
    {
      sprintf(errorBuf,"Task::Task() - failed to Set scheduler info");
      throw Exception(errorBuf);
    }


  // Keep track of the most recently created Task for cleanup on signal/exit
  _theTask = this;

  _name = strdup(name);

  _eventTriggers = 0;

  _mode = Nominal;
  _cmdPtr = 0;
  _maxCmdBytes = 0;

  dprintf("Task::Task() \"%s\" - _theTask=0x%x\n", name, _theTask);

  signal(SIGINT, Task::signalHandler);
  signal(SIGTERM, Task::signalHandler);
  signal(SIGQUIT, Task::signalHandler);

  atexit(Task::cleanup);

  // We don't wait for children to die, and we don't
  // want any zombies either...
  //  signal(SIGCHLD, SIG_IGN);

  _eventTriggers = new EventTriggers(serverPid());
}




Task::~Task()
{
  Boolean debug = False;
  dprintf("Task::~Task() - delete _eventTriggers");
  delete _eventTriggers;
  dprintf("Task::~Task() - free(_name)");
  free((void *)_name);
  _theTask = 0;
  dprintf("Task::~Task() - done");
}



void Task::processCommand(Command *command)
{
  return;
}


size_t Task::maxCommandBytes()
{
  return 512;
}



void Task::allocateBuffers()
{
  _maxCmdBytes = maxCommandBytes();

  // Allocate request buffer
  if ((_cmdPtr = (Command *)malloc(_maxCmdBytes)) == 0) {

    Syslog::write("Task::init() - couldn't malloc %d bytes!\n",
		  _maxCmdBytes);

    exit(1);
  }
}



void Task::run()
{
  allocateBuffers();

  while (True) {
    Receive(serverPid(), (void *)_cmdPtr, _maxCmdBytes);
    processCommand(_cmdPtr);
  }
}


void Task::triggerEvent(EventCode event)
{
  Boolean debug = False;
  dprintf("Task::triggerEvent() - _eventTriggers ptr = x%x\n", _eventTriggers);
  _eventTriggers->notify(event);
}


void Task::addCallback(SharedObject::RequestCode code, CallbackPtr callback)
{
  CallbackEntry *entry = new CallbackEntry(code, callback);
  _callbacks.add(&entry);
}


const pid_t Task::serverPid()
{
	// We used to just assume our parent was our server, but
	// that's no longer the case, since supervisor starts up
	// everyone. Nowadays, we need to use a vehicle config IF
	// to find out who our parent is.

	return VehicleConfigurator::getServerPid( (char *)name());
}



void Task::signalHandler(int signalNo)
{
  Boolean debug = False;
  dprintf("Task::signalHandler() for task %s - call cleanup()\n",
	  _theTask->name());

  // Call exit(), which will invoke Task::cleanup()
  exit(1);
}


void Task::cleanup()
{
  Boolean debug = False;

  if (!_theTask)
    // Doesn't exist
    return;

  dprintf("Task::cleanup() - delete _theTask=0x%x (named %s)\n",
	  _theTask, _theTask->name());

  delete _theTask;

  dprintf("Task::cleanup() - done deleting _theTask=0x%x\n",
	  _theTask);

  dprintf("Task::cleanup() - done\n");
  return;
}




