/****************************************************************************/
/* Summary  : Reset the drop weight PIC once, then exit with the following  */
/*            error return code:                                            */
/*               0 - PIC reset to 15 minutes.                               */
/*               1 - Reset failed, countdown continuing.                    */
/*               2 - Reset failed, PIC not responding, count down status    */
/*                   unknown.                                               */
/*               3 - Reset failed, couldn't open the serial port.           */
/*               4 - Drop weight not specified in devices.cfg, so it        */
/*                   probably doesn't exist on this vehicle.                */
/* Filename : Reset.cc                                                      */
/* Author   : Rob McEwen                                                    */
/* Project  : Multibeam (Seafloor Mapping)                                  */
/* Version  : 1.0                                                           */
/* Created  : 2004/10/27                                                    */
/* Modified :                                                               */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/****************************************************************************/
/*-----------------------------------------------------------------------*
  $Id: Reset.cc,v 1.5 2005/10/26 01:17:40 dorado Exp $
  *-----------------------------------------------------------------------*/
//#include "DropWeight.h"
#include "SerialDevice.h"
#include "Syslog.h"
#include "System.h"
#include "stdlib.h"
#include <time.h>

enum ResetStatusEnum {Reset, StillCounting, NoPICResponse, NoSerialResponse,
		      NoDropWeight, UnknownError};

void readPIC( SerialDevice *PIC, short *Bwt );

int main(void)
{
// Set task priority
// if (System::setTaskPriority(NonCriticalDriverPriority) == -1)
// return 1;
//
// Find which serial port Sib's dropweight PIC is on from devices.cfg:
//
   const char *fileName = System::configurationFile("devices.cfg");

   Boolean debug = False;
   char errorBuf[256];
   char buf[512];
   buf[511] = '\0';
   char args[512];
   int i, nToken = 0;
   const char *programName = "Not Set";
   args[0] = '\0';
   char *argsPtr = args;
   char *argArray[32];
   char *token;
   Boolean Found = False;

   FILE *fp = fopen(fileName, "r");
   if (fp == 0) 
   {
      sprintf(errorBuf, "Reset:: - \n"
	      "Couldn't open task config file \"%s\"", fileName);
      throw Exception(errorBuf);
   }


   while (fgets(buf, sizeof(buf), fp) != 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.
      //
      nToken = 0;
      args[0] = '\0';
      argsPtr = args;
      ptr = buf;
      token = strtok(buf, " \t");
      //while ((token = strtok(ptr, " \t")) != 0) 
      while( token != NULL ) 
      {
	 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");
	       dprintf("%s", errorBuf);
	       fclose(fp);
	       // throw Exception(errorBuf);
	    }
	 }
	 dprintf(" argArray[%d] = %s\n", nToken, argArray[nToken]);
	 nToken++;
	 token = strtok(NULL, " \t");
      } // end while ((token = strtok(ptr, " \t")) != 0) 
      //
      dprintf(" First token = %s:\n", programName);
      //
      //
      if( !strcmp( programName, "dropWeightServer" ) )
      {
	 dprintf(" dropWeightServer found in devices.cfg!\n");
	 Found = True;
	 dprintf(" nToken = %d", nToken);
	 for( i=0; debug & i<nToken; i++ )
	 {
	    dprintf(" argArray[%d] = %s", i, argArray[i]);
	 }
	 break;
      }
   }  //end    while (fgets(buf, sizeof(buf), devices) != 0) 
   if( !Found )
   {
      Syslog::write(" dropWeightServer was not found in the devices.cfg.\n");
      exit( NoDropWeight );
   }

   fclose(fp);

   //dprintf(" Exiting Reset\n");
   //return 0;
   SerialDevice *PIC;
   try 
   {
      SerialParameters params(&nToken, argArray);
      PIC = new SerialDevice(&params);
   }
   catch (Exception e) 
   {
      Syslog::write("reset - caught Exception: %s\n", e.msg);
      exit( NoSerialResponse );
   }
   catch (...) 
   {
      Syslog::write("reset - caught exception in defining SerialParameter\n"
	            "        or SerialDevice objects.");
      exit( NoSerialResponse );
   }

   short WdtBefore[1], WdtAfter[1];

   readPIC( PIC, WdtBefore );
   //
   // Write the reset sequence to the PIC.
   char *resetWDog = "!\r";
   dprintf(" reset - Reset the countdown timer.\n");
   try { PIC->write( resetWDog, strlen(resetWDog) ); }
   catch( ... ) 
   { 
      Syslog::write(" reset - Error writing the ! to the PIC.\n"); 
      exit( StillCounting );
   } 
   readPIC( PIC, WdtAfter );

   char timestr[128];
   size_t len;
   time_t timer;
   struct tm tmTime;
   struct timespec currentTime;
   //
   // Read the MVC clock, than massage these time structures around until
   // the right time format is produced as a character string in timestr.
   //
   clock_gettime(CLOCK_REALTIME, &currentTime);
   timer = currentTime.tv_sec;
   _gmtime(&timer, &tmTime);
   len = strftime(timestr, 32, "%y/%m/%d  %T", &tmTime);

   if( *WdtAfter > 890 )
   {
      Syslog::write("Reset succeeded on %s.\n  "
		    "PIC watchdog reset from %d to %d seconds.\n", 
		    timestr, *WdtBefore, *WdtAfter);
      exit( Reset );
   }
   else 
   {
      Syslog::write(" Reset failed on %s.  \n", timestr);
      exit( StillCounting );
   }   
   Syslog::write(" reset - Should not get here.\n");
   exit( UnknownError );
   return UnknownError;
}


void readPIC( SerialDevice *PIC, short *Wdt )
{
   //
   // Read PIC output.  
   //
   int nRead = 0;
   char record[1024];
   int maxRecordBytes = 512;
   int readTimeout = 3000;
   char *recordTerminator = "\xa";
   Boolean readError = False;
   short Bwt, WDC, ACommsDN, DW_out;
   short GulperSolenoidOut, GulperProxIn;
   short GulperHighSideStat, GulperSolenoidFB;
   int nBytesRead = 0;
   Boolean debug = False;

   try 
   {
      // Read data record from device
      nBytesRead = PIC->readUntil((char *)record, maxRecordBytes, 
				  (char *)recordTerminator, 
				  readTimeout);
   }
   catch (SerialDevice::TimedOut) 
   {
      readError = True;
      dprintf("reset - serial device timed out");
      dprintf("reset - record so far: %s", record);
   }
   catch (SerialDevice::BufferFull) 
   {
      readError = True;
      dprintf("reset - serial device buffer full");
   }
   catch (Exception e) 
   {
      readError = True;
      dprintf("reset - caught exception:  abort");
      throw;
   }

   dprintf("reset - PIC->readUntil() done");

   //For now, assume the PIC isn't responding:
   if (readError) 
   {
      Syslog::write(" Pic not responding.  Countdown continuing.");
      exit( NoPICResponse );  
   }

   dprintf(" %s", record);
   nRead = sscanf(  (char *)record,  "wdt = %d,  bwt = %d,  WDC = %d,  "
		    "ACommsDN = %d,  DW_out = %d,   " 
		    "GulperSolenoidOut = %d,   GulperProxIn = %d,   "
		    "GulperHighSideStat = %d,   GulperSolenoidFB = %d",
		    Wdt, &Bwt, &WDC, &ACommsDN, &DW_out,
		    &GulperSolenoidOut, &GulperProxIn, &GulperHighSideStat,
		    &GulperSolenoidFB);
   if( nRead != 9 )
   {
      //
      // Make sure there is a terminating null before writing it.
      //
      if( strlen((char*)record) >= 1024 ) record[1024] = '\0';
      dprintf(" %s", record);
      Syslog::write(" reset - Unable to read complete PIC record.\n");
      exit( NoPICResponse );
   }
   //printf(" Wdt = %d Bwt = %d ACommsDN = %d DW_out = %d\n",
   //         *Wdt, Bwt, ACommsDN, DW_out);
}
