/** \file
 *
 *  Contains the Power24vConverter class implementation.
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 *
 *  This class simply provides power and a data writer to
 *  allowing a mission to request it
 */

#include "Power24vConverter.h"
#include "Power24vConverterIF.h"

#include "controlModule/VerticalControlIF.h"
#include "data/ConfigReader.h"
#include "data/Slate.h"
#include "data/StrValue.h"
#include "data/UniversalDataReader.h"
#include "data/UniversalDataWriter.h"
#include "units/Units.h"

Power24vConverter::Power24vConverter( const Module* module )
    : SyncSensorComponent( Power24vConverterIF::NAME, module ),
      loadControl_( Power24vConverterIF::LOAD_CONTROL, !simulateHardware(), logger_, this ),
      powerOffTimeout_( 600 ),
      powerOffTime_( Timestamp::NOT_SET_TIME ),
      powered_( false ),
      debug_( false )
{
    // Slate outputs
    power24vConverterDataWriter_ = newDataWriter( Power24vConverterIF::POWER_24V_CONVERTER );

    this->setAllowableFailures( 10 );
    this->setRetryTimeout( 150 );

    // This configures the advanced run modes.
    setRunState( START );
}

Power24vConverter::~Power24vConverter()
{}


/// Do what needs to be done to run
Component::RunState Power24vConverter::start()
{
    if( debug_ ) logger_.syslog( "Start", Syslog::INFO );

    // Power up the LCB. Respond to a fault if encountered.
    if( !powerUpLoadControl() )
    {
        return STOP;
    }

    return RUNNABLE;
}

/// Might follow a STOP...START sequence
Component::RunState Power24vConverter::starting()
{
    if( debug_ ) logger_.syslog( "Starting", Syslog::INFO );

    return RUNNABLE; // Power is already on.
}

/// Pause for a short period (indicated by pauseTime)
Component::RunState Power24vConverter::pause()
{
    // State not used at this time. Just stop.
    if( debug_ ) logger_.syslog( "Pause", Syslog::INFO );
    return STOP;
}

/// Should eventually follow a PAUSE request: should set continueTime
Component::RunState Power24vConverter::paused()
{
    if( debug_ ) logger_.syslog( "Paused", Syslog::INFO );
    return STOP;
}


Component::RunState Power24vConverter::resume()
{
    if( debug_ ) logger_.syslog( "Resume", Syslog::INFO );
    return STOPPED; // State not used at this time
}


Component::RunState Power24vConverter::resuming()
{
    if( debug_ ) logger_.syslog( "Resuming", Syslog::INFO );
    return STOPPED; // State not used at this time
}


Component::RunState Power24vConverter::runnable()
{
    if( debug_ ) logger_.syslog( "Runnable", Syslog::INFO );

    // Stop if there's no need for 24v power.
    if( !isDataRequested() )
    {
        if( powerOffTime_ == Timestamp::NOT_SET_TIME )
        {
            // Start the power off timer.
            powerOffTime_ = Timestamp::Now();
        }
        else if( powerOffTime_.elapsed() > powerOffTimeout_ )
        {
            return STOP;
        }
    }

    // Log the usual
    loadControl_.requestVoltageAndCurrent();
    if( loadControl_.hasError() )
    {
        logger_.syslog( "LCB fault: " + loadControl_.errorString(), Syslog::FAULT );
        this->setFailure( FailureMode::HARDWARE );
        stop();
    }

    // Let's have a check of the power state just in case something dropped behind our back. If we're here the state should be ON or powering up.
    if( loadControl_.getPowerState() != LoadControl::ON  && loadControl_.getPowerState() != LoadControl::POWER_UP )
    {
        logger_.syslog( "24V Power is reporting not powering/powered.", Syslog::FAULT );
        this->setFailure( FailureMode::HARDWARE );
        return STOP;
    }
    else
    {
        this->resetFailCount();
    }

    return RUNNABLE;
}

Component::RunState Power24vConverter::stop()
{
    if( debug_ ) logger_.syslog( "Stop", Syslog::INFO );

    // Disarm the power off timer.
    powerOffTime_ = Timestamp::NOT_SET_TIME;

    // Power down now, check for faults next cycle.
    powerDownLoadControl();

    return STOPPING;
}


Component::RunState Power24vConverter::stopping()
{
    if( debug_ ) logger_.syslog( "Stopping", Syslog::INFO );

    if( !simulateHardware() )
    {
        // See if anything went wrong that may have caused this request for uninitialize
        loadControl_.readFaults();
        if( loadControl_.hasError() )
        {
            // And things that set failures second
            logger_.syslog( " LCB fault: " + loadControl_.errorString(), Syslog::FAULT );
            this->setFailure( FailureMode::HARDWARE );
            return STOP;
        }
    }
    // Currently used to delay one more cycle to allow for full power down
    return STOPPED;
}


Component::RunState Power24vConverter::stopped()
{
    if( debug_ ) logger_.syslog( "Stopped", Syslog::INFO );
    if( isDataRequested() )
    {
        return start();
    }

    return STOPPED;
}


bool Power24vConverter::isDataRequested()
{
    // Check to see if 24v power is needed
    return power24vConverterDataWriter_->isDataRequested();
}


bool Power24vConverter::powerUpLoadControl()
{
    logger_.syslog( "Powering up.", Syslog::INFO );

    if( !simulateHardware() )
    {
        if( !loadControl_.powerUp() )
        {
            logger_.syslog( "Failed to power up.", Syslog::FAULT );
            this->setFailure( FailureMode::HARDWARE );
            return false;
        }
    }

    powered_ = true;
    power24vConverterDataWriter_->write( Units::BOOL, powered_ );
    return true;
}


bool Power24vConverter::powerDownLoadControl()
{
    logger_.syslog( "Powering down.", Syslog::INFO );

    if( !simulateHardware() )
    {
        // First power down then query for faults next cycle
        if( !loadControl_.powerDown() )
        {
            logger_.syslog( "Failed to power down.", Syslog::FAULT );
            this->setFailure( FailureMode::HARDWARE );
            return false;
        }
    }

    powered_ = false;
    power24vConverterDataWriter_->write( Units::BOOL, powered_ );
    return true;
}


/// Should return [myNamespace]::SIMULATE_HARDWARE, or [myNamespace]::POWER, etc
ConfigURI Power24vConverter::getConfigURI( ConfigOption configOption ) const
{
    return configOption == CONFIG_SIMULATE_HARDWARE ? Power24vConverterIF::SIMULATE_HARDWARE : ConfigURI::NO_CONFIG_URI;
}
