/*
 *  crusader100_read_rss
 *
 *  compile: gcc -Wall -D_DEBUG crusader100_read_rss.c -o crusader100_read_rss
 *
 *  Licence: GNU GPL ver.2
 *
 *  Changelog:
 *  2007-01-27/Libor  oprava cteni RSS<100, tj. dvoucifernych
 *  2008-05-23/Libor  zacatek prace na podpore Crusader Aggregator 100M
 *  2008-06-07/Libor  prvni verze se zakladni funkcnosti
 *  2008-07-10/Libor  cte krome hodnot registru vsechny veliciny
 *  2008-07-13/Libor  oprava neinicializovane promenne pro soucet RSS
 *
 */


#include <assert.h>
#include <stdio.h>          /* Standard input/output definitions */
#include <sys/types.h>      
#include <sys/stat.h>       
#include <fcntl.h>          /* File control definitions */
#include <termios.h>
#include <unistd.h>         /* UNIX standard function definitions */
#include <stdlib.h>
#include <limits.h>         /* INT_MAX and others */
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <ctype.h>			/* isdigit() a.s.o. */
#include <time.h>


static char   *jmeno_seriaku = "";
static time_t  cilovy_cas = 0;
static time_t  cas_startu = 0;

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

#define FALSE 0
#define TRUE 1

#ifdef _DEBUG
#	define DEBUG(level, fmt, args...) fprintf(stderr, fmt, ## args)
#	define DBG(fmt, args...) DEBUG(L_DBG, "DEBUG: " fmt, ## args)
#else
#	define DEBUG(level, fmt, args...)
# 	define DBG(fmt, args...)
#endif

#define L_ALERT  -3
#define L_CRIT   -2
#define L_ERR    -1
#define L_DEFAULT 0
#define L_WARN    1
#define L_NOTICE  2
#define L_INFO    3
#define L_DBG     4

int otevri_seriak(char *devname)
{
	int fd;
	/*
	   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.
	 */
	fd = open(devname, O_RDWR | O_NOCTTY | O_NDELAY); 
	if (fd < 0) { perror(devname); }
	return fd;
}


int nastav_seriak(int fd, struct termios *oldtio)
{
	struct termios newtio;

	tcgetattr(fd, oldtio); /* save current serial port settings */
	memset(&newtio, 0, 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 = 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(fd, TCIFLUSH);
	tcsetattr(fd,TCSANOW, &newtio);

	return 0;
}

int restore_seriak(int fd, struct termios *stored_tio)
{
	/* restore the old port settings */
	tcsetattr(fd, TCSANOW, stored_tio);
	return 0;
}

void print_usage()
{
	printf("\nusage: seriak <jmeno seriaku> {<cilovy cas> | +<offset>}\n\n");
}

void check_params( int argc, char *argv[])
{
    int   relative = 0;
    char *nptr = NULL, *endptr = NULL;
    
    if ( argc != 3 || (argc == 3 && ( strcmp("-h", argv[1])==0 || strcmp("--help", argv[1])==0 )) ) {
        print_usage();
        exit(EXIT_FAILURE);
    }
    jmeno_seriaku = argv[1];
    
    nptr = argv[2];
    if ( argv[2][0] == '+' ) {  /* relativni cas */
        if ( strlen(argv[2]) < 1 ) {
            print_usage();
            exit(EXIT_FAILURE);
        }
        nptr++;
        relative = 1;
    }
    cilovy_cas = strtol(argv[2], &endptr, 10);

#ifdef _DEBUG
    fprintf (stderr, "arg[2]: %s, endptr: %p, cilovy_cas: %ld\n", argv[2], endptr, cilovy_cas);
#endif

    if ( !(argv[2] != '\0' && *endptr == '\0') )
    {
        fprintf(stderr, "ERROR: spatny tvar ciloveho casu");
        print_usage();
        exit(EXIT_FAILURE);
    }
    
    if (relative) {
        cilovy_cas += cas_startu;
    }
}

/**
 *  Hleda zda se v prijimacim bufferu buff naleza ocekavany retezec exp_res.
 * @param buf pointer na prijimaci buffer
 * @param buflen delka prijimaciho bufferu
 * @param exp_res ocekavany vysledek, ktery hledame ve vstupnim bufferu
 * @return 0 kdyz je to OK, jinak 1.
 *
 */
int check_response(char *buf, int buflen, char *exp_res)
{
	return 0;
}

/**
 * vypise na stderr obsah daneho bufferu jako text i jako hexakody.
 * misto tisknutelnych znaku se vypisuje tecka
 * @param buf ukazatel na buffer
 * @param bufsize velikost bufferu
 * @param pocet pocet bajtu, ktere se maji vypsat
 */
void print_debug_buffer(char *buf, size_t bufsize, size_t pocet)
{
	time_t cas;
	int i;
	
	assert(bufsize >= 0);
	assert(buf != NULL);
	
	DBG("print_debug_buffer(bufsize=%d, pocet=%d)\n", bufsize, pocet);
	cas = time(0);
	if (pocet > bufsize) {
		fprintf(stderr, "prilis maly buffer, snizuji pocet = bufsize\n");
		pocet = bufsize;
	}
	
	fprintf(stderr, "DEBUG: cas: %ld, buf: '", cas);
	for ( i = 0; i < pocet; ++i )
	{
		if (buf[i] >= ' ') {
			fprintf(stderr, "%c", buf[i]);
		} else {
			fprintf(stderr, ".");
		}
	}
	fprintf(stderr, "'  hex: ");
	for ( i = 0; i < pocet; ++i ) {
		fprintf(stderr, "%02x ", buf[i]);
	}
	fprintf(stderr, "\n");
}

// TODO check HUGE_VAL, ERANGE, see strtod(3)
int get_double(char *str, int len, double *value)
{
	double v;
	char *eptr;
	
	DBG("get_double(str='%.*s', len=%d)\n", len, str, len);
	eptr = NULL;
	v = strtod(str, &eptr);
	if (*eptr == '\0') {
		DBG("get_double: value = %6.2f\n", v);
		*value = v;
		return 0;
	}
	else {
		DBG("get_double: error in conversion\n");
		*value = 0;
		return -1;
	}
}
int get_long(char *str, int len, long *value)
{
	long v;
	char *eptr;
	
	DBG("get_long(str='%.*s', len=%d)\n", len, str, len);
	eptr = NULL;
	v = strtol(str, &eptr, 10);
	if (*eptr == '\0') {
		DBG("get_long: value = %ld\n", v);
		*value = v;
		return 0;
	}
	else {
		DBG("get_long: error in conversion\n");
		*value = 0;
		return -1;
	}
}

#define CRS_NAMERENO_RSS	1 << 0
#define CRS_NAMERENO_P_LSR	1 << 1
#define CRS_NAMERENO_I_BIAS	1 << 2
#define CRS_NAMERENO_U_APD	1 << 3
#define CRS_NAMERENO_T_APD	1 << 4
#define CRS_NAMERENO_T_CPU	1 << 5
#define CRS_NAMERENO_L	1 << 6
#define CRS_NAMERENO_HEAT	1 << 7

typedef enum {
	RSS_MIN,
	RSS_MAX,
	RSS_SUM,
	RSS_CNT,
	RSS_LAST
} rss_names_t;

/**
  *  Struktura obsahujici prectene udaje. Pokud byl behem merici periody nektery udaj precten vicekrat,
  *  obsahuje naposledy prectenou hodnotu. To se netyka RSS (Received Signal Strength), kde se uchovava
  *  ctene minimum, maximim, soucet a pocet ctenych hodnot. Ze souctu se pak pocita prumerna hodnota.
  *
  */
typedef struct {
	int    namereno;		/// bitove pole obsahujici informaci o tom, ktere hodnoty jsou skutecne zmerene
	long   rss[RSS_LAST];	/// bez prefixu
	double laser_power;		/// Pl [mW], vykon laseru
	double laser_bias;		/// Ib [mA], bias proud laserem
	double apd_voltage;		/// Ua [V]
	double apd_temp;		/// Ta [C]
	double apd_ureg;		/// Ur bezrozmerne, hodnota registru menice APD
	double cpu_temp;		/// T  [C]
	int    da_reg[2];		/// L  bezrozmerne, hodnoty registru D/A prevodniku
} crusader_meas_t;

void clear_value(char* varr, size_t va_size)
{
	memset(varr, 0, va_size);
}

#define KONECNY_STAV	0x1000

/*
 * Stavy prijimaciho automatu
 */
typedef enum {
	ST_START,		/// pocatecni stav automatu, zacina parsovani radky, obvykle po ST_NL nebo kdyz se ocekavaji dalsi hodnoty na radce
	ST_NL,			/// '\n' new line, synchronizuje automat, prechoazi do ST_START
	ST_KOMENTAR, 	/// NL -> '!', oznacuje komentar do dalsiho NL. Komentar '!OK' ma specialni vyznam
	ST_PLUS,
	ST_RSS,			/// NL -> ' ', zacina cteni RSS, akceptuje cislice 0-9, jiny znak trigruje prechod do ST_KOMENTAR,
					///            tj. ignorujeme RSS protistrany a bargraf
	ST_U,
	ST_U_A,
	ST_U_APD,
	ST_U_R,
	ST_U_REG,
	ST_T,
	ST_T_A,
	ST_T_APD,
	ST_T_CPU,
	ST_P,
	ST_PL,
	ST_PLASER,
	ST_I,
	ST_IB,
	ST_IBIAS,
	ST_H,
	ST_L,
	ST_L1,
	ST_L2,
	ST_ERROR = KONECNY_STAV | 500
	
} state_t;

int sezer_seriak(int seriak)
{
	ssize_t precteno;
	time_t cas = 0;
	int chyba;
	static char buf[129];
	static size_t bufsize = sizeof(buf);
	int i = 0;
	char *p;

	DBG("sezer_seriak(fd=%d)\n", seriak);	
	precteno = read(seriak, buf, bufsize-1);
	DBG("sezer_seriak: precteno = %d\n", precteno);
	DBG("sezer_seriak: cas = %lu, cilovy_cas = %lu\n", cas, cilovy_cas);
	while (precteno && cas < cilovy_cas) {
		i++;
		cas = time(0);
		DBG("sezer_seriak: i=%2d precteno = %d\n", i, precteno);
		DBG("sezer_seriak: i=%2d cas = %lu, cilovy_cas = %lu\n", i, cas, cilovy_cas);
		if (precteno < 0) {
			DBG("if (precteno < 0)...\n");
			chyba = errno;
			if (chyba == EAGAIN) {
				fprintf(stderr, "continuing...\n");
				sleep(1);
				goto loop_end;
			}
			perror("chyba cteni: ");
			fprintf(stderr, "chyba = %d\n", chyba);
			fprintf(stderr, "EAGAIN = %d\n", EAGAIN);
			goto err;
		}
		if (precteno > 0) {
			for (p = buf; p < buf+precteno; p++) {
				if (*p == '+') goto end;
			}
		}
#ifdef _DEBUG
		print_debug_buffer(buf, bufsize, precteno);
#endif
loop_end:
        	precteno = read(seriak, buf, bufsize-1);
	}
end:
	DBG("sezer_seriak(), done\n");
	return 0;
err:
	DBG("sezer_seriak(), fail\n");
	return -1;
}

int main(int argc, char *argv[])
{
	struct termios old_tio;
	int seriak = -1;
	int chyba;
	ssize_t precteno;
	ssize_t ibuflen;
	char buf[129];
	size_t bufsize = sizeof(buf);
	time_t cas = 0;
	char *p;

	crusader_meas_t mereni;
	mereni.rss[RSS_MIN] = INT_MAX;
	mereni.rss[RSS_MAX] = 0;
	mereni.rss[RSS_CNT] = 0;
	mereni.rss[RSS_SUM] = 0;
	mereni.laser_power  = 0;
	mereni.laser_bias   = 0;
	mereni.apd_voltage  = 0;
	mereni.apd_temp     = 0;
	mereni.cpu_temp     = 0;
	long rss_avg;
	
	state_t state, next_state;
	int vidx;	/// index cislice hodnoty (value)
	char val_arr[16];
	char state_text[128];
	int state_text_len;
	int clear_state_text = 1;
	long int long_val;
	double double_val;
	long long loopcnt = 0;
	int posledni_kus = 0;
	int temp_sign = 1;

	cas_startu = time(0);
	DEBUG(L_INFO, "Cas startu: %ld - %s", cas_startu, ctime(&cas_startu));

	check_params(argc, argv);

	DEBUG(L_INFO, "Cilovy cas: %ld - %s", cilovy_cas, ctime(&cilovy_cas));

	seriak = otevri_seriak(jmeno_seriaku);
	if ( seriak == -1 )
	{
		char msg[256];
		snprintf(msg, sizeof(msg)-1, "Nejde otevrit seriak (%s)", jmeno_seriaku);
		msg[255] = '\0';
		perror(msg);
		free(msg);
		goto error1;
	}
	nastav_seriak(seriak, &old_tio);
	DBG("MAIN: write 'Z'\n");
	write(seriak, "Z\r", 2);
	sezer_seriak(seriak);
	
	DBG("MAIN: write 'E0'\n");
	write(seriak, "E0\r", 3);
	sezer_seriak(seriak);
	
	write(seriak, "#1\r", 3);

	clear_value(val_arr, sizeof(val_arr));

	state_text_len = 0;
	memset(state_text, 0, sizeof(state_text));
	clear_state_text = 1;
	for ( ; cas < cilovy_cas && posledni_kus == 0; cas = time(0) )
	{
		precteno = read(seriak, buf, bufsize-1);    /* one character less for terminating '\0' */
		if (precteno < 0) {
			chyba = errno;
			if (chyba == EAGAIN) {
				DEBUG(L_INFO, "continuing...\n");
				sleep(1);
				continue;
			}
			perror("chyba cteni: ");
			fprintf(stderr, "chyba = %d\n", chyba);
			fprintf(stderr, "EAGAIN = %d\n", EAGAIN);
			goto error1;
		}

// pouze pri cteni souboru
//		if (precteno != bufsize-1)
//			posledni_kus = 1;
		
#ifdef _DEBUG
			fprintf(stderr, "Precteno: %d\n", precteno);
			print_debug_buffer(buf, bufsize, precteno);
#endif
		
		ibuflen = precteno;
		p = buf;
		state = ST_START;		/// zaciname s cistym stitem, tj. jakoby na novem radku
		next_state = ST_ERROR;	/// pokud se nic nestane, skoncime s chybou
		while (ibuflen > 0)
		{
			loopcnt++;
			assert(state_text_len >= 0);
			assert(state_text_len < sizeof(state_text));
			
			state_text[state_text_len] = *p;
			state_text_len++;
		
			switch (state) {
			case ST_START:
				DBG("ST_START\n");
			case ST_NL:
				DBG("ST_NL\n");
				temp_sign = 1;  // pokud nenajdeme minus, hodnota je kladna
				vidx = 0;
				switch (*p) {
					case '!':
						next_state = ST_KOMENTAR;
						break;
					case '+':
						next_state = ST_PLUS;
						break;
					case 0x0a:
						next_state = ST_NL;
						break;
					default:
						next_state = ST_ERROR;
				}
				break;
			case ST_ERROR:
				DBG("ST_ERROR\n");
			case ST_KOMENTAR:
				DBG("ST_KOMENTAR\n");
				if (*p == 0x0a)
					next_state = ST_NL;
				else
					next_state = ST_KOMENTAR;
				break;
			case ST_PLUS:
				DBG("ST_PLUS\n");
				switch (*p) {
				case ' ':
					next_state = ST_RSS;
					break;;
				case 'U':
					next_state = ST_U;
					break;
				case 'P':
					next_state = ST_P;
					break;
				case 'T':
					next_state = ST_T;
					break;
				case 'I':
					next_state = ST_I;
					break;
				case 'L':
					next_state = ST_L;
					break;
				case 'H':
					next_state = ST_H;
					break;
				default:
					next_state = ST_ERROR;
				}
				break;
			case ST_RSS:
				DBG("ST_RSS\n");
				// ignore spaces
				if (*p == ' ' && state_text_len == 1) {
					clear_state_text = 1;
					break;
				}
				// do not read rss yet, wait for space, grr!!!
				// TODO je potreba vyresit cteni RSS, ktera ma na obou stranach mezeru
				//      takze ji neni mozne jednoduse bezstavove parsovat
				//      resenim bude asi dat nove stavy RSSM, M jako mezera,
				//      RSSMC, C jako cifra a RSSMCM, M jako mezera.
				//      kdyz se precte druha mezera, prevede se state_text string na cislo
				if (isdigit(*p)) break;
				
				DBG("ST_RSS: *p = '%c'\n", *p);
				state_text[state_text_len-1] = '\0';
				if (get_long(state_text, state_text_len, &long_val) < 0) {
					fprintf(stderr, "ERROR in conversion, exiting.\n");
					exit(1);
				}
				mereni.rss[RSS_SUM] += long_val;
				mereni.rss[RSS_CNT]++;
				if (long_val < mereni.rss[RSS_MIN]) mereni.rss[RSS_MIN] = long_val;
				if (long_val > mereni.rss[RSS_MAX]) mereni.rss[RSS_MAX] = long_val;
				DBG("RSS: cnt=%ld  curr=%ld min=%ld  max=%ld  sum=%ld\n", mereni.rss[RSS_CNT], long_val, mereni.rss[RSS_MIN], mereni.rss[RSS_MAX], mereni.rss[RSS_SUM]);
				next_state = ST_KOMENTAR;
				break;
			case ST_PLASER:
				DBG("ST_PLASER\n");
				// ignore spaces
				if (*p == ' ' && state_text_len == 1) {
					clear_state_text = 1;
					break;
				}
				// hodnota je float
				// TODO kontrolovat pritomnost pouze jedne tecky
				if (isdigit(*p) || *p == '.') break;
				
				DBG("ST_PLASER: *p = '%c'\n", *p);
				state_text[state_text_len-1] = '\0';
				if (get_double(state_text, state_text_len, &double_val) < 0) {
					fprintf(stderr, "ERROR in conversion, exiting.\n");
					exit(1);
				}
				mereni.laser_power = double_val;
				// jednotky ignorujeme, stejne to jsou vzdy mW
				next_state = ST_KOMENTAR;
				break;
			case ST_IBIAS:
				DBG("ST_IBIAS\n");
				// ignore spaces
				if (*p == ' ' && state_text_len == 1) {
					clear_state_text = 1;
					break;
				}
				// hodnota je float
				// TODO kontrolovat pritomnost pouze jedne tecky
				if (isdigit(*p) || *p == '.') break;
				
				DBG("ST_IBIAS: *p = '%c'\n", *p);
				state_text[state_text_len-1] = '\0';
				if (get_double(state_text, state_text_len, &double_val) < 0) {
					fprintf(stderr, "ERROR in conversion, exiting.\n");
					exit(1);
				}
				mereni.laser_bias = double_val;
				// jednotky ignorujeme, stejne to jsou vzdy mA
				next_state = ST_KOMENTAR;
				break;
			case ST_T_APD:
				DBG("ST_T_APD\n");
				// ignore spaces
				if (*p == ' ' && state_text_len == 1) {
					clear_state_text = 1;
					break;
				}
				// hodnota muze byt zaporna
				if (*p == '-') {
					DBG("ST_T_APD: MINUS\n");
					temp_sign = -1;
					clear_state_text = 1;
					break;
				}
				// hodnota je float
				if (isdigit(*p) || *p == '.') break;
				
				DBG("ST_T_APD: *p = '%c'\n", *p);
				state_text[state_text_len-1] = '\0';
				if (get_double(state_text, state_text_len, &double_val) < 0) {
					fprintf(stderr, "ERROR in conversion, exiting.\n");
					exit(1);
				}
				mereni.apd_temp = double_val * temp_sign;
				
				// jednotky ignorujeme, stejne to jsou vzdy 'C'
				next_state = ST_KOMENTAR;
				break;
			case ST_T_CPU:
				DBG("ST_T_CPU\n");
				// ignore spaces
				if (*p == ' ' && state_text_len == 1) {
					clear_state_text = 1;
					break;
				}
				// hodnota muze byt zaporna
				if (*p == '-') {
					DBG("ST_T_CPU: MINUS\n");
					temp_sign = -1;
					clear_state_text = 1;
					break;
				}
				// hodnota je float
				if (isdigit(*p) || *p == '.') break;
				
				DBG("ST_T_CPU: *p = '%c'\n", *p);
				state_text[state_text_len-1] = '\0';
				if (get_double(state_text, state_text_len, &double_val) < 0) {
					fprintf(stderr, "ERROR in conversion, exiting.\n");
					exit(1);
				}
				mereni.cpu_temp = double_val * temp_sign;
				// jednotky ignorujeme, stejne to jsou vzdy 'C'
				next_state = ST_KOMENTAR;
				break;
			case ST_U_APD:
				DBG("ST_U_APD\n");
				// ignore spaces
				if (*p == ' ' && state_text_len == 1) {
					clear_state_text = 1;
					break;
				}
				// hodnota je float
				if (isdigit(*p) || *p == '.') break;
				
				DBG("ST_U_APD: *p = '%c'\n", *p);
				state_text[state_text_len-1] = '\0';
				if (get_double(state_text, state_text_len, &double_val) < 0) {
					fprintf(stderr, "ERROR in conversion, exiting.\n");
					exit(1);
				}
				mereni.apd_voltage = double_val;
				// jednotky ignorujeme, stejne to jsou vzdy mA
				next_state = ST_KOMENTAR;
				break;
			case ST_U:
				DBG("ST_U\n");
				switch (*p) {
					case 'a':
						next_state = ST_U_A;
						break;
					case 'r':
						next_state = ST_U_R;
						break;
					default:
						next_state = ST_ERROR;
				}
				break;
			case ST_P:
				DBG("ST_P\n");
				switch (*p) {
					case 'l':
						next_state = ST_PL;
						break;
					default:
						next_state = ST_ERROR;
				}
				break;
			case ST_PL:
				DBG("ST_PL\n");
				switch (*p) {
					case '=':
						next_state = ST_PLASER;
						break;
					default:
						next_state = ST_ERROR;
				}
				break;
			case ST_T:
				DBG("ST_T\n");
				switch (*p) {
					case '=':
						next_state = ST_T_CPU;
						break;
					case 'a':
						next_state = ST_T_A;
						break;
					default:
						next_state = ST_ERROR;
				}
				break;
			case ST_T_A:
				DBG("ST_T_TA\n");
				switch (*p) {
					case '=':
						next_state = ST_T_APD;
						break;
					default:
						next_state = ST_ERROR;
				}
				break;
			case ST_I:
				DBG("ST_I\n");
				switch (*p) {
					case 'b':
						next_state = ST_IB;
						break;
					default:
						next_state = ST_ERROR;
				}
				break;
			case ST_IB:
				DBG("ST_IB\n");
				switch (*p) {
					case '=':
						next_state = ST_IBIAS;
						break;
					default:
						next_state = ST_ERROR;
				}
				break;
			case ST_U_A:
				DBG("ST_U_A\n");
				switch (*p) {
					case '=':
						next_state = ST_U_APD;
						break;
					default:
						next_state = ST_ERROR;
				}
				break;
			case ST_U_R:
				DBG("ST_U_R\n");
				switch (*p) {
					case '=':
						next_state = ST_U_REG;
						break;
					default:
						next_state = ST_ERROR;
				}
				break;
			case ST_L:
				DBG("ST_L\n");
			case ST_H:
				DBG("ST_H\n");
				next_state = ST_KOMENTAR;
				break;
			default:
				DBG("default\n");
				/* nyni H, L */
				next_state = ST_ERROR;
			}
			if (state != next_state || clear_state_text == 1) {
				state_text_len = 0;
				clear_state_text = 0;
			}
			state = next_state;
			ibuflen--;
			p++;
#ifdef _DEBUG
			fprintf(stderr, "state_text: ");
			print_debug_buffer(state_text, sizeof(state_text), state_text_len);
#endif

        }
		loopcnt++;
    }
//	restore_seriak(seriak, &old_tio);
	close(seriak);


	rss_avg = mereni.rss[RSS_CNT]>0 ? mereni.rss[RSS_SUM]/mereni.rss[RSS_CNT] : -1;

	fprintf(stderr, "Number of received valid values: %ld\n", mereni.rss[RSS_CNT]);
	fprintf(stderr, "Total sum of RSS: %ld\n", mereni.rss[RSS_SUM]);
	fprintf(stderr, "Average RSS: %ld\n", rss_avg);
	fprintf(stderr, "Maximum RSS: %ld\n", mereni.rss[RSS_MAX]);
	fprintf(stderr, "Minimum RSS: %ld\n", mereni.rss[RSS_MIN]);
	fprintf(stderr, "LASER power: %6.2f\n", mereni.laser_power);
	fprintf(stderr, "LASER bias:  %6.2f\n", mereni.laser_bias);
	fprintf(stderr, "APD voltage: %6.2f\n", mereni.apd_voltage);
	fprintf(stderr, "APD temp:    %6.2f\n", mereni.apd_temp);
	fprintf(stderr, "CPU temp:    %6.2f\n", mereni.cpu_temp);
	fprintf(stderr, "loops: %lld\n", loopcnt);
	printf("%ld %ld %ld %ld %6.2f %6.2f %6.2f %6.2f %6.2f\n", mereni.rss[RSS_CNT], rss_avg, mereni.rss[RSS_MAX], mereni.rss[RSS_MIN], mereni.laser_power, mereni.laser_bias, mereni.apd_voltage, mereni.apd_temp, mereni.cpu_temp);

	return EXIT_SUCCESS;

error1:
	if (seriak >= 0) {
		restore_seriak(seriak, &old_tio);
		close(seriak);
	}
	return EXIT_FAILURE;
}

