// ********************************************************************
//  ph.c
//  23AUG18 - begin file
//  JUL19 - switching to Tom's new code that first tells the pH to take
//          a sample, then come back later and ask for the results
// ********************************************************************


#include  <cfxbios.h>		// Persistor BIOS and I/O Definitions
#include  <cfxpico.h>		// Persistor PicoDOS Definitions
#include  <stdio.h>
#include  <stdlib.h>
#include  <string.h>
#include  "gpio_mux.h"
#include  "setup.h"
#include  "sbd.h"
#include  "sbe.h"
#include  "spi_cmd.h"
#include  "ph.h"
#include  "lifo.h"
#include  "miss_fnx.h"
#include  "dat_fnx.h"

//ph uses same ports as ALT/ADP 
#if PH==1
// PROCEED ...#endif is at the end of this file

//external declarations

extern union CS CS0, CS1, CS2;
extern short TSerInUse;  //GLOBAL defined in start.c for Benthos
                  // =1 if TSerial port of TPU is in use, else 0
                  //Used to prevent code from changing System clock or turning off
                  //the oscillator or TPU in low power states.To do so messes up 
                  //TSerial interrupt servicing and baud rates.
extern struct all_param all;  //defined in cfxmain.c
extern char   Verbose;  // switch for displaying more info
extern short  iparam [ MAX_PARAM ]; // global parameter settings
long ReadCharsFromConsole(char *buffer, long n);//CFxPicoPreMain.c
extern short TSerInUse;  //GLOBAL defined in start.c for Benthos
                  // =1 if TSerial port of TPU is in use, else 0
                  //Used to prevent code from changing System clock or turning off
                  //the oscillator or TPU in low power states.To do so messes up 

//internal declarations
short ph_is_on;  //1 if acm is on and ready to talk
  
char s_NoLOCresp[]= "No LOCAL resp to %s:\n";  //a string used often in diagnostics 

//*************************************************************************************
void  ph_alloc  ( struct ph_param *ph ) //*********************************
// ** allocate memory for ph serial in/out & data
{  unsigned char *buf;
   long nbytes;
   
   ph_off();  // make sure ph modem is turned off
   
   ph->max = PH_NSCANS; // max #scans expected
   ph->i=0;
   //printf("ph max scans  = %d, ph sensors = %d\n", PH_NSCANS,N_PH_OUT);
   nbytes = ((long)PH_NSCANS )*(N_PH_OUT)*(2);

   //**allocate data buffer area
   buf = (unsigned char *) malloc(nbytes);
   if (buf == NULL)
   { printf("ERROR ph_alloc\n");
   }
   ph->buf = buf; // otherwise OK, store buffer pointer
   printf("    ph ptr = %p, nbytes=%ld \n",ph->buf, nbytes);
   return;
}

//*************************************************************************************
short ph_off() // turn off ph sensor
{  //ALWAYS returns 0 (assumes success)
   short istat = 0;
   struct spi_param *ss;
   ss = all.sspi; // points to the spi comms struct
   setup_spi ( CS_GPIO, ss );
   
   istat = spi_aux_pwr( -AUX_ON_ALT, ss );  //turn off power
   if (Verbose) cprintf("Err Stat off_acm = %d\n", istat);
   DelayMilliSecs(10);  // wait 
  
   istat = spi_mux( 0, SER_ADP, ss ); // turn off rs232, returns TRUE if ok
   if (!istat) cprintf("Err Stat off_mux\n");

   return ( 0 );   //But per TT8 version, this is ignored
   
} // end acm_off

//*************************************************************************************
short start_ph(void) // power up and initialize pH
{  //On success returns 
   //         0 = success
   //On errors returns:  
   //        -2 = 
   
   short i=0;
   short istat; 
   char s[]="\n err on ph %s chan = %d \n";
  
   struct spi_param *ss;
   TUPort    *tup;

   tup = all.tu_port[TU_ADP].tup;  // tpu uart port pointer for ACM/ADP
     
   ss = all.sspi; // points to the spi comms struct
   setup_spi ( CS_GPIO, ss );
   
   //pH uses ADP subchannel of serial MUX1 with Baud=BAUD_MUX1
   istat = spi_mux( 1, SER_ADP, ss ); // turn on rs232 & configure mux
   if ( !istat )
     cprintf("Err Stat mux_ph\n" );
     
   istat = spi_aux_pwr( AUX_ON_ALT, ss ); // ph now has power
   if ( istat != SPI_CRC_OK )
   {  // error sending the 'ON' command
      cprintf("Err Stat start_ph = %d\n", istat);
      return ( -2 );
   } 
   ph_is_on=1;  //the acm is on and set up for communication
   
   return(0);
}  // end start_ph

//*************************************************************************************
void Transparent_ph( void )  //talk directly to the pH. Quits when both ends are idle 
{ //for COM_to seconds.  Assumes the pH is powered up and leaves it on when done.
  
  short COM_to, ifecho;
  char c;
  unsigned long now,tl_com;
  TUPort    *tup;

  tup = all.tu_port[TU_ADP].tup;  // tpu uart port pointer for ACM/ADP
  
 
  SerInFlush();  //flush Spray COMM input buffer
  printf("Idle channel timeout (seconds)? ");fflush(stdout);
  scanf("%d",&COM_to);
  printf("Local echo of Spray output (y/n)? ");fflush(stdout);
  ifecho=YesNo();

 
  TURxFlush  ( tup );   // flush the tpu port input buffer
  SerInFlush();  //flush Spray COMM input buffer
  
  //transfer bytes in both directions until neither end has transmitted
  //for tl_com seconds
  now=tl_com=RTCGetTime(0,0);
  while( (short)(now-tl_com) <= COM_to )
  { if( SCIRxQueuedCount() )
    { //xfer byte waiting at Spray COMM port to AC modem
      c = (char)SCIRxGetChar();
      TUTxPutByte ( tup, c, 0);   
      if(ifecho)SCITxPutChar(c);  //provide local echo
      tl_com=RTCGetTime(0,0);  //secs of last char from COMM
    }
   
    if( TURxQueuedCount(tup) )
    { //xferbyte waiting at AC modem to Spray COMM port
	    c = (char)TURxGetByte(tup,FALSE);  	//read in character
	    SCITxPutChar(c);
      tl_com=RTCGetTime(0,0);  //secs of last char from modem
    }
    
    now=RTCGetTime(0,0); 
  }
  printf("Channel has been Idle for %d seconds\n",COM_to);
  
  return;
}  //end Transparent

//*************************************************************************************
short ph_send_ss ( void )
 { //send ss to start sampling.  Must wait at least 1.5sec before calling ph_get_sl
   //always returns 0
   
  TUPort    *tup;
  
  tup = all.tu_port[TU_ADP].tup;  // tpu uart port pointer for ACM/ADP
  tputs    (tup, "2\r");  //* send cr to wake it up
  DelayMilliSecs(300); //let it wake up
  TURxFlush  ( tup );   // flush the tpu port input buffer
  tputs    (tup, "ss\n");  //* send ss to start the sample
  DelayMilliSecs(200);
  
  return(0);
 }
 
//*************************************************************************************
short ph_get_sl ( struct ph_param *DP )   
 {//get the answer from the ph.  Wait 1.5s after calling ph_send_ss before calling
  //returns the number of values read from the pH
  
  char ch[N_PH_BUF];
  //uchar dbg[80];
  short n=-1, found=0; //i;
  short temp;
  uchar *ptr;
  float v1=-9,v2=-9,v3=-9,v4=-9,v5=-9,v6=-9;
  
  TUPort    *tup;
  
  tup = all.tu_port[TU_ADP].tup;  // tpu uart port pointer for ACM/ADP


  TURxFlush  ( tup );   // flush the tpu port input buffer
  tputs    (tup, "sl\n");  //* send sl cm
  DelayMilliSecs(200);
  
  
  strncpy(ch,"\0",N_PH_BUF); //clear ch
  //sprintf(dbg,"First pH read follows:");
  //buf_to_cf1V(strlen(dbg),dbg);  //and write to CF1
  n = tgets( tup, ch, N_PH_BUF-1, 4, '\r', &found); //* read up to N_PH_BUF char
  //buf_to_cf1V(n,ch);  //and write to CF1
  //printf("ch = %s\n",ch);
  //printf("n=%d: ch= ",n);
  //for (i=0;i<n;i++)printf("%02x ",ch[i]);
  //printf("\n");
  DelayMilliSecs(100);  
  
  
  strncpy(ch,"\0",N_PH_BUF); //clear ch
  //sprintf(dbg,"2nd pH read follows:");
  //buf_to_cf1V(strlen(dbg),dbg);  //and write to CF  
  n = tgets( tup, ch, N_PH_BUF-1, 4, '!', &found); //* read up to N_PH_BUF char
  //buf_to_cf1V(n,ch);  //and write to CF1
  printf("ch = %s\n",ch);
  //printf("n=%d: ch= ",n);
  //for (i=0;i<n;i++)printf("%02x ",ch[i]);
  //printf("\n");
 
  if ( (ptr=strstr(ch,"#")) != NULL )//start at beg. of raw buffer
  { //Found the packet
    //printf("ch addr = %p, ptr = %p\n",&ch,ptr);
    ptr += 2; 
    n=sscanf(ptr,"%lu\t%d/%d/%d %d:%d:%d\t%d\t%f\t%f\t%f\t%f\t%f\t%f",
             &DP->s_no,&temp,&temp,&temp,&temp,&temp,&temp, &DP->hum,
              &v1, &v2, &v3, &v4, &v5, &v6);                                 
  }

  if(n<14) buf_to_cf1V(strlen(ch),ch);  //store bad line in CF for diagnosis

  //convert float to int
  DP->phv1 = intDval( (v1 + PH_V_OFFSET) * 10 );
  DP->phv2 = intDval( (v2 + PH_V_OFFSET) * 10 );
  DP->phv3 = intDval( (v3 + PH_V_OFFSET) * 10 );
  DP->phv4 = intDval( (v4 + PH_V_OFFSET) * 10 );
  DP->phv5 = intDval( (v5 + PH_V_OFFSET) * 10 );
  DP->phv6 = intDval( (v6 + PH_V_OFFSET) * 10 );
  
  printf("n=%d, v1=%d\n", n,DP->phv1);
  return(n);
 }
 
//************************************************************************************* 
void store_ph_dat ( struct ph_param *DP ) //*********************************
{ // store the present profile data to RAM buffer area
  long offset;
  short *d; // points to next spot in RAM
  short *x; // points to the data
  short i;
  
  if (DEBUG) cprintf("store_ph_dat, DP->i=%d, DP->max=%d: ",DP->i,DP->max);
  
  // store profile data====================================================
  if ( DP->i < DP->max )  
  { // still have room & is part of profile, store values to RAM
  
    offset = (long) DP->i * (long) N_PH_OUT * 2; //Offset into dat
    d = DP->buf + offset;  //points to next output
    x = &(DP->phv1);  // points to first data point to write out
    
    for (i=0;i< N_PH_OUT; i++) //----------------------
    { //xfr these values
        if (Verbose) printf("ptr=%p, val=%5u\n",d,*x);  // print value
        *d++ = *x++; // xfr data to RAM
    }  //------------------------------------------------
    if (Verbose) cprintf("\n"); 

    DP->i++;  //increment index
  } //=====================================================================

  return;
 }
 
//************************************************************************************* 
 short ph_data_pack( void )  //****************************************************
 { //*** average and pack data for iridium xmit ********************************
  // does time averaging of ph measurements
  // returns # bytes written to the temporary buffer
  
  short n, navg,npts, *dptr, nrec;
  short i,j, num_avg, nsbe_off;
  long x[MAX_AVG_PTS];  //=temporary buffer of averaged data
  char b[MAX_AVG_BUF]; // =tmp buffer of the packed data + overhead
  struct ph_param *DP;
  
  short nsensors=6; //v1-v6
 
  //--mar11, define sensor ID: type=MSN, ver=LSN for ID byte
  ushort s_type[6] = { 8, 8, 8, 8, 8, 8 };
  ushort s_ver[6]  = { 1, 2, 3, 4, 5, 6 };

 
  //--- initialize ------------------------------------------------------
  DP = all.ph;
  npts = 0;
  n = DP->i;  //=# of points written out in time series
  cprintf("#pts in series = %5d\n",n);
  nrec = N_PH_OUT; //=#variables per record
  nsbe_off = N_PH_OUT; //for compatibility with dat_fnx.c
  
  //---------------------------------------------------------------------
  // do a time-average to limit overall amount of data < MAX_AVG_PTS
  navg = n/MAX_AVG_PTS + 1;  //min averaging allowed w/o buffer overflow
  num_avg = iparam [ IA_num_avg ]; // = desired # to avg per output pt
  // use num_avg unless navg>num_avg
  if (navg > num_avg) num_avg = navg;  //this makes sure we don't overflow
  all.eng->navg = num_avg;  // save #pts averaged in engineering
  //---------------------------------------------------------------------


  if (n>0) //------------------------------------------------------------
  { // then pack the data -----------------------------------------------
    for (i=0;i<nsensors;i++)  // do for v1-v6
    { //.................................................................
      // >BF, can add packing option dependent upon ee_use_opt mask
      // only do the following if the right bit is set
      j = nsbe_off -i; // address distance between sensor & DP->nsbe flag
      printf("processing sensor %d offset to nsbe=%d\n",i,j);
      dptr = (all.ph->buf)+2*i;  // pointer to data buffer
      //printf("call=ph_avg_dat(%d,%p,%d,%d,%d,%ld,%d)\n",i,dptr, nrec, num_avg,n,x,j);
      //dptr += nrec;  // skip past surface values
      npts = ph_avg_dat(i,dptr, nrec, num_avg,n,x,j);  //average time series
      // now we want to store npts of x to the tmp output area = b
      // >BF sensor type 5 = ADP, so following will need to be changed
      // for opt2, oxy2.  I suggest:
      // opt1, opt 2, oxy2 = sensor 4,
      // version 0, 1, 2 respectively.
      // an array of sensor type/version could be used for lookup with i
      //  s_type[6] = { 1, 2, 3, 4, 4, 4 };
      //  s_ver[6]  = { 0, 0, 0, 0, 1, 2 };
      //  pack_dat(b,x,npts, s_type[i], s_ver[i] );
      // mar11 packing change to use s_type, s_ver
	  // npts = pack_dat(b,x,npts,i+1,0 );<--DOX, replace this call with:
	  npts = pack_dat(b,x,npts, s_type[i], s_ver[i] );
	  // now append this on to the end of lifo

    //start a new LIFO message for profile data to make sure all of
    //profile ends up in the same Iridium message
    if(i==0)
	    new_msg_lifo(npts,(unsigned char *)b);
    else
	   append_lifo(npts,(unsigned char *)b);

    } // end for i=0,1,2,3 = v1-v6l ...........................
  } // end if n>0 -------------------------------------------------------
  

  return(npts); 
} //*** end data_pack *********************************************************

//*****************************************************************************
short ph_avg_dat(short ix,short *dptr,short nrec, short navg, //******************
              short n, long *x, short nsbe_off)
{ // average time series :
  // ix=0..3 : ph v1-v6
  // dptr points to 1st datum,
  // each datum is offset by nrec
  // navg = #input pts to average for ea output point
  // n = total # input pts
  // x points to output array
  // nsbe_off = addr distance from sensor ix to nsbe flag
  //		nsbe = 0 if no data, else nsbe = 1
  // returns # written out to x
  
  short i,j,k, kk, jsbe;
  ushort *up, val;// minval, cfake;
  long avg =0;  // accumulator for avg
  
  if (Verbose) 
     { cprintf("ph_avg_dat: ");
       DelayMilliSecs(500); // 1 second delay to let print
      }
  // initialize counters ------------------------------------------------
  i=0;  // i counts # input points processed so far
  j=0;  // j points to next output point
  k=0;  // = #pts averaged so far into temporary accum.
  kk=0; // = #valid pts
  up = (ushort*) dptr;  // point as unsigned short
  //---------------------------------------------------------------------
   if (Verbose) 
     { printf("i=%d, n=%d, dptr=%p,up=%p\n",i,n,dptr,up);
       DelayMilliSecs(1000); // 1 second delay to let print
      }  
  while (i++<n) //-------------------------------------------------------
  { // step thru all of the input samples -------------------------------
     
    val = *up;
  
    if (Verbose)
     {
      printf("up = %p, val = %x\n",up,val);
      
     }
  
    jsbe = 1;
    // debug printf("jsbe = %d val=%u \n",jsbe, val);
    if (jsbe>0) //then it is valid ..........................
    { // we got it w/o error from SBE
      if (val>0) // ..........................
      {  // valid #, add into accum.
         avg += (long) val;   //  add value
         kk++;
      } // end if good value .................
      
      k++; // increment #valid points averaged so far
    } // end if jsbe>0 ......................................

    up += nrec;  // point to next input
    
    if (k==navg) // .........................................
    {  // then it's time to average
       if (kk>0) // then enough pts to include in avg
          x[j++] = avg/kk; //save avg value
       else  //...............................
       // NO points in the data series, set to a fake value
          x[j++] = -9;  
       //if(Verbose)cprintf("%ld ",x[j-1]);
       k=0;     // reset #valid jsbe points
       kk=0;    // reset #points>0
       avg=0;   // reset accum.
    } // end if k==navg .....................................
    
  } // end while i<n ------------------------------------------------------
  //-----------------------------------------------------------------------

  if (kk>0) //-----------------------------------------------
  { // then there are a few points left to average
    x[j++] = avg/kk; // save the last value
  } // end kk>0 ---------------------------------------------
  if(Verbose)cprintf("\n");
  return(j);  //return # written out
} //*** end ph_avg_dat ***********************************************************
 
//***************************************************************************** 
void store_ph2flash ( struct ph_param *DP ) //*******************************
{ // store the present ph data to flash
  uchar *cp;
  short n2;
  

    n2 = (N_PH_OUT + 4) * 2;  //time (2 bytes)
                              //s_no (4 bytes) 
                              //hum  (2 bytes)  -- > 8 bytes overhead
                              //voltages (6 * 2 =12 bytes)
                              //--------------------------
                              // = 20 bytes
        
    
     cp = (uchar*) &(DP->secs);   // points to parameter seconds
     flash_store(n2, cp,  CF_PH ); // ph data only

  return;
} //***************************************************************************

//***************************************************************************** 
void buf_to_cf1V( long nbytes , uchar *bufptr)  //buffer to flash file
{ //send buffer pointed to by ptr to Flash file in blocks of 200 bytes
  long nblks,l;
  short blksiz=200,nlast;  //bytes in full,last (partial) block
  short j;
  uchar *ptr;
  
  ptr=bufptr;
  nblks=nbytes/blksiz;  //number of full blocks of blksiz bytes
  nlast=nbytes-nblks*blksiz;  //bytes in last partial block
  
  for(l=0;l<nblks;l++)
  { //write one block of 200 bytes (1st byte=# of bytes to follow)
    j=0; // counter of #tries writing to cf1
    flash_store( blksiz, ptr, CF_ACA ); // transfer to the flash
	ptr += blksiz;  //bump to next block
  }
  if(nlast>=0)
  { //finish last partial block (could have zero bytes of data)
    j=0; // counter of #tries writing to cf1
	flash_store( nlast, ptr,CF_ACA ); // transfer to the cf1
  }
}  //end buf_to_cf1V //**********************************************************

//*******************************************************************************  
void test_ph( void )  // test above functions for ph sensor
 {char ch='?';
  short nread,tmo=0;
  //char reply[450];
  TUPort    *tup;
  struct spi_param *ss;
   
  ss = all.sspi; // points to the spi comms struct
  setup_spi ( CS_GPIO, ss );

  tup = all.tu_port[TU_ADP].tup;  // correct tpu uart port pointer
    
  menu_ph();
  
  while ( ch !='Q')
  { SerInFlush(); // delete any old stuff 
      if (ch !=' ' && ch>0 ) cprintf("A_1>\n"); // display directory prompt
	  ch = -1; // illegal value - means it timed out
	  ch = SCIRxGetCharWithTimeout( 1000 ); // get a new character
	  tmo = check_tmo ( &ch, tmo );
	    // increments the tmo counter if ch<0, and
	    // goes to sleep mode after max_tmo.  Returns *ch as upper-case

	  switch (ch)
	  { // call the desired function
      case 'Q' : break;  // will quit the program
      case '?' : break;  // displays the directory prompt
      case  -1 :  break; // wait for another character
      case 'U' :   menu_ph(); break;


      case 'D' :  //do ts command
         ph_send_ss();
         DelayMilliSecs(1000);
         ph_get_sl(all.ph);
         //all.ph->phv1= intDval(v1);all.ph->phv2= intDval(v2);
         //all.ph->phv3= intDval(v3);all.ph->phv4= intDval(v4);
         //all.ph->phv5= intDval(v5);all.ph->phv6= intDval(v6);        

         printf("s_no = %lu, hum=%d, phv1=%d, phv2=%d, phv3=%d, phv4=%d, phv5=%d, phv6=%d\n",
                  all.ph->s_no, all.ph->hum,all.ph->phv1, all.ph->phv2, all.ph->phv3,
                  all.ph->phv4, all.ph->phv5, all.ph->phv6);
       break;
      case 'W' : 
        ph_off();
        printf("Power Up ");fflush(stdout);
        nread=start_ph(); //make sure LOC is powered up
        if (nread >= 0)
          printf("OK\n");
        else 
          printf("Err=%d\n",nread);
        break;
      case 'T' :  //transparent pass Spray COMM to/from ph
        Transparent_ph();
        break;
      case 'O' : 
        ph_off(); 
        printf("pH is OFF\n");
        break; 
              // pump control
        case 'H' : sbe_pump_hi(  );  
        		cprintf("SBE pump is on, high speed\n");
        		break;  // turn on  sbe pump
        case '1' : sbe_pump_lo(  );  
        		cprintf("SBE pump is on, low speed\n");
        		break;  // turn on  sbe pump
        case '2' : sbe_pump_off(  ); 
        		cprintf("SBE pump is OFF\n");
        		break;  // turn off sbe pump
      default  : printf(" ??\n");
 	} // end switch
  }  // ** end while 

  ph_off();  // make sure pH is off
  printf("pH is off\n");

  return;
 }  // end test_acm

//*************************************************************************************
void menu_ph(void)
{ //print out menu selections 
  printf("pH\n"
         "Q.uit      : U.pdt menu : D. get TS\n"
         "W.ake pH   : O.ff       : T.ransparent\n"
         "H.i pump   : 1.Low pump : 2.off pump\n"
         //"V.oltages  : B.aud      : N.oiseAGC    : C.ycle pwr\n"
         //"A.ll Sregs : S.et Sreg  : G.et Sreg    : F.ill phony\n"
         //"A.ll Sregs : S.et Sreg  : G.et Sreg    : P.wr On\n"
         //"D.o_acm    : L. Assign LIFO\n"
         );
  return;
} // end menu_ph
 
#endif
//*************************************************************************************
//*************************************************************************************
//*****************************************************************************
//*** end file ****************************************************************
//*****************************************************************************