/** \file
 *
 *  Contains the CommandExec class implementation.
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 */

#include "CommandExec.h"

#include "bitModule/IBITIF.h"
#include "bitModule/CBITIF.h"
#include "component/ComponentRegistry.h"
#include "data/ConfigDataElement.h"
#include "data/ElementURI.h"
#include "data/Slate.h"
#include "data/StrValue.h"
#include "data/UniversalDataElement.h"
#include "data/UniversalDataReader.h"
#include "data/UniversalDataWriter.h"
#include "dockModule/DockIF.h"
#include "io/DeviceIOStream.h"
#include "io/LPC3Reg.h"
#include "logger/DecimationLogWriter.h"
#include "logger/MissionStateLogger.h"
#include "logger/Reporter.h"
#include "missionScript/MissionItem.h"
#include "module/Config.h"
#include "sensorModule/NAL9602.h"
#include "sensorModule/NAL9602IF.h"
#include "scienceModule/WetLabsUBATIF.h"
#include "supervisor/DataReceiver.h"
#include "supervisor/Maintainer.h"
#include "supervisor/ScheduledItem.h"
#include "supervisor/SendData.h"
#include "supervisor/Supervisor.h"
#include "units/UnitRegistry.h"
#include "units/Units.h"
#include "utils/CodedStr.h"
#include "VehicleIF.h"

#include <cstdlib>
#include <ctype.h>
#include <sys/types.h>
#include <unistd.h>
#include <regex.h>

CommandExec* CommandExec::Instance_( NULL );

const Timespan CommandExec::PERIOD( 0.1 );

Mutex CommandExec::QueueCommandMutex_;
FlexArray<Str*> CommandExec::cmdQueue_( true, MAX_CMDS );

Mutex CommandExec::CommandHistoryMutex_;
FlexArray<Str*> CommandExec::cmdHistory_( true, HISTORY_SIZE );

char CommandExec::CommandBuffer_[BUFF_SIZE];
int CommandExec::InputIndex_( 0 );

bool CommandExec::AtMode_( false );

const Timespan CommandExec::BANG_TIMEOUT( 5 );

Scheduler* CommandExec::Scheduler_( NULL );
Mutex CommandExec::scheduleMutex_;

FlexArray<Command*> CommandExec::commands_( true );

// BANG_CMD_OUTPUT_LINE_PREFIX: Each "\n" in the output from a
// banged command is replaced with this string
static const Str BANG_CMD_OUTPUT_LINE_PREFIX( "\n  " );

CommandExec::CommandExec( Supervisor& supervisor )
    : AsyncComponent( "CommandExec", NULL, PERIOD ),
      parsedCommandCount_( 0 ),
      supervisor_( supervisor ),
      cmdHistoryIndex_( 0 ),
      burnStopTime_( Timestamp::NOT_SET_TIME ),
      doBytes_( 0 ),
      doSbd_( Str::EMPTY_STR )
{
    setAllowableFailures( 1 );
    setRetryTimeout( 5 );

    Instance_ = this;
    Scheduler_ = new Scheduler();

    strobeModeWriter_ = newDataWriter( NAL9602IF::STROBE_MODE );
    dockingModuleStateWriter_ = newDataWriter( DockIF::DOCKING_STATE_CMD );
    dockingModuleStateReader_ = newDataReader( DockIF::DOCKING_STATE );
    enableUbatWriter_ = newDataWriter( WetLabsUBATIF::UBAT_ENABLE_MODE );
    ibitRunningReader_ = newDataReader( IBITIF::IBIT_RUNNING_STATE );
    scanRunningReader_ = newDataReader( CBITIF::GF_ACTIVE_STATE );
    platformConversationReader_ = newUniversalReader( UniversalURI::PLATFORM_CONVERSATION );
    platformConversationWriter_ = newUniversalWriter( UniversalURI::PLATFORM_CONVERSATION );

    addCommand( "?", "Show help" )
    .addSyntax( "Get general help", CommandHelp )
    .addSyntax( "Get help on a command", CommandHelp )
    .addArg( ARG_COMMAND );

    addCommand( "!", "Bash command with no stdin, stdout routed to syslog IMPORTANT", true )
    .addSyntax( "bash command", CommandBang )
    .addArg( ARG_STRING );

    addCommand( "burn", "Burn (or stop burning) the burnwire", true )
    .addSyntax( "Terminate burnwire burn", CommandBurn, BURN_OFF )
    .addArg( "off" )
    .addSyntax( "Start burnwire burn", CommandBurn, BURN_ON )
    .addArg( "on" )
    .addSyntax( "Burn for #seconds, Q interrupts", CommandBurn, BURN_SECONDS )
    .addArg( ARG_SECONDS );

    addCommand( "strobe", "Enable (or disable) the strobe", true )
    .addSyntax( "Turn strobe off", CommandStrobe, STROBE_OFF )
    .addArg( "off" )
    .addSyntax( "Turn strobe on", CommandStrobe, STROBE_ON )
    .addArg( "on" );

    addCommand( "configSet", "Set configuration variable value" )
    .addSyntax( "List configuration values currently persisted", CommandConfigSet, CONFIG_SET_LIST )
    .addArg( "list", NOT_REQUIRED )
    .addSyntax( "Reload persisted values, after a unintentional crash, for example", CommandConfigSet, CONFIG_SET_RELOAD )
    .addArg( "reload" )
    .addSyntax( "Set configuration value with specified unit, optionally persisted", CommandConfigSet, CONFIG_SET_CONFIG )
    .addArg( ARG_CONFIG_VARIABLE ).addArg( ARG_LIST ).addArg( ARG_UNIT ).addArg( "persist", NOT_REQUIRED )
    .addSyntax( "Configure logging where service type can be [log]Courier,Express,Priority, or Normal, and DecimationType can be none, all, linearApprox, or mostRecent, and optionally persisted ", CommandConfigSet, CONFIG_SET_LOGGING )
    .addArg( ARG_SERVICE_TYPE ).addArg( ARG_DECIMATION_TYPE ).addArg( ARG_VARIABLE ).addArg( ARG_LIST, NOT_REQUIRED ).addArg( ARG_UNIT, REQUIRED_AFTER_PREVIOUS ).addArg( "persist", NOT_REQUIRED )
    ;

    addCommand( "conversation", "Show, start and stop satellite conversations" )
    .addSyntax( "Show satellite conversation state", CommandConversation, CONVERSATION_SHOW )
    .addSyntax( "Start satellite conversation state", CommandConversation, CONVERSATION_START )
    .addArg( "start" )
    .addSyntax( "Stop satellite conversation state", CommandConversation, CONVERSATION_STOP )
    .addArg( "stop" );

    addCommand( "DDM", "Set the docking module state to standby, arm, or detach", true )
    .addSyntax( "Standby Mode", CommandDDM, DockIF::STANDBY )
    .addArg( "standby" )
    .addSyntax( "Armed Mode", CommandDDM, DockIF::ARM )
    .addArg( "arm" )
    .addSyntax( "Detach Mode", CommandDDM, DockIF::DETACH )
    .addArg( "detach" );

    addCommand( "docking", "Set the docking actuator state to standby, arm, detach, or slide", true )
    .addSyntax( "Standby Mode", CommandDockingServo, DockIF::STANDBY )
    .addArg( "standby" )
    .addSyntax( "Armed Mode", CommandDockingServo, DockIF::ARM )
    .addArg( "arm" )
    .addSyntax( "Detach Mode", CommandDockingServo, DockIF::DETACH )
    .addArg( "detach" )
    .addSyntax( "Slide Mode (Stepper only)", CommandDockingServo, DockIF::SLIDE )
    .addArg( "slide" )
    .addSyntax( "Unrequest data from docking arm. Expect power off.", CommandDockingServo, DockIF::UNKNOWN )
    .addArg( "unrequest" );

    addCommand( "failComponent", "Show, set, or clear failed state of components", true )
    .addSyntax( "List all failed components", CommandFailComponent, FAIL_COMPONENT_LIST )
    .addSyntax( "Show failure state of one component", CommandFailComponent, FAIL_COMPONENT_SHOW )
    .addArg( "show" ).addArg( ARG_COMPONENT )
    .addSyntax( "Clear failure state of one component", CommandFailComponent, FAIL_COMPONENT_NONE )
    .addArg( "none" ).addArg( ARG_COMPONENT )
    .addSyntax( "Set failure state of one component to 'Hardware Failure'", CommandFailComponent, FAIL_COMPONENT_HARD )
    .addArg( "hardware" ).addArg( ARG_COMPONENT )
    .addSyntax( "Set failure state of one component to 'Software Failure'", CommandFailComponent, FAIL_COMPONENT_SOFT )
    .addArg( "software" ).addArg( ARG_COMPONENT )
    .addSyntax( "Create component software failure via an actual segfault", CommandFailComponent, FAIL_COMPONENT_SEGFAULT )
    .addArg( "segfault" ).addArg( ARG_COMPONENT );

    addCommand( "failVariable", "Show, set, or clear failed state of values", true )
    .addSyntax( "List all failed variables", CommandFailVariable, FAIL_VARIABLE_LIST )
    .addSyntax( "Show failure state of one variable", CommandFailVariable, FAIL_VARIABLE_SHOW )
    .addArg( "show" ).addArg( ARG_VARIABLE )
    .addSyntax( "Set 'unavailable' state of one variable", CommandFailVariable, FAIL_VARIABLE_UNAVAILABLE )
    .addArg( "unavailable" ).addArg( ARG_VARIABLE )
    .addSyntax( "clear 'unavailable' state of one variable", CommandFailVariable, FAIL_VARIABLE_AVAILABLE )
    .addArg( "available" ).addArg( ARG_VARIABLE )
    .addSyntax( "Set 'invalid' state of one variable", CommandFailVariable, FAIL_VARIABLE_INVALID )
    .addArg( "invalid" ).addArg( ARG_VARIABLE )
    .addSyntax( "clear 'invalid' state of one variable", CommandFailVariable, FAIL_VARIABLE_VALID )
    .addArg( "valid" ).addArg( ARG_VARIABLE );

    addCommand( "fileExec", "Execute contents of file that follows (.sbd or plain text) as if it were sent remotely", true )
    .addSyntax( "Path to file", CommandFileExec )
    .addArg( ARG_TOKEN );
    doBuffer_[0] = 0;

    addCommand( "get", "Get variable value" )
    .addSyntax( "Get value, optionally with specified unit", CommandGet )
    .addArg( ARG_VARIABLE ).addArg( ARG_UNIT, NOT_REQUIRED );

    addCommand( "gfscan", "Allow CBIT to scan for ground faults" )
    .addSyntax( "Run a ground fault scan", CommandGFscan );

    addCommand( "help", "Show help, optionally for a specific command" )
    .addSyntax( "Get general help", CommandHelp )
    .addSyntax( "Get help on a command", CommandHelp )
    .addArg( ARG_COMMAND );

    addCommand( "ibit", "Run Initiated Built In Test" )
    .addSyntax( "Run standard IBIT", CommandIbit )
    .addSyntax( "Run IBIT test of mcp5553 device", CommandIbitMcp3553 )
    .addArg( "mcp5553" ).addArg( ARG_INT, NOT_REQUIRED );

    addCommand( "load", "Load, but do not run mission" )
    .addSyntax( "Load mission file so that subsequent 'set' commands can execute before 'run'", CommandLoad )
    .addArg( ARG_MISSION );

    addCommand( "maintain", "Maintain variable value" )
    .addSyntax( "List maintained values", CommandMaintainList )
    .addArg( "list", NOT_REQUIRED )
    .addSyntax( "Clear all maintained values", CommandMaintainClear )
    .addArg( "clear" )
    .addSyntax( "Maintain simulator output value", CommandMaintainAdd, SyncComponent::CYCLE_SIMULATOR )
    .addArg( "simulator" ).addArg( ARG_VARIABLE ).addArg( ARG_LIST ).addArg( ARG_UNIT )
    .addSyntax( "Maintain sensor output value", CommandMaintainAdd, SyncComponent::CYCLE_SENSOR )
    .addArg( "sensor" ).addArg( ARG_VARIABLE ).addArg( ARG_LIST ).addArg( ARG_UNIT )
    .addSyntax( "Maintain derivation output value", CommandMaintainAdd, SyncComponent::CYCLE_DERIVATION )
    .addArg( "derivation" ).addArg( ARG_VARIABLE ).addArg( ARG_LIST ).addArg( ARG_UNIT )
    .addSyntax( "Maintain mission output value", CommandMaintainAdd, SyncComponent::CYCLE_MISSION )
    .addArg( "mission" ).addArg( ARG_VARIABLE ).addArg( ARG_LIST ).addArg( ARG_UNIT )
    .addSyntax( "Maintain control value", CommandMaintainAdd, SyncComponent::CYCLE_CONTROL )
    .addArg( "control" ).addArg( ARG_VARIABLE ).addArg( ARG_LIST ).addArg( ARG_UNIT )
    .addSyntax( "Maintain servo output value", CommandMaintainAdd, SyncComponent::CYCLE_SERVO )
    .addArg( "servo" ).addArg( ARG_VARIABLE ).addArg( ARG_LIST ).addArg( ARG_UNIT )
    .addSyntax( "Maintain sensor output value unavailable", CommandMaintainUnavailable, SyncComponent::CYCLE_SENSOR )
    .addArg( "unavailable" ).addArg( "sensor" ).addArg( ARG_VARIABLE )
    .addSyntax( "Maintain servo output value unavailable", CommandMaintainUnavailable, SyncComponent::CYCLE_SERVO )
    .addArg( "unavailable" ).addArg( "servo" ).addArg( ARG_VARIABLE )
    .addSyntax( "Maintain sensor output value invalid", CommandMaintainInvalid, SyncComponent::CYCLE_SENSOR )
    .addArg( "invalid" ).addArg( "sensor" ).addArg( ARG_VARIABLE )
    .addSyntax( "Maintain servo output value invalid", CommandMaintainInvalid, SyncComponent::CYCLE_SERVO )
    .addArg( "invalid" ).addArg( "servo" ).addArg( ARG_VARIABLE )
    .addSyntax( "Remove maintained value", CommandMaintainRemove )
    .addArg( "remove" ).addArg( ARG_VARIABLE );

    addCommand( "quick", "Toggle state of quicker than realtime execution", true )
    .addSyntax( "Show state of quicker than realtime execution", CommandQuick, QUICK_SHOW )
    .addSyntax( "Turn on quicker than realtime execution", CommandQuick, QUICK_ON )
    .addArg( "on" )
    .addSyntax( "Turn off quicker than realtime execution", CommandQuick, QUICK_OFF )
    .addArg( "off" );

    addCommand( "quit", "Stop any running mission and exit LRAUV program", true )
    .addSyntax( "Quit immediately", CommandQuit );

    addCommand( "report", "Report variable value" )
    .addSyntax( "List reported values", CommandReportList )
    .addArg( "list", NOT_REQUIRED )
    .addSyntax( "Clear all reported values", CommandReportClear )
    .addArg( "clear" )
    .addSyntax( "Add report on variable modification", CommandReportAdd, REPORT_ADD_MOD )
    .addArg( "mod" ).addArg( ARG_VARIABLE ).addArg( ARG_UNIT, NOT_REQUIRED )
    .addSyntax( "Add report on variable touch", CommandReportAdd, REPORT_ADD_TOUCH )
    .addArg( "touch" ).addArg( ARG_VARIABLE ).addArg( ARG_UNIT, NOT_REQUIRED )
    .addSyntax( "Add periodic report every #seconds", CommandReportAdd, REPORT_ADD_PERIODIC )
    .addArg( "periodic" ).addArg( ARG_SECONDS ).addArg( ARG_VARIABLE ).addArg( ARG_UNIT, NOT_REQUIRED )
    .addSyntax( "Remove reported value", CommandReportRemove )
    .addArg( "remove" ).addArg( ARG_VARIABLE );

    addCommand( "restart", "Restart, from least impact (logs) to most impact (system)" )
    .addSyntax( "Restart logs by closing currently open logs and opening a new set.", CommandRestart, RESTART_LOGS )
    .addArg( "logs" )
    .addSyntax( "Restart application with optional Config dir, log symlink, pause and/or command(s)", CommandRestart, RESTART_APPLICATION )
    .addArg( "application" )
    .addArg( "-c", NOT_REQUIRED ).addArg( ARG_CONFIG_DIR, REQUIRED_AFTER_PREVIOUS )
    .addArg( "-l", NOT_REQUIRED ).addArg( ARG_TOKEN, REQUIRED_AFTER_PREVIOUS )
    .addArg( "-p", NOT_REQUIRED ).addArg( ARG_INT, REQUIRED_AFTER_PREVIOUS )
    .addArg( "-x", NOT_REQUIRED ).addArg( ARG_QUOTED_STRING, REQUIRED_AFTER_PREVIOUS )
    .addSyntax( "Restart operating system by rebooting.", CommandRestart, RESTART_SYSTEM )
    .addArg( "system" )
    .addSyntax( "Restart motherboard by a hardware reset. Achtung baby!", CommandRestart, RESTART_HARDWARE )
    .addArg( "hardware" );

    addCommand( "resume", "Resume loaded mission" )
    .addSyntax( "Resume loaded mission", CommandResume )
    .addArg( ARG_MISSION, NOT_REQUIRED );

    addCommand( "retransmit", "Retransmit data" )
    .addSyntax( "Retransmit sbd via Iridium", CommandRetransmit, RETRANSMIT_SBD )
    .addArg( "sbd" ).addArg( ARG_TOKEN, REQUIRED, "latest|previous|YYYYMMDDThhmmss" ).addArg( ARG_INT, REQUIRED, "part#" ).addArg( ARG_STRING, REQUIRED, "#left" );

    addCommand( "run", "Run mission" )
    .addSyntax( "Rum mission file, optionally in real-time and/or quitting at the end", CommandRun )
    .addArg( ARG_MISSION, NOT_REQUIRED ).addArg( "quitatend", NOT_REQUIRED );

    addCommand( "schedule", "Schedule commands for later execution" )
    .addSyntax( "List commands currently scheduled", CommandSchedule, SCHEDULE_LIST )
    .addArg( "list", NOT_REQUIRED )
    .addSyntax( "Clear all commands currently scheduled, optionally only those with a corresponding id", CommandSchedule, SCHEDULE_CLEAR )
    .addArg( "clear" ).addArg( ARG_TOKEN, NOT_REQUIRED, "id" )
    .addSyntax( "Pause initiation of scheduled commands", CommandSchedule, SCHEDULE_PAUSE )
    .addArg( "pause" )
    .addSyntax( "Remove single scheduled command", CommandSchedule, SCHEDULE_REMOVE )
    .addArg( "remove" ).addArg( ARG_INT, REQUIRED, "index#" )
    .addSyntax( "Resume paused initiation of scheduled commands", CommandSchedule, SCHEDULE_RESUME )
    .addArg( "resume" )
    .addSyntax( "Schedule a command as soon as possible with id, idIndex, and idCount", CommandSchedule, SCHEDULE_ASAP )
    .addArg( "asap" ).addArg( ARG_QUOTED_STRING ).addArg( ARG_TOKEN, REQUIRED, "id" ).addArg( ARG_INT, REQUIRED, "idIndex" ).addArg( ARG_FLOAT, REQUIRED, "idCount" )
    .addSyntax( "Schedule a command next in sequence with optional id, idIndex, and idCount", CommandSchedule, SCHEDULE_NEXT )
    .addArg( ARG_QUOTED_STRING ).addArg( ARG_TOKEN, NOT_REQUIRED, "id" ).addArg( ARG_INT, REQUIRED_AFTER_PREVIOUS, "idIndex" ).addArg( ARG_FLOAT, REQUIRED_AFTER_PREVIOUS, "idCount" )
    .addSyntax( "Schedule a command at a specific time with optional id, idIndex, and idCount", CommandSchedule, SCHEDULE_TIME )
    .addArg( ARG_TIMESTAMP ).addArg( ARG_QUOTED_STRING ).addArg( ARG_TOKEN, NOT_REQUIRED, "id" ).addArg( ARG_INT, REQUIRED_AFTER_PREVIOUS, "idIndex" ).addArg( ARG_FLOAT, REQUIRED_AFTER_PREVIOUS, "idCount" )
    ;

    addCommand( "send", "Send variable value or command" )
    .addSyntax( "Send value with specified unit", CommandSend, SEND_VALUE )
    .addArg( ARG_VARIABLE ).addArg( ARG_UNIT ).addArg( ARG_TOKEN, REQUIRED, "destinationURI" )
    .addSyntax( "Send command", CommandSend, SEND_COMMAND )
    .addArg( "cmd" ).addArg( ARG_QUOTED_STRING ).addArg( ARG_TOKEN, REQUIRED, "destinationURI" );

    addCommand( "set", "Set variable value" )
    .addSyntax( "Set value(s) with specified unit", CommandSet )
    .addArg( ARG_VARIABLE ).addArg( ARG_LIST ).addArg( ARG_UNIT )
    .addSyntax( "Set string value", CommandSet )
    .addArg( ARG_VARIABLE ).addArg( "string" ).addArg( ARG_QUOTED_STRING );

    addCommand( "show", "Show information" )
    .addSyntax( "Show 'best' value used for universal", CommandShowBest )
    .addArg( "best" ).addArg( ARG_UNIVERSAL )
    .addSyntax( "List components with names like the regular expression", CommandShowComponent )
    .addArg( "component" ).addArg( ARG_REGEX )
    .addSyntax( "List dependencies of a component", CommandShowDependencies )
    .addArg( "dependencies" ).addArg( "component" ).addArg( ARG_COMPONENT )
    .addSyntax( "List dependencies of a variable", CommandShowDependencies )
    .addArg( "dependencies" ).addArg( "variable" ).addArg( ARG_VARIABLE )
    .addSyntax( "Show the current mission behavior stack", CommandShowStack )
    .addArg( "stack" )
    .addSyntax( "Show the current mission state", CommandShowState )
    .addArg( "state" )
    .addSyntax( "List variables with names like the regular expression", CommandShowVariable )
    .addArg( "variable" ).addArg( ARG_REGEX );

    addCommand( "stop", "Stop currently running mission" )
    .addSyntax( "Stop now", CommandStop );

    addCommand( "ubat", "Enable (or disable) the UBAT" )
    .addSyntax( "Disable the UBAT", CommandUbat, UBAT_OFF )
    .addArg( "off" )
    .addSyntax( "Enable the UBAT", CommandUbat, UBAT_ON )
    .addArg( "on" );
}

void CommandExec::CommandBang( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandBang( parsedCommand );
}
void CommandExec::commandBang( ParsedCommand* parsedCommand )
{
    const char *cmd( parsedCommand->getStringArg().cStr() );
    if( NULL == cmd || 0 == strlen( cmd ) )
    {
        logger_.syslog( "No command to execute", Syslog::FAULT );
        return;
    }
    FILE *p( popen( cmd, "r" ) );
    if( NULL == p )
    {
        logger_.syslog( "Cannot execute " + Str( cmd ), Syslog::FAULT );
        return;
    }

    Timestamp timeoutTime;
    Str output( "" );
    char buff[128];
    int fd;

    fd = fileno( p );
    timeoutTime = Timestamp::Now() + CommandExec::BANG_TIMEOUT;
    while( Timestamp::Now() < timeoutTime )
    {
        fd_set read_file_descr;
        struct timeval timeout;
        int ret;
        FD_ZERO( &read_file_descr );
        FD_SET( fd, &read_file_descr );
        timeout.tv_sec = 0;
        timeout.tv_usec = 100000;
        ret = select( fd + 1, &read_file_descr, NULL, NULL, &timeout );
        if( ret && FD_ISSET( fd, &read_file_descr ) )
        {
            int bytesRead = read( fd, buff, sizeof( buff ) - 1 );
            if( 0 >= bytesRead )
            {
                break;
            }
            buff[bytesRead] = '\0';
            output += buff;
            FD_CLR( fd, &read_file_descr );
        }
    }
    pclose( p );

    // Did the command time out?
    if( Timestamp::Now() > timeoutTime )
    {
        logger_.syslog( Str( "Command " ) + Str( cmd ) + Str( " timed out" ), Syslog::FAULT );
        return;
    }

    // If we read output from the command then show it
    if( 0 < output.length() )
    {
        if( Str::NO_POS != output.find( '\n' ) )
        {
            int numLines;
            Str *lines = output.split( numLines, "\n" );
            output = lines[0];
            for( int i = 1; i < numLines; ++i )
            {
                output += BANG_CMD_OUTPUT_LINE_PREFIX + lines[i];
            }
            delete[] lines;
        }
        logger_.syslog( output, Syslog::IMPORTANT );
    }
}

void CommandExec::CommandStrobe( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandStrobe( parsedCommand, ( StrobeMode ) mode );
}
void CommandExec::commandStrobe( ParsedCommand* parsedCommand, StrobeMode mode )
{
    switch( mode )
    {
    case STROBE_OFF:
        strobeModeWriter_->write( Units::COUNT, ( int ) NAL9602::STROBE_TEMP_OFF );
        logger_.syslog( "Deactivating strobe", Syslog::IMPORTANT );
        break;
    case STROBE_ON:
        strobeModeWriter_->write( Units::COUNT, ( int ) NAL9602::STROBE_ON );
        logger_.syslog( "Activating strobe", Syslog::IMPORTANT );
        break;
    }
}

void CommandExec::CommandBurn( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandBurn( parsedCommand, ( BurnMode ) mode );
}
void CommandExec::commandBurn( ParsedCommand* parsedCommand, BurnMode mode )
{
    float value = NULL == parsedCommand ? 0 : parsedCommand->getNumberArg();
    switch( mode )
    {
    case BURN_SECONDS:
        printf( "Burning dropweight wire for %f seconds, use 'burn off' to stop.\n", value );
        if( value > 0 )
        {
            Timespan burnDuration( value );
            burnStopTime_ = Timestamp::Now() + burnDuration;
            LPC3Reg::ActivateBurnwire();
            // Burning is deactivated by the main getLines() loop.
            break;
        }
    // no break if value is <= 0
    case BURN_OFF:
        logger_.syslog( "Deactivating dropweight wire", Syslog::IMPORTANT );
        burnStopTime_ = Timestamp::NOT_SET_TIME;
        LPC3Reg::DeactivateBurnwire();
        break;
    case BURN_ON:
        logger_.syslog( "Activating dropweight wire", Syslog::IMPORTANT );
        LPC3Reg::ActivateBurnwire();
        break;
    }
}

void CommandExec::CommandConfigSet( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandConfigSet( parsedCommand, ( ConfigSetMode ) mode );
}
void CommandExec::commandConfigSet( ParsedCommand* parsedCommand, ConfigSetMode mode )
{
    int persistIndex = 3;
    switch( mode )
    {
    case CONFIG_SET_LIST:
        Config::LoadPersistedConfigSets( logger_, true, true );
        return;
    case CONFIG_SET_RELOAD:
        Config::LoadPersistedConfigSets( logger_, true, false );
        return;
    case CONFIG_SET_CONFIG:
        break;
    case CONFIG_SET_LOGGING:
        persistIndex = 5;
        break;
    }

    int rank, m, n, o;
    double* numbers = parsedCommand->getTokenArg().toDoubleArray( rank, m, n, o );
    if( rank > 0 )
    {
        // TODO(byraanan): add configSet support for list variables.
        logger_.syslog( "configSet of list variable is not supported.", Syslog::FAULT );
        return;
    }
    const double number( numbers[0] );
    delete[] numbers;

    DataValue* dv = NULL;
    const Unit* unit( parsedCommand->getUnitArg() );
    bool persist = parsedCommand->hasArgIndex( persistIndex );
    if( NULL != unit )
    {
        dv = unit->dataValue( number );
    }

    if( mode == CONFIG_SET_CONFIG )
    {
        const ElementURI* name( parsedCommand->getVariableArg() );
        unsigned short code( name->getCode() );
        if( code == ElementURI::NO_CODE )
        {
            logger_.syslog( "Unable to set non-existent variable: " + *name, Syslog::FAULT );
            return;
        }
        ConfigDataElement* dataElement = dynamic_cast<ConfigDataElement*>( Slate::GetElement( code ) );
        if( NULL == dataElement )
        {
            logger_.syslog( "Unable to set non-configuration variable: " + *name, Syslog::FAULT );
            return;
        }
        if( dataElement->isSecure() )
        {
            logger_.syslog( "Cannot configSet secure configuration variable: " + *name, Syslog::FAULT );
            return;
        }
        if( dataElement->requiresRestart() )
        {
            if( !persist )
            {
                logger_.syslog( "configSet " + *name + " without persist will have no effect.", Syslog::FAULT );
            }
            else
            {
                logger_.syslog( "configSet " + *name + " requires a restart to take effect.", Syslog::IMPORTANT );
            }
        }
        dataElement->setConfigValue( dv );


        // Set the non-config value (to trigger a copy-over)
        commandSet( parsedCommand );

    }
    else
    {
        Supervisor::ConfigureDecimation( parsedCommand->getServiceTypeArg(),
                                         parsedCommand->getDecimationTypeArg(),
                                         *parsedCommand->getVariableArg(), dv, logger_ );
        dv = NULL;
    }

    if( persist )
    {
        if( mode == CONFIG_SET_CONFIG )
        {
            const ElementURI* name( parsedCommand->getVariableArg() );
            const Unit* unit( parsedCommand->getUnitArg() );
            char line[256];
            snprintf( line, sizeof( line ), "%s=%g %s;\n",
                      name->cStr(), number, unit->getName() );
            Config::Persist( *name, line, logger_ );
        }
        else
        {
            Str name = Str( Service::ServiceType2Str( parsedCommand->getServiceTypeArg() ) ) + " "
                       + *parsedCommand->getVariableArg();
            Str line = Str( Service::ServiceType2Str( parsedCommand->getServiceTypeArg() ) ) + " "
                       + DecimationLogWriter::DecimationType2Str( parsedCommand->getDecimationTypeArg() ) + " "
                       + *parsedCommand->getVariableArg();

            if( NULL != parsedCommand->getUnitArg() )
            {
                const Unit* unit( parsedCommand->getUnitArg() );
                line = line + " " + number + " " + unit->getName() + ";\n";
                Config::Persist( name, line.cStr(), logger_ );
            }
            else
            {
                line += ";\n";
                Config::Persist( name, line.cStr(), logger_ );
            }

        }
    }

    if( NULL != dv )
    {
        delete dv;
    }

}

void CommandExec::CommandConversation( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandConversation( parsedCommand, ( ConversationMode ) mode );
}
void CommandExec::commandConversation( ParsedCommand* parsedCommand, ConversationMode mode )
{
    switch( mode )
    {
    case CONVERSATION_SHOW:
        break;
    case CONVERSATION_START:
        // Start conversation
        platformConversationReader_->requestData( true );
        break;
    case CONVERSATION_STOP:
        // Stop conversation
        platformConversationReader_->requestData( false );
        break;
    }
    int started( platformConversationWriter_->isAnyDataRequested() );
    logger_.syslog( "Conversation is ", started ? "started." : "stopped.", Syslog::IMPORTANT );
}


void CommandExec::CommandDDM( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandDDM( parsedCommand, ( DockIF::DockingState ) mode );
}
void CommandExec::commandDDM( ParsedCommand* parsedCommand, DockIF::DockingState mode )
{
    dockingModuleStateReader_->requestData( true );
    switch( mode )
    {
    case DockIF::STANDBY:
        dockingModuleStateWriter_->write( Units::ENUM, ( int ) DockIF::STANDBY );
        logger_.syslog( "Switching docking moudle to STANDBY.", Syslog::IMPORTANT );
        break;
    case DockIF::ARM:
        dockingModuleStateWriter_->write( Units::ENUM, ( int ) DockIF::ARM );
        logger_.syslog( "Switching docking moudle to ARM.", Syslog::IMPORTANT );
        break;
    case DockIF::DETACH:
        dockingModuleStateWriter_->write( Units::ENUM, ( int ) DockIF::DETACH );
        logger_.syslog( "Switching docking moudle to DETACH.", Syslog::IMPORTANT );
        break;
    default:
        break;
    }
}


void CommandExec::CommandDockingServo( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandDockingServo( parsedCommand, ( DockIF::DockingState ) mode );
}
void CommandExec::commandDockingServo( ParsedCommand* parsedCommand, DockIF::DockingState mode )
{
    dockingModuleStateReader_->requestData( true );
    switch( mode )
    {
    case DockIF::STANDBY:
        dockingModuleStateWriter_->write( Units::ENUM, ( int ) DockIF::STANDBY );
        logger_.syslog( "Switching docking moudle to STANDBY.", Syslog::IMPORTANT );
        break;
    case DockIF::ARM:
        dockingModuleStateWriter_->write( Units::ENUM, ( int ) DockIF::ARM );
        logger_.syslog( "Switching docking moudle to ARM.", Syslog::IMPORTANT );
        break;
    case DockIF::DETACH:
        dockingModuleStateWriter_->write( Units::ENUM, ( int ) DockIF::DETACH );
        logger_.syslog( "Switching docking moudle to DETACH.", Syslog::IMPORTANT );
        break;
    case DockIF::SLIDE:
        dockingModuleStateWriter_->write( Units::ENUM, ( int ) DockIF::SLIDE );
        logger_.syslog( "Switching docking module to SLIDE.", Syslog::IMPORTANT );
        break;
    case DockIF::UNKNOWN:
        dockingModuleStateReader_->requestData( false );
        logger_.syslog( "Setting data request false.", Syslog::IMPORTANT );
        break;
    default:
        break;
    }
}


void CommandExec::CommandFailComponent( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandFailComponent( parsedCommand, ( FailComponentMode ) mode );
}
void CommandExec::commandFailComponent( ParsedCommand* parsedCommand, FailComponentMode mode )
{
    const CodedStr* name = parsedCommand->getComponentArg();
    Component* component = NULL == name ? NULL : ComponentRegistry::Find( *name );
    if( NULL != name && NULL == component )
    {
        logger_.syslog( "Could not find component with name: " + *name, Syslog::FAULT );
        return;
    }

    bool noFailedComponents( true );
    switch( mode )
    {
    case FAIL_COMPONENT_LIST:
        logger_.syslog( "Failed components:", Syslog::IMPORTANT );
        for( unsigned int i = 0; i < ComponentRegistry::Length(); ++i )
        {
            const Component* component = ComponentRegistry::GetIndexed( i );
            if( NULL != component && component->isFailed() )
            {
                logger_.syslog( component->getName() + ": "
                                + FailureMode::ToString( component->getFailureType() ),
                                Syslog::IMPORTANT );
                noFailedComponents = false;
            }
        }
        if( noFailedComponents )
        {
            logger_.syslog( "No failed Components.", Syslog::IMPORTANT );
        }
        break;
    case FAIL_COMPONENT_SHOW:
        break;
    case FAIL_COMPONENT_NONE:
        if( NULL != component )
        {
            component->setFailure( FailureMode::NONE );
        }
        break;
    case FAIL_COMPONENT_HARD:
        if( NULL != component )
        {
            component->setFailure( FailureMode::HARDWARE );
        }
        break;
    case FAIL_COMPONENT_SOFT:
        if( NULL != component )
        {
            component->setFailure( FailureMode::SOFTWARE );
        }
        break;
    case FAIL_COMPONENT_SEGFAULT:
        if( NULL != component )
        {
            component->segfault();
        }
        break;
    }
    if( NULL != component )
    {
        logger_.syslog( *name + " failureMode is "
                        + FailureMode::ToString( component->getFailureType() ),
                        Syslog::IMPORTANT );
    }
}

void CommandExec::CommandFailVariable( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandFailVariable( parsedCommand, ( FailVariableMode ) mode );
}
void CommandExec::commandFailVariable( ParsedCommand* parsedCommand, FailVariableMode mode )
{
    const ElementURI* name = parsedCommand->getVariableArg();
    unsigned short varCode = name == NULL ? ElementURI::NO_CODE : name->getCode();
    if( NULL != name && ElementURI::NO_CODE == varCode )
    {
        logger_.syslog( "Not a variableName:" + *name, Syslog::FAULT );
        return;
    }

    DataElement* dataElement = name == NULL ? NULL : Slate::GetElement( varCode );
    if( NULL != name && NULL == dataElement )
    {
        logger_.syslog( "Could not find variable with name:" + *name, Syslog::FAULT );
        return;
    }
    bool noFailedVars( true );

    switch( mode )
    {
    case FAIL_VARIABLE_LIST:
        logger_.syslog( "Failed variables:", Syslog::IMPORTANT );
        for( unsigned short int code = 0; code < Slate::GetElementURICount(); ++code )
        {
            ElementURI* elementURI = Slate::GetElementURI( code );
            if( NULL == elementURI )
            {
                continue;
            }
            DataElement* dataElement = Slate::GetElement( code );
            if( NULL != dataElement && dataElement->isUnavailable() )
            {
                logger_.syslog( *elementURI + " is unavailable.", Syslog::IMPORTANT );
                noFailedVars = false;
            }
            if( NULL != dataElement && dataElement->isInvalid() )
            {
                logger_.syslog( *elementURI + " is invalid.", Syslog::IMPORTANT );
                noFailedVars = false;
            }
        }
        if( noFailedVars )
        {
            logger_.syslog( "No failed Variables.", Syslog::IMPORTANT );
        }
        break;
    case FAIL_VARIABLE_SHOW:
        break;
    case FAIL_VARIABLE_UNAVAILABLE:
        if( NULL != dataElement )
        {
            dataElement->setUnavailable( true, this );
        }
        break;
    case FAIL_VARIABLE_AVAILABLE:
        if( NULL != dataElement )
        {
            dataElement->setUnavailable( false, this );
        }
        break;
    case FAIL_VARIABLE_INVALID:
        if( NULL != dataElement )
        {
            dataElement->setInvalid( true, this );
        }
        break;
    case FAIL_VARIABLE_VALID:
        if( NULL != dataElement )
        {
            dataElement->setInvalid( false, this );
        }
        break;
    }
    if( NULL != dataElement && dataElement->isUnavailable() )
    {
        logger_.syslog( dataElement->getUri() + " is unavailable.", Syslog::IMPORTANT );
    }
    if( NULL != dataElement && dataElement->isInvalid() )
    {
        logger_.syslog( dataElement->getUri() + " is invalid.", Syslog::IMPORTANT );
    }
}


void CommandExec::CommandFileExec( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandFileExec( parsedCommand );
}
void CommandExec::commandFileExec( ParsedCommand* parsedCommand )
{
    doBuffer_[0] = 0;
    FILE* doFile = fopen( parsedCommand->getTokenArg().cStr(), "rb" );
    if( NULL == doFile )
    {
        logger_.syslog( "No file named " + parsedCommand->getTokenArg(), Syslog::ERROR );
        return;
    }
    doBytes_ = fread( doBuffer_, 1, 1024, doFile );
    fclose( doFile );
    if( parsedCommand->getStringArg().find( ".sbd" ) == parsedCommand->getTokenArg().length() - 4 )
    {
        doSbd_ = parsedCommand->getTokenArg();
    }
    else
    {
        doSbd_ = Str::EMPTY_STR;
    }

    // Execution handled in the main getLines() loop
}


void CommandExec::CommandGet( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandGet( parsedCommand );
}
void CommandExec::commandGet( ParsedCommand* parsedCommand )
{
    const ElementURI* elementURI = parsedCommand->getVariableArg();
    unsigned short code( elementURI->getCode() );
    if( code == ElementURI::NO_CODE && elementURI->length() > 2
            && ( elementURI->cStr()[ 0 ] == '_' || elementURI->cStr()[ 0 ] == '#' )
            && elementURI->cStr()[ 1 ] == '.' )
    {
        // This is a new scratchpad variable
        const Unit* unit = parsedCommand->getUnitArg();
        unit = unit == NULL ? &elementURI->getUnit() : unit;
        DataElement* dataElement = new SimpleDataElement( *elementURI, unit->dataValue( elementURI->getBinaryType() ), 0 );
        Slate::MapDataElement( ( ElementURI * )elementURI, dataElement );
        ElementURI* regURI = Slate::FindElementURI( *elementURI );
        code = regURI == NULL ? ElementURI::NO_CODE : regURI->getCode();
    }
    DataElement* de = Slate::GetElement( code );
    if( NULL == de )
    {
        logger_.syslog( "Element not initialized", Syslog::FAULT );
        return;
    }
    ConfigDataElement* cfgElement = dynamic_cast<ConfigDataElement*>( de );
    if( NULL != cfgElement && cfgElement->isSecure() )
    {
        logger_.syslog( "Can not get a secure configuration variable", Syslog::FAULT );
        return;
    }
    DataValue* dv = de->getDataValue();
    if( NULL == dv )
    {
        logger_.syslog( "Element has no value", Syslog::FAULT );
        return;
    }

    const Unit* unit = parsedCommand->getUnitArg();
    if( NULL == unit )
    {
        logger_.syslog( *elementURI + " " + dv->toString(), Syslog::IMPORTANT );
    }
    else
    {
        logger_.syslog( *elementURI + " " + dv->toString( *unit ) + " " + unit->getAbbreviation(), Syslog::IMPORTANT );
    }
}

void CommandExec::CommandGFscan( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandGFscan( parsedCommand );
}
void CommandExec::commandGFscan( ParsedCommand* parsedCommand )
{
    // Request that CBIT run a GF scan
    scanRunningReader_->requestData( true );

    // Request will be stopped in the main getLines() loop
}

void CommandExec::CommandHelp( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandHelp( parsedCommand );
}
void CommandExec::commandHelp( ParsedCommand* parsedCommand )
{
    const Command* command = parsedCommand->getCommandArg();
    FILE* out = CommandLine::getOutFile();
    if( NULL != command && NULL != out )
    {
        command->help( true, out );
    }
    else
    {
        for( unsigned int i = 0; i < commands_.size(); ++i )
        {
            commands_.get( i )->help( false, out );
        }
    }
}

void CommandExec::CommandIbit( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandIbit( parsedCommand );
}
void CommandExec::commandIbit( ParsedCommand* parsedCommand )
{
    // Request that IBIT be run
    ibitRunningReader_->requestData( true );

    // The IBIT request will be shut off in the main getLines() loop
}

void CommandExec::CommandIbitMcp3553( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandIbit( parsedCommand );
}
void CommandExec::commandIbitMcp3553( ParsedCommand* parsedCommand )
{
    const char* ioName = "/dev/mcp3553_0";
    DeviceIOStream io( ioName, true );
    FILE* out = CommandLine::getOutFile();

    if( NULL != out && ( !io.isReadable() || !io.isWritable() ) )
    {
        fprintf( out, "Error, could not open port %s\n", ioName );
        return;
    }

    int number = parsedCommand->getIntegerArg();
    int tests = number < 0 ? 100 : number;
    if( tests == 0 )
    {
        return;
    }
    int testsDone = 0;

    double elapsed;
    double elapsedMin = 10.0;
    double elapsedMax = 0.0;
    double elapsedSum = 0;
    double value;
    double valueMin = ( double )( 1 << 22 );
    double valueMax = -valueMin;
    double valueSum = 0;
    char buffer[] = "               ";
    for( int i = 0; i < tests; ++i )
    {
        io.write( "\n", 1 );
        elapsed = 0;
        Timestamp startTime( Timestamp::Now() );
        for( int j = 0; j < 1000; ++j )
        {
            int bytesRead = io.read( buffer, 11 ).bytesRead();
            if( bytesRead > 0 && ( *buffer == '-' || isdigit( *buffer ) ) )
            {
                elapsed = startTime.elapsed().asDouble();
                elapsedSum += elapsed;
                elapsedMin = AuvMath::Min( elapsed, elapsedMin );
                elapsedMax = AuvMath::Max( elapsed, elapsedMax );
                buffer[bytesRead] = 0;
                value = atof( buffer );
                valueSum += value;
                valueMin = AuvMath::Min( value, valueMin );
                valueMax = AuvMath::Max( value, valueMax );
                ++testsDone;
                break;
            }
        }
    }
    fprintf( out, "%d of %d tests complete\n", testsDone, tests );
    fprintf( out, "min elapsed: %g msec\n", elapsedMin * 1000 );
    fprintf( out, "max elapsed: %g msec\n", elapsedMax * 1000 );
    fprintf( out, "mean elapsed: %g msec\n", elapsedSum / testsDone * 1000 );
    fprintf( out, "min value: %d\n", ( int )( valueMin * 1000 ) );
    fprintf( out, "max value: %d\n", ( int )( valueMax * 1000 ) );
    fprintf( out, "mean value: %d\n", ( int )( valueSum / testsDone * 1000 ) );
}

void CommandExec::CommandLoad( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandLoad( parsedCommand );
}
void CommandExec::commandLoad( ParsedCommand* parsedCommand )
{
    const Str& missionName( parsedCommand->getMissionArg() );

    bool loaded = supervisor_.loadMission( missionName.cStr() );
    if( loaded )
    {
        logger_.syslog( "Loaded " + missionName, Syslog::IMPORTANT );
    }
    else
    {
        logger_.syslog( "Could not load " + missionName, Syslog::CRITICAL );
    }
}

void CommandExec::CommandMaintainAdd( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandMaintainAdd( parsedCommand, ( SyncComponent::CycleOrder )mode );
}
void CommandExec::commandMaintainAdd( ParsedCommand* parsedCommand, SyncComponent::CycleOrder cycleOrder )
{
    const ElementURI* name = parsedCommand->getVariableArg();
    // get the code
    unsigned short code = name->getCode();
    if( ElementURI::NO_CODE == code )
    {
        logger_.syslog( "unknown maintain variable: " + *name, Syslog::FAULT );
        return;
    }

    const Unit* unit = parsedCommand->getUnitArg();
    int rank, m, n, o;
    double* numbers = parsedCommand->getTokenArg().toDoubleArray( rank, m, n, o );
    if( rank == 0 )
    {
        DataValue* dataValue = ( *unit )( numbers[0] );
        supervisor_.addMaintain( code, dataValue, cycleOrder + SyncComponent::CYCLE_INCREMENT - 1 );
    }
    else
    {
        BlobValue* blobValue = new BlobValue( *unit, BLOB_FLOAT64LE );
        blobValue->setFrom( *unit, numbers, m, n, o );
        supervisor_.addMaintain( code, blobValue, cycleOrder + SyncComponent::CYCLE_INCREMENT - 1 );
    }
}

void CommandExec::CommandMaintainClear( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandMaintainClear( parsedCommand );
}
void CommandExec::commandMaintainClear( ParsedCommand* parsedCommand )
{
    // tell the maintainer to remove all items from being maintained
    supervisor_.removeAllMaintains();
}

void CommandExec::CommandMaintainUnavailable( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandMaintainUnavailable( parsedCommand, ( SyncComponent::CycleOrder )mode );
}
void CommandExec::commandMaintainUnavailable( ParsedCommand* parsedCommand, SyncComponent::CycleOrder cycleOrder )
{
    const ElementURI* name = parsedCommand->getVariableArg();
    // get the code
    unsigned short code = name->getCode();
    if( ElementURI::NO_CODE == code )
    {
        logger_.syslog( "unknown maintain variable: " + *name, Syslog::FAULT );
        return;
    }
    supervisor_.addMaintain( code, NULL, cycleOrder + SyncComponent::CYCLE_INCREMENT - 1, true );
}

void CommandExec::CommandMaintainInvalid( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandMaintainInvalid( parsedCommand, ( SyncComponent::CycleOrder )mode );
}
void CommandExec::commandMaintainInvalid( ParsedCommand* parsedCommand, SyncComponent::CycleOrder cycleOrder )
{
    const ElementURI* name = parsedCommand->getVariableArg();
    // get the code
    unsigned short code = name->getCode();
    if( ElementURI::NO_CODE == code )
    {
        logger_.syslog( "unknown maintain variable: " + *name, Syslog::FAULT );
        return;
    }
    supervisor_.addMaintain( code, NULL, cycleOrder + SyncComponent::CYCLE_INCREMENT - 1, false, true );
}

void CommandExec::CommandMaintainList( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandMaintainList( parsedCommand );
}
void CommandExec::commandMaintainList( ParsedCommand* parsedCommand )
{
    // list all items being maintained
    supervisor_.listMaintains();
}

void CommandExec::CommandMaintainRemove( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandMaintainRemove( parsedCommand );
}
void CommandExec::commandMaintainRemove( ParsedCommand* parsedCommand )
{
    const ElementURI* name = parsedCommand->getVariableArg();
    // get the code
    unsigned short code = name->getCode();
    if( ElementURI::NO_CODE == code )
    {
        logger_.syslog( "unknown variable name: " + *name, Syslog::FAULT );
        return;
    }
    // tell the maintainer to stop maintaining the item
    supervisor_.removeMaintain( code );
}

void CommandExec::CommandQuick( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandQuick( parsedCommand, ( QuickMode )mode );
}
void CommandExec::commandQuick( ParsedCommand* parsedCommand, QuickMode mode )
{
    switch( mode )
    {
    case QUICK_SHOW:
        break;
    case QUICK_ON:
#ifdef __FASTSIM
        Handler::SetAllowFastSim( true );
#else
        logger_.syslog( "Quicker than real-time not allowed.", Syslog::FAULT );
#endif
        break;
    case QUICK_OFF:
#ifdef __FASTSIM
        Handler::SetAllowFastSim( false );
#endif
        break;
    }
    logger_.syslog( "Quicker than real-time state is ",
                    Handler::IsFastSimAllowed() ? "on" : "off", Syslog::IMPORTANT );
}

void CommandExec::CommandQuit( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandQuit( parsedCommand );
}
void CommandExec::commandQuit( ParsedCommand* parsedCommand )
{
    setEnabled( false );
    setState( Component::BLOCK_COMPLETED );
}


void CommandExec::CommandReportAdd( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandReportAdd( parsedCommand, ( ReportAddMode ) mode );
}
void CommandExec::commandReportAdd( ParsedCommand* parsedCommand, ReportAddMode mode )
{
    Reporter::ReportType type;

    switch( mode )
    {
    case REPORT_ADD_MOD:
        // report changes
        type = Reporter::REPORT_CHANGE;
        break;
    case REPORT_ADD_PERIODIC:
        // report periodically
        type = Reporter::REPORT_PERIODICALLY;
        break;
    case REPORT_ADD_TOUCH:
        // report when touched
        type = Reporter::REPORT_TOUCHED;
        break;
    default:
        logger_.syslog( "unknown report mode.", Syslog::FAULT );
        return;
    }

    // get the interval at which to report the item
    float time( 0.0 );
    if( Reporter::REPORT_PERIODICALLY == type )
    {
        time = parsedCommand->getNumberArg();
    }

    Timespan t( time );
    const ElementURI* name( parsedCommand->getVariableArg() );
    // get the code
    unsigned short code = name->getCode();
    if( ElementURI::NO_CODE == code )
    {
        logger_.syslog( "unknown report variable: " + *name, Syslog::FAULT );
        return;
    }
    // tell the reporter to add the item
    supervisor_.addReport( code, type, &t );
}

void CommandExec::CommandReportClear( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandReportClear( parsedCommand );
}
void CommandExec::commandReportClear( ParsedCommand* parsedCommand )
{
    // tell the reporter to remove all items from being reported
    supervisor_.removeAllReports();
}

void CommandExec::CommandReportList( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandReportList( parsedCommand );
}
void CommandExec::commandReportList( ParsedCommand* parsedCommand )
{
    // list all items being reported
    supervisor_.listReports();
}

void CommandExec::CommandReportRemove( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandReportRemove( parsedCommand );
}
void CommandExec::commandReportRemove( ParsedCommand* parsedCommand )
{
    const ElementURI* name( parsedCommand->getVariableArg() );
    // get the code
    unsigned short code = name->getCode();
    if( ElementURI::NO_CODE == code )
    {
        logger_.syslog( "unknown variable name: " + *name, Syslog::FAULT );
        return;
    }
    // tell the reporter to stop reporting the item
    supervisor_.removeReport( code );
}

void CommandExec::CommandRestart( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandRestart( parsedCommand, ( RestartType ) mode );
}
void CommandExec::commandRestart( ParsedCommand* parsedCommand, RestartType mode )
{
    switch( mode )
    {
    case RESTART_APPLICATION:
        commandRestartApplication( parsedCommand );
        break;
    case RESTART_HARDWARE:
        commandRestartHardware( parsedCommand );
        break;
    case RESTART_LOGS:
        commandRestartLogs( parsedCommand );
        break;
    case RESTART_SYSTEM:
        commandRestartSystem( parsedCommand );
        break;
    }
}

void CommandExec::commandRestartApplication( ParsedCommand* parsedCommand )
{
    char line[BUFF_SIZE];
    size_t pos( 0 );
    line[pos] = 0;

    const Str& configDir = parsedCommand->getConfigDirArg();
    if( configDir != Str::EMPTY_STR )
    {
        pos += snprintf( line + pos, BUFF_SIZE - pos, " -c %s", configDir.cStr() );
    }

    const Str& logSym = parsedCommand->getTokenArg();
    if( logSym != Str::EMPTY_STR )
    {
        pos += snprintf( line + pos, BUFF_SIZE - pos, " -l %s", logSym.cStr() );
    }

    const int pause = parsedCommand->getIntegerArg();
    if( pause > 0 )
    {
        pos += snprintf( line + pos, BUFF_SIZE - pos, " -p %d", ( int )pause );
    }

    const Str& execStr = parsedCommand->getStringArg();
    if( execStr != Str::EMPTY_STR )
    {
        pos += snprintf( line + pos, BUFF_SIZE - pos, " -x \"%s\"", execStr.cStr() );
    }

    Supervisor::SetRestart( true );
    Supervisor::SetArgs( line );
    commandQuit( parsedCommand );
}

void CommandExec::commandRestartHardware( ParsedCommand* parsedCommand )
{
    // Note that while it would be great to have a log message here noting that a restart hardware was
    // requested, it's not easily possible to flush the syslog to disk and keep that information prior
    // to actually asserting the reset line.
    Supervisor::ClearRunningFile();
    Supervisor::SetRestart( true ); // We did command it after all
    LPC3Reg::SWReset(); // Push the button. See you on the other side.
    logger_.syslog( "This message should not be logged if the hardware reset was sucessful. Best of luck.", Syslog::FAULT );
}

void CommandExec::commandRestartLogs( ParsedCommand* parsedCommand )
{
    Supervisor::RestartLogs();
}

void CommandExec::commandRestartSystem( ParsedCommand* parsedCommand )
{
    AtMode_ = true;

    Supervisor::ClearRunningFile();
    int status = system( "reboot &" );
    if( -1 == status )
    {
        logger_.syslog( "Cannot execute reboot", Syslog::FAULT );
    }

    AtMode_ = false;

    commandQuit( parsedCommand );
}

void CommandExec::CommandResume( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandResume( parsedCommand );
}
void CommandExec::commandResume( ParsedCommand* parsedCommand )
{
    bool running = supervisor_.resumeMission( parsedCommand->getMissionArg().cStr() );
    if( running )
    {
        logger_.syslog( "Resuming mission and schedule", Syslog::IMPORTANT );
        DoScheduleResume();
        DoScheduleList();
    }
    else
    {
        logger_.syslog( "Could not resume mission.", Syslog::CRITICAL );
    }
}

void CommandExec::CommandRetransmit( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandRetransmit( parsedCommand, ( RetransmitType )mode );
}

void CommandExec::commandRetransmit( ParsedCommand* parsedCommand, RetransmitType retransmitType )
{
    switch( retransmitType )
    {
    case RETRANSMIT_SBD:
    {
        char sentName[] = "Logs/YYYYMMDDTHHMMSS/Priority####.lzma.parts/####.sbd...";
        int index = strtol( parsedCommand->getStringArg().cStr(), NULL, 10 );
        if( index == 0 && !parsedCommand->getStringArg().startsWith( "0" ) )
        {
            logger_.syslog( "Invalid SBD index: " + parsedCommand->getStringArg(), Syslog::FAULT );
            return;
        }
        FILE* sbdFile( NULL );
        for( int i = 0; NULL == sbdFile && i < Service::SERVICE_COUNT; ++i )
        {
            snprintf( sentName, sizeof( sentName ), "Logs/%s/%s%04d.lzma.parts/%04d.sbd", parsedCommand->getTokenArg().cStr(),
                      Service::ServiceType2Str( ( Service::ServiceType )i ), parsedCommand->getIntegerArg(), index );
            sbdFile = fopen( sentName, "rb" );
            if( NULL == sbdFile )
            {
                printf( "No file %s\n", sentName );
            }
        }
        if( NULL == sbdFile )
        {
            logger_.syslog( "No sbd corresponding to log " + parsedCommand->getTokenArg() + ", part#" + ( int )parsedCommand->getNumberArg() + ", #left=" + index, Syslog::FAULT );
            return;
        }
        fclose( sbdFile );
        Supervisor::QueueSBD( sentName );
        break;
    }
    }
}

void CommandExec::CommandRun( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandRun( parsedCommand );
}
void CommandExec::commandRun( ParsedCommand* parsedCommand )
{
    const Str& missionName( parsedCommand->getMissionArg() );
    bool quitAtEnd = parsedCommand->hasArgIndex( 1 );

    bool running = supervisor_.runMission( missionName.cStr(), quitAtEnd );
    if( running )
    {
        logger_.syslog( "Running " + missionName, Syslog::IMPORTANT );
    }
    else
    {
        logger_.syslog( "Could not run " + missionName, Syslog::CRITICAL );
    }
}

void CommandExec::CommandSchedule( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandSchedule( parsedCommand, ( ScheduleType )mode );
}
void CommandExec::commandSchedule( ParsedCommand* parsedCommand, ScheduleType scheduleType )
{
    MutexLocker locker( scheduleMutex_ );

    if( Scheduler_ == NULL )
    {
        logger_.syslog( "No valid Scheduler instance. Re-instantiating Scheduler.", Syslog::FAULT );
        Scheduler_ = new Scheduler();
    }

    int id_count = 0;
    int id_index = 0;
    Str id = parsedCommand->getTokenArg();
    if( !isnan( parsedCommand->getNumberArg() ) )
    {
        id_count = ( int )parsedCommand->getNumberArg();
        id_index = parsedCommand->getIntegerArg();
        logger_.syslog( "Scheduling command #" + Str( id_index, 10 ) + " of " + id_count + " with id=" + id, Syslog::IMPORTANT );
    }
    switch( scheduleType )
    {
    case SCHEDULE_ASAP:
        Scheduler_->Schedule( new ScheduledItemASAP( parsedCommand->getStringArg(), id, id_index, id_count ), logger_ );
        break;
    case SCHEDULE_CLEAR:
        Scheduler_->Clear( id, logger_ );
        break;
    case SCHEDULE_LIST:
        Scheduler_->List( logger_ );
        break;
    case SCHEDULE_NEXT:
        Scheduler_->Schedule( new ScheduledItemNext( parsedCommand->getStringArg(), id, id_index, id_count ), logger_ );
        break;
    case SCHEDULE_PAUSE:
        Scheduler_->Pause( Syslog::IMPORTANT, Timestamp::Now(), logger_ );
        break;
    case SCHEDULE_REMOVE:
        Scheduler_->Remove( parsedCommand->getIntegerArg(), logger_, true );
        break;
    case SCHEDULE_RESUME:
        Scheduler_->Resume( logger_ );
        break;
    case SCHEDULE_TIME:
        Scheduler_->Schedule( new ScheduledItemTimed( parsedCommand->getTimestampArg(), parsedCommand->getStringArg(), id, id_index, id_count ), logger_ );
        break;
    }
}

void CommandExec::CommandSend( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandSend( parsedCommand, ( SendType ) mode );
}
void CommandExec::commandSend( ParsedCommand* parsedCommand, SendType mode )
{
    SendDestination* destination = new SendDestination( parsedCommand->getTokenArg(), mode == SEND_VALUE, logger_, Syslog::FAULT );
    if( destination->hasError() )
    {
        return;
    }
    if( mode == SEND_VALUE )
    {
        const ElementURI* elementURI = parsedCommand->getVariableArg();
        unsigned short code( elementURI->getCode() );
        if( code == ElementURI::NO_CODE && elementURI->length() > 2
                && ( elementURI->cStr()[ 0 ] == '_' || elementURI->cStr()[ 0 ] == '#' )
                && elementURI->cStr()[ 1 ] == '.' )
        {
            // This is a new scratchpad variable
            const Unit* unit = parsedCommand->getUnitArg();
            unit = unit == NULL ? &elementURI->getUnit() : unit;
            DataElement* dataElement = new SimpleDataElement( *elementURI, unit->dataValue( elementURI->getBinaryType() ), 0 );
            Slate::MapDataElement( ( ElementURI * )elementURI, dataElement );
            ElementURI* regURI = Slate::FindElementURI( *elementURI );
            code = regURI == NULL ? ElementURI::NO_CODE : regURI->getCode();
        }
        DataElement* de = Slate::GetElement( code );
        if( NULL == de )
        {
            logger_.syslog( "Element not initialized", Syslog::FAULT );
            return;
        }
        ConfigDataElement* cfgElement = dynamic_cast<ConfigDataElement*>( de );
        if( NULL != cfgElement && cfgElement->isSecure() )
        {
            logger_.syslog( "Can not get a secure configuration variable", Syslog::FAULT );
            return;
        }
        DataValue* dv = de->getDataValue();
        if( NULL == dv )
        {
            logger_.syslog( "Element has no value", Syslog::FAULT );
            return;
        }

        const Unit* unit = parsedCommand->getUnitArg();
        SendData::Push( destination, dv, unit, false );
    }
    else if( mode == SEND_COMMAND )
    {
        StrValue* dv = new StrValue( parsedCommand->getStringArg() );
        SendData::Push( destination, dv, NULL, true );
    }
}

void CommandExec::CommandSet( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandSet( parsedCommand );
}
void CommandExec::commandSet( ParsedCommand* parsedCommand )
{
    const ElementURI* name( parsedCommand->getVariableArg() );
    unsigned short code( name->getCode() );
    const Unit* unit = parsedCommand->getUnitArg();
    unit = unit == NULL ? &name->getUnit() : unit;
    if( code == ElementURI::NO_CODE )
    {
        if( name->length() <= 2
                || ( name->cStr()[ 0 ] != '_' && name->cStr()[ 0 ] != '#' )
                || name->cStr()[ 1 ] != '.'
                || parsedCommand->getStringArg() != Str::EMPTY_STR )
        {
            logger_.syslog( "Unable to set non-existent variable: " + *name, Syslog::FAULT );
            return;
        }
        DataValue* dataValue = unit->dataValue( name->getBinaryType(), name->getBlobType() );
        DataElement* dataElement = new SimpleDataElement( *name, dataValue, 0 );
        Slate::MapDataElement( ( ElementURI * )name, dataElement );
        ElementURI* regURI = Slate::FindElementURI( *name );
        code = regURI == NULL ? ElementURI::NO_CODE : regURI->getCode();
    }

    if( parsedCommand->getStringArg() != Str::EMPTY_STR )
    {
        const Str str = parsedCommand->getStringArg();
        StrValue value( str );
        DataElement* de = Slate::GetElement( code );
        ConfigDataElement* cfgElement = dynamic_cast<ConfigDataElement*>( de );
        if( NULL != cfgElement && cfgElement->isSecure() )
        {
            logger_.syslog( "Can not set a secure configuration variable", Syslog::FAULT );
            return;
        }
        bool written = Slate::WriteOnce( code, this, value );
        if( !written )
        {
            logger_.syslog( Str( "Unable to write value " ) + str + " to element " + *name, Syslog::FAULT );
            return;
        }
    }
    else if( parsedCommand->getTokenArg() != Str::EMPTY_STR )
    {
        bool written( false );
        int rank, m, n, o;
        double* numbers = parsedCommand->getTokenArg().toDoubleArray( rank, m, n, o );
        if( rank == 0 )
        {
            written = Slate::WriteOnce( code, this, *unit, numbers[0] );
        }
        else
        {
            BlobValue* blobValue = new BlobValue( *unit, BLOB_FLOAT64LE );
            blobValue->setFrom( *unit, numbers, m, n, o );
            written = Slate::WriteOnce( code, this, *blobValue );
            delete blobValue;
        }
        delete[] numbers;
        if( !written )
        {
            logger_.syslog( Str( "Unable to write value " ) + parsedCommand->getTokenArg() + " to element " + *name, Syslog::FAULT );
            return;
        }
    }
}

void CommandExec::CommandShowBest( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandShowBest( parsedCommand );
}
void CommandExec::commandShowBest( ParsedCommand* parsedCommand )
{
    const UniversalURI* universal( parsedCommand->getUniversalArg() );
    unsigned short code = universal->getCode();
    if( code == ElementURI::NO_CODE )
    {
        logger_.syslog( "Unknown variable name: " + *universal, Syslog::FAULT );
        return;
    }
    DataElement* element = Slate::GetElement( code );
    if( NULL == element )
    {
        logger_.syslog( "No registered variable with name: " + *universal, Syslog::FAULT );
        return;
    }
    if( !element->isUniversal() )
    {
        logger_.syslog( "Not a universal: " + *universal, Syslog::FAULT );
        return;
    }

    DataElement* best = ( ( UniversalDataElement* )element )->getBestElement();

    Str bestStr;
    if( best == NULL )
    {
        bestStr = "null";
    }
    else
    {
        bestStr = best->getUri();
    }
    logger_.syslog( element->getUri() + " best is " + bestStr, Syslog::IMPORTANT );
}

void CommandExec::CommandShowComponent( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandShowComponent( parsedCommand );
}
void CommandExec::commandShowComponent( ParsedCommand* parsedCommand )
{
    const Str& searchStr( parsedCommand->getRegexArg() );
    regex_t reg;

    // compile the regular expression
    if( 0 != regcomp( &reg, searchStr.cStr(), REG_ICASE ) )
    {
        logger_.syslog( "could not compile regular expression", Syslog::FAULT );
        return;
    }

    // loop through the slate to search for the search string
    for( unsigned short int code = 0; code < CodedStr::NO_CODE; ++code )
    {
        const CodedStr* name = Slate::GetName( code );
        if( NULL == name )
        {
            return;
        }

        // see if the element matches the search string
        int rc = regexec( &reg, name->cStr(), 0, NULL, 0 );
        if( REG_NOMATCH == rc )
        {
            continue;
        }

        // start building the output string
        logger_.syslog( *name, Syslog::IMPORTANT );
    }
    // free regular expression
    regfree( &reg );
}

void CommandExec::CommandShowDependencies( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandShowDependencies( parsedCommand );
}
void CommandExec::commandShowDependencies( ParsedCommand* parsedCommand )
{
    const CodedStr* nameArg = parsedCommand->getComponentArg();
    const ElementURI* varArg = parsedCommand->getVariableArg();

    FILE* out = CommandLine::getOutFile();
    if( NULL == out )
    {
        return;
    }

    fprintf( out, "%s dependencies for %s:\n", parsedCommand->getSyntax()->getArg( 1 )->getKeyword(), NULL == nameArg ? varArg->cStr() : nameArg->cStr() );
    for( unsigned short int code = 0; code < DataAccess::NO_CODE; ++code )
    {
        DataAccess* dataAccess = Slate::GetDataAccess( code );
        if( NULL == dataAccess )
        {
            continue;
        }
        unsigned short int ownerCode = dataAccess->getOwnerCode();
        if( NULL != nameArg && nameArg->getCode() != ownerCode )
        {
            continue;
        }
        unsigned short int elementURICode = dataAccess->getElementCode();
        unsigned short int universalURICode = dataAccess->getUniversalCode();
        if( NULL != varArg && varArg->getCode() != elementURICode && varArg->getCode() != universalURICode )
        {
            continue;
        }

        CodedStr* name = Slate::GetName( ownerCode );
        if( NULL == name )
        {
            continue;
        }
        ElementURI* elementURI = Slate::GetElementURI( elementURICode );
        if( NULL == elementURI )
        {
            continue;
        }
        ElementURI* universalURI = Slate::GetElementURI( universalURICode );
        bool writer = dataAccess->getAccessorType() == DataAccessor::DATA_WRITER;
        fprintf( out, "\"%s\" %s \"%s\";\n",
                 name->cStr(),
                 ( writer ? "->" : "<-" ),
                 ( NULL == universalURI ? elementURI->cStr() : universalURI->cStr() ) );
    }
}

void CommandExec::CommandShowStack( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandShowStack( parsedCommand );
}
void CommandExec::commandShowStack( ParsedCommand* parsedCommand )
{
    logger_.syslog( "Behavior Stack:\n", Syslog::IMPORTANT );
    supervisor_.showStack();
}

void CommandExec::CommandShowState( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandShowState( parsedCommand );
}
void CommandExec::commandShowState( ParsedCommand* parsedCommand )
{
    logger_.syslog( "Mission State:\n", Syslog::IMPORTANT );
    MissionStateLogger::ShowMissionState( logger_ );
}

void CommandExec::CommandShowVariable( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandShowVariable( parsedCommand );
}
void CommandExec::commandShowVariable( ParsedCommand* parsedCommand )
{
    const Str& searchStr( parsedCommand->getRegexArg() );
    regex_t reg;

    // compile the regular expression
    if( 0 != regcomp( &reg, searchStr.cStr(), REG_ICASE ) )
    {
        logger_.syslog( "could not compile regular expression", Syslog::FAULT );
        return;
    }

    // loop through the slate to search for the search string
    for( unsigned short int code = 0; code < Slate::GetElementURICount(); ++code )
    {
        ElementURI* elementURI = Slate::GetElementURI( code );
        if( NULL == elementURI )
        {
            continue;
        }

        // see if the element matches the search string
        int rc = regexec( &reg, elementURI->cStr(), 0, NULL, 0 );
        if( REG_NOMATCH == rc )
        {
            continue;
        }

        // start building the output string
        Str outputStr( *elementURI );
        Str type = Str( "unknown" );
        outputStr += " (";

        // see if we can find a unit for this element
        DataElement* de = Slate::GetElement( code );
        if( NULL != de )
        {
            const Unit* u = de->getUnit();
            if( NULL != u )
            {
                type = u->getName();
            }
            if( de->isOrphaned() )
            {
                continue;
            }
        }

        // print the output
        outputStr += type + ")";
        logger_.syslog( outputStr, Syslog::IMPORTANT );
    }
    // free regular expression
    regfree( &reg );
}

void CommandExec::CommandStop( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandStop( parsedCommand );
}
void CommandExec::commandStop( ParsedCommand* parsedCommand )
{
    DoSchedulePause( Syslog::IMPORTANT, Timestamp::Now() );
    supervisor_.stopMission( "CommandExec::commandStop" );
}

void CommandExec::CommandUbat( ParsedCommand* parsedCommand, int mode )
{
    Instance_->commandUbat( parsedCommand, ( UbatMode ) mode );
}
void CommandExec::commandUbat( ParsedCommand* parsedCommand, UbatMode mode )
{
    // Check for UBAT on board and loaded
    int UBATLoaded = false;
    Slate::ReadOnce( WetLabsUBATIF::LOAD_AT_STARTUP, Units::BOOL, UBATLoaded, logger_ );

    if( !UBATLoaded )
    {
        logger_.syslog( "UBAT is not configured to load at startup.", Syslog::FAULT );
        return;
    }

    switch( mode )
    {
    case UBAT_OFF:
        enableUbatWriter_->write( Units::COUNT, ( int ) WetLabsUBATIF::UBAT_DISABLE );
        logger_.syslog( "Disabling UBAT.", Syslog::IMPORTANT );
        break;
    case UBAT_ON:
        enableUbatWriter_->write( Units::COUNT, ( int ) WetLabsUBATIF::UBAT_ENABLE );
        logger_.syslog( "Enabling UBAT.", Syslog::IMPORTANT );
        break;
    }
}

CommandExec::~CommandExec()
{
    if( NULL != Scheduler_ )
    {
        delete Scheduler_;
        Scheduler_ = NULL;
    }
    if( NULL != Instance_ )
    {
        Instance_ = NULL;
    }
}

void CommandExec::initialize()
{
    logger_.syslog( "Initializing the command executive.", Syslog::INFO );

    // Start conversation as default
    platformConversationReader_->requestData( true );
    Slate::ReadOnce( VehicleIF::KEY_TEXT_CFG, keyText_, logger_ );

    if( NULL == Scheduler_ )
    {
        logger_.syslog( "Initializing the command scheduler.", Syslog::INFO );
        Scheduler_ = new Scheduler();
    }
}

void CommandExec::run()
{
    // Service CommandBurn
    if( burnStopTime_ != Timestamp::NOT_SET_TIME && burnStopTime_ <= Timestamp::Now() )
    {
        commandBurn( NULL, BURN_OFF );
    }

    // Service Do
    if( doBytes_ > 0 )
    {
        if( doSbd_ != Str::EMPTY_STR )
        {
            if( ! DataReceiver::ReceiveSbd( doBuffer_, doBytes_, keyText_.asString().cStr(), logger_ ) )
            {
                logger_.syslog( "Error parsing sbd " + doSbd_, Syslog::ERROR );
            }
        }
        else
        {
            DataReceiver::Receive( doBuffer_, doBytes_, logger_ );
        }
        doBytes_ = 0;
    }

    // Service CommandGFscan
    int scanRunning( 0 );
    if( scanRunningReader_->isRequestingData() && scanRunningReader_->read( Units::BOOL, scanRunning ) && scanRunning )
    {
        scanRunningReader_->requestData( false );
    }

    // Service CommandIbit
    int ibitRunning( 0 );
    if( ibitRunningReader_->isRequestingData() && ibitRunningReader_->read( Units::BOOL, ibitRunning ) && ibitRunning )
    {
        ibitRunningReader_->requestData( false );
    }

    if( segfaultRequested_ )
    {
        segfaultImpl();
    }


    // Service the scheduler.
    DoScheduled();

    // Service the command queue.
    processCommandQueue( PERIOD );

    if( !enabled_ )
    {
        supervisor_.terminate();
    }
}

void CommandExec::uninitialize()
{
    logger_.syslog( "Uninitializing the command executive.", Syslog::INFO );
    if( NULL != Scheduler_ )
    {
        logger_.syslog( "Uninitializing the command scheduler.", Syslog::INFO );
        delete Scheduler_;
        Scheduler_ = NULL;
    }
}

void CommandExec::QueueCommand( const char* command )
{
    if( NULL == Instance_ )
    {
        return;
    }

    MutexLocker locker( QueueCommandMutex_ );
    cmdQueue_.push( new Str( command ) );
}

void CommandExec::ListCommandQueue()
{
    if( NULL == Instance_ )
    {
        return;
    }

    MutexLocker locker( QueueCommandMutex_ );
    for( unsigned int i = 0; i < cmdQueue_.size(); ++i )
    {
        Instance_->logger_.syslog( "Command #" + Str( ( int )i ) + ": " + cmdQueue_[i], Syslog::IMPORTANT );
    }
}

void CommandExec::DoCommand( const char* command, bool forceRun )
{
    if( NULL == Instance_ )
    {
        return;
    }

#ifdef __FASTSIM
    if( !Supervisor::IsRunningSimsInRealTime() && Handler::IsFastSimAllowed() )
    {
        forceRun = true;
    }
#endif

    Str cmd( command );
    if( forceRun || cmd.startsWith( "restart h" ) )
    {
        // Jump ahead of the queue and force execution of the command.
        cmd += "\n";
        Instance_->setLine( cmd.cStr(), forceRun );
        return;
    }

    // Add to the commmand queue
    Instance_->QueueCommand( command );
}

void CommandExec::DoSchedulePause( Syslog::Severity schedulePauseSeverity, Timestamp pauseTime )
{
    if( NULL == Instance_ || NULL == Scheduler_ )
    {
        return;
    }
    MutexLocker locker( scheduleMutex_ );
    Scheduler_->Pause( schedulePauseSeverity, pauseTime, Instance_->logger_ );
}

void CommandExec::DoScheduleResume()
{
    if( NULL == Instance_ || NULL == Scheduler_ )
    {
        return;
    }
    MutexLocker locker( scheduleMutex_ );
    Scheduler_->Resume( Instance_->logger_ );
}

void CommandExec::DoScheduleList()
{
    if( NULL == Instance_ || NULL == Scheduler_ )
    {
        return;
    }
    MutexLocker locker( scheduleMutex_ );
    Scheduler_->List( Instance_->logger_ );
}

void CommandExec::DoScheduled()
{
    if( NULL == Instance_ || NULL == Scheduler_ )
    {
        return;
    }
    MutexLocker locker( scheduleMutex_ );
    Scheduler_->DoScheduled( Instance_->logger_ );
}

bool CommandExec::isWritable()
{
    return enabled_ == true && state_ != BLOCK_COMPLETED;
}

const char* CommandExec::GetCommandHistory( size_t index )
{
    MutexLocker locker( CommandHistoryMutex_ );
    Str* cmd = cmdHistory_.get( ( int )index );
    return NULL != cmd ? cmd->cStr() : "";
}

unsigned int CommandExec::GetCommandHistorySize()
{
    MutexLocker locker( CommandHistoryMutex_ );
    return cmdHistory_.size();
}

FlexArray<Command*>& CommandExec::KnownCommands()
{
    return commands_;
}


Str* CommandExec::getNextCommand()
{
    MutexLocker locker( QueueCommandMutex_ );
    return cmdQueue_.pop( 0 );
}

// Execute commands in the order they were recived (FIFO).
void CommandExec::processCommandQueue( const Timespan& timeout )
{
    Timestamp startTime = Timestamp::Now();

    while( cmdQueue_.size() > 0 && startTime.elapsed() <= timeout )
    {
        Str* cmd = getNextCommand();

        if( NULL == cmd )
            continue;

        // update the command buffer and execute
        setLine( cmd->cStr(), true );
        execLine();

        /// done with this commmand.
        delete cmd;
    }
}

/// Report if a valid arg matches the supplied arg.
/// If so, deal with it.
/// If the last word in buffer is a valid arg, deal with it.
/// If we need to toss the last character, return false.
bool CommandExec::parseBufferEnd()
{
    // First deal with total deletions, etc.
    for( int i = parsedCommandCount_ - 1; i >= 0; --i )
    {
        ParsedCommand& parsedCommand = parsedCommands_[i];
        if( parsedCommand.getStart() > InputIndex_ )
        {
            parsedCommand.clearAll();
            --parsedCommandCount_;
        }
    }

    ParsedCommand* currentCommand = parsedCommandCount_ == 0 ? NULL : &parsedCommands_[parsedCommandCount_ - 1];
    CommandArgType currentArgType = currentCommand == NULL ? ARG_NONE : currentCommand->getCurrentArgType();
    bool inQuotedString = currentCommand == NULL ? false : currentCommand->isInQuotedString();

    // Maybe we've got a new command starting up?
    if( ( InputIndex_ == 0 && parsedCommandCount_ == 0 ) ||
            ( InputIndex_ > 0
              && CommandBuffer_[InputIndex_ - 1] == ';'
              && ( currentCommand == NULL ||
                   ( currentArgType != ARG_STRING
                     && !inQuotedString
                     && currentCommand->getStart() < InputIndex_ ) ) ) )
    {
        parsedCommands_[parsedCommandCount_].setStart( InputIndex_ );
        ++parsedCommandCount_;
    }

    if( parsedCommandCount_ == 0 )
    {
        return false;
    }

    ParsedCommand& pCommand( parsedCommands_[parsedCommandCount_ - 1] );

    return pCommand.parseBufferEnd( commands_,
                                    CommandBuffer_ + pCommand.getStart(), InputIndex_ - pCommand.getStart() );

}

// Command buffer interface. Sets the chars in the buffer one by one and triggers executaion as needed.
void CommandExec::setLine( const char* line, bool allowExec )
{
    MutexLocker locker( setCommandMutex_ );
    clearParsed();
    size_t startAt = 0;
    size_t len = strlen( line );
    bool quoteMode = false;

    for( size_t i = 0; i < len; ++i )
    {
        quoteMode ^= ( line[i] == '\"' );

        // Note: CR or LF would not normally occur in the argument to setLine
        // but if they do, force the left half of the command to execute.
        // If a ';' is encountered, force the left half of the command to exeucte
        // only if it starts with an 'l', which would be a load command.
        if( line[i] == '\r' || line[i] == '\n'
                || ( allowExec && !quoteMode && line[i] == ';' && line[startAt] == 'l' ) )
        {
            execLine();
            clearParsed();
            // Advance startAt
            startAt = i + 1;
        }
        else
        {
            setChar( line[i] );
            // Advance startAt for ';' or leading spaces
            if( line[i] == ' ' && i == startAt )
            {
                startAt = i + 1;
            }
            else if( !quoteMode && line[i] == ';' )
            {
                startAt = i + 1;
            }
        }
    }
}

void CommandExec::setChar( const char theChar )
{
    if( InputIndex_ >= BUFF_SIZE - 1 )
    {
        return;
    }
    CommandBuffer_[ InputIndex_ ] = theChar;
    CommandBuffer_[ InputIndex_ + 1 ] = 0;

    // parse the command buffer
    if( Instance_->parseBufferEnd() )
    {
        ++InputIndex_;
    }

    CommandBuffer_[ InputIndex_ ] = 0;
}

void CommandExec::execLine()
{
    // Add item to history
    if( strlen( CommandBuffer_ ) && 0 < CommandExec::HISTORY_SIZE )
    {
        CommandHistoryMutex_.lock();
        if( CommandExec::HISTORY_SIZE <= ( int )cmdHistory_.size() )
        {
            delete cmdHistory_.pop( 0 );
        }
        cmdHistory_.push( new Str( CommandBuffer_ ) );
        CommandHistoryMutex_.unlock();
    }

    if( InputIndex_ > 0 )
    {
        // reset the command buffer
        *CommandBuffer_ = 0;
        InputIndex_ = 0;

        // reset the command history index
        cmdHistoryIndex_ = cmdHistory_.size();

        for( int i = 0; i < parsedCommandCount_; ++i )
        {
            ParsedCommand& parsedCommand = parsedCommands_[i];
            logger_.syslog( "got command " + parsedCommand.toString(), Syslog::IMPORTANT );
            parsedCommand.execute( logger_ );
            parsedCommand.clearAll();
        }
        parsedCommandCount_ = 0;
    }
}

void CommandExec::clearParsed()
{
    while( parsedCommandCount_ > 0 )
    {
        --parsedCommandCount_;
        parsedCommands_[parsedCommandCount_].clearAll();
    }
}
