/****************************************************************************/
/* Copyright (c) 2000 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  :                                                               */
/* Filename : FuelCell.cc                                                    */
/* Author   :                                                               */
/* Project  :                                                               */
/* Version  : 1.0                                                           */
/* Created  : 06/05/2000                                                    */
/* Modified :                                                               */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/****************************************************************************/

#include "FuelCell.h"

#include "mvc_fcc_api.h"
#include <sys/sys_msg.h>

#include "FuelCellIF.h"
#include "LayeredControlIF.h"

#include "EventTriggers.h"
#include "Task.h"
#include "PeriodicTask.h"
#include "Attributes.h"
#include <IntegerAttribute.h>
#include <AttributeParser.h>

#ifdef _SIM
#include "SimulatedFuelCellIF.h"
#endif

//#define GET_STATUS_PERIOD 1000
//#define GET_DATA_PERIOD  2000

/*
CLASS 
FuelCell

DESCRIPTION
FuelCell ethernet driver, based upon Fuel Cell Interface Document.
The "Driver" (this code), is responsible for periodically calling both 
get_FCC_status() and get_FCC_data, and Publishing events as necessary.

Commands send from the MVC to the FCC are issued directly by the FuelCellServer object.


AUTHOR
John Rieffel
*/

FuelCell::FuelCell(char *configFile)
  :PeriodicTask("FuelCell"),
   attributes("fuelcell")
{

  createFCAttributes();
  loadFCConfigFile( configFile);
  reportFCAttributes();
#ifdef _SIM
  try{
    Syslog::write("constructing simulated fuel cell IF\n");
    simulatedfuelcell = new SimulatedFuelCellIF("simulatedfuelcell");
  }
  catch(SharedObjectClient::MissingServer e) {
    Syslog::write("FuelCell.cc --simulated Fuel Cell is not running. \n");
  }
  catch(...) {
    Syslog::write("FuelCell.cc -- failed on instantiation of SimulatedFuelCellIF");
  }  
#endif

  if (init() == -1)
    {
      Syslog::write("FuelCell -- FuelCell() error initializing\n");
      //error
    }
  _output = new FuelCellOutput();
  _output->clear();
  _output->write();
  
  addPeriodicCallback(GET_STATUS_PERIOD, (CallbackMethod)FuelCell::get_status_callback);
  addPeriodicCallback(GET_DATA_PERIOD, (CallbackMethod)FuelCell::get_data_callback);
  
    _log = new FuelCellLog(this, DataLog::BinaryFormat);
  
}

FuelCell::~FuelCell()
{
  delete _output;
    delete _log;
}

// initialize
DeviceIF::Status  FuelCell::init()
{
  // nothing particular to do for now?
  return DeviceIF::Ok;
}

// get Fuel Cell Status (every 1 second) via fcc_api::get_FCC_status
DeviceIF::Status FuelCell::get_status_callback()
{
  int reply_code;
  
#ifdef _SIM
  SimulatedFuelCellIF::FCC_status fcc_status;
  reply_code = (int)simulatedfuelcell->get_simFCC_status(&fcc_status);
#endif
#ifndef _SIM
  FCC_STATUS fcc_status;
    
  reply_code = get_FCC_status(&fcc_status);
#endif  
  
  if (reply_code!=FCC_ERR_OK)
    {

      switch(reply_code)
	{
	case FCC_ERR_INVALID_PARAM:
	  
	  Syslog::write("FuelCell.cc:get_status)callback()-- invalid parameter sent");
	  throw Exception("FuelCell.cc:get_status)callback()-- invalid parameter sent");
	  return DeviceIF::Error;
	  
	  break;
	  
	case FCC_ERR_COMM:
	  Syslog::write("FuelCell.cc:get_status)callback()-- comm error");
	  throw Exception("FuelCell.cc:get_status)callback()-- comm error");
	  break;

	}
    }
  // no errors -- FCC Status is OK
  else
    {
      if (fcc_status.health != FCC_HEALTHY)
	{
	  
	  // first of all, trigger an event.
	  // for simplicity, there is only a "NewAlarm" Event
	  // and the (heretofore non-existant) client can then read
	  // the health flag to learn details of outstanding alarms
	  
	  Syslog::write("(Smart)FuelCell.cc: Alarm Recieved\n");
	  triggerEvent(FuelCellIF::NewAlarm); 
	  
	  // now, if it is a RED alert, do something about it yourself.
	  // 0x2AAAA is 101010101010101010, i.e. all RED alarms)
	  // so if we bitwise AND the health with 0x2AAAA
	  // result will be zero ONLY if there are NO RED alarms.
	  if ((fcc_status.health & 0x2AAAA) != 0)
	    {
	      Syslog::write("(Smart)FuelCell.cc: Red Alert Detected...taking appropriate actions\n");
	      // first, confirm that the fuel cell is running
	      // just to be sure, let's get the status AGAIN
#ifdef _SIM
	      reply_code = (int)simulatedfuelcell->get_simFCC_status(&fcc_status);
#endif
#ifndef _SIM
	      reply_code = get_FCC_status(&fcc_status);
#endif  
	      
	      if (fcc_status.fc_control_state == FC_STATE_ONLINE)
		{
		  //okay, it's running, so send an offline command
		  Syslog::write("(Smart)FuelCell.cc: Red Alert Detected...Sending offline() Command\n");
#ifdef _SIM
		  reply_code = simulatedfuelcell->send_simFCC_command(SimulatedFuelCellIF::FCC_CMD_OFFLINE);
#endif
#ifndef _SIM
		  reply_code = send_FCC_command(FCC_CMD_OFFLINE);
#endif
		  if (reply_code!=FCC_ERR_OK)
		    {
		      switch(reply_code)
			{
			case FCC_ERR_INVALID_PARAM:
			  
			  Syslog::write("(Smart)FuelCell.cc: invalid parameter sent while trying to Offline due to red-alert");
			  throw Exception("(Smart)FuelCell.cc: invalid parameter sent while trying to Offline due to red-alert");
			  break;
			  
			case FCC_ERR_COMM:
			  Syslog::write("(Smart)FuelCell.cc: comm error while trying to Offline due to red-alert");
			  throw Exception("(Smart)FuelCell.cc: comm error while trying to Offline due to red-alert");
			  break;
			}
		    } // end if (reply_code)
		}
	      else
		{
		  Syslog::write("(Smart)FuelCell.cc: Red Alarm Recieved, but fuel cell isn't running, so can't send offline!\n  Aborting Mission anyway");
		  
		}
	      
	      //then, once you hear a response, issue a LayeredControlIF::abortMission()
	      Syslog::write("(SmartFuelCell.cc: Red Alarm Detected...sending abortMission to layeredcontrol IF\n");
	      
	      try 
		{
		// Create an interface to LayeredControl
		LayeredControlIF *layeredControl = new LayeredControlIF("layeredControl");
		// LayeredControl is running; abort the mission
		layeredControl->abortMission();
		// Done with interface; delete it to avoid memory leaks
		delete layeredControl;   
		}
	      catch (...)
		{
		  Syslog::write("SmartFuelCell.cc: Couldn't abort mission -- Layered Control not running!\n");
		  // LayeredControl isn't running 
		} 
	    } // end of red alarm clause
	  // in this case, it is a yellow alarm
	  // that is, health is bitwise-anded with 010101010101010101
	  else if ((fcc_status.health & 0x15555) != 0)
	    {
	      Syslog::write("FuelCell.cc - Alarm is Yellow.  No action taken (beyond triggering NewAlarm Event)\n");
	    }
	  
	} //no alarm
      else
	{
	  // printf("alarm cleared\n");
	  // it's healthy (again?)
	  triggerEvent(FuelCellIF::ClearAlarm);
	}	  
      
      // now other statuses to return;
      _output->data.fcc_status.health = fcc_status.health;
      _output->data.fcc_status.fc_control_state = (FuelCellIF::FC_Control_State)fcc_status.fc_control_state;
      _output->data.fcc_status.FC_remaining_kwh = fcc_status.FC_remaining_kwh;
      _output->data.fcc_status.RB_remaining_wh = fcc_status.RB_remaining_wh;
      _output->data.fcc_status.BIT_status = fcc_status.BIT_status;
      _output->data.fcc_status.BIT_result = fcc_status.BIT_result;
      try {
	_output->write();
      }
      catch(SharedData::AccessError error)
	{
	  fprintf(stderr,"%s",error.msg);
	  return DeviceIF::Error;
	}
      _log->write();
    }
  return DeviceIF::Ok;
}


// get Fuel Cell Data (every 960 second == 15 minutes = 960000 millisec) via fcc_api::get_FCC_data
DeviceIF::Status FuelCell::get_data_callback()
{
  int reply_code;

#ifdef _SIM
  SimulatedFuelCellIF::FCC_data fcc_data;
  reply_code = simulatedfuelcell->get_simFCC_data(&fcc_data);
#endif
#ifndef _SIM

  FCC_DATA fcc_data;
  reply_code = get_FCC_data(&fcc_data);
#endif

    if (reply_code!=FCC_ERR_OK)
    {
      switch(reply_code)
	{
	case FCC_ERR_INVALID_PARAM:
	  
	  Syslog::write("FuelCell.cc:get_status)callback()-- invalid parameter sent");
	  throw Exception("FuelCell.cc:get_status)callback()-- invalid parameter sent");
	  return DeviceIF::Error;
	  break;

	case FCC_ERR_COMM:
	  Syslog::write("FuelCell.cc:get_status)callback()-- comm error");
	  throw Exception("FuelCell.cc:get_status)callback()-- comm error");
	  break;
	  
	  
	}
      
    }
  else
    {

      _output->data.fcc_data.elapsed_time = fcc_data.elapsed_time;
      _output->data.fcc_data.anolyte_pump_a_current = fcc_data.anolyte_pump_a_current;
      _output->data.fcc_data.anolyte_pump_b_current = fcc_data.anolyte_pump_b_current;
      _output->data.fcc_data.catholyte_pump_current = fcc_data.catholyte_pump_current;
      _output->data.fcc_data.peroxide_pump_current = fcc_data.peroxide_pump_current;
      _output->data.fcc_data.anolyte_temp = fcc_data.anolyte_temp;
      _output->data.fcc_data.catholyte_temp = fcc_data.catholyte_temp;
      _output->data.fcc_data.fwd_peroxide_temp = fcc_data.fwd_peroxide_temp;
      _output->data.fcc_data.aft_peroxide_temp = fcc_data.aft_peroxide_temp;
      _output->data.fcc_data.stack_voltage = fcc_data.stack_voltage;
      _output->data.fcc_data.stack_current = fcc_data.stack_current;
      _output->data.fcc_data.battery_voltage = fcc_data.battery_voltage;
      _output->data.fcc_data.battery_current = fcc_data.battery_current;
      _output->data.fcc_data.peroxide_used = fcc_data.peroxide_used;
      _output->data.fcc_data.v_setpoint = fcc_data.v_setpoint;
      _output->data.fcc_data.makeup_injection_count = fcc_data.makeup_injection_count;
      _output->data.fcc_data.base_injection_duration = fcc_data.base_injection_duration;
      
      try {
	_output->write();
      }


      catch(SharedData::AccessError error)
	{
	  fprintf(stderr,"%s",error.msg);
	  return DeviceIF::Error;
	}
      
      _log->write();

    }
  return DeviceIF::Ok;
}

void FuelCell::createFCAttributes( void )
{
  //  attributes.add( new IntegerAttribute("target_node","target_node",&target_node);
  Syslog::write("FuelCell::createFCAttributes()\n");
  attributes.add( new IntegerAttribute("STATUS_PERIOD", "STATUS_PERIOD",&GET_STATUS_PERIOD));
  attributes.add( new IntegerAttribute("DATA_PERIOD", "DATA_PERIOD",&GET_DATA_PERIOD));
}

void FuelCell::loadFCConfigFile( char *filename)
{
  Syslog::write("FuelCell::loadFCConfigFile()\n");
  //char *filename;
  //strcpy(filename, "fuelcell.cfg");
  AttributeParser::parse(filename, &attributes);
}

void FuelCell::reportFCAttributes( void )
{
  Syslog::write("FuelCell.cc - Attributes: STATUS_PERIOD: %d, DATA_PERIOD: %d\n",GET_STATUS_PERIOD, GET_DATA_PERIOD);
}
