#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

/* baudrate settings are defined in <asm/termbits.h>, which is
included by <termios.h> */
#define BAUDRATE B9600            
/* change this definition for the correct port */
#define MODEMDEVICE "/dev/ttyS0"
#define _POSIX_SOURCE 1 /* POSIX compliant source */

#define FALSE 0
#define TRUE 1
#define MAXLEN 100
#define MAXTRIES 5
#define LOGFILE "rawdata.log"
#define MAXDATA 5

void connect_to_car();

int comport;
char datafname[MAXDATA][100]={ "ic.dat", "iv.dat", "oc.dat", "ov.dat", "speed.dat" };

int main(int argc, char *argv[])
{
  struct termios oldtio,newtio;
  char serdev[100]=MODEMDEVICE;

/* 
  Open modem device for reading and writing and not as controlling tty
  because we don't want to get killed if linenoise sends CTRL-C.
*/
 if(argc>1)
  strcpy(serdev,argv[1]);

 comport = open(serdev, O_RDWR | O_NOCTTY ); 
 if (comport <0) {perror(serdev); exit(-1); }

 tcgetattr(comport,&oldtio); /* save current serial port settings */
 bzero(&newtio, sizeof(newtio)); /* clear struct for new port settings */

/* 
  BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
  CRTSCTS : output hardware flow control (only used if the cable has
            all necessary lines. See sect. 7 of Serial-HOWTO)
  CS8     : 8n1 (8bit,no parity,1 stopbit)
  CLOCAL  : local connection, no modem contol
  CREAD   : enable receiving characters
*/
 newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
 
/*
  IGNPAR  : ignore bytes with parity errors
  ICRNL   : map CR to NL (otherwise a CR input on the other computer
            will not terminate input)
  otherwise make device raw (no other input processing)
*/
 newtio.c_iflag = 0; // IGNPAR | ICRNL;
 
/*
 Raw output.
*/
 newtio.c_oflag = 0;
 
/*
  ICANON  : enable canonical input
  disable all echo functionality, and don't send signals to calling program
*/
 newtio.c_lflag = ICANON;
 
/* 
  initialize all control characters 
  default values can be found in /usr/include/termios.h, and are given
  in the comments, but we don't need them here
*/
 newtio.c_cc[VINTR]    = 0;     /* Ctrl-c */ 
 newtio.c_cc[VQUIT]    = 0;     /* Ctrl-\ */
 newtio.c_cc[VERASE]   = 0;     /* del */
 newtio.c_cc[VKILL]    = 0;     /* @ */
 newtio.c_cc[VEOF]     = 4;     /* Ctrl-d */
 newtio.c_cc[VTIME]    = 0;     /* inter-character timer unused */
 newtio.c_cc[VMIN]     = 1;     /* blocking read until 1 character arrives */
 newtio.c_cc[VSWTC]    = 0;     /* '\0' */
 newtio.c_cc[VSTART]   = 0;     /* Ctrl-q */ 
 newtio.c_cc[VSTOP]    = 0;     /* Ctrl-s */
 newtio.c_cc[VSUSP]    = 0;     /* Ctrl-z */
 newtio.c_cc[VEOL]     = 0;     /* '\0' */
 newtio.c_cc[VREPRINT] = 0;     /* Ctrl-r */
 newtio.c_cc[VDISCARD] = 0;     /* Ctrl-u */
 newtio.c_cc[VWERASE]  = 0;     /* Ctrl-w */
 newtio.c_cc[VLNEXT]   = 0;     /* Ctrl-v */
 newtio.c_cc[VEOL2]    = 0;     /* '\0' */

/* 
  now clean the modem line and activate the settings for the port
*/
 tcflush(comport, TCIFLUSH);
 tcsetattr(comport,TCSANOW,&newtio);

/*
  terminal settings done, now handle input
  In this example, inputting a 'z' at the beginning of a line will 
  exit the program.
*/
 connect_to_car();
 
 /* restore the old port settings */
 tcsetattr(comport,TCSANOW,&oldtio);

 return(0);
}

int readport(char *buf, int len)
{
 int cnt;
 FILE *fplog;
 time_t logtime;
 char *timestr;

 cnt = read(comport,buf,len);
 buf[cnt]=0;
 fplog = fopen(LOGFILE,"a");
 if(fplog!=NULL)
 {
  time(&logtime);
  timestr = ctime(&logtime);
  timestr[strlen(timestr)-1]=0;
  fprintf(fplog, "[%s] - R - %s\n",timestr,buf);
  fclose(fplog);
 }
 return cnt;
}
 
int writeport(char *buf, int len)
{
 int cnt;
 FILE *fplog;
 time_t logtime;
 char *timestr;

 cnt = write(comport,buf,len);
 fplog = fopen(LOGFILE,"a");
 if(fplog!=NULL)
 {
  time(&logtime);
  timestr = ctime(&logtime);
  timestr[strlen(timestr)-1]=0;
  fprintf(fplog, "[%s] - W - %s\n",timestr,buf);
  fclose(fplog);
 }
 return cnt;
}

void flushport()
{
 fd_set comset;
 struct timeval tv;
 int retval;
 char *tmp;

 tcflush(comport, TCIFLUSH);

 FD_ZERO(&comset);
 FD_SET(comport,&comset);

 tv.tv_sec=30;
 tv.tv_usec=0;

 retval = select(comport+1, &comset, NULL, NULL, &tv);
 if(retval)
 {
  printf("-- flush: %d\n",retval);
  tmp = (char *) malloc(retval);
  readport(tmp,retval);
 }

 tcflush(comport, TCIFLUSH);
}

int send_connstr()
{
 static char connstr[MAXLEN]="\003E ON\rCH 2\rC KI4FED\r";
 char buf[MAXLEN];
 int cnt, tries=0;
 int isconn=FALSE;
 
 writeport(connstr,strlen(connstr));
 do
 {
  cnt = readport(buf,MAXLEN);
  buf[cnt]=0;
  printf("--> %s",buf);
  if(strstr(buf,"CONNECTED")!=NULL)
  {
   if(strstr(buf,"DISCONNECTED")!=NULL)
   {
    writeport(connstr,strlen(connstr));
    tries++;
   }
   else
    isconn=TRUE;
  }

 }while(!isconn && tries < MAXTRIES);

 return isconn;
}

void send_disconnstr()
{
 static char disconnstr[MAXLEN]="\003d\r";
 
 writeport(disconnstr,strlen(disconnstr));
}

int process(char *buf)
{
 int fh;
 unsigned short data;
 int bufused,bufpos,buflen;
 int i;

 for(i=0; i<strlen(buf); i++)
 {
  if(!(isxdigit(buf[i]) || isspace(buf[i])))
   return FALSE;
 }
 
 buflen = strlen(buf);
 bufpos=0;
 bufused=0;
 for(i=0; i<MAXDATA; i++)
 {
  data=0;
  if(bufpos<buflen)
  {
   sscanf(buf+bufpos,"%hx %n",&data, &bufused);
   bufpos += bufused;
  } 

  fh=open(datafname[i],O_CREAT | O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR);
  write(fh,&data,sizeof(unsigned short));
  close(fh);
 } 

 return(0);
}

void connect_to_car()
{
 int isconn, cnt;
 char buf[MAXLEN];

 send_disconnstr();
 flushport();
 isconn = send_connstr();
 if(!isconn)
 {
  printf("Error: Unable to connect!\n");
  send_disconnstr();
  exit(1);
 }

 while(TRUE)
 {
  while(isconn)		/* Loop as long as we are connected */
  {
     cnt = readport(buf,MAXLEN); 

     printf("DATA: %s",buf);

     if(strstr(buf,"DISCONNECTED")!=NULL)
    	isconn=FALSE;

     process(buf);
  }
  
  isconn = send_connstr();
  if(!isconn)
  {
   printf("Error: Unable to connect!\n");
   send_disconnstr();
   exit(1);
  }
 }
}
