/*
    $Id: tsl.c,v 1.12 2004/01/26 06:58:50 james Exp james $

    tsl, temperature sensor data logger
    Copyright (C) 2000  James Cameron (quozl@us.netrek.org)

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#ifndef lint
static char vcid[] = "$Id: tsl.c,v 1.12 2004/01/26 06:58:50 james Exp james $";
#endif /* lint */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <ctype.h>
#include <string.h>

#define MAXSENSOR 4

/* compile support for conversion from celcius to fahrenheit */
#define FEATURE_CONVERSION 1

#ifdef TSL_DJGPP
/* ftp://sunsite.anu.edu.au/pub/pc/simtelnet/gnu/djgpp/v2tk/pmcom10.zip */
#include <../contrib/pmcom/com.h>

char serial;

void serial_open(char *device)
{
  int result;

  serial = atoi(device)-1;
  result = COMPortOpen(serial, (long) 2400, 8, 'N', 2, 0, NULL);

  switch(result) {
  case 0:
    COMSetDtr(serial, 1);
    return;
  case COMERR_NOCHIP:
    printf("tsl: COMPortOpen: No UART chip detected for that serial port\n");
    exit(1);
  case COMERR_NOMEMORY:
    printf("tsl: COMPortOpen: No memory to allocate receive buffer\n");
    exit(1);
  case COMERR_GENERAL:
    printf("tsl: COMPortOpen: Library setup failure\n");
    exit(1);
  default:
    printf("tsl: COMPortOpen: unknown status\n");
    exit(1);
  }
}

void serial_halt()
{
  COMSetDtr(serial, 0);
}

void serial_resume()
{
  COMSetDtr(serial, 1);
}

char *serial_read(char *buffer, int size)
{
  char *p, c;
  int l;

  p = buffer;
  l = 0;
  for(;;) {
    int result;

    result = COMReadChar(serial, &c, NULL);
    switch (result) {
    case 0:
      break;
    case COMERR_RXOVERFLOW:
      printf("tsl: COMReadChar: receive buffer overflow\n");
      continue;
    case COM_BUFEMPTY:
      continue;
    }

    *p++ = (char) (c & 0xff);
    l++;
    if (c == '\n' || l == size-1) {
      *p++ = '\0';
      return buffer;
    }
  }
}

#else /* TSL_DJGPP */
#include <termios.h>
#include <unistd.h>

FILE *serial;

void serial_open(char *device)
{
  int fd;
  struct termios termios;

  serial = fopen(device, "r" );
  if (serial == NULL) {
    char buffer[1024];

    sprintf(buffer, "fopen: %s", device);
    perror(buffer);
    exit(1);
  }

  /* set the serial port characteristics */
  fd = fileno(serial);
  if (tcgetattr(fd, &termios) < 0) {
    perror("tcgetattr");
    exit(1);
  }

  if (cfsetospeed(&termios, B2400) < 0) {
    perror("cfsetospeed");
    exit(1);
  }

  if (cfsetispeed(&termios, B2400) < 0) {
    perror("cfsetispeed");
    exit(1);
  }

  if (tcsetattr(fd, TCSANOW, &termios) < 0) {
    perror("tcsetattr");
    exit(1);
  }
}

void serial_halt()
{
  /* not required, operating system buffers data */
}

void serial_resume(char *device)
{
  /* not required, operating system buffers data */
}

char *serial_read(char *buffer, int size)
{
  char *p;

  p = fgets(buffer, size, serial);
  if (p == NULL) {
    perror("fgets");
    exit(1);
  }

  return p;
}

#endif /* TSL_DJGPP */

int main ( int argc, char *argv[] )
{
  char *device, *p, buffer[128], *pad = "           ";
  char *name = "%4.4d%3.3d.log";
  FILE *output = NULL;
  int i, number, sample, was, seen[MAXSENSOR];
#ifdef FEATURE_CONVERSION
  int cf = 0;
#endif
  time_t now;
  struct tm *tm;
  float datum[MAXSENSOR];
  int breaker = 0;

  /* reset sensor result array */
  for (i=0; i<MAXSENSOR; i++) {
    seen[i] = 0;
    datum[i] = 0.0;
  }

  /* say g'day */
  printf("Temperature Sensor Logger, 1.4\n"
	 "Copyright (C) 2003  James Cameron (quozl@us.netrek.org)\n"
	 "%s\n\n"
"This program comes with ABSOLUTELY NO WARRANTY; for details see source.\n"
"This is free software, and you are welcome to redistribute it under certain\n"
"conditions; see source for details.\n\n", vcid);

  /* allow user to specify alternate serial port as first argument */
#ifdef TSL_CYGWIN
  device = "COM1";		/* for CYGWIN build */
#else
#ifdef TSL_DJGPP
  device = "1";			/* for DJGPP build */
#else
  device = "/dev/ttyS0";	/* for UNIX build */
#endif
#endif
  if (argc > 1) {
    device = argv[1];
    printf("Selected input serial port %s\n", device);
  }

  /* allow user to specify sample recording rate */
  sample = 30;
  if (argc > 2) {
    sample = atoi(argv[2]);
    printf("Selected sample recording rate %d seconds\n", sample);
  }

  /* allow user to specify recording file name */
  if (argc > 3) {
    name = argv[3];
    printf("Selected recording file name %s\n", name);
  }

#ifdef FEATURE_CONVERSION
  /* allow user to specify conversion */
  if (argc > 4) {
    cf = atoi(argv[4]);
    if (cf) printf("Selected conversion from celcius to fahrenheit\n");
  }
#endif

  /* open the serial port, causing DTR to be raised and the sensor to run */
  serial_open(device);
  atexit(serial_halt);

  printf("Awaiting data from microcontroller ...\r");

  /* infinite loop */
  for(;;) {

    /* get a line of data from the sensor */
    p = serial_read(buffer, 128);

    /* remove the terminating lf or cr */
    p = buffer + strlen(buffer) - 1;
    while (*p == '\r' || *p == '\n') {
      *p-- = '\0';
      if (p < buffer) break;
    }

    /* remove preceeding lf or cr */
    p = buffer;
    while (*p == '\r' || *p == '\n') p++;

    /* establish timestamp for this sample */
    now = time(NULL);
    tm = localtime(&now);

    /* check packet header for reset detection */
    if (*p == 'R') {
      printf("Microcontroller reports version %s\n\n", p+2);
#ifdef FEATURE_CONVERSION
      /* if conversion to fahrenheit is enabled in tsl, 
         check that the reset message from the microcontroller agrees,
         and if not, turn off conversion mode */
      if (cf) {
	if (strstr(p, " F")) {
	  printf("Warning: microcontroller is set to fahrenheit, disabling conversion\n\n");
	  cf = 0;
	}
      }
#endif
#ifdef FEATURE_DECIMAL_HOURS
      printf(
	     "%s+---------+--------+--------+--------+--------+\n"
	     "%s| Time    |   DS1  |   DS2  |   DS3  |   DS4  |\n"
	     "%s+---------+--------+--------+--------+--------+\n",
	     pad, pad, pad);
#else
      printf(
	     "%s+-------------------+--------+--------+--------+--------+\n"
	     "%s| Year-Day Time     |   DS1  |   DS2  |   DS3  |   DS4  |\n"
	     "%s+-------------------+--------+--------+--------+--------+\n",
	     pad, pad, pad);
#endif
      continue;
    }

    /* ignore any other packet header other than sensor reports */
    if (!isdigit(*p)) continue;

    /* decode the sensor number */
    number = atoi(p)-1;
    if (number < 0 || number > MAXSENSOR-1) continue;

    /* check for missing blank, ignore the line as corrupt */
    p++;
    if (*p != ' ') continue;

    /* check for verbose mode additional data, and skip it */
    if (strlen(p) > 9) p += 11;

    /* read and decode the data */
    p++;
    sscanf(p, "%f", &datum[number]);

#ifdef FEATURE_CONVERSION
    /* celcius to fahrenheit conversion */
    if (cf) datum[number] = datum[number] * 9.0 / 5.0 + 32.0;
#endif

    /* we've seen this sensor, start a countdown */
    seen[number] = 5;

    /* display the current samples */
#ifdef FEATURE_DECIMAL_HOURS
    printf("%s| %7.4f |", pad,
	      (float) tm->tm_hour + 
	      (float) tm->tm_min / 60.0 + 
	      (float) tm->tm_sec / 3600.0);
#else
    printf("%s| %04d-%03d %02d:%02d:%02d |", pad, tm->tm_year+1900,
	      tm->tm_yday+1, tm->tm_hour, tm->tm_min, tm->tm_sec);
#endif

    for (i=0; i<MAXSENSOR; i++) {
      if (seen[i]) {
	printf(" %6.2f |", datum[i]);
	seen[i]--;
      } else {
	printf("        |");
      }
    }
    printf("\r");

    /* save this sample to output log if we have reached sample time */
    if ((now / sample) != was)
    {
      char new[256];
      static char old[256];

      was = now / sample;

      /* create a file name */
      sprintf(new, name, tm->tm_year+1900, tm->tm_yday+1);
      if (output == NULL) {
	output = fopen(new, "a");
	if (output == NULL) { perror(new); exit(1); }
	strcpy(old, new);
      } else {
	if (strcmp(old, new)) {
	  fclose(output);
	  output = fopen(new, "a");
	  if (output == NULL) { perror(new); exit(1); }
	  strcpy(old, new);
	}
      }

      /* gnuplot deserves a blank line for a new sample range, aka run */
      if (!breaker) {
	fprintf(output, "\n");
	breaker++;
      }

      /* format the file output, comma separated value list */
#ifdef FEATURE_DECIMAL_HOURS
      fprintf(output, "%7.4f",
	      (float) tm->tm_hour + 
	      (float) tm->tm_min / 60.0 + 
	      (float) tm->tm_sec / 3600.0);
#else
      fprintf(output, "%04d,%03d,%02d,%02d,%02d", tm->tm_year+1900,
	      tm->tm_yday+1, tm->tm_hour, tm->tm_min, tm->tm_sec);
#endif
      for (i=0; i<MAXSENSOR; i++) {
	if (seen[i]) {
	  fprintf(output, ",%.2f", datum[i]);
	} else {
	  fprintf(output, ",");
	}
      }
      fprintf(output, "\n");

      /* require the operating system to flush to disk */
      fflush(output);

      /* close the file if the sample period is large enough */
      if (sample > 59) {
	fclose(output);
	output = NULL;
      }

      /* force display to new line */
      printf("\n");
    }
  }
}
