View Full Version : [C++] un timer con ciclo while per lettura su porta seriale in linux
DomusP45
03-05-2012, 19:08
Salve a tutti...
sto impazzendo per fare una lettura sulla porta seriale vincolata ad un timer con un ciclo while.
Il codice che ho usato è:
int Timeout = 3;
long endValue = clock()+CLOCKS_PER_SEC* Timeout;
//primo tentativo di lettura
int i = PollComport(Pnum,buffer);
//se non ha letto niente (i==0)
while (clock()<endValue || i>0){
i = PollComport(Pnum,buffer);
sleep(1);
}
La funzione PollComport è una funziona di lettura seriale della libreria che sto usando (in pratica è una read) che restituisce il numero di byte letti (li ritorna in i).
Pertanto a me serve che questo ciclo finisca o perchè ha letto (quindi i è diventata diversa da zero) o perchè è finito il tempo di attesa, impostato a priori cioè 3 secondi.
Perchè non funziona? Rimane bloccato....
Aiutatemi a capire il problema dove sta.
Grazie anticipatamente a tutti quelli che mi risponderanno...
[Kendall]
04-05-2012, 09:18
Salve a tutti...
sto impazzendo per fare una lettura sulla porta seriale vincolata ad un timer con un ciclo while.
Il codice che ho usato è:
int Timeout = 3;
long endValue = clock()+CLOCKS_PER_SEC* Timeout;
//primo tentativo di lettura
int i = PollComport(Pnum,buffer);
//se non ha letto niente (i==0)
while (clock()<endValue || i>0){
i = PollComport(Pnum,buffer);
sleep(1);
}
La funzione PollComport è una funziona di lettura seriale della libreria che sto usando (in pratica è una read) che restituisce il numero di byte letti (li ritorna in i).
Pertanto a me serve che questo ciclo finisca o perchè ha letto (quindi i è diventata diversa da zero) o perchè è finito il tempo di attesa, impostato a priori cioè 3 secondi.
Perchè non funziona? Rimane bloccato....
Aiutatemi a capire il problema dove sta.
Grazie anticipatamente a tutti quelli che mi risponderanno...
Domus come ti accennavo nella passata discussione, se imposti il tempo di timeout a 3 secondi, come nell'esempio, lui deve per forza uscire dopo 3 secondi, o al verificarsi della seconda condizione (in quanto il codice legato al timer è corretto). L'unica motivazione per la quale il ciclo può rimanere bloccato è perchè l'esecuzione stessa del PollComport(...) sia bloccante (analogamente ad un cin.get(), per fare un esempio). Se ci posti il codice di quella funzione (o la documentazione riguardante se non è opensource) potremmo aiutarti più facilmente.
DomusP45
04-05-2012, 09:28
;37389465']Domus come ti accennavo nella passata discussione, se imposti il tempo di timeout a 3 secondi, come nell'esempio, lui deve per forza uscire dopo 3 secondi, o al verificarsi della seconda condizione (in quanto il codice legato al timer è corretto). L'unica motivazione per la quale il ciclo può rimanere bloccato è perchè l'esecuzione stessa del PollComport(...) sia bloccante (analogamente ad un cin.get(), per fare un esempio). Se ci posti il codice di quella funzione (o la documentazione riguardante se non è opensource) potremmo aiutarti più facilmente.
Ti posto il link della libreria che uso, dove sono spiegate le varie funzioni.
http://www.teuniz.net/RS-232/
tra le varie cose dice appunto di collegarla ad un timer...
Il problema è che per valutare il tempo trascorso stai usando clock().
http://linux.die.net/man/3/clock :read:
clock() restituisce il tempo di cpu usato dal programma, non il tempo "assoluto" che è trascorso.
In pratica ti dice per quanto tempo il tuo programma ha usato cicli di clock della cpu ma nel frattempo la cpu potrebbe essere stata usata per eseguire altra roba (e quel tempo non viene messo in conto).
Questa funzione è molto utile per misurare con buona approssimazione le prestazioni di un programma anche se c'è altra roba in esecuzione, ma è disastrosa in loop che attendono andando in sleep (smettendo di consumare tempo di cpu durante il "sonno").
Il tuo loop fa un check sulle condizioni di uscita (roba che richiede nanosecondi di tempo di cpu usato dal programma)
prova a leggere dalla seriale (roba da microsecondi di tempo di cpu usato dal programma, visto che non resta in attesa continuando a rileggere)
e poi va in sleep per 1 secondo di tempo reale (anche questo richiede microsecondi di tempo di cpu usato dal programma, perchè mentre il tuo programma è in attesa non "consuma" tempo sulla cpu).
In pratica ad ogni ciclo si attende circa 1 secondo di tempo "assoluto", ma il tempo effettivamente speso ad eseguire istruzioni sulla cpu (quello misurato con clock() ) è tanto se è 10 microsecondi per ciclo.
Significa che per "consumare" i 3 secondi di "tempo di cpu speso ad eseguire istruzioni del tuo programma" potrebbero servire tranquillamente 300000 secondi e passa di tempo "assoluto" (qualche giorno :rolleyes: ).
Semmai al posto di clock() usa clock_gettime() e le altre funzioni collegate ad essa:
http://linux.die.net/man/2/clock_gettime
Che invece restituisce il tempo "reale" trascorso.
Ricordati anche di gestire il wrap-around del clock altrimenti ti causerà un altra situazione disastrosa di "attesa per giorni" quando "conteggio_timer_iniziale"+"intervallo di attesa" supera il valore massimo di conteggio del tempo restituito dalla funzione.
DomusP45
05-05-2012, 20:41
ma non c'è un modo alternativo per avere una gestione del genere? insomma, un timeout di 3 secondi massimo per una lettura?
La libreria che uso è quella che ho indicato...e dice che per la lettura conviene legarla ad un timer...ora, come lo faccio un timer del genere?
Alla fine quello che mi serve che faccia è semplice, relativamente poco complicato credo rispetto a cicli molto più complessi...
cosa posso usare invece di un while?
ma non c'è un modo alternativo per avere una gestione del genere? insomma, un timeout di 3 secondi massimo per una lettura?
Se ti basta un tempo di attesa approssimato è sufficiente fare questo:
int k;
.....
k= 3; // attendi per massimo tre volte
while (k>0 || i>0){
i = PollComport(Pnum,buffer);
sleep(1); // vai in sleep per circa 1 secondo
if (k>0) --k; // decrementa conteggio attesa
}
Occhio che comunque ci sarebbe parecchia roba da migliorare.
Ad esempio di solito quando si leggono dati dalla seriale, subito dopo PollComport() si dovrebbe verificare se sono arrivati tutti i dati ed uscire subito dal ciclo invece di attendere lo scadere del timeout.
Anche a 9600 bit/s in un secondo fai in tempo ad esmepio a trasmettere circa 480 byte e riceverne di risposta 480 (ipotizzando una comunicazione master-slave).
Quindi sarebbe conveniente ridurre il tempo di attesa per ciclo (usando ad esempio la funzione nanosleep (http://linux.die.net/man/2/nanosleep) ) in modo da poter uscire dal ciclo molto prima dello scadere del tempo di attesa massimo di 3 secondi.
(es: con un ciclo che fa un attesa di 1/100 di secondo e itera per massimo 300 volte ).
Inoltre, se i dati arrivano in più spezzoni, devi prenderli da buffer e copiarli altrove (appendendoli a quelli arrivati prima), altrimenti poi alla fine del ciclo hai solo l'ultimo spezzone arrivato ed avrai invece scartato quelli che avevi letto prima nelle iterazioni precedenti.
Non sapendo che formato hanno i dati o che protocollo di comunicazione utilizzi (es: MODBUS) è difficile darti altre indicazioni, ma tieni conto di quanto detto sopra.
Ci sarebbero metodi più sofisticati tipo usare i POSIX timer (http://linux.die.net/man/2/timer_create) ecc. ecc. ma quelli te li dovrai studiare con calma per conto tuo (fai prima che a colpi di domande e risposte sul forum ed in rete si trova documentazione più che adeguata a riguardo).
DomusP45
06-05-2012, 10:54
Se ti basta un tempo di attesa approssimato è sufficiente fare questo:
int k;
.....
k= 3; // attendi per massimo tre volte
while (k>0 || i>0){
i = PollComport(Pnum,buffer);
sleep(1); // vai in sleep per circa 1 secondo
if (k>0) --k; // decrementa conteggio attesa
}
Occhio che comunque ci sarebbe parecchia roba da migliorare.
Ad esempio di solito quando si leggono dati dalla seriale, subito dopo PollComport() si dovrebbe verificare se sono arrivati tutti i dati ed uscire subito dal ciclo invece di attendere lo scadere del timeout.
Anche a 9600 bit/s in un secondo fai in tempo ad esmepio a trasmettere circa 480 byte e riceverne di risposta 480 (ipotizzando una comunicazione master-slave).
Quindi sarebbe conveniente ridurre il tempo di attesa per ciclo (usando ad esempio la funzione nanosleep (http://linux.die.net/man/2/nanosleep) ) in modo da poter uscire dal ciclo molto prima dello scadere del tempo di attesa massimo di 3 secondi.
(es: con un ciclo che fa un attesa di 1/100 di secondo e itera per massimo 300 volte ).
Inoltre, se i dati arrivano in più spezzoni, devi prenderli da buffer e copiarli altrove (appendendoli a quelli arrivati prima), altrimenti poi alla fine del ciclo hai solo l'ultimo spezzone arrivato ed avrai invece scartato quelli che avevi letto prima nelle iterazioni precedenti.
Non sapendo che formato hanno i dati o che protocollo di comunicazione utilizzi (es: MODBUS) è difficile darti altre indicazioni, ma tieni conto di quanto detto sopra.
Ci sarebbero metodi più sofisticati tipo usare i POSIX timer (http://linux.die.net/man/2/timer_create) ecc. ecc. ma quelli te li dovrai studiare con calma per conto tuo (fai prima che a colpi di domande e risposte sul forum ed in rete si trova documentazione più che adeguata a riguardo).
Allora, ti ringrazio delle info e della disponibilità. Su questo forum riesco sempre ad avere le idee più chiare. Penso che il "nanosleep" (che sarebbe usleep) potrebbe essere la soluzione, anche perchè a me arrivano array di massimo 21 byte, ma proprio al massimo...la stragrande maggioranza sono da 10-12 byte. Quindi un ciclo indipendente dal clock ma con uno sleep ed una variabile posticcia come la k indicata da te potrebbe pure andare, ma a me serviva il fatto dei secondi perchè è richiesto che questa classe che sto scrivendo riceva in input dall'utente quanto vuole sia il timeout (in secondi) che vuole attendere la risposta sulla seriale. Quindi cercando in giro sulla rete quello che ho trovato era quello che ho scritto sopra. Però, ora ho le idee più chiare riguardo alla funzione "clock()" e suoi derivati...visto quello che mi hai detto...lo terrò presente.
[Kendall]
06-05-2012, 16:38
Il problema è che per valutare il tempo trascorso stai usando clock().
http://linux.die.net/man/3/clock :read:
clock() restituisce il tempo di cpu usato dal programma, non il tempo "assoluto" che è trascorso.
In pratica ti dice per quanto tempo il tuo programma ha usato cicli di clock della cpu ma nel frattempo la cpu potrebbe essere stata usata per eseguire altra roba (e quel tempo non viene messo in conto).
Questa funzione è molto utile per misurare con buona approssimazione le prestazioni di un programma anche se c'è altra roba in esecuzione, ma è disastrosa in loop che attendono andando in sleep (smettendo di consumare tempo di cpu durante il "sonno").
Il tuo loop fa un check sulle condizioni di uscita (roba che richiede nanosecondi di tempo di cpu usato dal programma)
prova a leggere dalla seriale (roba da microsecondi di tempo di cpu usato dal programma, visto che non resta in attesa continuando a rileggere)
e poi va in sleep per 1 secondo di tempo reale (anche questo richiede microsecondi di tempo di cpu usato dal programma, perchè mentre il tuo programma è in attesa non "consuma" tempo sulla cpu).
In pratica ad ogni ciclo si attende circa 1 secondo di tempo "assoluto", ma il tempo effettivamente speso ad eseguire istruzioni sulla cpu (quello misurato con clock() ) è tanto se è 10 microsecondi per ciclo.
Significa che per "consumare" i 3 secondi di "tempo di cpu speso ad eseguire istruzioni del tuo programma" potrebbero servire tranquillamente 300000 secondi e passa di tempo "assoluto" (qualche giorno :rolleyes: ).
Semmai al posto di clock() usa clock_gettime() e le altre funzioni collegate ad essa:
http://linux.die.net/man/2/clock_gettime
Che invece restituisce il tempo "reale" trascorso.
Ricordati anche di gestire il wrap-around del clock altrimenti ti causerà un altra situazione disastrosa di "attesa per giorni" quando "conteggio_timer_iniziale"+"intervallo di attesa" supera il valore massimo di conteggio del tempo restituito dalla funzione.
Non voglio dire tu non abbia ragione, anche perchè la mia esperienza con le funzioni dell'header ctime sono ristrette alla realizzazione di una sola classe per la creazione di un cronometro, ma nelle occasioni che ho avuto di utilizzarlo, non mi ha mai dato problemi in abbinata con la funzione Sleep(), quindi quello che dici mi lascia un po' perplesso sinceramente.
Per fare un esempio banale, il seguente codice, da quel che dici, non dovrebbe funzionare, invece se compilato attende proprio il tempo specificato (che poi è lo stesso bene o male che avevo consigliato a Domus).
#include <iostream>
#include <ctime>
#include <windows.h>
using namespace std;
int main()
{
int seconds = 3;
cout << clock() << endl;
clock_t endTime = clock() + seconds * CLOCKS_PER_SEC;
while (clock() <= endTime) {
Sleep(1);
}
cout << clock() << endl;
cin.get();
}
;37402100']Non voglio dire tu non abbia ragione, anche perchè la mia esperienza con le funzioni dell'header ctime sono ristrette alla realizzazione di una sola classe per la creazione di un cronometro, ma nelle occasioni che ho avuto di utilizzarlo, non mi ha mai dato problemi in abbinata con la funzione Sleep(), quindi quello che dici mi lascia un po' perplesso sinceramente.
La risposta alle tue perplessità sta nell'esempio che hai scritto subito dopo. ;)
;37402100']
#include <windows.h>
Windows, non Linux.
Solo perchè due funzioni hanno lo stesso nome e parametri compatibili non è detto che siano uguali in tutto e per tutto, specialmente se vengono linkate da librerie differenti.
Visti i problemi di DomusP45 e la libreria (crossplatform) che utilizzava ho ipotizzato che stesse usando la clock() POSIX (Linux ha lo standard POSIX come riferimento per le funzioni di base del kernel e le librerie fondamentali).
Da non confondere con la clock() della C library standard (nel senso di ISO/IEC 9899:1990 e ISO/IEC 9899:1990/DAM 1)
o con la clock() di Visual C++ (ANSI 4.12.2.1), queste ultime due sono quasi uguali anche come funzionamento in esecuzione.
Avendo già avuto a che fare con porting vari e con software crossplatform avevo già avuto modo di farmi le ossa con problemi simili.
A volte non ci si rende manco conto di aver linkato librerie con "funzioni compatibili per nome e parametri" perchè magari si è azzeccato l'ordine di inclusione per puro caso, poi magari si cambia qualcosa in un header e dopo in esecuzione succedono cose strane a cascata in posti che manco ci si aspetta.
Non è un caso che con il tempo sia diventato un fanatico di Qt
(thread, timer, UI, I/O XML e SVG, ecc. ecc. tutto multipiattaforma e ben collaudato).
[Kendall]
06-05-2012, 21:12
La risposta alle tue perplessità sta nell'esempio che hai scritto subito dopo. ;)
Windows, non Linux.
Solo perchè due funzioni hanno lo stesso nome e parametri compatibili non è detto che siano uguali in tutto e per tutto, specialmente se vengono linkate da librerie differenti.
Visti i problemi di DomusP45 e la libreria (crossplatform) che utilizzava ho ipotizzato che stesse usando la clock() POSIX (Linux ha lo standard POSIX come riferimento per le funzioni di base del kernel e le librerie fondamentali).
Da non confondere con la clock() della C library standard (nel senso di ISO/IEC 9899:1990 e ISO/IEC 9899:1990/DAM 1)
o con la clock() di Visual C++ (ANSI 4.12.2.1), queste ultime due sono quasi uguali anche come funzionamento in esecuzione.
Avendo già avuto a che fare con porting vari e con software crossplatform avevo già avuto modo di farmi le ossa con problemi simili.
A volte non ci si rende manco conto di aver linkato librerie con "funzioni compatibili per nome e parametri" perchè magari si è azzeccato l'ordine di inclusione per puro caso, poi magari si cambia qualcosa in un header e dopo in esecuzione succedono cose strane a cascata in posti che manco ci si aspetta.
Non è un caso che con il tempo sia diventato un fanatico di Qt
(thread, timer, UI, I/O XML e SVG, ecc. ecc. tutto multipiattaforma e ben collaudato).
Si ma nel caso specifico utilizzavo la libreria standard. Effettivamente non avendo l'intero listato di Domus non possiamo sapere quale libreria abbia usato, ma a suo tempo, quando gliel'avevo consigliata, ho fatto esplicito riferimento alla libreria "ctime".
Comunque ultimamente pure io, pur usando soprattutto windows, mi sto dedicando parecchio alle librerie QT. E' un framework a dir poco poderoso... Completo e affidabile... Ho solo qualche dubbio sul suo futuro dopo gli ultimi svolgimenti, ma spero che il C++ sarà ancora il fulcro di questo splendido framework.
DomusP45
06-05-2012, 22:03
Signori é sempre un piacere leggere come siete attenti alle problematiche di tutti,anche chi ne sa meno di voi (io Qt non so manco cos'é :-P )
Sono felice peró xké ne ho capito qualcosa in più,e a quanto pare con la modifica della variabile posticcia,sembra andare bene.
Grazie davvero tanto per la vostra disponibilità!!
[Kendall]
06-05-2012, 22:15
Signori é sempre un piacere leggere come siete attenti alle problematiche di tutti,anche chi ne sa meno di voi (io Qt non so manco cos'é :-P )
Sono felice peró xké ne ho capito qualcosa in più,e a quanto pare con la modifica della variabile posticcia,sembra andare bene.
Grazie davvero tanto per la vostra disponibilità!!
Dacci un'occhiata Domus, fidati: http://qt.nokia.com/downloads.
Oltre ad essere un framework molto valido, presenta pure quella che secondo me è, insieme a Visual Studio, la miglior ide per il c++ (anche con progetti generici, e non solo QT). Oltre a questo ha il vantaggio di essere multipiatta, a differenza di Visual Studio... Prova a installarla e vedrai che non te ne pentirai.
DomusP45
06-05-2012, 22:22
Non ne dubito...anzi,lo proverò di sicuro appena ho finito con sta classe per la pinza..che ormai é agli sgoccioli spero...
Ancora grazie e buona domenica!!
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.