Newsgroup: it.comp.lang.delphi
Titolo: Repeat until Keypressed

Ho il seguente problema:
devo acquisire, continuamente, dati dalla
porta parallela e mostrarli a video,
aggiornati in tempo reale.
Con il buon Pascal facevo
repeat
 acquisisci (x)
 writeln (x);
until keypressed
e tutto funzionava bene.
Ora sto provando a fare la stessa
cosa con Delphi, ma non ci riesco;
il dato non viene aggiornato in tempo
reale. Ho provato a mettere un bottone,
ma ogni volto che voglio vedere il nuovo
valore devo premere il bottone.
Mi date qualche dritta?
Grazie

 valore devo premere il bottone.
Posta un po' di codice.
--
- Carlo -

Ho il seguente problema:
devo acquisire, continuamente, dati dalla
porta parallela e mostrarli a video,
aggiornati in tempo reale.
Con il buon Pascal facevo
repeat
 acquisisci (x)
 writeln (x);
until keypressed
e tutto funzionava bene.
Ora sto provando a fare la stessa
cosa con Delphi, ma non ci riesco;
il dato non viene aggiornato in tempo
reale. Ho provato a mettere un bottone,
ma ogni volto che voglio vedere il nuovo
valore devo premere il bottone.
Mi date qualche dritta?
Grazie

Posta un po' di codice.
Ho usato Delphi 1.0, perche' mi da' ancora
la possibilita' di utilizzare il vecchio comando
"Port"; il sistema operativo e'  Win98/ME
BiSwitch e' il componente "interruttore"
che si trova in Delphi 1.0.
unit Unit1;
interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, ExtCtrls, VBXCtrl, Switch;
type
  TForm1 = class(TForm)
    Label1: TLabel;
    Label2: TLabel;
    Shape1: TShape;
    BiSwitch1: TBiSwitch;
    procedure BiSwitch1On(Sender: TObject);
    procedure BiSwitch1Off(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
const
    vref = 2.500;          { Tensione di riferimento dell'ADC }
    REGDATI = $378;        { Indirizzo del registro dati }
    REGSTATO = REGDATI+1;  { Indirizzo del registro di stato }
var
    i: 1..12;
    vin: real;
    peso, dato: word;
    ack: 0..1;
    acceso: boolean;
    b: byte; { Il Byte presente nel registro di stato }
implementation
{$R *.DFM}
procedure TForm1.BiSwitch1On(Sender: TObject);
{ Invia un impulso di clock all'ADC commutando da +5V a 0V il pin D0 }
procedure clock;
begin
  port[regdati]:=1; { Fronte di salita: /CS=0 CK=1 }
  port[regdati]:=0; { Fronte di discesa: /CS=0 CK=0 }
end;
begin
  BiSwitch1.Caption := 'ON';
  acceso := true;
 repeat
   port[regdati]:=2; { Reset iniziale ADC: CS=1 CK=0 }
   port[regdati]:=0; { Attiva ADC: /CS=0 CK=0 }
   clock; { Primo impulso di clock "vuoto" }
   clock; { Secondo impulso di clock "vuoto" }
   peso:=2048; { Il primo bit e' D11 ovvero l'MSB: 2 ^ 11}
   dato:=0;  { Azzera la variabile di accumulo del valore finale }
   for i:=1 to 12 do
     begin
       clock;  { Nel primo ciclo costituisce il terzo impulso di clock
"vuoto" }
       b:=port[regstato]; { Legge l'intero byte presente nel reg. di
stato }
       ack:= b and 64;  { Estrae il bit proveniente dall'ADC } 
       if ack <> 0 then dato:= dato + peso; { Se il bit=1 aggiungi il
suo peso }
         peso:=peso div 2; { Passa al peso del bit successivo }
       end;
   port[regdati]:= 2; { Disattiva l'ADC ponendo /CS=1 e CK=0 }
   vin:= dato * vref/4096; { Calcola il valore analogico Vin associato
al valore letto }
   Label1.Caption := FloatToStrF(vin, ffFixed, 7, 3);
 until acceso;
end;
procedure TForm1.BiSwitch1Off(Sender: TObject);
begin
 acceso := false;
 BiSwitch1.Caption := 'OFF';
 Label1.Caption := '';
end;
end.


Con il buon delphi se vuoi fare come gli antichi ci metti un timer
(componente TTimer) e sull'evento OnTimer ci metti il codice che aggiorna il
rusultato.
Quindi metti come interval 100 millisecondi (dovrebbero essere sufficienti)
e ottieni l'effetto desiderato a poco prezzo.
Ciao

e ottieni l'effetto desiderato a poco prezzo.
Perfetto. Provo subito.
Ciao

 end;
*credo* (uso il condizionale perchè non ho modo di provare) che il problema
sia il seguente:
il ciclo repeat - until fa schizzare verso l'alto l'uso della cpu del
sistema, per cui la tua applicazione non è in grado di gestire i messaggi di
"ridisegno" che gli arrivano da Windows.
Nel caso specifico Windows "invita" tua applicazione a cambiare il contenuto
della label tramite un messaggio, ma la tua applicazione essendo "occupata"
accoda il messaggio e si incaricherà di gestirlo appena possibile. Questo
"appena possibile" si concretizza in un "quando termina il ciclo", per cui
tu perdi il tuo "real-time".
Se questo è vero lo puoi vedere il pochi secondi. Subito dopo aver
modificato il contenuto della Label richiama Application.ProcessMessages() e
vedi se funziona.
In caso funzioni fammi sapere che ti consiglio una soluzione ancora migliore
per ottenere un risultato il più possibile vicino possibile a quello di un
sistema Real Time.
Ciao
--
- Carlo -

Perfetto. Provo subito.
Niente timer se vuoi il real-time.
I timer sono molto imprecisi, essendo, nella realta` dei fatti, dei
messaggi.  Si dovrebbe anche dire: niente Windows, se vuoi il
real-time.  Windows NON e`, notoriamente, un sistema operativo adatto
ad applicazioni RT.  In ogni caso, questo genere di applicazioni si
scrive sempre adottando un thread ad altissima priorita` per le
operazioni di acquisizione (meglio sarebbe avere un driver) e un
thread di aggiornamento dell'interfaccia a priorita` piu` bassa.


Infatti io ho detto "se vuoi fare come gli antichi" cioè so benissimo quello
ke tu hai detto ed è giusto, tuttavia è la soluzione più immediata
nonostante sia "alla buona".
Non credo che lui debba farci chissà cosa con questa applicazione quindi la
precisione assoluta non dovrebbe servirgli.
Se mi sbaglio avvisatemi.
Ciao

 precisione assoluta non dovrebbe servirgli.
Non vuol dire. Consigliare un timer è sbagliato anche perchè in questo modo
non preleveresti più i dati appena possibile, ma appena scatta l'interval
del timer. Questo significherebbe:
perdere notevoli quantitativi di misure intermedie e soprattutto ottenere
risultati neppure lontanamente paragonabili a quelli di un sistema
real-time. Non che con le altre soluzioni si abbiano risultati in real-time,
ma sicuramente di gran lunga (nell'ordine delle decine di volte) superiori a
quella del Timer


precisione assoluta non dovrebbe servirgli.
Non so su che base tu possa dire una cosa del genere.

invocare dal codice Application.ProcessMessages().
Non va bene neanche questo se si vuole un'acquisizione precisa.
Il codice di acquisizione dati non deve essere soggetto ai "comodi"
dell'interfaccia grafica.  L'interfaccia ha un'importanza relativa nei
sistemi che devono lavorare in tempo reale.  Se l'interfaccia perde
qualche colpo non c'e` problema, se si perde qualche dato, invece, che
arriva durante il tempo di esecuzione di
Application.ProcessMessages(), allora nascono i guai.

 Application.ProcessMessages(), allora nascono i guai.
Lo so benissimo. Infatti lo ho sottolineato anche nel mio messaggio:
"Nel caso specifico Windows "invita" tua applicazione a cambiare il
contenuto della label tramite un messaggio, ma la tua applicazione essendo
"occupata" accoda il messaggio e si incaricherà di gestirlo appena
possibile. Questo "appena possibile" si concretizza in un "quando termina il
ciclo", per cui tu perdi il tuo "real-time". Se questo è vero lo puoi vedere
il pochi secondi. Subito dopo aver modificato il contenuto della Label
richiama Application.ProcessMessages() e vedi se funziona. In caso funzioni
fammi sapere che ti consiglio una soluzione ancora migliore per ottenere un
risultato il più possibile vicino possibile a quello di un sistema Real
Time."
Il sistema che gli avrei suggerito è quello di cui ho anche parlato altre
volte, ovvero decentrare il codice di acquisizione in un Thread con priorità
REALTIME_PRIORITY e assegnare il ridisegno a procedure con priorità low se
non addirittura idle!
Sinceramente non ho capito il perchè di questo tuo intervento, quando sono
stato io stesso a precisare quello di cui parli sopra.
--
- Carlo -

stato io stesso a precisare quello di cui parli sopra.
Perche` nel messaggio a cui sto rispondendo, e` proposta una soluzione
"immediata", che soluzione, nei fatti, non e`.

 "immediata", che soluzione, nei fatti, non e`.
Non è soluzione, infatti, come non lo è nessun' altra tecnica in questo
contesto.
Parlare di operazioni real-time su un sistema Windows, di fatto, non prevede
soluzioni attendibili.
--
- Carlo -

contesto.
Vedi di darci un taglio, tu hai proposto una *soluzione* (ti ricordo
che hai scritto "se vuoi la soluzione piů immediata[...]invocare dal
codice Application.ProcessMessages()", poi affermi che questa non e`
una soluzione.  Vedi di deciderti.
L'altra tecnica proposta, come quella di suddividere l'elaborazione in
thread paralleli con priorita` diverse e`, comunque, la soluzione
giusta, perche` si fa tutto quanto e` in potere del programmatore per
evitare colli di bottiglia a livello software.
Poi e` rimandato al sistema operativo l'eventuale problema di
disallineare acquisizione/elaborazione.  Ma a quel punto si e` sicuri
che e` il s.o. a scazzare, non la progettazione del programma.

 evitare colli di bottiglia a livello software.
Per quanto tu ti intestardisci a dire che è la soluzione giusta questa non
lo è! Puoi suggerirla (come ho fatto io, d'altro canto) come soluzione più
performante per ottenere risultati simili a quelli di un sistema real-time,
ma visto che fai il pignolo ti faccio notare che tu non rispondi alla
richiesta "acquisire dati in real time", ma alla richiesta "ottenere
risultati il più possibile simili a quelli di un real-time".
Mi permetto di andare oltre e di segnalarti che aumentare la priorità dei
thread ha valore relativo nel contesto per due motivi fondamentali:
1 - la priorità dei Thread è seconda alla priorità dei processi, per cui un
processo con priorità maggiore della tua e con Thread e priorità più bassa
di quella realtime otterranno maggiore attenzione dal sistema operativo
2 - Più o meno dati alla mano, aumentare la priorità del binomio Processo -
Thread è sicuramente un buon esempio di programmazione, ma cambia ben poco
ai fini del reale servizio da parte di Windows.
Con questo spero di aver concluso!
Bye bye!
--
- Carlo -

Chiedo scusa.
Non era mia intenzione suscitare polemiche, ma forse
l'errore e' stato mio nel definire "real time"
quello di cui ho bisogno.
Per "real time" intendevo un aggiornamento ogni x
ms, magari impostabile (una sorta di "polling");
del resto questo programma dovra' acquisire, in una
seconda versione, anche
l'umidita' relativa, per cui sara' d'obbligo "multiplexare"
fra questi due dati di ingresso (l'ADC ha due
canali di ingresso e viene programmato attraverso due bit di
controllo, insomma come un normale MUX).
Le finalita' non sono professionali, ma didattiche,
per cui la precisione e' il fattore da tenere
in ultima considerazione.
Grazie ancora
Ciao a tutti

ms, magari impostabile (una sorta di "polling");
Ecco una possibile interfaccia grafica di un progetto simile:
http://www.geocities.com/CapeCanaveral/Launchpad/3632/pc_therm.htm
Sviluppato in Visual basic, io, ovviamente, voglio usare Delphi.
Ciao


Si, ma io non ho detto ke la mia soluzione è real time ma ha una precisione
discreta 100ms cioè 10 volte al secondo e mi sembra abbastanza considerando
che non se ne accorge nemmeno.
Ciao

 che non se ne accorge nemmeno.
Per un' applicazione che riceve/invia dati sulle porte di I/O in genere 10
campionamenti al secondo sono un numero davvero esiguo.
--
- Carlo -

"Alessandro"

Un po' di risposte varie al problema generale.
a) per forzare il repaint di una label (in un ciclo stretto) _dovrebbe_
bastare un Label.Repaint (v. help per particolarita' di
implementazione).
b) potresti dare un'occhiata al componente IdAntiFreeze (degli Indy) per
vedere come evita il congelamento dell'interfaccia anche in operazioni
intrinsecamente bloccanti come la lettura da un file.
c) usare un timer, come gia' detto, non e' accurato, ma quando hai
terminato il campionamento puoi interrogare un timer ad alta precisione
(QueryPerformanceTimer / Frequency) per sapere con maggior precisione
quanto dista il punto da quello precedente (nel caso tu debba plottare
il tutto su grafico x / t).
d) potresti spezzare l'applicazione in due processi: un processo ad alta
priorita' che legge la porta e scrive i dati su un Memory Mapped File
(circolare) o su una named pipe (queste mi pare non esistano in 9x).
L'avvio / arresto della applicazione di lettura possono essere
effettuati dalla applicazione di interfaccia (priorita' normale). I
comandi di start/stop possono essere inviati tramite messaggi custom
(RegisterWindowMessage): ovviamente il processo di lettura dovra' avere
e servire una coda di messaggi.
Molte se non tutte delle funzioni / strutture citate sono (almeno)
wrappate dalle JCL (
http://jvcl.sourceforge.net
).
Non parlo di driver perche' nulla ne conosco.
Saluti
Paolo Arosio

 Con questo spero di aver concluso!
Mi mancavano questi Carlo vs. Andrea!! ^_____^
--
Jax

questa sì che si può chiamare "discussione" !!!!
....ecco perchè amo il delphi....
"Carlo Bertuccini" <cber...@DOHlibero.it> ha scritto nel messaggio
news:sG8D9.5397$2B1.1...@twister2.libero.it...


....ecco perchè amo il delphi....
Ecco, bravo. E già che ci sei impara anche a quotare, grazie ;)
-- 
ABRACADABRA: Parola magica utilizzata nei riti propiziatori 
praticati dai programmatori come ultima risorsa atta a far si 
che il problema in questione venga risolto. 
Ma non sempre funziona.