/*
  charger.c

  Copyright 20-Nov-2002 MBARI
  Written: 20-Nov-2002 Mark Sibenac
  Last mod: 20-Nov-2002 sib - creation

  Bluefin Battery Charger
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/unistd.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>

#include "error/error.h"
#include "xantrex_xfr/xfr.h"
#include "smart_battery/smart_battery.h"
#include "serial/serial.h"
#include "charger.h"

typedef enum {MENU_TOP, MENU_BATT, MENU_CHARGER, MENU_AUTO, MENU_NUM} MENU_t;

#define ALARM_PERIOD (30) /* seconds */
#define MAX_ENERGY (4000.0) /* WHr */
#define CHARGE_VOLTAGE (35.5) /* volts */
#define NOMINAL_CURRENT (0.0) /* Amps */

int DEBUG_LEVEL = 0; // default to no debug

void printTopMenu (void)
{
  printf ("\nCharger Software\n");
  printf ("compiled: %s\n\n", __DATE__);
  printf ("1.  Low-level battery commands\n");
  printf ("2.  Low-level charger commands\n");
  printf ("3.  Automated charging\n");
  printf ("Q.  Quit\n");
  printf ("?.  This menu\n");
}

void printBattMenu (void)
{
  printf ("\nBattery Menu\n\n");
  printf ("1.  Set Address\n");
  printf ("2.  Set State\n");
  printf ("3.  Set Energy Level\n");
  printf ("4.  Set Streaming Mode\n");
  printf ("5.  Set Master Mode\n");
  printf ("6.  Crash Battery uController\n");
  printf ("I.  Print All Batt Info\n");
  printf ("T.  Top Menu\n");
  printf ("?.  This Menu\n");
}

void printChargerMenu (void)
{
  printf ("\nCharger Menu\n\n");
  printf ("1.  Initialize\n");
  printf ("2.  Reset\n");
  printf ("3.  Trigger\n");
  printf ("4.  Enable Output\n");
  printf ("5.  Disable Output\n");
  printf ("6.  Set OVP\n");
  printf ("7.  Set VMax Limit\n");
  printf ("8.  Set Voltage\n");
  printf ("9.  Set IMax Limit\n");
  printf ("0.  Set Current\n");
  printf ("I.  Print All Charger Info\n");
  printf ("T.  Top Menu\n");
  printf ("?.  This Menu\n");
}

void printAutomatedChargingMenu (void)
{
  printf ("\nAutomated Charging Menu\n\n");
  printf ("1.  Set Vtrip for charging\n");
  printf ("2.  Set 3 Currents\n");
  printf ("3.  Set Batts to Charge\n");
  printf ("4.  Charge\n");
  printf ("5.  Stop Charge\n");
  printf ("6.  Set Vmin for Balancing/Discharging\n");
  printf ("7.  Balance Packs\n");
  printf ("8.  Stop Balance/Discharge - Set Idle State\n");
  printf ("9.  Discharge Pack\n");
  printf ("I.  Print Charging Info\n");
  printf ("T.  Top Menu\n");
  printf ("?.  This Menu\n");
}

void getBattNumsStr (char *str, SmartBattery_t *sm_batts)
{
  int i;
  char buff[5];

  for (i=0; i<MAX_BATTS; ++i)
    {
      if (sm_batts[i].batt_alive)
	{
	  sprintf (buff, "%d ", i);
	  strcat (str, buff);
	}
    }
}

void printBattsOnLine (SmartBattery_t *sm_batts)
{
  char batts[256*4] = "";

  getBattNumsStr (batts, sm_batts);
  printf ("Batts Online: %s\n", batts);
}

char getKeyBoard (void)
{
  char str_in[2] = "\000";
  int ret;
  struct timeval tv;
  fd_set readFds;

  tv.tv_sec = 0; tv.tv_usec = 100000; // sleep for 100ms waiting for keyboard
  FD_ZERO (&readFds);
  FD_SET (0/*stdin*/, &readFds);
  
  if ((ret = select (1, &readFds, 0, 0, &tv)) < 0)
    { 
      ERROR ("err: getKeyBoard select failed %d: %s\n", ret, strerror(errno));
      return 0;
    }
  else if (ret > 0) // did not timeout; got a character
    {
      if ((ret = read (0/*stdin*/, str_in, 1)) < 0)
	{
	  ERROR ("err: getKeyBoard read failed %d: %s\n", ret, 
		 strerror(errno));
	}
      DEBUG (5, "getKeyBoard read got '%c'\n", *str_in);
      return *str_in;
    }

  return 0;
}
int getBattNum (SmartBattery_t  *sm_batts)
{
  char batts[256*4] = "";
  int batt;
  getBattNumsStr(batts, sm_batts);
  printf ("Enter batt number [%s]: ", batts);
  scanf ("%d", &batt);
  if (!sm_batts[batt].batt_alive)
    {
      ERROR ("err: Batt %d is not alive\n", batt);
      return -1;
    }
  return batt;
}

int updateBattInfo (int batt_port, int batt, SmartBattery_t *sm_batt)
{
  sm_batt->address = batt;
  battQuerySummary (batt_port, batt, &sm_batt->summary);
  battQueryCellVolts (batt_port, batt, &sm_batt->volts);
  battQueryTemps (batt_port, batt, &sm_batt->temps);
  
  return 0;
}

int checkBattFaults (int batt_port, int xfr_port, SmartBattery_t *sm_batt, 
		     AutoCharger_t *autocharger)
{
  char fault=0;

  if (sm_batt->summary.state!=BATT_STATE_CHARGE)
    return 0;

  if (sm_batt->summary.max_volt >= autocharger->vtrip)
    {
      fault = 1;
      printf ("battery %d max_volt=%f >= vtrip=%f -- taking off charge\n", sm_batt->address,
	      sm_batt->summary.max_volt, autocharger->vtrip);
    }
  if (sm_batt->summary.error != BATT_ERROR_CLEAR)
    {
      fault = 1;
      printf ("battery %d error state=%s -- taking off charge\n", sm_batt->address,
	      BattStateStr(sm_batt->summary.error));
    }
  if (sm_batt->summary.pack_I <
      (-1.10 * autocharger->currents[autocharger->current_state] - 0.5))
    {
      fault = 1;
      printf ("battery %d I=%f < max=%f -- taking off charge\n", sm_batt->address,
	      sm_batt->summary.pack_I, 
	      -1.10 * autocharger->currents[autocharger->current_state] - 0.5);
    }

  if (fault)
    {
      if (autocharger->num_batts_on_charge)
	--autocharger->num_batts_on_charge;
      printf ("Reducing charging current by %f Amps to %f Amps\n", 
	      autocharger->currents[autocharger->current_state],
	      autocharger->currents[autocharger->current_state] *
	      autocharger->num_batts_on_charge);
      xfrSetCurrent (xfr_port, 
		     autocharger->currents[autocharger->current_state] * 
		     autocharger->num_batts_on_charge);

      printf ("Taking batt %d off of charge\n", sm_batt->address);
      battSetState (batt_port, sm_batt->address, BATT_STATE_OFF);
    }

  return 0;
}

void printBattInfo (SmartBattery_t *sm_batt)
{
  int h,m,s,i;

  printf ("\nBattery info for #%d = 0x%02X\n", sm_batt->address, 
	  sm_batt->address);
  h = (sm_batt->summary.time)/60/60;
  m = (sm_batt->summary.time-h*60*60)/60;
  s = (sm_batt->summary.time-h*60*60-m*60);
  printf ("state=%s, error=%s, mode=%s\n", 
	  BattStateStr(sm_batt->summary.state),
	  BattErrorStr(sm_batt->summary.error),
	  BattModeStr(sm_batt->summary.mode));
  printf ("%0.2fV %0.2fA MaxTemp=%0.2f MinVolt=%0.2f MaxVolt=%0.2f\n",
	  sm_batt->summary.pack_V,
	  sm_batt->summary.pack_I,
	  sm_batt->summary.max_temp,
	  sm_batt->summary.min_volt,
	  sm_batt->summary.max_volt);
  printf ("water leak=%d C=%0.2fWHr time=%02d:%02d:%02d\n",
	  sm_batt->summary.water_leak,
	  sm_batt->summary.capacity,
	  h, m, s);
  printf ("Volts: ");
  for (i=0; i<NUM_CELLS; ++i)
    printf ("%0.2f ", sm_batt->volts.cell_volts[i]);
  printf ("\n");
  printf ("Temps: ");
  for (i=0; i<NUM_TEMPS; ++i)
    printf ("%0.2f ", sm_batt->temps.pack_temps[i]);
  printf ("<-- oil\n");
}

void printAllBattInfo (SmartBattery_t *sm_batt)
{
  int i;

  for (i=0; i < MAX_BATTS; ++i)
    {
      if (sm_batt[i].batt_alive)
	printBattInfo (&sm_batt[i]);
    }
}

int updateChargerInfo (int xfr_port, XFRState_t *xfr)
{
  int ret;

  if (!(ret = xfrGetVOut (xfr_port, &xfr->VOut)))
    return ret;
  if (!(ret = xfrGetVSet (xfr_port, &xfr->VSet)))
    return ret;
  if (!(ret = xfrGetIOut (xfr_port, &xfr->IOut)))
    return ret;
  if (!(ret = xfrGetISet (xfr_port, &xfr->ISet)))
    return ret;
  if (!(ret = xfrGetOut (xfr_port, &xfr->OutEnabled)))
    return ret;
  if (!(ret = xfrGetErr (xfr_port, &xfr->err)))
    return ret;
  if (!(ret = xfrGetStatus (xfr_port, &xfr->asts, &xfr->sts, &xfr->fault)))
    return ret;
  
  return ret;
}

void printChargerInfo (int xfr_port, XFRState_t *xfr)
{
  printf ("Charger info\n");
  updateChargerInfo (xfr_port, xfr);
  printf ("Vout=%0.4fV Iout=%0.4fA OutputEnable=%d\n", xfr->VOut, xfr->IOut,
	  xfr->OutEnabled);
  printf ("Vset=%0.4fV Iset=%0.4fA\n", xfr->VSet, xfr->ISet);
  printf ("Err=%d Asts=0x%04X Sts=0x%04X Fault=0x%04X\n\n", xfr->err,
	  xfr->asts, xfr->sts, xfr->fault);
}

void initAutoCharger (AutoCharger_t *autocharger, SmartBattery_t *sm_batts)
{
  int i;
  for (i=0; i<MAX_BATTS; ++i)
    {
      sm_batts[i].batt_alive = 0; // not alive
      autocharger->batts[i] = 0; // do not charge
    }
  autocharger->vtrip = CHARGER_VTRIP_DEFAULT;
  autocharger->vmin = CHARGER_VMIN_DEFAULT;
  for (i=0; i<MAX_CHARGE_STAGES; ++i)
    {
      autocharger->currents[i] = CHARGER_CURRENT_DEFAULTS(i);
    }
  autocharger->state = CHARGER_IDLE;
}

void printAutoChargerInfo (AutoCharger_t *autocharger)
{
  int i;
  printf ("\nAuto Charger Info:\n");
  printf ("vtrip = %0.4f, vmin = %0.4f\n", autocharger->vtrip, autocharger->vmin);
  printf ("Charge states: ");
  for (i=0; i<MAX_CHARGE_STAGES; ++i)
    {
      printf ("%0.4f ", autocharger->currents[i]);
    }
  printf ("\nstate = %s\n", ChargerState(autocharger->state));
  printf ("Batts to Charge: ");
  for (i=0; i<MAX_BATTS; ++i)
    {
      if (autocharger->batts[i])
	printf ("%d ", i);
    }
  printf ("\n\n");
}

void flushStdin (void)
{
  char str[256];
  int ret;
  struct timeval tv;
  fd_set readFds;

  tv.tv_sec = 0; tv.tv_usec = 0; // sleep for 0ms waiting for keyboard
  FD_ZERO (&readFds);
  FD_SET (0/*stdin*/, &readFds);
  
  if ((ret = select (1, &readFds, 0, 0, &tv)) < 0)
    { 
      ERROR ("err: flushStdin select failed %d: %s\n", ret, strerror(errno));
      return;
    }
  else if (ret > 0) // did not timeout; got a character
    {
      if ((ret = read (0/*stdin*/, str, 255)) < 0)
	{
	  ERROR ("err: flushStdin read failed %d: %s\n", ret, 
		 strerror(errno));
	}
    }
}

int main (int argc, char **argv)
{
  int xfr_port, batt_port;
  int i;
  MENU_t menu = MENU_TOP;
  char keyboard = 0;
  char shutdown = 0;
  struct termios termios_p;
  time_t t1, talarm;
  SmartBattery_t sm_batts[MAX_BATTS];
  XFRState_t xfr;
  AutoCharger_t autocharger;
  char cdevname[128]="tcp:10.250.1.10:3002";
  char bdevname[128]="tcp:10.250.1.10:3003";

  for (i=1; i < argc; ++i) 
  {
    if (*argv[i] == '-') // got something
    { 
      if (!strcmp(argv[i]+1, "cdev")) // power supply/charger device name
	strncpy (cdevname, argv[i+1], 128);
      else if (!strcmp(argv[i]+1, "bdev")) // battery device name
	strncpy (bdevname, argv[i+1], 128);
      else if (!strcmp(argv[i]+1, "debug")) // debug level
	DEBUG_LEVEL = atoi(argv[i+1]);
      else // print usage
	{
	  printf ("usage: charger [-cdev charger_dev_name] [-bdev batt_dev_name] [-debug "
		  "debug_level]\n");
	  printf ("eg.    charger -cdev tcp:10.250.1.10:3002 -bdev /dev/ttyS1 -debug 2\n");
	  printf ("default cdev='%s'\n", cdevname);
	  printf ("default bdev='%s'\n", bdevname);
	  printf ("default debug level=%d\n", DEBUG_LEVEL);
	  return -1;
	}
    }
  }

  printf ("cdev='%s'\n", cdevname);
  printf ("bdev='%s'\n", bdevname);
  printf ("debug level=%d\n", DEBUG_LEVEL);

  initAutoCharger (&autocharger, sm_batts); // zero and set fields to defaults

  if (tcgetattr (0/*stdin*/, &termios_p) < 0)
    {
      ERROR ("err: main() could not tcgetattr on stdin: %s\n",
	     strerror (errno));
      return -1;
    }

  termios_p.c_lflag |= ICANON;	// Enable Line Oriented Mode 

  if (tcsetattr (0/*stdin*/, TCSANOW, &termios_p) < 0)
    {
      ERROR ("err: main() tcsetattr failed on stdin: %s\n", strerror(errno));
      return -1;
    }

  DEBUG (1, "Debug Level=%d\n", DEBUG_LEVEL);
  DEBUG (1, "time = %d\n", (int)time(0));

  if (openPort (cdevname, &xfr_port, B9600, 8, 1, "NONE") < 0)
    {
      ERROR ("err: Could not open '%s'\n", cdevname);
      return -1;
    }
  if (openPort (bdevname, &batt_port, B9600, 8, 1, "NONE") < 0)
    {
      ERROR ("err: Could not open '%s'\n", bdevname);
      return -1;
    }

  if (openLogFile (xfr_port, "xfr") < 0)
    {
      ERROR ("err: Could not open xfr_ logfile\n");
      return -1;
    }

  if (openLogFile (batt_port, "battery") < 0)
    {
      ERROR ("err: Could not open battery_ logfile\n");
      return -1;
    }
		
  DEBUG (1, "xfr_port fd= %d\n" , xfr_port);
  DEBUG (1, "batt_port fd = %d\n" , batt_port);
  xfrGetErr (xfr_port, &xfr.err); // initial comms for xfr power supply
  
  if (battSync (batt_port, sm_batts) <= 0) // find all batteries
    { // if there was a problem, signify shutdown
      //shutdown = 1;
    }

  for (i=0; i<MAX_BATTS; ++i)
    {
      if (sm_batts[i].batt_alive)
	{
	  autocharger.batts[i] = 1; // set charging flag
	  sm_batts[i].port = batt_port;
	}
    }

  printBattsOnLine (sm_batts);
  printTopMenu();

  t1 = time(0);
  talarm = t1 + ALARM_PERIOD;

  while (!shutdown) {
    // first check for keyboard hit
    if ((keyboard = getKeyBoard ())) {
      switch (menu) {
      case MENU_TOP: 
	switch (keyboard) {
	case '1': // low-level battery commands
	  menu = MENU_BATT;
	  printBattMenu();
	  break;
	case '2': // low-level charger commands
	  menu = MENU_CHARGER;
	  printChargerMenu();
	  break;
	case '3': // automated charging commands
	  menu = MENU_AUTO;
	  printAutomatedChargingMenu();
	  break;
	case 'Q':
	case 'q': // Quit
	  // shutdown properly
	  shutdown = 1;
	  break;
	case 'T':  
	case 't':
	case '?': // Print this menu
	  printTopMenu();
	  break;
	case 0x0a: // line feed
	case 0x0d: // carriage return 
	  break; // do nothing
	default : // uh-oh
	  printf ("Command '%c' not recognized...\n", keyboard);
	} // case MENU_TOP: switch (keyboard)
	break;

      case MENU_BATT:
	switch (keyboard) {
	case '1': {// set address
	  unsigned int old_add=0, new_add=0;
	  printBattsOnLine(sm_batts);
	  printf ("Enter old address (0-all): ");
	  scanf ("%d", &old_add);
	  printf ("Old address = %d\n", old_add);
	  printf ("Enter new address: ");
	  scanf ("%d", &new_add);
	  printf ("New address = %d\n", new_add);
	  battSetAddress (batt_port, old_add, new_add);
	  break;
	}
	case '2': { // set state
	  BattState_t state=BATT_STATE_OFF;
	  int batt = 0;
	  if ((batt = getBattNum (sm_batts)) < 0)
	    break;
	  printf ("Enter State	1)Off, 2)Discharge, 3)Charge, 4)Balance: ");
	  scanf ("%d", (int*)&state);
	  if (state < 0 || state >= BATT_STATE_UNKNOWN)
	    {
	      ERROR ("err: Batt state %d is not known\n", state);
	      break;
	    }
	  battSetState (batt_port, batt, state-1);
	  break;
	}
	case '3': { // set energy level
	  double C=0.0;
	  int batt = 0;
	  if ((batt = getBattNum (sm_batts)) < 0)
	    break;
	  printf ("Enter Energy Level (%0.2fC): ", 
		  sm_batts[batt].summary.capacity);
	  scanf ("%lf", &C);
	  if (C < 0.0 || C >= MAX_ENERGY)
	    {
	      ERROR ("err: Batt energy=%0.2f is out of range\n", C);
	      break;
	    }
	  battSetEnergy (batt_port, batt, C);
	  break;
	}
	case '4': { // set streaming level
	  int stream = 0;
	  int batt = 0;
	  if ((batt = getBattNum (sm_batts)) < 0)
	    break;
	  printf ("Enter Streaming Level (1-on, 0-off): ");
	  scanf ("%d", &stream);
	  if (stream != 1 && stream != 0)
	    {
	      ERROR ("err: Batt stream mode=%d is out of range\n", stream);
	      break;
	    }
	  battSetStreaming (batt_port, batt, stream);
	  break;
	}
	case '5': { // set master mode
	  int mode = 0;
	  int batt = 0;
	  if ((batt = getBattNum (sm_batts)) < 0)
	    break;
	  printf ("Enter Master mode (%d-master, %d-slave): ", 
		  BATT_MODE_MASTER, BATT_MODE_SLAVE);
	  scanf ("%d", &mode);
	  if (mode != BATT_MODE_MASTER && mode != BATT_MODE_SLAVE)
	    {
	      ERROR ("err: Batt mode=%d is out of range\n", mode);
	      break;
	    }
	  battSetMaster (batt_port, batt, mode);
	  break;
	}
	case '6': { // crash battery ucontroller
	  int batt = 0;
	  if ((batt = getBattNum (sm_batts)) < 0)
	    break;
	  battCrash (batt_port, batt);
	  break;
	}
	case 'I': 
	case 'i': { // print all batt info
	  int i;
	  for (i=0; i<MAX_BATTS; ++i)
	    if (sm_batts[i].batt_alive) 
	      {
		updateBattInfo (batt_port, i, &sm_batts[i]);
		printBattInfo (&sm_batts[i]);
	      }
	  break;
	}
	case 'T':  
	case 't': // go up to top menu
	  menu = MENU_TOP;
	  printTopMenu();
	  break;
	case '?': // print this menu
	  printBattMenu();
	  break;
	case 0x0a: // line feed
	case 0x0d: // carriage return 
	  break; // do nothing
	default : // uh-oh
	  printf ("Command '%c' not recognized...\n", keyboard);
	} // case MENU_BATT: switch (keyboard)
	break;

      case MENU_CHARGER:
	switch (keyboard) {
	case '1': // initialize
	  xfrInit (xfr_port);
	  break;
	case '2': // reset
	  xfrReset (xfr_port);
	  break;
	case '3': // trigger
	  xfrTrigger (xfr_port);
	  break;
	case '4': // enable output
	  xfrEnableOutput (xfr_port);
	  break;
	case '5': // disable output
	  xfrDisableOutput (xfr_port);
	  break;
	case '6': { // set ovp
	  double ovp = 0.0;
	  printf ("Enter OVP: ");
	  scanf ("%lf", &ovp);
	  xfrOverVoltSet (xfr_port, ovp);
	  break;
	}
	case '7': { // set vmax limit
	  double vmax = 0.0;
	  printf ("Enter VMAX: ");
	  scanf ("%lf", &vmax);
	  xfrVMax (xfr_port, vmax);
	  break;
	}
	case '8': { // set voltage
	  double vset = 0.0;
	  printf ("Enter Voltage Set Point: ");
	  scanf ("%lf", &vset);
	  xfrSetVolts (xfr_port, vset);
	  break;
	}
	case '9': { // set imax limit
	  double imax = 0.0;
	  printf ("Enter IMAX: ");
	  scanf ("%lf", &imax);
	  xfrIMax (xfr_port, imax);
	  break;
	}
	case '0': { // set current
	  double iset = 0.0;
	  printf ("Enter Current Set Point: ");
	  scanf ("%lf", &iset);
	  xfrSetCurrent (xfr_port, iset);
	  break;
	}
	case 'I': 
	case 'i': // print all charge info
	  printChargerInfo (xfr_port, &xfr);
	  break;
	case 'T':  
	case 't': // go up to top menu
	  menu = MENU_TOP;
	  printTopMenu();
	  break;
	case '?': // print this menu
	  printChargerMenu();
	  break;
	case 0x0a: // line feed
	case 0x0d: // carriage return 
	  break; // do nothing
	default : // uh-oh
	  printf ("Command '%c' not recognized...\n", keyboard);
	} // case MENU_CHARGER: switch (keyboard)
	break;

      case MENU_AUTO:
	switch (keyboard) {
	case '1': { // set vtrip
	  double vtrip;
	  printf ("Vtrip=%0.4f	Enter new Vtrip: ", autocharger.vtrip);
	  scanf ("%lf", &vtrip);
	  if (vtrip < BALANCE_VMIN || vtrip > MAX_VTRIP)
	    {
	      ERROR ("err: vtrip=%0.4f is not within range.\n", vtrip);
	    } else {
	      autocharger.vtrip = vtrip;
	    }
	  break;
	}
	case '2': { // set 3 currents
	  double current;
	  int i;
	  printf ("Charge currents are: ");
	  for (i=0; i<MAX_CHARGE_STAGES; ++i)
	    {
	      printf ("%d=%0.4f ", i, autocharger.currents[i]);
	    }
	  printf ("\nEnter new currents separated by spaces: ");
	  flushStdin(); // get that pesky newline
	  for (i=0; i<MAX_CHARGE_STAGES; ++i)
	    {
	      scanf ("%lf", &current);
	      if (current < 0.0 || current > MAX_CHARGE_CURRENT)
		{
		  ERROR ("err: current=%0.4f is out of range\n", current);
		  continue;
		}
	      autocharger.currents[i] = current;
	    }
	  flushStdin(); // get that pesky newline
	  printf ("\nNew currents are: ");
	  for (i=0; i<MAX_CHARGE_STAGES; ++i)
	    {
	      printf ("%d=%0.4f ", i, autocharger.currents[i]);
	    }
	  printf ("\n");
	  break;
	}
	case '3': { // set batts to charge
	  int i, batt, ret;
	  char str[MAX_BATTS*4], *pstr, *ppstr;
	  printf ("Batts to charge are set to: ");
	  for (i=0; i<MAX_BATTS; ++i)
	    {
	      if (autocharger.batts[i])
		printf ("%d ", i);
	    }
	  printf ("\nEnter new batts to charge separated by spaces: \n");
	  for (i=0; i<MAX_BATTS; ++i)
	    {
	      autocharger.batts[i] = 0;
	    }
	  fflush (stdout);
	  autocharger.num_batts_to_charge = 0;
	  flushStdin(); // get that pesky newline
	  ret = read (0, str, MAX_BATTS*4);
	  DEBUG (103, "str='%s'\n", str);
	  ppstr = str;
	  while ((pstr=strsep (&ppstr, " \012\015\n\r")))
	    {
	      sscanf(pstr, "%d", &batt);
	      if (batt < 0 || batt >= MAX_BATTS)
		{
		  ERROR ("err: batt=%d is out of range\n", batt);
		  continue;
		}
	      autocharger.batts[batt] = 1;
	      ++autocharger.num_batts_to_charge;
	      DEBUG (103, "got %d num=%d\n", batt,autocharger.num_batts_to_charge); 
	    }
	  --autocharger.num_batts_to_charge; // strsep counts the last 1 twice
	  flushStdin(); // get that pesky newline
	  printf ("New batts to charge are: ");
	  for (i=0; i<MAX_BATTS; ++i)
	    {
	      if (autocharger.batts[i])
		printf ("%d ", i);
	    }
	  printf ("\n");
	  break;
	}
	case '4': // charge
	  autocharger.state = CHARGER_START;
	  printf ("\nPreparing to Charge packs\n");
	  break;
	case '5': // stop charge
	  autocharger.state = CHARGER_STOP;
	  printf ("\nPreparing to Stop Charging packs\n");
	  break;
	case '6': { // set vmin
	  double vmin;
	  printf ("Vmin=%0.4f	Enter new Vmin: ", autocharger.vmin);
	  scanf ("%lf", &vmin);
	  if (vmin < BALANCE_VMIN || vmin > MAX_VTRIP)
	    {
	      ERROR ("err: vmin=%0.4f is not within range.\n", vmin);
	    } else {
	      autocharger.vmin = vmin;
	    }
	  break;
	}
	case '7': // balance packs
	  autocharger.state = CHARGER_BALANCE_START;
	  printf ("\nPreparing to Balance packs\n");
	  break;
	case '8': { // stop balance/idle
	  int i;
	  for (i=0; i<MAX_BATTS; ++i) {
	    if (sm_batts[i].batt_alive) {
	      printf ("Turning battery 0x%02X=%d off\n", sm_batts[i].address, 
		      sm_batts[i].address);
	      if (!battSetState (sm_batts[i].port, sm_batts[i].address, BATT_STATE_OFF))
		sm_batts[i].summary.state = BATT_STATE_OFF;
	      printBattInfo (&sm_batts[i]);
	    }
	  }
	  autocharger.state = CHARGER_IDLE;
	  break;
	}
	case '9':  // discharge packs
	  autocharger.state = CHARGER_DISCHARGE_START;
	  printf ("\nPreparing to Discharge packs\n");
	  break;
	case 'I': 
	case 'i': // print charging info
	  printAutoChargerInfo (&autocharger);
	  break;
	case 'T':  
	case 't': // go up to top menu
	  menu = MENU_TOP;
	  printTopMenu();
	  break;
	case '?': // print this menu
	  printAutomatedChargingMenu();
	  break;
	case 0x0a: // line feed
	case 0x0d: // carriage return 
	  break; // do nothing
	default : // uh-oh
	  printf ("Command '%c' not recognized...\n", keyboard);
	} // case MENU_AUTO: switch (keyboard)
	break;

      default:
      } // switch (menu)
    } // if (keyboard = ...)
	
    // now see if alarm is here
    t1 = time(0);
    if (t1 >= talarm) // ring!! ring!!! ring!!!!
      {
	int i;
	talarm = t1 + ALARM_PERIOD;
	DEBUG (102, "ALARM!!! time=%d next alarm=%d\n", (int)t1, (int)talarm); 
	for (i=0; i<MAX_BATTS; ++i)
	  if (sm_batts[i].batt_alive) 
	    {
	      updateBattInfo (batt_port, i, &sm_batts[i]);
	    }
	switch (autocharger.state) {
	case CHARGER_IDLE :
	  // do nothing; chill out
	  DEBUG (5, "autocharger.state = CHARGER_IDLE\n");
	  break;
	case CHARGER_START :
	  // turn on charging flood gates and pump some current
	  DEBUG (2, "autocharger.state = CHARGER_START\n");
	  xfrSetVolts (xfr_port, CHARGE_VOLTAGE);
	  xfrSetCurrent (xfr_port, NOMINAL_CURRENT);
	  xfrEnableOutput (xfr_port);
	  autocharger.num_batts_to_charge = 0;
	  autocharger.num_batts_on_charge = 0;
	  autocharger.current_state = 0;
	  for (i=0; i<MAX_BATTS; ++i)
	    if (autocharger.batts[i]) 
	      {
		++autocharger.num_batts_to_charge;
		++autocharger.num_batts_on_charge;
		battSetState (batt_port, i, BATT_STATE_CHARGE);
		xfrSetCurrent (xfr_port, NOMINAL_CURRENT + 
			       autocharger.num_batts_on_charge *
			       autocharger.currents[0]);
		msleep (1000);
	      }
	  printf ("\n\n");
	  printAutoChargerInfo (&autocharger);
	  printChargerInfo (xfr_port, &xfr);
	  printAllBattInfo (sm_batts);
	  autocharger.state = CHARGER_STAGES;
	  break;
	case CHARGER_STAGES :
	  DEBUG (2, "autocharger.state = CHARGER_STAGES\n");
	  for (i=0; i<MAX_BATTS; ++i)
	    if (autocharger.batts[i]) // check only batts to charge 
	      {
		checkBattFaults (batt_port, xfr_port, &sm_batts[i], &autocharger);
	      }
	  if (!autocharger.num_batts_on_charge)
	    {
	      if (++autocharger.current_state >= MAX_CHARGE_STAGES)
		{
		  autocharger.state = CHARGER_STOP;
		  break;
		} else {
		  autocharger.num_batts_on_charge = 0;
		  xfrSetCurrent (xfr_port, NOMINAL_CURRENT);
		  for (i=0; i<MAX_BATTS; ++i)
		    if (autocharger.batts[i]) 
		      {
			++autocharger.num_batts_on_charge;
			battSetState (batt_port, i, BATT_STATE_CHARGE);
			xfrSetCurrent (xfr_port, NOMINAL_CURRENT + 
				       autocharger.num_batts_on_charge *
				       autocharger.currents[autocharger.current_state]);
		      }
		}
	    }
	  printf ("\n\n");
	  printAutoChargerInfo (&autocharger);
	  printChargerInfo (xfr_port, &xfr);
	  printAllBattInfo (sm_batts);
	  break;
	case CHARGER_STOP :
	  DEBUG (2, "autocharger.state = CHARGER_STOP\n");
	  xfrDisableOutput (xfr_port);
	  autocharger.num_batts_on_charge = 0;
	  for (i=0; i<MAX_BATTS; ++i)
	    if (autocharger.batts[i]) 
	      {
		battSetState (batt_port, i, BATT_STATE_OFF);
	      }
	  printf ("\n\n");
	  printAutoChargerInfo (&autocharger);
	  printChargerInfo (xfr_port, &xfr);
	  printAllBattInfo (sm_batts);
	  autocharger.state = CHARGER_POST;
	  break;
	case CHARGER_POST :
	  DEBUG (2, "autocharger.state = CHARGER_POST\n");
	  autocharger.state = CHARGER_IDLE;
	  break;
	case CHARGER_BALANCE_START : {
	  int i;
	  DEBUG (2, "autocharger.state = CHARGER_BALANCE_START\n");
	  for (i=0; i<MAX_BATTS; ++i) {
	    if (sm_batts[i].batt_alive) {
	      printBattInfo (&sm_batts[i]);
	      printf ("Putting battery 0x%02X=%d into Balancing state\n",
		      sm_batts[i].address, sm_batts[i].address);
	      if (!battSetState (sm_batts[i].port, sm_batts[i].address, BATT_STATE_BALANCE))
		sm_batts[i].summary.state = BATT_STATE_BALANCE;
	    }
	  }
	  autocharger.state = CHARGER_BALANCE;
	  break;
	} // case CHARGER_BALANCE_START
	case CHARGER_BALANCE : {
	  int i;
	  DEBUG (2, "autocharger.state = CHARGER_BALANCE\n");
	  for (i=0; i<MAX_BATTS; ++i) {
	    if (sm_batts[i].batt_alive && sm_batts[i].summary.state == BATT_STATE_BALANCE) {
	      printBattInfo (&sm_batts[i]);
	      if (sm_batts[i].summary.min_volt <= autocharger.vmin) {
		printf ("Cutting off balancing of battery %d\n", i);
		battSetState (sm_batts[i].port, sm_batts[i].address, BATT_STATE_OFF);
	      }
	    }
	  }
	  break;
	} // case CHARGER_BALANCE
	case CHARGER_DISCHARGE_START : {
	  int i;
	  DEBUG (2, "autocharger.state = CHARGER_DISCHARGE_START\n");
	  for (i=0; i<MAX_BATTS; ++i) {
	    if (sm_batts[i].batt_alive) {
	      printBattInfo (&sm_batts[i]);
	      printf ("Putting battery 0x%02X=%d into Discharge state\n",
		      sm_batts[i].address, sm_batts[i].address);
	      if (!battSetState (sm_batts[i].port, sm_batts[i].address, BATT_STATE_DISCHARGE))
		sm_batts[i].summary.state = BATT_STATE_DISCHARGE;
	    }
	  }
	  autocharger.state = CHARGER_DISCHARGE;
	  break;
	} // case CHARGER_DISCHARGE_START
	case CHARGER_DISCHARGE : {
	  int i;
	  DEBUG (2, "autocharger.state = CHARGER_BALANCE\n");
	  for (i=0; i<MAX_BATTS; ++i) {
	    if (sm_batts[i].batt_alive && sm_batts[i].summary.state == BATT_STATE_DISCHARGE) {
	      printBattInfo (&sm_batts[i]);
	      if (sm_batts[i].summary.min_volt <= autocharger.vmin) {
		printf ("Cutting off discharge of battery %d\n", i);
		battSetState (sm_batts[i].port, sm_batts[i].address, BATT_STATE_OFF);
	      }
	    }
	  }
	  break;
	} // case CHARGER_DISCHARGE
	} // switch (autocharger.state)
	DEBUG (101, "AutoCharger State=%s\n", ChargerState(autocharger.state));
      } // if (t1 > talarm)
  } // while

  closeLogFile (xfr_port);
		closeLogFile (batt_port);
  closePort (batt_port);
  closePort (xfr_port);

  termios_p.c_lflag |= ICANON;	// Enable Line Oriented Mode
	
  if (tcsetattr (0/*stdin*/, TCSANOW, &termios_p) < 0)
    {
      ERROR ("err: main() tcsetattr failed on stdin: %s\n", strerror(errno));
      return -1;
    }

  printf ("\n\nHave a nice day...\n");

  return 0;
}

