//=========================================================================
// Summary  : Main header for Supervisor component
// Filename : Supervisor.h
// Author   : Tom O'Reilly, Marsh
// Project  : */
// Revision : 1
// Created  : 2000/08/19
// Modified : 2000/08/19
//=========================================================================
// Description :
//=========================================================================
#ifndef _SUPERVISOR_H
#define _SUPERVISOR_H

#include <time.h>
#include <list>
#include "EventTriggers.h"
#include "PeriodicTask.h"
#include "TaskInterface.h"

#include "SupervisorInput.h"
#include "SupervisorCommand.h"
#include "SupervisorOutput.h"

#include "VehicleConfigurationIF.h"
#include "LayeredControlIF.h"
#include "PowerSystemIF.h"
#include "RemoteLogIF.h"

#ifdef USE_WD
#include "AmproCommand.h"
#endif


struct TaskEntry {

    TaskEntry(pid_t pid,
	      TaskInterface *interface,
	      int node,
	      const char *args,
	      bool persistent = False) {
	_pid = pid;
	_interface = interface;
	_persistent = persistent; 
	_node = node;
	_args = strdup(args);
	_kill = 0;
    }

    ~TaskEntry() {
	free((void *)_args);
    }

    pid_t _pid;
    Boolean _persistent;
    TaskInterface *_interface;
    int _node;
    const char *_args;
    int _kill;
};
typedef list<TaskEntry *> TaskList;
/*
  CLASS
Supervisor

DESCRIPTION

Supervisor has the following responsibilities:

* Create Task processes, based on current vehicle configuration
* Monitor Tasks, restart aborted Tasks
* Periodically reset watchdog timer

AUTHOR
Tom O'Reilly
*/
class Supervisor {

 public:

    ///////////////////////////////////////////////////////////////////
    // Constructor
    Supervisor(const char *umbrellaLogDirName,
		   Boolean simulated = False,
	       Boolean realTailCone = True,
	       const char *planFileName = "",
	       const char *abortFileName = "",
	       char **env = NULL);

    ~Supervisor();

    void run();

 protected:

    enum MissionState{
	NotRunning = 0,
	Running,
	AbortSent,
	KillSent
    } _missionState;

    enum DriverType{
	Normal = 0,
	Persistent,
	Both
    };
	
    VehicleConfigurationIF *_vconfig;
    LayeredControlIF *_layeredControl;
    PowerSystemIF *_power;
    RemoteLogIF *_rem;

    char _umbrellaLogDirName[32];
    char _currentLogDirectory[32];

    EventTriggers *_eventTriggers;

    char **_env;
    SupervisorOutput *_output;
    SupervisorInput _input;
    SupervisorCommand *_command;

    Boolean _useNodeTwo;

    ///////////////////////////////////////////////////////////////////
    // Create "core" vehicle tasks
    //  void createCoreTasks();

    ///////////////////////////////////////////////////////////////////
    // Create simulation tasks
    //  void createSimTasks();

  ///////////////////////////////////////////////////////////////////
  // Create vehicle tasks, as specified in
  // config file
  void createServerTasks();
  void createDriverTasks(Supervisor::DriverType);

  //true will also slay persistent tasks if there are any in the given list
  void slayTasks(TaskList * taskList, bool immediate = false, bool persistent = False);

  ///////////////////////////////////////////////////////////////////
  // Power on and off the devices
  int powerOnDevices();
  int powerOffDevices();

  ///////////////////////////////////////////////////////////////////
  // Trigger events...
  void triggerEvent(EventCode event);

  ///////////////////////////////////////////////////////////////////
  // Starting up the power driver is a special case
  void createPowerDriverTask();

  ///////////////////////////////////////////////////////////////////
  // Check status of all tasks.
  // Return number of tasks that have died.
  int checkTasks(TaskList * taskList);

  void dealWithDeadTask( VehicleConfigurationIF::Name taskName );
  ///////////////////////////////////////////////////////////////////
  // Let hardware watchdog know that we're still alive
  void keepAlive();

  ///////////////////////////////////////////////////////////////////
  //  stop the supervisor and all tasks
  void stopSupervisor(int exitCode);

  void commandDriver(char* name, Supervisor::DriverType, Boolean turnOn);

  void startMission();

  void abortMission();
  void killMission();

  void missionCompleted();

  void reloadConfig();

  ///////////////////////////////////////////////////////////////////
  // Fork and execute task; add to task table
  // [input] taskName: name of task
  // [input] args: pointer to argument string (concatenation of argv[1]
  //               through argv[argc-1]
  // [input] restarted: False on first time task is started, else True
  int createTask(TaskList * taskList,
		 const char *taskName,
		 const char *args,
		 int node,
		 Boolean restarted,
		 bool persistent = False);

  ///////////////////////////////////////////////////////////////////
  // Check for a task in the table
  Boolean checkTaskEntry(TaskList *taskList, const char *taskName, const char *args, int node);

  ///////////////////////////////////////////////////////////////////
  // Add task entry to table
  void addTaskEntry(TaskList * taskList,
		    const char *taskName,
		    pid_t pid,
		    int node,
		    const char *argv,
		    bool persistent = False);

  TaskList _serverTasks;
  TaskList _driverTasks;

  Boolean _simulated;
  Boolean _realTailCone;
  Boolean _attemptProcRestart;

  Boolean _stopSupervisor;

  long _killMissionSent;
  
  pid_t _teePID;

  char _planFileName[64];
  char _abortFileName[64];

 private:
  /////////////////////////////////////////////////////////////
  // reportprocesstimes
  // creates a log file of process times for the mission
  // needs to be called before Supervisor kills off the children
  void reportprocesstimes();

  ////////////////////////////////////////////////////////////////////
  // Signal handler
  static void signalHandler(int signalNo);

  ////////////////////////////////////////////////////////////////////
  // Sig handler to catch child deaths
  static void signalChildHandler(int signalNo);

  ////////////////////////////////////////////////////////////////////
  // Destroy latest TaskServer object
  static void cleanup();

  ////////////////////////////////////////////////////////////////////
  // Points to most recently created Supervisor object. There should only
  // be one of these per process. We use this approach instead of
  // singleton, since singleton requires more code fiddling as new
  // subclasses are added.
  static Supervisor *_theSupervisor;

  ////////////////////////////////////////////////////////////////////
  // Keeps count of the number of times a driver has failed
  int _faultCount[100];

#ifdef USE_WD
  AmproCommand *_watchdog;

  void touchWatchdog( int secs ) l
#endif

};


#endif
