Newsgroup: it.comp.programmare.win32
Titolo: Porte seriali...

Ho una domanda da farvi, sulla gestione delle porte seriali.
La domanda è semplice: in Windows, che è un sistema event-oriented, come
si gestisce la ricezione di dati dalle porte seriali?!
Normalmente io in DOS o sui dispositivi embedded basati su
microcontroller mi scrivo i miei ISR con i quali gestisco tutto quello
che mi arriva dalle porte seriali (o altri dispositivi tipo schede di
I/O), mentre in Windows ho utilizzato delle librerie (COMM-DRV/Lib, a
mio parere molto buone) che mi mettevano a disposizione anche gli
interrupt e tutto quello che era necessario per creare ed agganciare le
mie routine di gestione a questi.
Ora, tralasciando la parte di invio dati (banale) se io mi voglio
scrivere  un software che acquisisca dati dalla seriale (NO POLLING!!!)
come faccio? In Windows ho un "evento" di tipo dato ricevuto, segnale
commutato o simile? A me sembra di no!
Ho possibilità di scrivere un interrupt handler, system hook o come si
chiama oppure mi scrivo una funzione che gira in un thread e legge dallo
stream agganciato alla seriale con WaitCommEvent e mi genera un
evento?!?
Grazie a tutti.
-- 
Posted via Mailgate.ORG Server - 
http://www.Mailgate.ORG

 wrote in message
news:2963026fc33cffb104c...@mygate.mailgate.org...

La WaitCommEvent direi che fa al caso tuo... la puoi usare anche in
modalita' asincrona (e gestirebbe gia' di suo un evento di
sincronizzazione... leggiti le Info sull'Api). Certo che cosi' facendo... ti
gestisci un carattere per volta...
Sko

 commutato o simile? A me sembra di no!
[cut]
Ovviamente un argomento così è già stato trattato, bastava cercare
nella NG.
Per non rifare l'ennesimo copia/incolla segui il seguente "algoritmo"
1) vai su 
www.google.it
2) clicca "Gruppi"
3) sulla casella "Crea, cerca e naviga i gruppi per discutere e
condividere idee" riporta queste parole chiave "win32 Codice a barre 
LeggiSeriale"
4) Clicca il tasto "Cerca nei gruppi"
5) appare una voce sola -> "Codice a barre" in cui è riportato il codice
per gestire l'evento da seriale.
Saluti
JJ
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 
Ciao,
ti ringrazio per la risposta, ma forse non mi sono spiegato.
La soluzione "Thread" che implementi tu ed alla quale mi hai 
rapidamente rimandato andrebbe anche bene, ma si tratta di un "polling".
Io vorrei capire (al di la della soluzione che poi uno implementa) se
vale la
pena scrivere del codice a livello più basso o meno.
Ovvero posso tenere la macchina non impegnata (leggi "interrupt") ed
impegnarla
solo se effettivamente mi arriva un dato?!
Grazie.
PS [OT] Ho cercato nel newsgroup le discussioni sulla seriale ma non ho
trovato
una risposta che faceva al caso mio ne tantomeno quella sul codice a
barre che
rientra nel caso di gestione di una periferica ma non è il mio.


 Sko
Grazie ma devo gestire un flusso di caratteri che devo passare ad un
parser per la gestione del protocollo di comunicazione tra due macchine.



Domanda...il protocollo e' a lunghezza fissa ? ha un carattere di fine
pacchetto ?
Sko

 Sko
Si, perchč?



Perche' nulla sarebbe + semplice che fare un thread dedicato alla ricezione,
con attesa su nbyte e terminatore preassegnato
Dimmi poi che c'e' pure il carattere di inizio pacchetto (STX) e siamo a
cavallo
Sko

 cavallo
Be fino a questo punto mi trovo, ma che succede se nei dati compare
l'STX?!
In genere io lavoro con risoluzione a carattere per poter fare il
parsing di
quello che arriva.
Questo vale sia per dati a lunghezza fissa che variabile, ovviamente a
lunghezza
fissa sarebbe più semplice.
Come fai tu non rischi di spezzare il flusso in ingresso su un STX che
in realtà
non è un STX? O mi sto sbagliando?


 wrote in message
news:10c5d3b5ee5e06b68f4...@mygate.mailgate.org...

Se il protocollo ha carattere iniziale/finale (STX/ETX) assegnati, lunghezza
fissa, basta scaricare il buffer di ricezione fino all'ETX x
sincronizzarsi.... e poi stare in attesa del pkg successivo... non so che
tipo di protocollo tu debba implementare, ma diciamo che all'80% di quelli
che ho sviluppato io cosi' me la son cavata
SKo

 Grazie.
.. aiutami a capire.
Perché dici che quella soluzione è un polling? 
Usi Windows 3.X ???
Cosa intendi per polling?



Ritengo intenda che il thread READER starebbe bloccato (forse non sa che le
chiamate di lettura han dei timeout...) ... mentre lui vorrebbe lavorare
Interrupt-like, con uan procedura scatenata da un evento di carattere
ricevuto...
Sko

 
Se vuoi un po' di pappa pronta, ho sviluppato una dll che permette di 
connettersi, indifferentemente, a seriale, modem e lan.
In lan si puň usare sia come client che server, tcp/udp.
Ho anche predisposto una classe per rendere piů agevole l'interfacciamento.
Se ti interessa posso darti altre info.
Naturalmente il tutto free to use.
Ciao
P.S. C++

 Sko
Ma è esattamente quello che fa il thread di lavoro! :)


 JJ
-- 



Xke' scusa, secondo te un thread che rimane in readfile (con le appropriate
configurazioni COMMTIMEOUT....) che fa, consuma cpu ?!?!?!?
Sko

 seriali o meglio essere interrotto da questi?!
Interrotto è una parola grossa. Al limite puoi bloccare quello che vuoi
con le funzioni del multithreading.
Sulle prestazioni posso garantire che non mi hanno ancora deluso.
D'altronde il chip UART ha un suo buffer....
[cut]
Ciao


Luigi Simonato scrive:
<omissis>
:: Poi non so se un thread che controlla la seriale possa essere più o
:: meno performante ma la cosa che mi interessa è: Windows può
:: intercettare i dati
:: seriali o meglio essere interrotto da questi?!
:
: Interrotto è una parola grossa. Al limite puoi bloccare quello che
: vuoi con le funzioni del multithreading.
:
: Sulle prestazioni posso garantire che non mi hanno ancora deluso.
: D'altronde il chip UART ha un suo buffer....
Confermo.
La soluzione di Luigi e' ottimale (benche' non l'abbia consultata...).
Se si associa un thread dedicato agli eventi della porta seriale (magari con 
la WaitCommEvent()), la migrazione concettuale - per chi era abituatao sotto 
MsDos a mascherare l'interruzione hardware con una propria Isr (Interrupt 
service routine) - e' minima.
La performance e' garantita: a suo tempo ebbi occasione di verificarla 
tramite test effettuati con programmi scritti per MsDos ad alte velocita' 
(115200 b/s)
susanna

 (115200 b/s)
Idem ma tra due applicazioni Win, con opportuno cavo e connettori!
E "visto che ci sono" riporto anche un link che riconferma ulteriormente
la tecnica 
http://www.codeproject.com/system/serial_com.asp
Saluti 


 JJ
Certo che pero' creare un thread per ogni carattere mi sembra un
overhead inutile.
Visto che l'iniziatore del post non ha piu' risposto penso abbia
trovato la sua strada.
Se qualcuno vuol comunque provare la mia soluzione ho messo su
http://www.ennepisoft.it
 una semplice pagina con un unico link valido
per scaricare il tutto.
Non ci sono i sorgenti della dll, ma ci sono quelli del programma che
la utilizza (non sono compilabili perche' usano un mio framework) come
esempio di utilizzo.
Saluti

 esempio di utilizzo.
Ma in soldoni... cosa fa la dll? Che tecnica usi?



Diciamo che tutte le comunicazioni usano una base comune con buffer
circolare (produttore-consumatore) e sezioni critiche.
Specificatamente per la seriale questo e' il nocciolo del thread di
ascolto:
<...>
  while(!onError) {
    DWORD mask = 0;
    WaitCommEvent(id, &mask, &Comm->oRead);
    if(EV_RXCHAR == (mask & EV_RXCHAR))
      while(Comm->isConnected() && Comm->readString(onError))
        Sleep(0);
    else {
      DWORD result = WaitForSingleObject(Comm->oRead.hEvent, INFINITE);
      if(WAIT_OBJECT_0 == result) {
        while(Comm->isConnected() && Comm->readString(onError))
          Sleep(0);
        }
      }
    if(!Comm->isConnected())
      break;
    }
<...>
poi nella readString() c'e' tutta la gestione per l'overlapped,
trasferimento al buffer circolare (sincronizzato dalle sezioni
critiche) ed eventuale segnalazione di dati pronti.
La scritura e', invece, sempre sincrona ed eseguita nel thread del
chiamante.
Comunque la comodita' della dll (e della classe che ne semplifica
l'uso) è che stabilito il tipo di comunicazione i dati si leggono e
scrivono allo stesso modo.
Saluti.

logicMind scrive:
:: susanna ha scritto:
::
:: [cut]
::: (magari con la WaitCommEvent()),
:: [cut]
::
:: Confermo, e manca nel codice che ho indicato perché
:: estratto brutalmente da un'applicazione che fa un check, in fase
:: di inizializzazione, sulla presenza del dispositivo (sulla seriale).
::
:: Senza WaitCommEvent e senza dispositivo collegato dovrebbe
:: innescare un treno velocissimo di thread inutili, se non ricordo
:: male... :)
: Certo che pero' creare un thread per ogni carattere mi sembra un
: overhead inutile.
Preciso: il thread e' *uno* solo, all'interno del quale la WaitCommEvent() 
pone in stato di 'bloccato' il thread stesso.
In questo modo il thread non usa tempo Cpu, ovvero non aggiunge alcun 
overhead all'applicazione. Non appena un evento seriale viene acquisisto dal 
sistema (es. ricezione di un carattere), la WaitCommEvent() 'sveglia' il 
thread che, a questo punto puo' elaborare il codice di servizio (cosi' come 
una Isr).
Naturalmente il thread - che e' implementato ad esempio come ciclo infinito, 
si 'riaddormenta' alla successiva chiamata WaitCommEvent().
: Se qualcuno vuol comunque provare la mia soluzione ho messo su
: 
http://www.ennepisoft.it
 una semplice pagina con un unico link valido
: per scaricare il tutto.
: Non ci sono i sorgenti della dll, ma ci sono quelli del programma che
: la utilizza (non sono compilabili perche' usano un mio framework) come
: esempio di utilizzo.
Interessante.
susanna

 trasferimento al buffer circolare 
[cut]
Sě, il fatto di avere un solo thread con all'interno un ciclo while
invece che una catena di thread puň essere una miglioria.
Ben fatto. ;)



Se ti servono maggiori info, chiedi (chiedete) pure :-)
Ciao
P.S. Ho ridato un'occhiata al programma e ho visto che manca la
terminazione (o almeno una innocente funzione vuota)  in caso non trovi
la dll.
Beh, l'avviso lo da, poi se uno va avanti lo stesso...
Se a qualcuno interessa anche il programma vedro' di aggiustarlo :-)
ariciao

Grazie a tutti,
in effetti ho utilizzato la soluzione con thread anche io,
per l'ottimizzazione faccio una lettura di più caratteri nel
buffer di seriale quando rilevo l'evento EV_RXCHAR!
In effetti avendo per lo più utilizzato programmazione della seriale a
basso
livello (micro o ms-dos) e come dicevo, solo attraverso librerie, mi
chiedevo
se l'utilizzo del thread non aggiungesse un overhead al processo di
controllo
ma in effetti non è così...
Per la cronaca l'applicativo Win32 dialoga a 115K con un
microcontrollore...
Grazie ancora!!


Grazie a tutti,
in effetti ho utilizzato la soluzione con thread anche io,
per l'ottimizzazione faccio una lettura di più caratteri nel
buffer di seriale quando rilevo l'evento EV_RXCHAR!
In effetti avendo per lo più programmato la seriale a
basso
livello (micro o ms-dos) e come dicevo, con Windows solo attraverso


 solo se effettivamente mi arriva un dato?!
Si, programmazione asincrona che per fortuna Windows supporta anche per
la seriale.
Leggi la documentazione relativa a GetOverlappedResult.
Se vuoi tutto pronto ed usabile, ad alto livello, dai una occhiata a
Twisted (usa Python):
http://twistedmatrix.com/trac/
Ecco un esempio scemo per il dialing tramite modem:
from twisted.python import log, usage
import sys
if sys.platform == 'win32':
    from twisted.internet import win32eventreactor
    win32eventreactor.install()
from twisted.internet.protocol import Protocol
from twisted.internet import reactor
from twisted.internet.serialport import SerialPort
class Echo(Protocol):
    def connectionMade(self):
        self.transport.write("ATE1\r")
    def dataReceived(self, data):
        sys.stdout.write(data)
        self.transport.write("ATDxxx\r")
s = SerialPort(Echo(), "COM3", reactor)
reactor.run()
Saluti  Manlio Perillo

 service routine) - e' minima.
non è tanto vero... parlando in generale in Win32 le notifiche asincrone
stile interruzione non esistono, al contrario di Unix che ha i segnali, e
questo comporta accorgimenti diversi nella scrittura del codice che
gestisce la notifica: un signal handler in un programma single-threaded su
Unix deve sempre essere preparato all'idea che dati a scope globale non
sincronizzati si trovino in uno stato inconsistente al momento
dell'esecuzione dell'handler, mentre questo non è vero in un programma
single-threaded per Win32, dove il programmatore è sempre al corrente di
dove/come/quando sta per ricevere una notifica e può ripristinare la
consistenza dei suoi dati globali prima che essa arrivi.


mrnt scrive:
: susanna ha scritto:
:
:: Se si associa un thread dedicato agli eventi della porta seriale
:: (magari con la WaitCommEvent()), la migrazione concettuale - per chi
:: era abituatao sotto MsDos a mascherare l'interruzione hardware con
:: una propria Isr (Interrupt service routine) - e' minima.
:
: non č tanto vero... parlando in generale in Win32 le notifiche
: asincrone stile interruzione non esistono, al contrario di Unix che
: ha i segnali, e questo comporta accorgimenti diversi nella scrittura
: del codice che gestisce la notifica: un signal handler in un
: programma single-threaded su Unix deve sempre essere preparato
: all'idea che dati a scope globale non sincronizzati si trovino in uno
: stato inconsistente al momento dell'esecuzione dell'handler, mentre
: questo non č vero in un programma single-threaded per Win32, dove il
: programmatore č sempre al corrente di dove/come/quando sta per
: ricevere una notifica e puņ ripristinare la consistenza dei suoi dati
: globali prima che essa arrivi.
Curioso che, parlando io di modelli in Win32 e MsDos, mi si obietti circa 
Unix e si glissi su MsDos.
Una Isr su MsDos, come chi ha introdotto l'argomento ha riportato, si 
avvicina maggiormente al modello del codice del thread sotto Win32 anche 
rispetto al punto di vista che hai riportato (prendendo per buone le cose 
che dici su Unix che non conosco a sufficienza, in questo senso).
Infatti le Isr in MsDos fanno regolarmente uso di dati globali 
opportunamente definiti e utilizzati dal codice rimanente.
susanna

 opportunamente definiti e utilizzati dal codice rimanente.
certo, ma devono essere presenti dei meccanismi che assicurano la
consistenza dei dati nel momento in cui vi si accede, cosa non
necessariamente vera per Win32; mi spiego meglio: un interrupt handler
naturalmente può essere richiamato in qualsiasi momento, nel senso che non
si può sapere a che punto sarà arrivata l'esecuzione del codice rimanente
quando salta l'interrupt (a meno che ovviamente non si tratti di
un'interrupt software); se il codice rimanente era arrivato ad un punto in
cui alcune variabili globali erano in uno stato inconsistente (ad es.
stava ricopiando una stringa in un array di char, ma non è arrivato a
porre il terminatore NULL) a quei dati l'ISR non vi può accedere: la
stringa non è NULL-terminated.
questo è precisamente lo stesso problema che si presenta su Unix durante
l'esecuzione di un signal handler; la soluzione dipende molto dal design e
dal tipo di interrupt o segnale, e a pensarci bene la sincronizzazione non
è affatto una soluzione (ho sbagliato io prima).
in Win32 invece questo non potrebbe mai essere un reale problema (parlando
sempre di programmi single-threaded ovviamente): in Win32 io so
perfettamente quand'è che vengono chiamate le mie callback, so
perfettamente quand'è che arrivano i messaggi alle mie finestre (poiché
verranno spediti dalla DispatchMessage che io chiamo), ed infine, so anche
perfettamente quand'è che entro in stato di wait e ho APC in coda.


 termine del suo quanto, [...]
o da una esplicita richiesta da parte del thread stesso ovviamente


mrnt scrive:
:: Curioso che, parlando io di modelli in Win32 e MsDos, mi si obietti
:: circa Unix e si glissi su MsDos.
:
: era per chiarire bene cos'è che manca a Win32: precisamente i segnali
: :)
Ok, facciamo un po' di accademia, che a volte e' anche piacevole.
:: Una Isr su MsDos, come chi ha introdotto l'argomento ha riportato, si
:: avvicina maggiormente al modello del codice del thread sotto Win32
:: anche rispetto al punto di vista che hai riportato (prendendo per
:: buone le cose che dici su Unix che non conosco a sufficienza, in
:: questo senso).
:
: eh no, ti assicuro che le ISR assomigliano decisamente di più ai
: signal handlers di Unix che a qualsiasi tipo di notifica che puoi
: avere su Windows: il design del kernel di per se' non prevede niente
: che assomigli all'interrompere l'esecuzione di un thread per un
: motivo diverso dal termine del suo quanto, e secondo me è un bene
: visto che ad ogni modo si riesce a far funzionare tutto anche così:
: dei signal non ne sento affatto la mancanza insomma :P
Per quanto gia' detto, non mi pronuncio.
:: Infatti le Isr in MsDos fanno regolarmente uso di dati globali
:: opportunamente definiti e utilizzati dal codice rimanente.
:
: certo, ma devono essere presenti dei meccanismi che assicurano la
: consistenza dei dati nel momento in cui vi si accede, cosa non
: necessariamente vera per Win32; mi spiego meglio: un interrupt handler
: naturalmente può essere richiamato in qualsiasi momento, nel senso
: che non si può sapere a che punto sarà arrivata l'esecuzione del
: codice rimanente quando salta l'interrupt (a meno che ovviamente non
: si tratti di un'interrupt software); se il codice rimanente era
: arrivato ad un punto in cui alcune variabili globali erano in uno
: stato inconsistente (ad es. stava ricopiando una stringa in un array
: di char, ma non è arrivato a porre il terminatore NULL) a quei dati
: l'ISR non vi può accedere: la stringa non è NULL-terminated.
D'accordo..
Anche se tu stai descrivendo una scorrettezza nella scrittura del codice 
piu' che una caratteristica del modello.
Dato che l'esecuzione di una Isr e', per definizione, asincrona, un codice 
che non ne tenga conto e' semplicemente un codice errato. E il codice errato 
non fa testo.
<omissis>
: in Win32 invece questo non potrebbe mai essere un reale problema
: (parlando sempre di programmi single-threaded ovviamente): in Win32
: io so perfettamente quand'è che vengono chiamate le mie callback, so
: perfettamente quand'è che arrivano i messaggi alle mie finestre
: (poiché verranno spediti dalla DispatchMessage che io chiamo), ed
: infine, so anche perfettamente quand'è che entro in stato di wait e
: ho APC in coda.
Qui pero' c'e' un equivoco. Io stavo parlando, esplicitamente, di codice 
multi thread per Win32.
E' proprio il thread sincronizzato sull'evento della 'seriale' che imita, 
per cosi' dire, il modello MsDos della Isr associata all'interruzione 
hardware.
In questo scenario il thread sincronizzato sulla WaitCommEvent() (o 
qualsiasi altra chiamata associata ad un evento asincrono) risulta essere un 
modello sufficientemente simile a MsDos + Isr: in entrambi i casi abbiamo 
l'esecuzione di un codice di fatto 'asincrono' rispetto alla porzione di 
codice principale.
susanna

 codice principale.
si si, adesso ho capito, ma ancora una volta c'è una significativa
differenza tecnica tra i due modelli, anzi tra i 3 volendo aggiungere
anche Unix e segnali:
1) in Win32 il codice asincrono viene eseguito nel contesto di un altro
thread;
2) in Unix il codice di un signal handler viene eseguito nello stesso
contesto del thread primario per programmi monothread, non so cosa succeda
nei multithread; suppongo che venga eseguito nello stesso contesto del
thread che ha chiamato la funzione signal per installare il signal handler;
3) in MS-DOS non ha nemmeno senso parlare di thread.
conseguenze? semplice, ad ulteriore dimostrazione della maggiore
somiglianza del DOS con Unix piuttosto che col modello Win32 nel contesto
di questo problema, Win32 è l'unico dei 3 modelli dove il codice asincrono
viene eseguito CONTEMPORANEAMENTE al codice "primario". ovvero:
- in MS-DOS, che io sappia e se ricordo bene, le interrupt possono
annidarsi solo quando una ISR ne chiama esplicitamente un'altra con INT
(le nested interrupts in altre parole sono tutte di tipo software, mai
hardware o internal);
- in Unix un signal handler viene chiamato nello stesso contesto del
thread che ha chiamato la funzione signal per registrarlo, quindi il
codice di un signal handler non può essere eseguito contemporaneamente a
quello principale del thread;
- in Win32 stiamo simulando l'idea di ISR utilizzando *due* threads, uno
dei quali, implementato a ciclo infinito, si blocca su una funzione ed
esegue il codice di gestione della nostra ISR ideale solo nei momenti in
cui si sblocca, cosa che non provoca l'interruzione dell'esecuzione
dell'altro thread.
faccio uno schema, visualizzalo con un font monospace:
 Unix        DOS         Win32
CP ISR      CP ISR      CP ISR
 |           |           |
 |           |           |
    |           |        |  |
    |           |        |  |
    |           |        |  |
    |           |        |  |
 |           |           |
 |           |           |
 |           |           |
 |           |           |
dove "CP" nello schema sta per "codice principale".
capito la differenza? :)
le linee verticali rappresentano i flussi di esecuzione del codice
principale e delle ISR, ideali o meno che siano; Win32 è l'unico modello
dove il flusso del codice principale non viene interrotto dall'esecuzione
della ISR, e ciò che cerco di dire dal primo post in questo topic (:D) è
che la cosa comporta nella scrittura del codice accortezze importanti che
sono differenti dai modelli MS-DOS e Unix.
tra l'altro è anche il motivo percui mi confondevo sull'uso della
sincronizzazione per la soluzione al problema: in DOS non ha senso, vabbè,
ma in Unix l'uso di mutex e semafori non potrebbe mai costituire una
soluzione al problema; IN WIN32 INVECE SI!! ;)
per il resto penso che la questione sia ben chiara ad entrambi, inutile
quindi ripetersi sulle stesse cose :)
PS: in quest'ultima parte del post parlando di modello Win32 mi riferivo
in realtà principalmente alla specifica soluzione di Luigi Simonato che
usa un thread secondario per bloccarsi sulla WaitCommEvent; ma suppongo
che sia un approccio abbastanza comune.
PPS: sono preoccupato, la mia mente sta diventando sempre più contorta :|
lo si vede sia dalle frasi che riesco a mettere miracolosamente assieme,
sia dai labirintici ragionamenti che riesco a produrre :|
