/* Ejemplo de adquisicion de datos de un regulador SOLENER */ /* desde Windows, generando un archivo CSV con los datos */ /* del informe ISPRA (C) MCGS 2015/21, tecnico@solener.com */ /* Se proporciona tal cual, sin garantia de ningun tipo */ #include #include #include #include "windows.h" /**************************************************/ HANDLE Evento; /**************************************************/ BOOL WINAPI console_ctrl_handler(DWORD dwCtrlType); /**************************************************/ int main(int argc, char **argv) { BOOL Redireccionado; char Puerto[20] = "COM1", PortName[30] = "\\\\.\\"; int i, p, Baudios = 9600; DWORD Periodo = 10000LU; HANDLE hComm, hOut; DCB dcb; COMMTIMEOUTS ct; BY_HANDLE_FILE_INFORMATION fi; for (i = 1; i < argc; i++) { if (argv[i][0] != '-') { fprintf(stderr, "Opcion desconocida: %s\n", argv[i]); return EXIT_FAILURE; } switch(argv[i][1]) { case 0: fputs("- no es una opcion valida\n", stderr); return EXIT_FAILURE; case 'h': fputs("Programa para registro de datos del RSD80 en CSV, (C) MCGS 2015\n" " Uso: RSD2CSV [-h] [-p Puerto] [-b Baudios] [-s Periodo]\n" " Si se omiten las opciones se asume -p COM1, -b 9600, -s 10\n" " Para guardar la salida, hacer RSD2CSV > log.csv\n", stderr); return EXIT_SUCCESS; case 'p': i++; if (i == argc) { fputs("Debe especificar el nombre de puerto detras de -p\n", stderr); return EXIT_FAILURE; } if (strlen(argv[i]) >= sizeof(Puerto)-1) { fputs("El nombre del puerto es demasiado largo\n", stderr); return EXIT_FAILURE; } strcpy(Puerto, argv[i]); break; case 'b': i++; if (i == argc) { fputs("Debe especificar los baudios detras de -b\n", stderr); return EXIT_FAILURE; } Baudios = atoi(argv[i]); if ((Baudios != 4800) && (Baudios != 9600) && (Baudios != 19200) && (Baudios != 38400)) { fputs("Los baudios indicados no son validos, deben ser 4800, 9600, 19200 o 38400\n", stderr); return EXIT_FAILURE; } break; case 's': i++; if (i == argc) { fputs("Debe especificar el periodo de muestreo detras de -s\n", stderr); return EXIT_FAILURE; } p = atoi(argv[i]); if ((p < 1) || (p > 600)) { fputs("El periodo de muestreo debe estar entre 1 y 600 segundos\n", stderr); return EXIT_FAILURE; } Periodo = 1000LU*(DWORD)p; break; default: fprintf(stderr, "Opcion -%c desconocida\n", argv[i][1]); return EXIT_FAILURE; } } strupr(Puerto); strcat(PortName, Puerto); fprintf(stderr, "Conectando al puerto %s a %i baudios, pulse Ctrl-C para terminar...\n", Puerto, Baudios); /* Veo si la salida esta redireccionada */ hOut = GetStdHandle(STD_OUTPUT_HANDLE); Redireccionado = GetFileInformationByHandle(hOut, &fi); hComm = CreateFile(PortName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hComm == INVALID_HANDLE_VALUE) { fputs("No se pudo abrir el puerto\n", stderr); return EXIT_FAILURE; } /* Ajusto los tiempos de comunicacion para que haya timeout en recepcion */ ct.ReadIntervalTimeout = MAXDWORD; ct.ReadTotalTimeoutMultiplier = MAXDWORD; ct.ReadTotalTimeoutConstant = 500; ct.WriteTotalTimeoutMultiplier = 0; ct.WriteTotalTimeoutConstant = 0; if (!SetCommTimeouts(hComm, &ct)) { fputs("No se pudieron cambiar los tiempos de comunicacion del puerto\n", stderr); CloseHandle(hComm); return EXIT_FAILURE; } /* Ajusto los parametros de comunicacion */ dcb.DCBlength = sizeof(DCB); if (!GetCommState(hComm, &dcb)) { fputs("No se pudieron obtener los parametros del puerto\n", stderr); CloseHandle(hComm); return EXIT_FAILURE; } dcb.DCBlength = sizeof(DCB); dcb.ByteSize = 8; dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT; switch (Baudios) { case 4800: dcb.BaudRate = CBR_4800; break; case 9600: dcb.BaudRate = CBR_9600; break; case 19200: dcb.BaudRate = CBR_19200; break; default: dcb.BaudRate = CBR_38400; } if (!SetCommState(hComm, &dcb)) { fputs("No se pudieron cambiar los parametros del puerto\n", stderr); CloseHandle(hComm); return EXIT_FAILURE; } Evento = CreateEvent(NULL, TRUE, FALSE, NULL); SetConsoleCtrlHandler(console_ctrl_handler, TRUE); for (;;) { DWORD ms, nb; /* Guardo el momento actual para programar el timeout de muestreo */ ms = GetTickCount(); /* Pido el informe ISPRA */ if (Redireccionado) fputc('.', stderr); if (WriteFile(hComm, "I\r\n", 3, &nb, NULL) && (nb == 3)) { char *p1, Buffer[2048]; int Caracter; /* Trato el informe ISPRA y lo imprimo */ for (Caracter = 0; Caracter < sizeof(Buffer)-1; ) { char c; ReadFile(hComm, &c, 1, &nb, NULL); if (!nb) break; if (c != '\r') Buffer[Caracter++] = c; } /* Marco el fin de la cadena */ Buffer[Caracter] = 0; /* Proceso la respuesta para unir los registros en una única cadena */ /* Para ello tengo que eliminar los \r\n seguidos de un dígito */ p1 = Buffer; for (;;) { char *p2; p1 = strchr(p1, '\n'); /* Compruebo si es el último grupo, que marca el fin de la cadena */ if (!p1 || !p1[2]) break; /* Busco la coma siguiente */ p2 = strchr(p1, ','); if (!p2) break; /* Desplazo la cadena para quitar el \r, el \n y lo que haya hasta la coma */ memmove(p1, p2, Caracter); } /* Adapto los caracteres para el CSV. Respeto lo que hay entre caracteres % */ for (p1 = strchr(Buffer+1, '%'); p1 && *p1; p1++) { switch (*p1) { case ',': *p1 = ';'; break; case '.': *p1 = ','; } } /* Imprimo el resultado */ printf("%s", Buffer); } else fputs("Error al solicitar la informacion\n", stderr); /* Espero al siguiente ciclo */ if ((Periodo+ms > GetTickCount()) && (WaitForSingleObject(Evento, Periodo+ms-GetTickCount()) != WAIT_TIMEOUT)) break; } if (Redireccionado) fputc('\n', stderr); fputs("Terminado por el usuario\n", stderr); CloseHandle(Evento); CloseHandle(hComm); return EXIT_SUCCESS; } /**************************************************/ BOOL WINAPI console_ctrl_handler(DWORD dwCtrlType) { SetEvent(Evento); return TRUE; } /**************************************************/