Newsgroup: it.comp.lang.delphi
Titolo: Comunicazioni seriali

Ho un'applicazione che legge, attraverso la porta seriale, il peso del
materiale in estrazione su una bilancia, registrato in uno strumento WL.
Fino a poco tempo fa l'applicazione girava solo su windows 98,
successivamente sono state effettuati piccoli interventi per renderla
compatibile con Windows XP, dove tutt'ora sta girando.
Riscontro, saltuariamente, un blocco durante l'operazione di lettura del
peso ed in particolare durante la chiamata dell'API ReadFile, che si blocca
bloccando l'intera applicazione. Indipendentemente dalle cause di questo
blocco, che quasi sicuramente sono esterne (problemi sul cavo, sullo
strumento, la temperatura o qualsivoglia) vorrei che la mia applicazione non
si bloccasse in attesa del completamento della lettura ma in qualche modo
potesse gestire una sorta di Time Out per poi ritentare o dare un messaggio
di errore all'Utente.
Quando apro la porta il codice è il seguente:
    [...]
  if strumento = WL then begin
   portaWL := CreateFile(PChar(config.WL), GENERIC_WRITE or GENERIC_READ, 0,
nil, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, 0);
    if portaWL = INVALID_HANDLE_VALUE then begin
      Result := False;
      Exit;
    end;
   if GetCommState(portaWL, dDCB) then begin
      dDCB.DCBLength := SizeOf(TDCB);
      dDCB.BaudRate := fileConfig.ReadInteger(config.WL + 'SETTINGS',
'BaudRate', 9600);
      dDCB.ByteSize := fileConfig.ReadInteger(config.WL + 'SETTINGS',
'ByteSize', 8);
      dDCB.Parity  := fileConfig.ReadInteger(config.WL + 'SETTINGS',
'Parity', 0);
   dDCB.StopBits := fileConfig.ReadInteger(config.WL + 'SETTINGS',
'StopBits', 0);
   dDCB.XonChar := #17;
   dDCB.XoffChar := #19;
   dDCB.EofChar := #0;
   dDCB.EvtChar := #0;
   if not SetCommState(portaWL, dDCB) then begin
       MessageDlg('Funzione SetCommState non riuscita', mtError, [mbOk], 0);
        Result := False;
        Exit;
      end;
   end else begin
      MessageDlg('Funzione GetCommState non riuscita', mtError, [mbOk], 0);
      Result := False;
      Exit;
   end;
    TempiPorta.ReadIntervalTimeout := 500;
    TempiPorta.ReadTotalTimeoutMultiplier := MAXDWORD;
    TempiPorta.ReadTotalTimeoutConstant := 500;
    TempiPorta.WriteTotalTimeoutMultiplier := MAXDWORD;
    TempiPorta.WriteTotalTimeoutConstant := 500;
    if not SetCommTimeouts(PortaWL, TempiPorta) then begin
      MessageDlg('Funzione SetCommTimeouts non riuscita', mtError, [mbOk],
0);
      Result := False;
      Exit;
    end;
    MenuPrincipale.lgtWLOpen.Active := true;
    MenuPrincipale.Refresh;
  end;
    [...]
e fin qui tutto bene (l'apertura riesce sempre).
Parte del codice di lettura del peso:
    [...]
     while True do begin
          MenuPrincipale.lgtWLComRead.Active := true;
          MenuPrincipale.lgtWLComRead.Refresh;
         if not ReadFile(portaWL, fromPortaWL[j], 1, NumBytes, nil) then
begin                (***)
            MenuPrincipale.lgtWLComRead.Active := false;
            MenuPrincipale.lgtWLComRead.Refresh;
           ok_ := ClearCommError(portaWL, NumError, nil);
         if not ok_ then
             MemorizzaOperazione(1, 'Errore di comunicazione; funzione
ClearCommError non riuscita');
         if ok_ and (NumError <> 0) then
             MemorizzaOperazione(1, 'Errore di comunicazione numero ' +
IntToHex(NumError, 4));
          end;
          MenuPrincipale.lgtWLComRead.Active := false;
          MenuPrincipale.lgtWLComRead.Refresh;
          if fromPortaWL[j] = #13 then
           Break;
          Inc(j);
          if j > 30 then begin
           // str_ := '000000000000000';
            Break;
          end;
     end;
    [...]
L'errore si verifica mediamente due volte al giorno mentre l'applicazione
gira 24x7 ed effettua una lettura sulla porta circa ogni due secondi.
Inutile dire che si verifica solo da quando siamo passati ad XP.
Non potendo eseguire in debug l'applicazione, dall'attività dei led
(l'oggetto lgtWLComRead è attivo)  posso essere certo che l'applicazione è
bloccata sulla ReadFile. Inoltre il cursore si trasforma pure in clessidra,
cosa che io non faccio mai esplicitamente a programma. Il ciclo di letture è
stato implementato come evento di un Timer.
Ho provato ad agire:
- sui timeout prima della chiamata alla SetCommTimeouts, abbassando il
valore da 500 fino a 100, ma non ho ottenuto risultato;
- diminuendo la velocità di comunicazione (che attualmente è a 9600) prima a
4800 e poi a 2400, ma lo stesso.
Ho visto tramite documentazione la possibilità di mettere tutte le procedure
di comunicazione in un thread separato che può a questo punto essere
agevolmente controllato dal thread principale dell'applicazione. Questo è un
intervento che sicuramente farò ma, visto che mi sembra una cosa un pò piu
complessa da fare preferirei prima mettere la classica pezza al problema e
poi ristrutturare con calma le routines di comunicazione dell'applicazione.
Qualunque consiglio, chiarimento o riferimento in merito è più che accettto.
Grazie, buon lavoro.
Gianluca.

ehm... mi ha troncato il post e mi ha messo il resto nell'allegato il resto
:-|

Scusandomi con tutti quandi, posto la continuazione del messaggio:
[...]
e fin qui tutto bene (l'apertura riesce sempre).
Parte del codice di lettura del peso:
     while True do begin
          MenuPrincipale.lgtWLComRead.Active := true;
          MenuPrincipale.lgtWLComRead.Refresh;
         if not ReadFile(portaWL, fromPortaWL[j], 1, NumBytes, nil) then
begin
            MenuPrincipale.lgtWLComRead.Active := false;
            MenuPrincipale.lgtWLComRead.Refresh;
           ok_ := ClearCommError(portaWL, NumError, nil);
         if not ok_ then
             MemorizzaOperazione(1, 'Errore di comunicazione; funzione
ClearCommError non riuscita');
         if ok_ and (NumError <> 0) then
             MemorizzaOperazione(1, 'Errore di comunicazione numero ' +
IntToHex(NumError, 4));
          end;
          MenuPrincipale.lgtWLComRead.Active := false;
          MenuPrincipale.lgtWLComRead.Refresh;
          if fromPortaWL[j] = #13 then
           Break;
          Inc(j);
          if j > 30 then begin
           // str_ := '000000000000000';
            Break;
          end;
     end;
Dalla situazione dei led (oggetto lgtWLComRead attivo) sono sicuro
praticamente al 100% che il programma è bloccato sulla chiamata alla
ReadFile. Non posso eseguire il debug dell'applicazione, quindi l'accensione
di alcuni led sulla maschera è l'unica informazione che ho relativamente
allo stato ed al punto in cui l'applicazione si ferma. Inutile dire che
prima, su un'altra macchina (irrecuperabile) dove era installato Windows 98
non ha mai avuto problemi del genere.
Il blocco avviene mediamente una/due volte al giorno, mentre l'applicazione
gira 24x6 ed effettua letture di peso sulla porta circa ogni due secondi.
Il ciclo di elaborazione della produzione e quindi contententi le letture è
stato inserito in un Timer, quindi bloccandosi la ReadFile, quest'ultimo si
blocca di conseguenza bloccando l'intera applicazione.
Tra gli interventi eseguiti:
- ho provato a modificare i timeouts portandoli da 500 a 100;
- ho modificato la velocità delle porte e degli strumenti WL fino a 2400,
ma niente di fatto.
Il problema sicuramente non risiede nell'applicazione di per sè, ma senza
dubbio non è accettabile che per qualunque motivo si blocchi così
frequentemente.
Avevo appena cominciato a valutare l'opzione di sviluppare ed inserire le
procedure di produzione invece che in un Timer in Thread secondario che
potrebbe piu facilmente essere controllato ed eventualmente terminato senza
perdita di dati, ma tale intervento richiede una certa analisi e deve essere
strutturato correttamente, mentre io al momento ho bisogno della classica
"toppa".
Qualunque suggerimento, esempio ma anche riferimento a documentazione è
sicuramente ben gradito.
Ciao a tutti, buon lavoro.

 Scusandomi con tutti quandi, posto la continuazione del messaggio:
[CUT]
Veramente il messaggio è arrivato tutto già la prima volta che lo hai 
spedito, senza allegati. L'ho scaricato con due newsreader diversi e si 
vede perfettamente.
-- 
Ciao,
   Stefano
"Ho smesso di essere presuntuoso quando ho scoperto di essere perfetto"
"Prima di parlare assicurati che quello che hai da dire sia più importante 
del silenzio"

 vede perfettamente.
Con Outlook Express, dopo averlo inviato mi ha messo nella posta inviata il
messaggio troncato con la seconda parte allegata in file .dat (con il nome
simile agli otto caratteri successivi nel messaggio). Avevo lasciato qualche
spazio e messo tre * tra parentesi per evidenziare la riga con il ReadFile.
Scaricandolo nuovamente stesso risultato. Ora cerco di capire perchè.
Gianluca.