Evitar quedarse bloqueado al leer del puerto serie (C++ WinAPI)

Iniciado por SARGE553413, 7 Diciembre 2014, 22:35 PM

0 Miembros y 1 Visitante están viendo este tema.

SARGE553413

Hola a todos.

Estoy haciendo una clase en C++ con funcionalidad para leer y escribir de distintas maneras en el puerto serie (con la Win32 API).
Como no tengo nada que use puerto serie, para hacer las pruebas estoy usando un emulador de puertos serie (com0com).

Estoy intentando implementar un método que lea todo lo que hay actualmente en el buffer del puerto serie, y si no hay nada, que no se quede bloqueado esperando.

He intentado hacerlo con OVERLAPPED pero, aparte de que no me ha funcionado, no me gusta la idea de tener que lanzar un hilo cada vez que quiero hacer esta opración.

Aquí el código que he escrito:

port = CreateFile(
string2LPCTSTR(this->portName), //\\\\.\\COM4"
GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // default security attributes
OPEN_EXISTING,
0,
NULL
);
if (port == INVALID_HANDLE_VALUE)
throw SerialPortNativeIOException("Port value is INVALID_HANDLE_VALUE");

DCB portConfig;
if (GetCommState(port, &portConfig) == 0){
closeConnection();
this->port=INVALID_HANDLE_VALUE;
throw SerialPortNativeIOException("Error trying to get comm state");
}

setPortConfig(portConfig); //Esto simplemente asigna valores...

if(SetCommState(port, &portConfig)==0){
closeConnection();
this->port=INVALID_HANDLE_VALUE;
throw SerialPortNativeIOException("Error trying to set comm state");
}

setTimeoutConfig(); //Esto simplemente asigna valores...

if(SetCommTimeouts(port, &timeoutConfig)==0){
closeConnection();
this->port=INVALID_HANDLE_VALUE;
throw SerialPortNativeIOException("Error trying to set comm timeout config.");
}
this->connected=true;

...

std::string SerialPortNative::readExisting(){
ulong readBytes=0;
OVERLAPPED oR = {0};
oR.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
if(!ReadFile(this->port,this->buffer,READEXISTING_BUFFER_SIZE,&readBytes,&oR))
if(GetLastError() != ERROR_IO_PENDING)
throw SerialPortNativeIOException("Error reading from serial port");
return string(this->buffer,0,readBytes);
}


Al usar el método 'readExisting()' la lectura da error y no es ERROR_IO_PENDING, es el error con código 3 (ERROR_PATH_NOT_FOUND).

Ya he leído la documentación y ejemplos de msdn.microsoft y he googleado, pero no consigo hacerlo funcionar.

¿Qué puedo hacer? Gracias, saludos.

x64core

Entonces cual es el problema ReadFile retornando ERROR_PATH_NOT_FOUND?
Si vas a solicitar de forma asincronica entonces necesitas establecer la bandera FILE_FLAG_OVERLAPPED en CreateFile, solicitando acceso exclusivo del dispositivo no es bueno, pero bueno ya sabras.

Si el hilo se queda bloqueado es porque la solicitud esta esperando que sea completada, podes crear un worker thread ( crearlo al construir la clase por ejemplo, asi no es necesario crear un hilo cada vez que se quiere leer ). Si la solicitud es asincronica, despues de llamar a ReadFile tendras que esperar por el evento usando WaitForSIngleObject.

SARGE553413

#2
Hola, gracias por la respuesta.

No me refería a que el hilo se quedase bloqueado, quiero decir que hacer ReadFile sin OVERLAPPED si se queda bloqueado, lo que quiero es tener una función que haga ReadFile a lo que haya en el buffer, y si no hay nada, que no se quede bloqueado.

Para ello creo que la manera de hacerlo es con OVERLAPPED, y al hacer ReadFile internamente se lanza un hilo, si yo lo he entendido bien.

Mi problema entonces es que no uso bien el OVERLAPPED porque la función ReadFile me devuelve un error que no es ERROR_IO_PENDING (que es lo que debería devolverme si no hay nada en el buffer).

He seguido intentándolo, pero aún no ha habido suerte.

Saludos.

EDITO:
He cambiado el CreateFile para ponerlo en lectura compartida y FILE_FLAG_OVERLAPPED y algunos otros errores que tenía, ahora el readExisting()
"parece" que funciona bien, porque al hacerlo me devuelve una cadena vacía.
El problema es que con el emulador que uso, al hacer write sobre COM3 (que está conectado a COM4), si no tengo abierto el hyperterminal no escribe nada en el buffer, no se por qué. El caso es que no puedo probarlo del todo pero creo que ya va bien, gracias por la respuesta. Si consigo comprobar que todo funciona subo el código para el que lo quiera ver.

Saludos.


EDITO 2:
Resulta que si hacemos el CreateFile con FILE_FLAG_OVERLAPPED ya no se pueden hacer lecturas síncronas. Tiene que ser lo uno o lo otro. La única solución que veo entonces (para hacer una clase que tenga métodos síncronos y asíncronos) es pasar del overlapped y crearme yo mis propios hilos. Si hay alguna manera mejor de hacerlo, agradeceré que me lo expliquen. No tengo claro si será mejor idea andar con hilos o, cada vez que quiera hacer el readExisting(), cambiar el readtimeout del puerto a 0.

Saludos.

x64core

Cita de: SARGE553413 en  8 Diciembre 2014, 14:36 PM
Hola, gracias por la respuesta.

No me refería a que el hilo se quedase bloqueado, quiero decir que hacer ReadFile sin OVERLAPPED si se queda bloqueado, lo que quiero es tener una función que haga ReadFile a lo que haya en el buffer, y si no hay nada, que no se quede bloqueado.

Para ello creo que la manera de hacerlo es con OVERLAPPED, y al hacer ReadFile internamente se lanza un hilo, si yo lo he entendido bien.

Mi problema entonces es que no uso bien el OVERLAPPED porque la función ReadFile me devuelve un error que no es ERROR_IO_PENDING (que es lo que debería devolverme si no hay nada en el buffer).

He seguido intentándolo, pero aún no ha habido suerte.

Saludos.
Me referia a eso mismo, Windows pone en modo de espera el hilo que es usado para solicitar peticiones sincronicas, queda esperando hasta que la solicitud es completada.

Cita de: SARGE553413 en  8 Diciembre 2014, 14:36 PM
EDITO:
He cambiado el CreateFile para ponerlo en lectura compartida y FILE_FLAG_OVERLAPPED y algunos otros errores que tenía, ahora el readExisting()
"parece" que funciona bien, porque al hacerlo me devuelve una cadena vacía.
El problema es que con el emulador que uso, al hacer write sobre COM3 (que está conectado a COM4), si no tengo abierto el hyperterminal no escribe nada en el buffer, no se por qué. El caso es que no puedo probarlo del todo pero creo que ya va bien, gracias por la respuesta. Si consigo comprobar que todo funciona subo el código para el que lo quiera ver.

Saludos.
Entonces ¿cómo el programador sabria si fue enviado con exito?

Cita de: SARGE553413 en  8 Diciembre 2014, 14:36 PM
EDITO 2:
Resulta que si hacemos el CreateFile con FILE_FLAG_OVERLAPPED ya no se pueden hacer lecturas síncronas. Tiene que ser lo uno o lo otro. La única solución que veo entonces (para hacer una clase que tenga métodos síncronos y asíncronos) es pasar del overlapped y crearme yo mis propios hilos. Si hay alguna manera mejor de hacerlo, agradeceré que me lo expliquen. No tengo claro si será mejor idea andar con hilos o, cada vez que quiera hacer el readExisting(), cambiar el readtimeout del puerto a 0.

Saludos.
Usando el mismo handle para para solicitudes sincronicas y asincronicas no es posible, sino ¿cómo haria Windows para saber cuando el programador necesita una petición sincronica o asincronica? al crear el objeto de archivo este contiene unas banderas donde guarda la información. Podes abrir más de un handle, una para peticiónes sincronicas y otro para asincronicas o podes usar ReOpenFile para cambiar los attributos del handle mismo.

-

Toma en cuenta que si las peticiones son asincronicas no habrá espera asi que el contenido del buffer es indefinido, para saber si la solicitud fue completada usa WaitForSingleObject con el mismo evento pasado a las funciones.