#include <math.h>
#include <i86.h>
#include <stdio.h>
#include "BuoyLauncherDriver.h"
#include "BuoyLauncherServer.h"
#include "BuoyLauncherLog.h"
#include "Attributes.h"
#include "IntegerAttribute.h"
#include "FloatAttribute.h"
#include "BooleanAttribute.h"
#include "AttributeParser.h"
#include "System.h"
#include "TimeP.h"
#include "VehicleConfigurationIF.h"
#include "PeriodicTask.h"
#include "Syslog.h"
#include "MathP.h"
#include "LcXmodem.h"

#define STATUS_INTERVAL 33

BuoyLauncherDriver::BuoyLauncherDriver(SerialDevice *device,
				       int period)
  : PeriodicTask("buoyLauncherDriver")
{
  _period = period;
  _device = device;
  _statusCalls = True;
  _initialized = False;

  // set up I/O
  _input = new BuoyLauncherInput(SharedData::ReadWrite);
  _input->data._command = 'i';
  _input->write();

  _output = new BuoyLauncherOutput(SharedData::Write);
  _lcx = new LcXmodem(_device);

  _log = new BuoyLauncherLog(this, DataLog::AsciiFormat);

  initialize();

  //set up interfaces to periodic task
  addPeriodicCallback(period, 
		      (CallbackMethod )BuoyLauncherDriver::periodicCallback);


}

BuoyLauncherDriver::~BuoyLauncherDriver()
{
  delete _input;
  delete _output;
  delete _lcx;
}

//////////////////////////////////////////////////////////////////
// periodicCallback()
// Main driver loop for driver.
//
//////////////////////////////////////////////////////////////////
void BuoyLauncherDriver::periodicCallback()
{
  // Check for a command
  _input->read();
  char cmd = _input->data._command;

  if (cmd != CLEAR_CMD && !_initialized)
  {
    Syslog::write("BuoyLauncherDriver::periodiceCallback:not initialized");
    return;
  }

  switch (cmd)
    {
    case CLEAR_CMD:
      break;              // No command

    case LAUNCH_REAL_HOT: // Launch real buoy
    case LAUNCH_REAL_COLD:// Launch real buoy but don't let it fire
    case LAUNCH_FAKE:     // Launch dummy buoy
      Syslog::write("BuoyLauncherDriver::Launch commanded: %c", cmd);
      launchBuoy(cmd);
      break;

    case ROTATE_BUOY:     // Rotate the carousel until a good buoy is
      Syslog::write("BuoyLauncherDriver::rotate Buoy commanded");
      rotateBuoy();       // at the data download position
      //      status(LC_STATUS);
      break;

    case ROTATE_CARO:     // Rotate the carousel one position, ignore buoys
      Syslog::write("BuoyLauncherDriver::rotate Carousel commanded");
      rotateCarousel();
      break;

    case XFER_ARGOS:      // Download a file to current buoy for ARGOS xfer
    case XFER_ARCHIVE:    // Download a file to current buoy for archive
      Syslog::write("BuoyLauncherDriver::data transfer commanded: %c", cmd);
      xferData(cmd);
      break;

    case CURRENT_BUOY:    // Set the current buoy to a given number.
      Syslog::write("BuoyLauncherDriver::current buoy commanded");
      currentBuoy();      // A subsequent ROTATE_BUOY cmd will goto that buoy
      break;

    case LCBC_STATUS:     // Get launcher and buoy status
    case LC_STATUS:       // Get the launcher status only
      Syslog::write("BuoyLauncherDriver::status requested: %c", cmd);
      status(cmd);
      break;

    case STATUS_ON:       // Turn on status-getting capability
      Syslog::write("BuoyLauncherDriver::turning status calls on");
      _statusCalls = True;
      break;

    case STATUS_OFF:      // (Temporarily) Turn off status-getting capability
      Syslog::write("BuoyLauncherDriver::turning status calls off");
      _statusCalls = False;
      break;

#if 0
    case ROTATE_LAUNCH_POS: // Move carousel from data to launch position
      //    case 'Z': // These commands are not implemented yet in the LC
      Syslog::write("BuoyLauncherDriver::rotate to launch pos commanded: %c", cmd);
      rotateToLaunchPosition(cmd);
      break;
#endif

    case UPLOAD_LC_LOG:
      Syslog::write("BuoyLauncherDriver::upload launcher log commanded: %c", cmd);
      uploadLog();
      break;

    case REINITIALIZE:
      Syslog::write("BuoyLauncherDriver::reinit commanded: %c", cmd);
      reinitLauncher();
      break;

    case RETRACT_LARMS:
      Syslog::write("BuoyLauncherDriver::retract arms commanded: %c", cmd);
      retractArms();
      break;

    case ROTATE_LAUNCH_POS:
    case ROTATE_DATA_POS:
      Syslog::write("BuoyLauncherDriver::rotate to position commanded: %c", cmd);
      rotateAPosition(cmd);
      break;

    case PUSH_OUT_RETRACT:
      Syslog::write("BuoyLauncherDriver::push and retract commanded: %c", cmd);
      pushAndRetract();
      break;

    default:
      Syslog::write("BuoyLauncherDriver::recvd unknown command: %c", cmd);
      break;
    };

  // Time to perform the routine status check?
  //
  if (_initialized)
  {
    // Perform a  status check after every command
    //
    if (_statusCalls)
    {
      Boolean statusDue = time((long*)0) > (_lastStatusCheck + STATUS_INTERVAL);
      if (cmd != CLEAR_CMD || statusDue)
      {
        Syslog::write("BuoyLauncherDriver::getting routine status");
        status(LC_STATUS);
        _lastStatusCheck = time((long*)0);
      }
    }
  }

  // Clear the command area in the input buffer. This tells the Server that
  // the driver is finished with the last command.
  //
  if (_input->data._command != CLEAR_CMD)
  {
    Syslog::write("Clearing _command, was %c\n", _input->data._command);
    _input->data._command = CLEAR_CMD;
    _input->write();
  }

}

///////////////////////////////////////////////////////////////////////
// Tell the LC to launch the current buoy. The command has to 
// distinguish between a real buoy and a dummy (or null) buoy.
//
Boolean BuoyLauncherDriver::launchBuoy(char cmd)
{
  Syslog::write("BuoyLauncher::processing launch command");

  int i;
  int success;

  _output->data._armsRetracted =
                   _output->data._lastLaunchDone = _output->data._inLaunchPosition = False;
  _output->write();

  for (i = 0, success = 0; i < 6 && success < 2;  i++)
  {
    if (sendCmdToLC(cmd))
      success++;
  }

  if (success < 2)
  {
    Syslog::write("BuoyLauncher::LaunchBuoy(): LC not receiving");
    return False;
  }

  // Wait for the buoy to move into the launch position
  //
  char ack = readAckFromLC(90);
  if (ACK == ack)
  {
    Syslog::write("BuoyLauncher::launchBuoy(): recvd ACK, buoy in position");
    _output->data._inLaunchPosition = True;
    _output->write(); // Let the world know
  }
  else if (NAK == ack)
  {
    Syslog::write("BuoyLauncher::launchBuoy(): NAK - buoy not in position");
    _output->data._lastLaunchDone = True;
    _output->write(); // Let the world know
    return False;
  }
  else
  {
    Syslog::write("BuoyLauncher::launchBuoy(): timeout-buoy not in position");
    return False;
  }

  // Wait for some kind of launch acknowledgement from the Launcher
  // indicating that the buoy was ejected.
  //
  ack = readAckFromLC(90);
  if (ACK == ack)
  {
    _output->data._lastLaunchOK = True;
    Syslog::write("BuoyLauncher::LaunchBuoy: recvd ACK, launchOK");
  }
  else if (NAK == ack)
  {
    _output->data._lastLaunchOK = False;
    Syslog::write("BuoyLauncher::LaunchBuoy: recvd NAK, launchOK");
  }
  else
  {
    _output->data._lastLaunchOK = False;
    Syslog::write("BuoyLauncher::LaunchBuoy: timeout, launchOK");
  }

  // Wait for some kind of launch acknowledgement from the Launcher
  // indicating that the arms have been retracted and the ball dropped.
  //
  ack = readAckFromLC(90);
  if (ACK == ack)
  {
    _output->data._armsRetracted = True;
    Syslog::write("BuoyLauncher::LaunchBuoy: recvd ACK, launchDone");
  }
  else if (NAK == ack)
  {
    _output->data._armsRetracted = False;
    Syslog::write("BuoyLauncher::LaunchBuoy: recvd NAK, launchDone");
  }
  else
  {
    _output->data._armsRetracted = False;
    Syslog::write("BuoyLauncher::LaunchBuoy: timeout, launchDone");
  }
  _output->data._lastLaunchDone = True;
  _output->data._inLaunchPosition = False; // Buoy no longer in position
  _output->write();

  return True;
}

////////////////////////////////////////////////////////////
// Tell the LC to rotate the carousel until a functional
// buoy is in the talking position, or until the LC
// determines that the carousel is empty
void BuoyLauncherDriver::rotateBuoy()
{
  Syslog::write("BuoyLauncher::processing rotateBuoy command");

  if (!sendCmdToLC(ROTATE_BUOY))
  {
    Syslog::write("BuoyLauncher::rotateBuoy: LC not receiving cmd");
    return;
  }

  char ack = readAckFromLC(400);
  if (ACK == ack)
  {
    _output->data._buoyResponsive = True;
    Syslog::write("BuoyLauncher::rotateBuoy: Normal completion");
    //sendTimeToLC(time((long*)0));
  }
  else if (NAK == ack) 
  {
    _output->data._buoyResponsive = False;
    Syslog::write("BuoyLauncher::rotateBuoy: Rotation unsuccessful");
  }
  else
  {
    _output->data._buoyResponsive = False;
    Syslog::write("BuoyLauncher::rotateBuoy: Rotation timeout");
  }
  _output->write();
  _lcx->resetSeqCount(); // Reset ARGOS sequence counter for this buoy
  return;
}

////////////////////////////////////////////////////////////
// Rotate the buoy that is in the data download position to
// the launch position.
//
void BuoyLauncherDriver::rotateToLaunchPosition(char cmd)
{
  Syslog::write("BuoyLauncher::processing rotateToLaunchPosition command");

  _output->data._lastLaunchDone = _output->data._inLaunchPosition = False;
  _output->write();
  
  if (!sendCmdToLC(cmd))
  {
    Syslog::write("BuoyLauncher::rotateToLaunchPosition not sent");
    return;
  }

  // Wait 60 seconds for the buoy to move into the launch position
  //
  if (readAckFromLC(60) == ACK)
  {
    Syslog::write("BuoyLauncher::LaunchBuoy(): recvd ACK, buoy in position");
    _output->data._inLaunchPosition = True;
    //
    // Important! This should only be True under very tight conditions.
    // When true, it means ejector wands will be able to extend so a
    // buoy has to be at the door.
    //
    _buoyInLaunchPosition = True;
  }
  else
  {
    _output->data._inLaunchPosition = False;
    _buoyInLaunchPosition = False;

    Syslog::write("BuoyLauncher::rotateToLaunchPosition timeout/NAK");
  }
  _output->write(); // Let the world know

}

////////////////////////////////////////////////////////////
// Eject the buoy that is in the launch position, even if
// no buoy actually resides in the launch position. 
// This command will not work unless the previous command
// was rotateToLaunchPosition and that was successful.
//
void BuoyLauncherDriver::ejectBuoy()
{
  Syslog::write("BuoyLauncher::processing ejectBuoy command");

  // The buoy has to be in the launch position as a result of the
  // rotateToLaunchPosition() or rotateDummyToLaunchPosition()
  //
  if (!_buoyInLaunchPosition) 
  {
    _output->data._inLaunchPosition = False; // Buoy not in position
    _output->write();
    return; // Not going to launch
  }

  int i;
  int success;

  // Have to tell LC twice, so try six times to get two successes
  //
  for (i = 0, success = 0; i < 6 && success < 2;  i++)
  {
    if (sendCmdToLC('E'))
      success++;
    else if (i == 6)
    {
      Syslog::write("BuoyLauncherDriver::ejectBuoy: Eject cmd not sent");
    }
  }
  if (success < 2)
  {
    Syslog::write("BuoyLauncher::LaunchBuoy(): LC not reading cmd");
  }

  // Wait for some kind of acknowledgement from the Launcher.
  //
  char ack = readAckFromLC(90);
  if (ack == ACK)
  {
    _output->data._lastLaunchOK = True;
    Syslog::write("BuoyLauncher::LaunchBuoy(): recvd ACK");
  }
  else
  {
    _output->data._lastLaunchOK = False;
    Syslog::write("BuoyLauncher::LaunchBuoy(): recvd NAK or timeout");
  }

  _output->data._lastLaunchDone = True;
  _output->data._inLaunchPosition = False; // Buoy no longer in position
  _output->write();
}

void BuoyLauncherDriver::rotateCarousel()
{

  if (!sendCmdToLC(ROTATE_CARO))
  {
    Syslog::write("BuoyLauncher::rotateCarousel: LC not receiving cmd");
    return;
  }

  char ack = readAckFromLC(30);
  if (ACK == ack)
  {
    Syslog::write("BuoyLauncher::rotateCarousel: Normal completion");
  }
  else if (NAK == ack) 
  {
    Syslog::write("BuoyLauncher::rotateCarousel: Rotation unsuccessful");
  }
  else
  {
    Syslog::write("BuoyLauncher::rotateCarousel: Rotation timeout");
  }
  return;
}

///////////////////////////////////////////////////////////////
// Transfer a data file to the Launcher/Buoy. Parameter 'type'
// specifies either ARGOS data or Archive data.
// 
void BuoyLauncherDriver::xferData(char type)
{
  Syslog::write("BuoyLauncher::processing loadData command");

  // First get the Buoy status and see if we can download data
  //
  if (!status(LCBC_STATUS))
  {
    Syslog::write("BuoyLauncher::xferData() LC not sending status, aborting");
    return;
  }

  // Abort if the buoy is not responsive
  //
  if (!_output->data._buoyResponsive)
  {
    Syslog::write("BuoyLauncher::xferData() buoy not talking, aborting");
    return;
  }

  // get the filename from the _input buffer
  //
  BuoyLauncherIF::Filename fname;
  strcpy(fname, _input->data._dataFilename);

  // try to open the file
  //
  FILE *ifile = fopen(fname, "rb");
  if (!ifile)
  {
    Syslog::write("BuoyLauncher::xferData():error opening file %s", fname);
    return;
  }

  // File opened, find the size
  //
  fseek(ifile, 0L, 2);                   // Goto the end of the file
  unsigned long filesize = ftell(ifile); // How many bytes am i from the start?
  rewind(ifile);

  // See if there is enough storage available for this file
  //
  long argosL = (type == XFER_ARGOS ? _output->data._argosBytesB :
		                      _output->data._archiveBytesB);
  if(argosL < filesize)
  {
    // Send a portion of the file, or abort?
    //
    Syslog::write("BuoyLauncher::xferData(): Only %ld bytes avail on LC/BC, need %ld",
                  argosL, filesize);
    filesize = argosL;
  }

  // Send current time to buoy
  time_t now = time((long*)0);
  if (!sendTimeToLC(now))
  {
    // Non-fatal error, continue
    Syslog::write("BuoyLauncher::xferData() couldn't send time to LC");
  }
  
  Syslog::write("BuoyLauncher::xferData(): transferring file %s", fname);

  // For the data xfer and launch sequence to work together well, the
  // portion size needs to be 9600. Don't change it unless a similar
  // change is made in the buoy launcher code that sends the data to
  // the buoy.
  //
  long portionsize = 9600;

  // Now, send the data over
  //
  BYTE *buf = (BYTE*)malloc(sizeof(BYTE)*portionsize);

  if (buf && filesize > 0)
  {
    while(filesize > 0)
    {
      unsigned long nbytes = fread(buf, sizeof(BYTE), portionsize, ifile);
  
      // Inform the LC that more data is coming
      //
      if (!sendCmdToLC(type))
      {
        // Fatal error, abort
        Syslog::write("BuoyLauncher::xferData() LC not responding to cmd");
        return;
      }

      // Send ARGOS data to buoy reformatted for transmission, archive
      // data is saved "as is."
      //
      int xm_stat = 0;
      if (type == XFER_ARGOS)
        xm_stat = _lcx->send30Xmodem(buf, nbytes);
      else
        xm_stat = _lcx->sendXmodem(buf, nbytes);

      if (xm_stat != OKAY)
      {
	Syslog::write("BuoyLauncherDriver::xferData: xmodem comms failure");
	free((char*)buf);
	return;
      }

      char ack = readAckFromLC(30);
      if (NAK == ack)
      {
	Syslog::write("BuoyLauncherDriver::xferData:data not on LC disk!");
      }
      else if ('t' == ack)
      {
	Syslog::write("BuoyLauncherDriver::xferData:data xfer ack timeout!");
      }
      else
      {
	Syslog::write("BuoyLauncherDriver::xferData: wrote %ld bytes to LC disk",
		      nbytes);
      }
      filesize -= nbytes;
      delay(2000);
    }

    free((char*)buf);

  }
  else
  {
    Syslog::write("BuoyLauncherDriver::xferData:unable to alloc %ld bytes",
		  filesize);
    _lcx->sendXmodem((unsigned char*)" ", 1);
  }

}

//////////////////////////////////////////////////////////////
// Tell the launcher we would like to select a specific buoy
// to play with.
//
void BuoyLauncherDriver::currentBuoy()
{
  Syslog::write("BuoyLauncher::processing currentBuoy");

  // Let the LC know we want a new "current" buoy
  //
  if (sendCmdToLC(CURRENT_BUOY))
  {
    // Send the buoy number over to LC
    //
    BYTE buoyNo = (BYTE)_input->data._currentBuoy;
    Syslog::write("BuoyLauncher::currentBuoy(): %d", (int)buoyNo);
    _lcx->sendXmodem((BYTE*)&buoyNo, sizeof(buoyNo));
  }
  else
    Syslog::write("BuoyLauncher::Launcher not accepting currentBuoy cmd");

}

//////////////////////////////////////////////////////////////
// Get the status from the Launcher and load the output buffer
// with the contents of the status record.
//
Boolean BuoyLauncherDriver::status(char cmd)
{
  // Do it only if status calls are turned on
  //
  if (!_statusCalls) return True;

  Syslog::write("BuoyLauncher::processing getLauncherStatus");

  // Let the LC know we want a status check
  //
  if (!sendCmdToLC(cmd))
  {
    Syslog::write("BuoyLauncher::status(): LC not responding to request");
    _output->data._launcherError     = BAD_XMODEM_SEND;
    _output->data._buoyResponsive    = False;
    _output->write();
    logError(BAD_XMODEM_SEND);
    return False;
  }

  // Read the status from the LC
  //
  LauncherStatus ls;
  long numbytes = 0;

  if (OKAY != _lcx->receiveXmodem((BYTE*)&ls, &numbytes))
  {
    logError(BAD_XMODEM_SEND);
    return False;
  }

  // If we received the LauncherStatus object, send the data back to the Server
  //
  Syslog::write("BuoyLauncher::recvd %ld bytes from LC, should be %d",
		numbytes, sizeof(ls));
  if (numbytes == sizeof(ls))
  {
    _output->data._launcherError     = ls.bLauncherError;
    _output->data._lastLaunchOK      = ls.bLastLaunchOK;
    _output->data._buoyResponsive    = ls.bBuoyResponsive;
    _output->data._nextBuoyReady     = ls.bNextBuoyReady;
    _output->data._currentBuoyNumber = ls.bCurrentBuoyNumber;
    _output->data._totalNumBuoys     = ls.bTotalNumBuoys;
    _output->data._numBuoysRemain    = ls.bNumBuoysRemain;
    _output->data._numBuoysLaunched  = ls.bNumBuoysLaunched;
    _output->data._timeSec           = ls.lTimeSecL;
    _output->data._argosBytesL       = ls.lARGOSBytesL;
    _output->data._archiveBytesL     = ls.lArchiveBytesL;
    _output->data._picoBytesL        = ls.lPicoBytesAvailL;
    _output->data._dataMemoryL       = ls.lDataMemoryL;

    _output->data._buoyNumber    = ls.Buoy.bBuoyNumber;
    _output->data._mainBattVolts = ls.Buoy.bMainBattVolts;
    _output->data._auxBattVolts  = ls.Buoy.bAuxBattVolts;
    _output->data._tempDegC      = ls.Buoy.bTempDegC;
    _output->data._timeSecB      = ls.Buoy.lTimeSecBuoy;
    _output->data._argosBytesB   = ls.Buoy.lARGOSBytesAvail;
    _output->data._archiveBytesB = ls.Buoy.lArchiveBytesAvail;
    _output->data._picoBytesB    = ls.Buoy.lPicoBytesAvail;
    _output->data._dataMemoryB   = ls.Buoy.lDataMemoryBuoy;
    _output->write();

    printStatus(ls);

    logError(ls.bLauncherError);
    _log->write();
  }
  return True;
}

////////////////////////////////////////////////////////////
// Send the current time to the Launcher
//
Boolean BuoyLauncherDriver::sendTimeToLC(time_t newtime)
{
  // Try to send the command to LC
  //
  if (sendCmdToLC(SET_TIME))
  {
    // LC ready to receive the time as a time_t value
    time_t now = time((long*)0);
    _lcx->sendXmodem((BYTE*)&now, sizeof(time_t));

    if ('t' == readAckFromLC(30))
      return False;
    else
      return True;
  }
  Syslog::write("BuoyLauncher::sendTimeToLC(): Launcher not responding T");
  return False;
}

////////////////////////////////////////////////////////////
// Set up the driver for initiating comms with Launcher
//
Boolean BuoyLauncherDriver::initialize()
{
  // Initialize contact with the LC. Sync it's time with ours
  //
  _device->flushReceiveBuf();

  // Try to make contact with Buoy Launcher
  //
  Boolean contact = False;
  for (int i = 0; i < 3; i++)
  {
    if (sendTimeToLC(time((long*)0)))
    {
      contact = True;
      break;
    }
    char eot = EOT;
    // code to flush an xmodem xfer
    //    for (int ii = 0; ii < 1024; ii++)
    //  _device->write(&eot, 1);
  }

  if (!contact)
  {
    Syslog::write("BuoyLauncherDriver::initialize() LC will not wakeup!");
    return False;
  }

  // Initialize I/O with BuoyLauncherServer
  //
  _input->data._command = CLEAR_CMD;
  _input->write();

  _output->data._lastLaunchDone = False;
  _output->data._armsRetracted = False;
  _output->data._inLaunchPosition = False;
  _output->data._buoyResponsive = False;
  _output->write();

  // Initialize status heartbeat
  _lastStatusCheck = time((long*)0);

  _buoyInLaunchPosition = False;

  _initialized = True;

  return DeviceIF::Ok;
}

///////////////////////////////////////////////////////////
// Read a 1-character Ack/NAK from the Launcher.
// Used to signal that a commanded task is finished.
// Return Ack, NAK, or 't' for timeout
//
char BuoyLauncherDriver::readAckFromLC(unsigned timeout_in_secs)
{
  char ack[2];
  try {
    _device->readNChars(ack, 1, timeout_in_secs*1000);
  }
  catch (SerialDevice::TimedOut) {
  Syslog::write("BuoyLauncherDriver::readAckFromLC() timeout (%d secs)",
		timeout_in_secs);
  return 't';
  }

  return ack[0];

}

Boolean BuoyLauncherDriver::sendCmdToLC(char command)
{
  char ack[8];
  char cmd[8];
  unsigned char chk = '*' + command;

  //  _device->commsDebugMode(SerialDevice::DebugOn);
  cmd[0] = '*'; cmd[1] = command; cmd[2] = (char)chk; cmd[3] = '\0';

  
  if ( (_device->write(cmd, strlen(cmd)) == strlen(cmd)))
  {
    Syslog::write("BuoyLauncher::\"%s\" sent to LC", cmd);//, chk);

  }
  else
  {
    Syslog::write("BuoyLauncher::failed to send \"%s\" to LC", cmd);
    //    _device->commsDebugMode(SerialDevice::DebugOff);
    return False;
  }

  const int MAX_ATTEMPTS = 3;
  for (int i = 0; i < MAX_ATTEMPTS; i++)
  {
    try {
      int nbytes = 0;
      do {
	nbytes += _device->readNChars(ack+nbytes, 3-nbytes, 15000);
      } while (nbytes < 3);
    }
    catch (SerialDevice::TimedOut) {
      Syslog::write("BuoyLauncherDriver::SendCmdToLC() timeout");
      if (i > 0) clearLC();
      continue;
    }
    ack[3] = '\0';
    Syslog::write("BuoyLauncher::recvd %s as response from LC", ack);
    chk = '#' + command;
    if (ack[1] == command && ack[2] == chk)
    {
      //      _device->commsDebugMode(SerialDevice::DebugOff);
      return True; // valid response
      //
      // Important! Set this to false after ANY command is sent to LC
      // to prevent wands from extending by accident. It can be set
      // True by the calling function if need be.
      //
      _buoyInLaunchPosition = False;
    }
    else
    {
      continue; // try again
    }
  }
  //  _device->commsDebugMode(SerialDevice::DebugOff);
  Syslog::write("BuoyLauncher::Launcher not responding to '%c'", command);
  return False;
}

void BuoyLauncherDriver::clearLC()
{
  char cmd[1024];
  memset(cmd, EOT, 1024);
  
  Syslog::write("BuoyLauncher::Clearing LC comm channel");
  _device->write(cmd, 1024);
}

void BuoyLauncherDriver::logError(int error_code)
{
  if (error_code < LAUNCHER_OK || error_code > LAST_LAUNCHER_ERROR)
  {
    Syslog::write("BuoyLauncherDriver::logError: Unknown code %d", error_code);
  }
  else
  {
    Syslog::write("BuoyLauncherDriver::Launcher status: %s (%d)",
		  error_msgs[error_code], error_code);
  }
}

void BuoyLauncherDriver::uploadLog()
{
  int     nRet;
  size_t  lChkBytes = 0L;
  long    lBytes = 0L;   // num bytes in status structure
  FILE   *infofile;
  const long UPLOAD_BYTES = 10240;
  char updata[UPLOAD_BYTES];

  char *logdir = getenv("AUV_LOG_DIR");
  if (logdir == NULL)
    sprintf(updata, "%s", "./lclog.log");
  else
    sprintf(updata, "%s/latest/lclog.log", logdir);

  // Open in truncate mode since the launcher sends the whole log file
  //
  infofile = fopen(updata, "w+");
  if (infofile == NULL)
  {
     Syslog::write("BuoyLauncher::uploadLog(): %s %s",
		   "Could not open file ", updata);
     return;
  }
   
  if (!sendCmdToLC(UPLOAD_LC_LOG))
  {
    Syslog::write("BuoyLauncher::status(): LC not responding to request");
    return;
  }

  BYTE nMinutes = (BYTE)_input->data._numMinutesToUpload;
  Syslog::write("BuoyLauncher::uploadLog() - uploading %d minutes worth",
                nMinutes);
  _lcx->sendXmodem((BYTE*)&nMinutes, sizeof(nMinutes));

  char *separator = "\n****************************************************\n";
  fwrite(separator, sizeof(char), strlen(separator), infofile);
  fwrite(separator, sizeof(char), strlen(separator), infofile);

  while(1)
  {
    lBytes = 0;
    nRet = _lcx->receiveXmodem((BYTE*)updata, &lBytes);

    Syslog::write("Upload Log::recvd %ld bytes from launcher", lBytes);
    if (nRet != OK || lBytes == 0)
    {
      Syslog::write("Upload Log::breaking - nRet = %d, lBytes = %ld",
                     nRet, lBytes);
      break;
    }
      
    Syslog::write("Upload Log::writing %ld bytes to log file", lChkBytes);
    lChkBytes = fwrite(updata, sizeof(BYTE), (size_t)lBytes, infofile);
    fwrite(separator, sizeof(char), strlen(separator), infofile);

    if (lChkBytes != (long)UPLOAD_BYTES)  // must be end of file on Launcher or error
    {
      Syslog::write("Upload Log::Num bytes written(%ld) != UPLOAD_BYTES (%d)",
                     lChkBytes, UPLOAD_BYTES);
      break;
    }
  }
   
  fclose(infofile);
  return;
}

void BuoyLauncherDriver::printStatus(LauncherStatus ls)
{
  Syslog::write("BuoyLauncher::Status=>Err:%d LastOK:%d ByResp:%d NxtRdy:%d ",
    ls.bLauncherError,
    ls.bLastLaunchOK,
    ls.bBuoyResponsive,
    ls.bNextBuoyReady);
  Syslog::write("                      CrtBnum:%d Total:%d Remain:%d Lnchd:%d ",
    ls.bCurrentBuoyNumber,
    ls.bTotalNumBuoys,
    ls.bNumBuoysRemain,
    ls.bNumBuoysLaunched);
/*
    ls.lTimeSecL;
    ls.lARGOSBytesL;
    ls.lArchiveBytesL;
    ls.lPicoBytesAvailL;
    ls.lDataMemoryL;
*/

  Syslog::write("Buoy::        Status=>Bnum:%d Batt:%d Aux:%d Temp:%d Time:%ld",
    ls.Buoy.bBuoyNumber,
    ls.Buoy.bMainBattVolts,
    ls.Buoy.bAuxBattVolts,
    ls.Buoy.bTempDegC,
    ls.Buoy.lTimeSecBuoy);
/*
    ls.Buoy.lARGOSBytesAvail;
    ls.Buoy.lArchiveBytesAvail;
    ls.Buoy.lPicoBytesAvail;
    ls.Buoy.lDataMemoryBuoy;
*/
}

void BuoyLauncherDriver::reinitLauncher()
{
  if (!sendCmdToLC(ROTATE_CARO))
  {
    Syslog::write("BuoyLauncher::reinitLauncher: LC not receiving cmd");
    return;
  }

  return;
}

void BuoyLauncherDriver::retractArms()
{
  if (!sendCmdToLC(RETRACT_ARMS))
  {
    Syslog::write("BuoyLauncher::retractArms: LC not receiving cmd");
    return;
  }

  char ack = readAckFromLC(30);
  if (ACK == ack)
  {
    Syslog::write("BuoyLauncher::retractArms: Normal completion");
  }
  else if (NAK == ack) 
  {
    Syslog::write("BuoyLauncher::retractArms: unsuccessful");
  }
  else
  {
    Syslog::write("BuoyLauncher::retractArms: timeout");
  }
  return;
}

void BuoyLauncherDriver::pushAndRetract()
{
  if (!sendCmdToLC(PUSH_OUT_RETRACT))
  {
    Syslog::write("BuoyLauncher::pushAndRetract: LC not receiving cmd");
    return;
  }

  char ack = readAckFromLC(30);
  if (ACK == ack)
  {
    Syslog::write("BuoyLauncher::pushAndRetract: Normal completion");
  }
  else if (NAK == ack) 
  {
    Syslog::write("BuoyLauncher::pushAndRetract: unsuccessful");
  }
  else
  {
    Syslog::write("BuoyLauncher::pushAndRetract: timeout");
  }
  return;
}

void BuoyLauncherDriver::rotateAPosition(char cmd)
{
  if (!sendCmdToLC(cmd))
  {
    Syslog::write("BuoyLauncher::rotateAPosition: LC not receiving cmd");
    return;
  }

  char ack = readAckFromLC(30);
  if (ACK == ack)
  {
    Syslog::write("BuoyLauncher::rotateAPosition: Normal completion");
  }
  else if (NAK == ack) 
  {
    Syslog::write("BuoyLauncher::rotateAPosition: unsuccessful");
  }
  else
  {
    Syslog::write("BuoyLauncher::rotateAPosition: timeout");
  }
  return;
}

////////////////////////////////////////////////////////////
void BuoyLauncherDriver::eventCallback(TaskInterface *taskIF,
				       EventCode event)
{
    return;
}

void BuoyLauncherDriver::loadConfiguration()
{
}

