/****************************************************************************/
/* Copyright (c) 2000 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  : Runs any of the suite of test programs, such as lblTest.      */
/*            Reads the devices.cfg file and determines the correct serial  */
/*            port.  Creates a separate directory for logging so that the   */
/*            last mission is NOT overwritten.                              */
/* Filename : test.cc                                                       */
/* Author   : Rob McEwen                                                    */
/* Project  :                                                               */
/* Version  : 1.0                                                           */
/* Created  : 00/9/6                                                        */
/* Modified :                                                               */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/****************************************************************************/
/*-----------------------------------------------------------------------*
  $Id: td.cc,v 1.9 2001/10/27 14:10:03 rob Exp $
 *-----------------------------------------------------------------------*/

#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <signal.h>
#include <process.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>

#include "Syslog.h"
#include "System.h"
#include "TimeP.h"
//
// *******Function Prototypes*******
//
// help - Prints info to stdout.
void help(void);

//
// List all of the devices that have test programs.  You must enter the 
// device name exactly as it appears in the name of the exectuable 
// (test program).  This code assumes that the executable name is the 
// concatanation of the device name and "Test".
//
// The second column is the server name.  
//
struct Names { 
               char *deviceName;
               char *serverName;
             };

struct Names names[] = {
                         {"crossbow",      "crossbowServer"    },
			 {"seabird",       "seabirdServer"     },
			 {"metrabyte",     "metrabyteServer"   },
			 {"methane",       "methaneDriver"     },
			 {"parosci",       "parosciServer"     },
			 {"psa916",        "psa916Server"      },
			 {"newTailCone",   "newTailConeServer" },
			 {"fuelCell",      "fuelCellServer"    },
			 {"biolume",       "biolumeDriver"     },
			 {"modem",         "acousticModem"     },
			 {"gps",           "gpsServer"         },
			 {"lbl",           "lblServer"         },
			 {"dvl",           "dvlServer"         },
			 {"scheduler",     "lblServer"         },
			 {"ips4",          "ips4Server"        },
			 {"octans",        "octansServer"      },
			 {"kearfott",      "kearfottServer"    },
			 {"buoyLauncher",  "buoyLauncherServer"},
			 {"help",          "none"              },
			 {"unknown device", "none"             },
                       };

//
// Beware that "tailCone" and "modem" do not follow the pattern of just
// replacing "Server" with "Test", thus necessitating the above struct 
// and special-case code below.
//
//			 {"tailCone",  "odysseyTailCone"},
//
// Count the number of devices, excluding "unknown device".
//
#define NDEV ( sizeof( names ) /sizeof( names[0] ) - 1 )

int main( int argc, char **argv ) 
{
  Boolean debug = False;
  FILE *devices;
  char *auvConfigDir = getenv(AuvConfigDirName);
  char *auvLogDir    = getenv(AuvLogDirName);
  const char *testLogDirName = "testLogs";
  char fileName[256];
  char errorBuf[256];
  char path[256];
  pid_t serverPid;
  Boolean Found = False;

  dprintf(" There are %d devices.\n", NDEV-1);        //Exclude "help"
  //
  // Check that the user has supplied an argument.
  //
  if( argc < 2 )
  {
    help();
    exit(-1);
  }
  //
  // First, cd to ../logs/.  If this doesn't work, write an error message.
  //
  DIR *directory;

  if ((directory = opendir(auvLogDir)) == 0) 
  {
    sprintf(errorBuf, " Can't open log directory \"%s\"", auvLogDir,
	    strerror(errno));
    Syslog::write(errorBuf);
    exit(-1);
  }
  //
  // Determine if the directory testLogs exists.  If not, create it.
  // mkdir() sets errno to 17 (EEXIST) if the directory already exists.
  //
  sprintf( path, "%s/%s", auvLogDir, testLogDirName);
  dprintf(" path = %s\n", path);

  mode_t mode = 0777;
  const int EEXIST = 17;                //Can't find the .h file!

  if ( (mkdir(path, mode) == -1) && ( errno != EEXIST ) ) 
    {
      sprintf(errorBuf, " Can't create %s; %d;  %s", 
	      path, errno, strerror(errno));
      Syslog::write(errorBuf);
      exit(-1);
    }
  //
  // Unlink the symbol "latest" and relink it to testLogs, to ensure that
  // it doesn't point to the mission data:
  //
  char linkName[256];
  sprintf(linkName, "%s/%s", auvLogDir, LatestLogDirName); 
  dprintf(" linkName = %s\n", linkName);

  unlink(linkName);

  char cmd[256];
  sprintf(cmd, "ln -s %s %s", path, linkName);

  // Have to use system() to create link, because QNX link() does not 
  // work for directories!!!
  if (system(cmd) != 0) {

    sprintf(errorBuf, " Symbolic link failed: %s \n", cmd);
    Syslog::write(errorBuf);
    exit(-1);
  }
  //
  // Open the configuration file pointed to by the environmental
  // variable AUV_CONFIG_DIR
  //
  sprintf(fileName,"%s/%s", auvConfigDir, "devices.cfg");
  
  dprintf(" The file name is %s.\n",fileName);
  
  if ((devices = fopen(fileName,"r"))==NULL)
  {
    sprintf(errorBuf, "test - Failure opening %s\n",fileName);
    Syslog::write(errorBuf);
    exit(-1);
  }
  //
  // Find the desired test program:
  //
  int i = 0;
  while( strcmp( argv[1], names[i].deviceName ) && (i < NDEV) ) i++;
  if( i == NDEV-1 )
  {
    help();
    exit(-1);
  }
  if( i == NDEV )
  {
    Syslog::write(" Argument not recognized.\n");
    help();
    exit(-1);
  }

  dprintf(" Test the %s.\n", names[i].deviceName );
  //
  // Form the name of the exectuable.  The tailcone is a special case, where
  // the name of the executable depends on auvConfigDir.
  //
  char executable[128];
  char configDir[128];
  char *configDirPtr = configDir;
  char *token, *namePtr;
  //
  // Parse out the name of the configuration directory, without the path.
  //
  sprintf( configDir, "%s", auvConfigDir );
  while ((token = strtok(configDirPtr, "/")) != 0) 
  {
      namePtr = token;
      configDirPtr = 0;   
  }
  //
  // namePtr now points to the filename stored at the end of the 
  // string configDir.
  //

  //
  // SPECIAL CASE for Dorodyssey tailConeTest:
  //
  if( strncmp( namePtr, "dorodyssey", 10 ) == 0 &&
      strcmp ( names[i].deviceName, "tailCone") == 0    )
  {

    // The Odyssey tail cone test program executable is called otailConeTest.
    strcpy( executable, "otailConeTest" );

    // The server name is "odysseyTailCone", not tailConeServer, so we must
    // reset the "names" struct.
    names[i].serverName = "odysseyTailCone";

  }
  else
  {
    //
    // For all other cases, append "Test" to the device name.
    //
    strcpy( executable, names[i].deviceName );
    strcat( executable, "Test" );
  }
  Syslog::write(" Using the configuration files in %s.\n", auvConfigDir );
  //
  // Extract the serial port information from the devices.cfg file:
  //
  char buf[512];

  while (fgets(buf, sizeof(buf), devices) != 0) 
  {
    char *ptr;

    if ((ptr = strchr(buf, '\r')) != 0) 
      // Remove carriage-return
      *ptr = '\0';

    if ((ptr = strchr(buf, '\n')) != 0) 
      // Remove newline
      *ptr = '\0';

    if ((ptr = strchr(buf, '#')) != 0) 
      // Ignore everything following comment character
      *ptr = '\0';
    //
    // Parse each record in devices.cfg.  buf[] will contain null-terminated
    // tokens.  args[] will contain the tokens, but without the intervening
    // nulls.
    //
    int nToken = 0;
    const char *programName;
    char args[512];
    args[0] = '\0';
    char *argsPtr = args;
    const char *argArray[32];
    ptr = buf;
    dprintf(" buf = %s\n", buf);
    while ((token = strtok(ptr, " \t")) != 0) 
    {
      ptr = 0;

      if (nToken == 0) {
	programName = token;
	argArray[0] = token;
      }
      else {
	// Argument to program
	//
	// argArray[nToken] = argsPtr;
	//
	// The above doesn't work because argsPtr points to args, which
	// doesn't have null-terminated tokens, as does buf. 
	//
	argArray[nToken] = token;
	sprintf(argsPtr, "%s ", token);
	argsPtr += strlen(argsPtr);
	if (argsPtr > (args + sizeof(args))) 
	{
	  sprintf(errorBuf, "Overflow of command argument buffer\n");
	  Syslog::write(errorBuf);
	  fclose(devices);
	  // throw Exception(errorBuf);
	}
      }
      dprintf(" argArray[%d] = %s\n", nToken, argArray[nToken]);
      nToken++;
    }
    dprintf(" First token = %s:\n", programName);
    //
    // If the server name in devices.cfg corresponds to the device the user
    // has selected above, then execute the coresponding test program.
    //
    if ( programName && strcmp( programName, names[i].serverName ) == 0 ) 
    {
      Found = True;
      //
      // If the user specified an additional argument, pass it through:
      //
      if( argc > 2 ) argArray[nToken] = argv[2];

      dprintf(" nToken = %d\n", nToken);
      dprintf(" argArray[0] = %s\n", argArray[0]);
      dprintf(" argArray[1] = %s\n", argArray[1]);
      dprintf(" argArray[2] = %s\n", argArray[2]);
      if( argc = 3 ) dprintf(" argArray[3] = %s\n", argArray[3]);
      char argString[100];
      
      Syslog::write(" Executing %s %s %s\n", executable, args, argArray[3]);
      dprintf(" Calling %s %s; %s\n", executable, args, programName);
      if(execvp( executable, argArray ))
      {
	sprintf(errorBuf, "Couldn't execute %s.\n", executable);
	Syslog::write(errorBuf);
      }
      break;
    }
    dprintf(" End of while loop.\n");
  }
  if( !Found )
  {
    Syslog::write(" The device %s is not in devices.cfg\n", names[i].deviceName );
  }
  dprintf(" End of testDev.\n");
  exit(0);
  return(0);
}
//
// PURPOSE: Print help information to stdout.
//
void help(void)
{
  int i;
  
  printf(" You must supply an argument.\n"
	 " Valid arguments are:\n");

  for( i=0; i<NDEV; i++ )
  {
    printf("   %s\n",names[i].deviceName);
  }
  printf("\n In general, type ctrl-C to exit.\n"
	 " Beware that some of the test programs, such as lblTest, \n"
	 " are interactive. \n");
}



