/* * 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 #include /* Standard input/output definitions */ #include #include #include /* File control definitions */ #include #include /* UNIX standard function definitions */ #include #include /* INT_MAX and others */ #include #include #include #include /* isdigit() a.s.o. */ #include static char *jmeno_seriaku = ""; static time_t cilovy_cas = 0; static time_t cas_startu = 0; /* baudrate settings are defined in , which is included by */ #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 { | +}\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; 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"); 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 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; // jednotky ignorujeme, stejne to jsou vzdy mA 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 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; // jednotky ignorujeme, stejne to jsou vzdy mA 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; } /* * 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 #include /* Standard input/output definitions */ #include #include #include /* File control definitions */ #include #include /* UNIX standard function definitions */ #include #include /* INT_MAX and others */ #include #include #include #include /* isdigit() a.s.o. */ #include static char *jmeno_seriaku = ""; static time_t cilovy_cas = 0; static time_t cas_startu = 0; /* baudrate settings are defined in , which is included by */ #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 { | +}\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; 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"); 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 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; // jednotky ignorujeme, stejne to jsou vzdy mA 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 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; // jednotky ignorujeme, stejne to jsou vzdy mA 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; } WebSVN - crusader - Diff - Rev 3 and 2 - /trunk/crusader_read_rss/crusader100_read_rss.c
  jablonka.czprosek.czf

crusader

Subversion Repositories:
[/] [trunk/] [crusader_read_rss/] [crusader100_read_rss.c] - Diff between revs 2 and 3

Show entire file Ignore whitespace

Rev 2 Rev 3

Powered by WebSVN 2.2.1