// log_adv_em.c // $Id: log_adv.c 134 2011-09-16 12:35:19Z csherwood $ // // This is a modification of log_any to see if we can reduce the CPU time and // get over the comm port hanging // // Testing using standard library calls to write to a file // Developing UNSAFE signal interupt handler // // NO arguments!- // requires port 4, 19200 N81 // // has a function that just adds the Moxa time, since serialByteRead() gets parts of // a logical data message, we only want to add the time to the entire line, once read // // csherwood@usgs.gov, emontgomery@usgs.gov // 11-Sep-2011 #include #include #include #include #include #include "serial.h" #include "moxadevice.h" #include #include #define MAX_FNAME_SIZE 255 #define BUFSIZE 255 #define BUFDSIZE 1023 // Global variables or structure for passing clean-up info to signal interrupt handler? FILE *fdp, *fdp_log, *fdp_cfg, *fdp_data; /* file stream pointer for stdio library calls */ int fds, fds_log, fds_cfg, fds_data, fds_port; /* file descriptor for system calls */ char tmp[255], instid[]="armadv"; char bufd[BUFDSIZE]; char datafilename[127]="/var/sda/testdata/instrument_YYYYMMDDHHMMSS.dat"; fd_set readfds; int tmpfd, maxfd = -1; int portnum=3; // function declarations static void my_exit(int sig); int tsout( FILE *fd, char *buf ); int my_output( FILE *fd, char *buf, int disp_data ); int main(int argc, char *argv[]) { int len, ier; char buf[BUFSIZE]; /* may be too small */ char logfname[MAX_FNAME_SIZE]; time_t now; struct tm gm, *gmp; struct termios options; int baudRateConst = B19200; char *inst_str=" B227H"; int display_data = 1; pid_t my_pid, parent_pid; char svnId[80]="$Id: log_adv.c 134 2011-09-16 12:35:19Z csherwood $"; // char portname[30]; char device[30]; int tstat, knt=0; char *firstc, *ptr; struct timeval tv; // Set to check every 2 seconds tv.tv_sec = 2; tv.tv_usec = 0; // on dell that has an actual db9 serial port, use /dev/ttyS0 // on lenovo with just USB, not sure what is right- maybe /dev/ttyUSB0? // on Moxa /dev/ttyM? signal(SIGINT,my_exit); /* terminal interuppt (e.g., ctrl-C) */ signal(SIGTERM,my_exit); /* terminate process */ now=time(NULL); gmp = gmtime(&now); gm = *gmp; my_pid = getpid(); parent_pid = getppid(); // open a log file with a name that includes the PID and date // for use with cron, use absolute path to open a log file with a name that includes the PID snprintf(logfname,MAX_FNAME_SIZE,"/var/sda/logs/%s_%04d%02d%02d%02d%02d%02d_%06d.log", instid,1900+gm.tm_year,gm.tm_mon+1,gm.tm_mday,gm.tm_hour,gm.tm_min,gm.tm_sec,(int)my_pid); fdp_log = fopen(logfname,"wt"); if(fdp_log == NULL) { // Send this message to a better place? stdout and stderr? fprintf(stdout,"Problem opening log file\n"); fprintf(stderr,"Problem opening log file\n"); my_exit(SIGUSR1); } // Log program name and PID to log file snprintf(buf,sizeof(buf)-1,"%s PID : %d",argv[0],my_pid ); ier=my_output( fdp_log, buf, display_data ); snprintf(buf,sizeof(buf)-1,"svnId: %s",svnId); ier=my_output( fdp_log, buf, display_data ); // Make a data file name // months in time structure are jan==0, so we need to add one to display correctly // this needs to be the full path name for when run in cron snprintf(datafilename,sizeof(buf),"/var/sda/data/%s/%s_%04d%02d%02d%02d%02d%02d_%06d.dat", instid,instid,1900+gm.tm_year,gm.tm_mon+1,gm.tm_mday,gm.tm_hour,gm.tm_min,gm.tm_sec,(int)my_pid); // open data file fdp_data = fopen(datafilename,"w"); if(fdp_data == NULL) { snprintf(buf,sizeof(buf)-1,"Problem opening data file: %s",datafilename); ier=my_output( fdp_log, buf, display_data ); my_exit( SIGUSR1 ); } snprintf(buf,sizeof(buf)-1,"Opened data file: %s",datafilename); ier=my_output( fdp_log, buf, display_data ); // Get system fd of log file. See TLPI p. 248 fds_data = fileno(fdp_data); snprintf(buf,sizeof(buf)-1,"System fds for datafile: %2d",fds_data); ier=my_output( fdp_log, buf, display_data ); snprintf(buf,sizeof(buf)-1,"Trying to open serial port %4d",portnum); ier=my_output( fdp_log, buf, display_data ); snprintf(device,29,"/dev/ttyM%1d",portnum); snprintf(buf,sizeof(buf)-1,"with device name = %s",device); ier=my_output( fdp_log, buf, display_data ); if ((portnum < 0) | (portnum > 7)) { snprintf(buf,sizeof(buf)-1,"Bad value requested for serial port number."); my_exit( SIGUSR1 ); } else { fds_port = open( device, O_RDWR|O_NOCTTY|O_NONBLOCK ); if( fds_port == -1){ snprintf(buf,sizeof(buf)-1,"Port open failed: fd=%d",fds_port); ier=my_output( fdp_log, buf, display_data ); my_exit( SIGUSR1 ); } //from sbgCom/comSerialUnix.c if (fcntl(fds_port, F_SETFL, O_NONBLOCK) != -1){ // Retreive current options if (tcgetattr(fds_port, &options) != -1){ // Define com port options options.c_cflag |= (CLOCAL | CREAD); // Enable the receiver and set local mode... options.c_cflag &= ~(PARENB|CSTOPB|CSIZE); // No parity, 1 stop bit, mask character size bits options.c_cflag |= CS8; // Select 8 data bits options.c_cflag &= ~CRTSCTS; // Disable Hardware flow control // Disable software flow control options.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); // We would like raw input options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG /*| IEXTEN | ECHONL*/); options.c_oflag &= ~OPOST; // Set our timeout to 0 options.c_cc[VMIN] = 0; options.c_cc[VTIME] = 1; // Set both input and output baud if ( (cfsetispeed(&options, baudRateConst) != -1) && (cfsetospeed(&options, baudRateConst) != -1) ){ // Define options if (tcsetattr(fds_port, TCSANOW, &options) != -1){ // flush port tcflush(fds_port, TCIOFLUSH); } else{ snprintf(buf,sizeof(buf)-1, "tcsetattr failed"); ier=my_output( fdp_log, buf, display_data ); } } else{ snprintf(buf,sizeof(buf)-1, "set speed failed\n"); ier=my_output( fdp_log, buf, display_data ); } } else{ snprintf(buf,sizeof(buf)-1, "tcgetattr failed\n"); ier=my_output( fdp_log, buf, display_data ); } } else { snprintf(buf,sizeof(buf)-1, "fcntl failed\n"); ier=my_output( fdp_log, buf, display_data ); } //StartLogger(); snprintf(buf,sizeof(buf)-1,"Starting to log..."); ier=my_output( fdp_log, buf, display_data ); ier=my_output( fdp_data, buf, 0 ); maxfd = fds_port; //This is where it waits until it get's something and then logs it while(1){ //sleep(1); FD_ZERO( &readfds); FD_SET( fds_port, &readfds); tmpfd= select( maxfd+1, &readfds, NULL, NULL, &tv); if( tmpfd <= 0) continue; tstat=gettimeofday(&tv, NULL); if( FD_ISSET( fds_port, &readfds)){ ier=fcntl(fds_port, F_SETFL,FNDELAY ); len = read(fds_port, bufd, BUFDSIZE ); //len= SerialBlockRead( portnum, bufd, 1024); bufd[len]= '\0'; knt=1; //write to data file // put the last char of each chunk in a place available to strncmp firstc=&bufd[0]; if ( knt > 0 && fds_data != -1){ // knt > 0 avoids partial first line //printf ("string is %s, compare to %s\n",firstc, inst_str); ptr=strstr(firstc,inst_str); //printf("comparison result is %d\n",ier); // needs to match: B227H 7 2011 09 14 18 00 01 // always at the beginning of a line- doesn't need other stuff if (strstr(firstc,inst_str)!=NULL) { //printf("found it at %d\n", (int)ptr); //printf ("%s\n",ptr); ier=tsout( fdp_data, ptr); } else { // the write makes a sensible looking data record, but it's not written // as one long record, it's a series of chunks, so pre-pending moxa // time doesn't make sense. If concatenate into a buffer until a // is recieved, that works better. //write( fds_data, bufd, len); write( fds_data, bufd, len); } } knt+=1; } } // end while } // end if portnum OK // Graceful exit my_exit( SIGUSR1 ); } // MY_EXIT - Clean up, close files, write to log, exit static void my_exit(int sig) { // sig is assigned by signal or passed by user // UNSAFE use of printf() flush(), close(), and exit() // TODO - Do this with async-safe calls? int ier; char buf[255]; // (0) identify cause of exit switch (sig) { case SIGINT : { sprintf(buf,"Caught SIGINT\n"); ier = my_output( fdp_log, buf, 0 ); break; } case SIGTERM : { sprintf(buf,"Caught SIGTERM\n"); ier = my_output( fdp_log, buf, 0 ); break; } case SIGUSR1 : { sprintf(buf,"Caught SIGUSR1\n"); ier = my_output( fdp_log, buf, 0 ); break; } default : { sprintf(buf,"Unknown signal\n"); ier = my_output( fdp_log, buf, 0 ); } } // Close files in order of importance: // (1) close serial port if( (ier=SerialClose(portnum)) != SERIAL_OK ) sprintf(buf,"Problem closing serial port %d; returned %d",portnum,ier); sprintf(buf,"Closed serial port %d",portnum); // (2) close data file if( fdp_data != NULL ){ write( fds_data, "\n", 1 ); fflush(fdp_data); fclose(fdp_data); } // (3) close log file if( fdp_log != NULL ){ ier = tsout(fdp_log,buf); fflush(fdp_log); fclose(fdp_log); } exit(0); } // TSOUT - prepend time stamp, print contents of buf, add CR int tsout( FILE *fd, char *buf ) { //time_t t; struct tm gm, *gmp; struct timeval tv; int iw, tstat, nlen=0; char tbuf[255]; char *lastc; tstat=gettimeofday(&tv,NULL); //gmp = gmtime(&t); // pointing at the tv seconds gets the time from gettimeofday, so usecs will be synched gmp = gmtime(&tv.tv_sec); gm = *gmp; // save local copy...see TLPI p. 192 snprintf(tbuf,29,"\n%4d %02d %02d %02d %02d %10.7f ", 1900+gm.tm_year,gm.tm_mon+1,gm.tm_mday,gm.tm_hour,gm.tm_min,(gm.tm_sec+((double)tv.tv_usec/1000000))); //printf("lengths of tbuf and buf are: %d, %d.\n", strlen(tbuf), strlen(buf)); nlen=strlen(buf); if ((strlen(tbuf) + strlen(buf)) < sizeof(tbuf)-1) { strncat(tbuf,buf,nlen); lastc=&buf[nlen-1]; if (strncmp(lastc,"\n",1) != 0) strncat(tbuf,"\n",1); iw = fprintf(fd,"%s",tbuf); fflush(fd); } else { printf("string too long- look for error in data!\n"); printf("%s\n",buf); //email manager that something's wrong iw=-1; } return(iw); } int my_output( FILE *fd, char *buf, int disp_data ) { // Prepend time stamp, print contents of buf, add CR struct tm gm, *gmp; struct timeval tv; int iw=1, tstat, nlen=0; char tbuf[255]; tstat=gettimeofday(&tv,NULL); // pointing at the tv seconds gets the time from gettimeofday, so usecs will be synched gmp = gmtime(&tv.tv_sec); gm = *gmp; // save local copy...see TLPI p. 192 snprintf(tbuf,29,"%4d %02d %02d %02d %02d %10.7f ", 1900+gm.tm_year,gm.tm_mon+1,gm.tm_mday,gm.tm_hour,gm.tm_min,(gm.tm_sec+((double)tv.tv_usec/1000000))); //check that concatenated string isn't too big nlen=strlen(buf); if ((strlen(tbuf) + strlen(buf)) < sizeof(tbuf)-1) { strcat(tbuf,buf); strcat(tbuf,"\n"); if (disp_data ==1) //display data and write to log file { iw = fprintf(stdout,"%s",tbuf); iw = fprintf(fd,"%s",tbuf); } else { iw = fprintf(fd,"%s",tbuf); //just write to the log file } fflush(fd); } else { printf("string too long- look for error in data!\n"); //email manager that something's wrong iw=-1; } return(iw); }