//	cpf-log.c


#define VERSION "0.92"

//	todo
//		udp broadcast

//	0.92	7/2/2005 10:24AM
//		current calc from a0-a1 instead of a1-a2
//		-S option added (similar ro -s but one volt measure0
//	0.91	26/9/2004 4:03PM
//		return from read_com port if no char available
//		-> prepare for interactive op
//	0.90	19/9/2004 9:34AM
//		for cygwin only
//		based on 0.82
//		changed to 9600 baud from 19200
//	0.82	10/7/2004 10:18PM


#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <termios.h>
#include <fcntl.h>

#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>

void quit(int);


char com_buffer[1024];
char com_port[128] = "/dev/com1";
long com_baud = 9600;

char *log_filename;
int  log_to_file = 0;
int  raw_mode = 0;
int  count_mode = 0;
long rec_count = 0L;
int  log_interval = 1;
long log_count = 0L;
long log_time;
int  log_day, log_hour, log_minute, log_second;
int  do_again = 1;
int  log_mode = 255;
int  debug_mode = 0;
int  current_sensing_mode = 0;

double vref = 5.0;		// this changes
int    adc_max = 4092;
int    lux_max = 0;
int    lux_current = 4092;
double current_sense = 0.16;

int  com_tty = -1;
FILE *log_file;
int  com_port_open = 0;

char time_buffer[1024];
long starting_time, ending_time;


speed_t tty_speed(long tty_baud)
{
	switch (tty_baud)
	{
		case 9600:
			    return B9600;
			    break;
		case 19200:
			    return B19200;
			    break;
		case 38400:
			    return B38400;
			    break;
		case 57600:
			    return B57600;
			    break;
		case 115200:
			    return B115200;
			    break;
		default:
			    return B19200;
			    break;
	}
}

struct termios oldtio, newtio;

void open_com_port(void)
{
	int flags = O_RDWR | O_NOCTTY | O_NONBLOCK;
	speed_t baudrate;

	if ((com_tty = open(com_port, flags)) == -1)
	{
		fprintf(stderr, "Error on opening device.\n%s\n", strerror(errno));
		quit(1);
	}

	bzero(&newtio, sizeof(oldtio));
	bzero(&newtio, sizeof(newtio));
	tcgetattr(com_tty, &oldtio);
	if (tcgetattr(com_tty, &newtio) < 0)
	{
		fprintf(stderr,"Error in getting device attributes: %s\n",strerror(errno));
		quit(1);
	}

	baudrate = tty_speed(com_baud);

	cfsetispeed(&newtio, baudrate);		// input at baudrate
	cfsetospeed(&newtio, baudrate);		// ouput at baudrate

	newtio.c_cflag |= (CLOCAL | CREAD);	// enable
	newtio.c_cflag &= ~PARENB;		// 8N1
	newtio.c_cflag &= ~CSTOPB;
	newtio.c_cflag &= ~CSIZE;
	newtio.c_cflag |= CS8;

	tcflush(com_tty, TCIOFLUSH);		//clear serial tty

	if (tcsetattr(com_tty, TCSANOW, &newtio) < 0)
	{ //set now
		fprintf(stderr,"Error in setting device attributes: %s\n", strerror(errno));
		quit(1);
	}

	com_port_open = 1;
}

int buffer_ready = -1;

void read_com_port(void)
{
	char buffer;
	static int  counter = 0;
	int  status;
	char *cptr;

	if (buffer_ready == -1)
	{
		memset(com_buffer, 0, strlen(com_buffer));
		buffer_ready = 0;
		counter = 0;
	}

	status = read(com_tty, &buffer, 1);

	if (status == -1 && errno == EAGAIN)
		return;
	else if (status == -1)
	{
		fprintf(stderr, "Error on reading from device: %s\n", strerror(errno));
		exit(1);
	}
	if ( (cptr = strchr("0123456789-:.,\r\n", buffer)) == NULL)
		return;
	if (buffer != '\n' && buffer != '\r')
		com_buffer[counter++] = buffer;
	if (buffer =='\n' || counter >= 1023)
		buffer_ready = 1;
}

void close_com_port(void)
{
	cfsetispeed(&newtio, B300);		// input at baudrate
	cfsetospeed(&newtio, B300);		// ouput at baudrate
	tcsetattr(com_tty, TCSANOW, &oldtio);

	if (com_port_open)
	{
		close(com_tty);
		com_tty = -1;
		com_port_open = 0;
	}
}


void time_stamp(int ending)
{
	time_t t;
	long   elapsed_time;
	double drift;

	memset(time_buffer, 0, strlen(time_buffer));
	t = time(NULL);
	strftime(time_buffer, 1023, "%Y-%m-%d,%H:%M:%S", localtime(&t));

	if (ending && log_mode)
	{
		ending_time = t;
		elapsed_time = ending_time - starting_time;
		drift = (double)(rec_count -1 - elapsed_time) / elapsed_time * 100;

		printf("time = %s (%ld seconds elapsed)\n", time_buffer, elapsed_time);
		printf("record = %ld (%ld of %ld seconds %.2f%% drifted)\n", rec_count, rec_count -1 - elapsed_time, elapsed_time, drift);
		if (log_to_file)
		{
			printf("logged = %ld records at %d seconds interval\n", log_count, log_interval);
		}
		printf("\n\n");
		fflush(stdout);

		if (log_to_file)
		{
			fprintf(log_file, "time = %s (%ld seconds elapsed)\n", time_buffer, elapsed_time);
			fprintf(log_file, "record = %ld (%ld of %ld seconds %.2f%% drifted)\n", rec_count, rec_count -1 - elapsed_time, elapsed_time, drift);
			fprintf(log_file, "logged = %ld records at %d seconds interval\n", log_count, log_interval);
			fprintf(log_file, "\n\n");
			fflush(log_file);
		}
	}
	else
	{
		starting_time = t;

		printf("time = %s\n", time_buffer);
		if (debug_mode)
		{
			if (log_to_file)
				printf("log file = %s (%d sec interval)\n", log_filename, log_interval);
			if (raw_mode == 0)
			{
				printf("voltage reference = %.3f\n", vref);
				printf("adc max counter = %d\n", adc_max);
				if (lux_max)
					printf("light max counter = %d\n", lux_max);
				else
					printf("light max counter = %d\n", lux_current);
				printf("current sense resistor = %.3f\n", current_sense);
			}
		}
		fflush(stdout);

		if (log_to_file)
		{
			fprintf(log_file, "# time = %s\n", time_buffer);
			fprintf(log_file, "# log file = %s (%d sec interval)\n", log_filename, log_interval);
			if (raw_mode == 0)
			{
				fprintf(log_file, "# voltage reference = %.3f\n", vref);
				fprintf(log_file, "# adc max counter = %d\n", adc_max);
				if (lux_max)
					fprintf(log_file, "# light max counter = %d\n", lux_max);
				else
					fprintf(log_file, "# light max counter = %d\n", lux_current);
				fprintf(log_file, "# current sense resistor = %.3f\n", current_sense);
				fprintf(log_file, "\n");
				fprintf(log_file, "Time,A1,A2,A3,A4,V1,V2,V3,V4,C,L\n");
			}
			else
			{
				fprintf(log_file, "\n");
				fprintf(log_file, "Time,A1,A2,A3,A4\n");
			}
			fflush(log_file);
		}
	}
}


void open_log_file(void)
{
	if (log_to_file)
		if ((log_file = fopen(log_filename, "a")) == NULL)
		{
			fprintf(stderr, "error opening %s\n", log_filename);
			log_to_file = 0;
		}
}

void close_log_file(void)
{
	if (log_to_file)
	{
		fclose(log_file);
		log_to_file = 0;
	}
}

int    ai0_val, ai1_val, ai2_val, ai3_val;
double voltage0, voltage1, voltage2, voltage3;
double current_flow, lux_perc;


double ai_voltage(int adc_val)
{
	double voltage = (double)adc_val;
	return voltage * vref / adc_max;
}

void process_line()
{
	com_buffer[10] = '\0';
	com_buffer[15] = '\0';
	com_buffer[20] = '\0';
	com_buffer[25] = '\0';

	ai0_val = atoi(&com_buffer[11]);
	ai1_val = atoi(&com_buffer[16]);
	ai2_val = atoi(&com_buffer[21]);
	ai3_val = atoi(&com_buffer[26]);

	if (strcmp(com_buffer, "0.00:00:00") == 0)
	{
		if (lux_max == 0)
			lux_current = ai3_val;
		if (log_mode == 0)
			time_stamp(0);
		log_mode = 1;
		rec_count = 0L;
	}

	voltage0 = ai_voltage(ai0_val);
	voltage1 = ai_voltage(ai1_val);
	voltage2 = ai_voltage(ai2_val);
	voltage3 = ai_voltage(ai3_val);

	if (strcmp(com_buffer, "----------") == 0)
	{
		if (lux_max)
			lux_perc = ai3_val * 100 / lux_max;
		else
			lux_perc = ai3_val * 100 / adc_max;
		if (log_mode == 1)
			time_stamp(1);
		log_mode = 0;

		log_time = -1L;
		log_day = log_hour = log_minute = log_second = 0;
	}
	else
	{
		if (lux_max)
			lux_perc = ai3_val * 100 / lux_max;
		else
			lux_perc = ai3_val * 100 / lux_current;

		if (sscanf(com_buffer, "%d.%02d:%02d:%02d,", &log_day, &log_hour, &log_minute, &log_second) == 4)
		{
			log_time = (long)log_day * 24 * 60 * 60;
			log_time += (long)log_hour * 60 * 60;
			log_time += (long)log_minute * 60;
			log_time += (long)log_second;
		}
		else
		{
			log_time = -1L;
			log_day = log_hour = log_minute = log_second = 0;
		}
	}

	if (current_sensing_mode)
		current_flow = voltage1 / current_sense;
	else
		current_flow = fabs(voltage0 - voltage1) / current_sense;
}

void print_line(void)
{
	if (raw_mode)
		sprintf(com_buffer+10, ",%04lu,%04lu,%04lu,%04lu", ai0_val, ai1_val, ai2_val, ai3_val);
	else
		sprintf(com_buffer+10, "  %04lu %04lu %04lu %04lu  %5.3f %5.3f %5.3f %5.3f  %5.3f %.1f",
			ai0_val, ai1_val, ai2_val, ai3_val,  voltage0, voltage1, voltage2, voltage3,  current_flow, lux_perc);
	printf("%s\n", com_buffer);
	fflush(stdout);
}

void log_line(void)
{
	if ( (log_time == -1L) || (log_time % log_interval != 0) )
		return;

	if (raw_mode)
	{
		if (count_mode)
			sprintf(com_buffer, "%010lu,%04lu,%04lu,%04lu,%04lu", log_time, ai0_val, ai1_val, ai2_val, ai3_val);
	}
	else
	{
		if (count_mode)
			sprintf(com_buffer, "%010lu,%04lu,%04lu,%04lu,%04lu,%5.3f,%5.3f,%5.3f,%5.3f,%5.3f,%.1f",
				log_time, ai0_val, ai1_val, ai2_val, ai3_val,  voltage0, voltage1, voltage2, voltage3,  current_flow, lux_perc);
		else
			sprintf(com_buffer+10, ",%04lu,%04lu,%04lu,%04lu,%5.3f,%5.3f,%5.3f,%5.3f,%5.3f,%.1f",
				ai0_val, ai1_val, ai2_val, ai3_val,  voltage0, voltage1, voltage2, voltage3,  current_flow, lux_perc);
	}
	fprintf(log_file, "%s\n", com_buffer);
	fflush(log_file);
	log_count++;
}

void quit(int code)
{
	close_com_port();
	close_log_file();

	fprintf(stderr, "Press Enter to exit...");
	fgetc(stdin);

	exit(code);
}

static void break_handler()
{
	do_again = 0;
}

void usage(void)
{
	printf("usage:   cpf-log [options [parameter]] [log_filename]\n");
	printf("options:      -h      display this text\n");
	printf("              -p i    com port (1)\n");
	printf("              -b l    baud rate (9600)\n");
	printf("              -r      raw output mode (vs processed output)\n");
	printf("              -c      count mode in log file\n");
	printf("              -t i    count between logging in file (1)\n");
	printf("              -v d    reference voltage (5.0)\n");
	printf("              -a i    adc max count (4096)\n");
	printf("              -l i    light adc max count (0)\n");
	printf("              -s d    current sense resistor (0.16)\n");
	printf("              -S d    current sense resistor (ch2 only)\n");
	fflush(stdout);

	quit(0);
}

int main(int argc, char **argv)
{
	int i, opt;

	printf("\ncpf-log " VERSION " (C) djpark@astsb.info (" __DATE__ ")\n\n");

	while ((opt = getopt(argc, argv, "p:b:rct:v:a:s:S:l:d")) > 0)
	{
		switch (opt)
		{
			case 'p':	if (strlen(optarg) == 1)
						sprintf(com_port, "com%s", optarg);
					else
						strcpy(com_port, optarg);
					break;
			case 'b':	if (strlen(optarg) > 0)
						com_baud = atol(optarg);
					break;
			case 'r':	raw_mode = 1;
					break;
			case 'c':	count_mode = 1;
					break;
			case 't':	if (strlen(optarg) > 0)
					{
						log_interval = atoi(optarg);
						if (log_interval < 1 || log_interval > 3600)
							log_interval = 1;
					}
					break;
			case 'v':	if (strlen(optarg) > 0)
						vref = atof(optarg);
					break;
			case 'a':	if (strlen(optarg) > 0)
						adc_max = atoi(optarg);
					break;
			case 'l':	if (strlen(optarg) > 0)
						lux_max = atoi(optarg);
					break;
			case 'S':	current_sensing_mode = 1;
			case 's':	if (strlen(optarg) > 0)
					{
						current_sense = atof(optarg);
						if (current_sense < 0.05)
							current_sense = 0.16;
					}
					break;
			case 'd':	debug_mode = 1;
					break;
			default:	usage();
					break;
		}
	}

	if (optind < argc)
	{
		if (strlen(argv[optind]) > 0)
		{
			log_filename = argv[optind];
			log_to_file = 1;
		}
	}

	if (log_to_file)
		open_log_file();

	signal(SIGHUP, break_handler);
	signal(SIGINT, break_handler);
	signal(SIGQUIT, break_handler);
	signal(SIGILL, break_handler);
	signal(SIGABRT, break_handler);
	signal(SIGTERM, break_handler);

	open_com_port();

	if (debug_mode)
	{
		printf("com port = %s\n", com_port);
		printf("baud rate = %ld\n", com_baud);
		printf("\n");
	}

	time_stamp(0);

	while (do_again)
	{
		while (buffer_ready != 1)
		{
			// check kbd for <esc><esc>
			read_com_port();
		}

		buffer_ready = -1;
		if (strlen(com_buffer) == 30)
		{
			process_line();
			print_line();
			if (log_to_file)
				log_line();
			rec_count++;
		}
	}

	time_stamp(1);

	fflush(stdout);
	fprintf(stderr, "processing stopped, exit\n");
	fflush(stderr);

	close_com_port();
	if (log_to_file)
		close_log_file();

	quit(0);
	return 0;	// never come here
}
