Newsgroup: it.comp.programmare.win32
Titolo: [API Win 32] ReadFile che deve ritornare all'arrivo di dcb.EofChar in COM1

Salve a tutti.
Devo implementare la comunicazione seriale con un dispositivo esterno.
Uso le API, CreateFile, ReadFile e compagni.
Uso la WriteFile per impartire un comando al dispositivo esterno, 
subito dopo la WriteFile chiamo la ReadFile per leggere la risposta
del dispositivo al mio commando. So che l'ultimo carattere di questa 
risposta e' sempre '>' ma il numero di caratteri di cui si compone la risposta
e' (ovviamente:-) variabile. Non dovrei avere necessita' di fare accessi asincroni
quindi niente overlapped.
Vorrei che la ReadFile ritornasse:
1) se ha letto '>', oppure
2) se c'e' stato un errore, oppure
3) se il timeout e' scaduto.
Dall'help vedo che la ReadFile ritorna:
"The ReadFile function returns when one of the following is true: 
a write operation completes on the write end of the pipe,
e questo direi che non mi interessa
the number of bytes requested has been read, 
neppure questo mi interessa 
or an error occurs."
questo mi interessa ma non e' la cosa fondamentale.
poi ritorna anche se il timeout e' scaduto e pure questo mi va bene. 
E dice anche che
"When a synchronous read operation reaches the end of a file, 
ReadFile returns TRUE and sets *lpNumberOfBytesRead to zero." 
Ma come faccio a dirgli che il carattere finale e' '>'?
Ho provato settando EofChar al valore '>' nella struttura dcb e tutto
funziona come vorrei nel senso che ReadFile legge effettivamente
fino a '>' e poi ritorna. Ma se cambio EofChar, ad esempio mettendolo
al valore '5' la ReadFile si comporta nello stesso modo e legge comunque
fino a '>', pur comparendo un '5' prima di '>'. 
Ora, le cose funzionano come io vorrei 
ma secondo la documentazione non
dovrebbero funzionare perche' setto anche dcb.fBinary a TRUE e cosi'
facendo, l'help del DCB mi informa che "no EOF check" e che non posso
mettere dcb.fBinary a FALSE. Il mio dubbio e':
mi tengo del codice che secondo la documentazione non dovrebbe andare
ma che (sembra) andare come vorrei?
Il codice e' il seguente:
    //apertura COM2 come file
    hCom = ::CreateFile(
                "COM1",
                GENERIC_READ | GENERIC_WRITE,
                0,
                NULL,
                OPEN_EXISTING,
                0,
                NULL
                );
 
    GetCommState(hCom , &dcb);
    dcb.fBinary         = TRUE;
    dcb.ByteSize        = 8;
    dcb.StopBits        = ONESTOPBIT;
    dcb.Parity          = NOPARITY;
    dcb.fOutxCtsFlow    = FALSE;
    dcb.fOutxDsrFlow    = FALSE;
    dcb.fDtrControl     = DTR_CONTROL_DISABLE;
    dcb.fDsrSensitivity = FALSE;
    dcb.fRtsControl     = RTS_CONTROL_DISABLE;
    dcb.fOutX           = FALSE;
    dcb.fInX            = FALSE;
    dcb.BaudRate        = CBR_38400;
    dcb.EofChar         = '>';
 
    fSuccess = SetCommState(hCom , &dcb);
    COMMTIMEOUTS StrutturaTimeOut;
    ::GetCommTimeouts(hCom,&StrutturaTimeOut);
    StrutturaTimeOut.ReadIntervalTimeout        = 50;
    StrutturaTimeOut.ReadTotalTimeoutConstant   = 15000;
    StrutturaTimeOut.ReadTotalTimeoutMultiplier = 0;
    StrutturaTimeOut.WriteTotalTimeoutConstant  = 50;
    StrutturaTimeOut.WriteTotalTimeoutMultiplier= 0;
    ::SetCommTimeouts(hCom,&StrutturaTimeOut);
 
    ...
 
    BOOL    Risultato, RisultatoRead;
    DWORD   ByteScritti;
    char    Letti[2048]="Nulla";
    DWORD   ByteLetti;
    CString Comando;
    Comando="gcp\r";
 
    Risultato = ::WriteFile(
                    hCom,
                    Comando,
                    strlen(Comando),
                    &ByteScritti,
                    NULL
                    );
    ByteLetti = 0;
 
    RisultatoRead = ::ReadFile(
                    hCom,
                    Letti,
                    2048,
                    &ByteLetti,
                    NULL
                    );
 
    //controllo se è scaduto il timeout:
    if(Letti[ByteLetti-1] != '>')
    {
        AfxMessageBox("È scaduto il timeout sulla ReadFile");
        exit(-8);
    }
 
Cosi' come e' scritto funziona ma non so il perche'.
Una alternativa potrebbe essere questa:
 
    for(int c=0; c<2048; c++)
    {
        RisultatoRead = ::ReadFile(
                            hCom,
                            &Letti[c],
                            1,
                            &ByteLetti,
                            NULL
                            );
        if(RisultatoRead)
        {
            if(Letti[c]=='>')
                break;
        }
        else//errore nella ReadFile
        {
            UltimoErrore("Errore nella ReadFile");
            CloseHandle(hCom);
            exit(-7);
        }
    }
    ByteLetti=c+1;
cioe' leggere carattere per carattere ma non mi sembra molto elegante.
In conlusione, e scusate la lunghezza del messaggio, come gestire
la ricezione seriale quando NON si sa quanti caratteri arriverranno?
Grazie a chiunque vorra' rispondere.
Saluti.

     }
Non ho letto tutto quello che hai scritto pero' penso che leggendo
la documentazione su WaitCommEvent e EV_RXFLAG, EV_ERR dovresti
risolvere facilmente.
Nota che se da un altro thread chiami SetCommMask( hComm, 0 )
WaitCommEvent ritorna subito.
Cmq i passi necessari dovrebbero essere i 3 che ti ho indicato.
ciao
Massimo