Differenze tra le versioni di "Indirizzare dati da un programma ad un altro e ad un Terminale"

Da Gambas-it.org - Wikipedia.
Riga 67: Riga 67:
 
   If Exist(FIFO) Then Kill "/tmp/FIFO"
 
   If Exist(FIFO) Then Kill "/tmp/FIFO"
 
    
 
    
  <FONT Color=gray>' 'Crea la 'pipe' FIFO:''</font>
+
  <FONT Color=gray>' ''Crea la 'pipe' FIFO:''</font>
 
   umask(0)
 
   umask(0)
 
    
 
    
Riga 73: Riga 73:
 
   If err = -1 Then Error.Raise("Impossibile creare il file-device !")
 
   If err = -1 Then Error.Raise("Impossibile creare il file-device !")
 
    
 
    
 +
<FONT Color=gray>' ''Apre il file speciale FIFO in scrittura e lo pone sotto 'osservazione':''</font>
 
   fl = Open FIFO For Write Watch
 
   fl = Open FIFO For Write Watch
 
      
 
      

Versione delle 19:25, 25 ott 2015

Il caso in questione è quello in cui si inviano dati da un programma Gambas ad un altro programma Gambas e ad un Terminale.


Invio dei dati da un programma Gambas ad un altro

In questo caso, abbiamo due progetti Gambas, che per comodità chiameremo A e B. Il progetto "B" invia dati al progetto "A". Per inviare i dati dal programma "B" al programma "A" si potranno utilizzare alcune modalità.


Uso della risorsa Pipe di Gambas

Paragrafo in costruzione


Uso di una named pipe mediante le funzioni esterne umask() e __xmknod() della libreria libc.so.6

Questa modalità è sostanzialmente analoga alla precedente, ma fa uso delle funzioni esterne umask() e __xmknod() della libreria libc.so.6.

Con la realizzazione di una named pipe viene creato un file speciale FIFO, avente i permessi di lettura e scrittura, che fa da medium, da conduttore di dati tra un programma che invia i dati e il programma che riceve i dati.

[MITTENTE]------->||[file-device]||------>[RICEVENTE]

Nel programma che riceve i dati la named pipe, ossia il file speciale FIFO, verrà creata invocando innanzitutto la funzione esterna umask() che imposta la maschera di creazione del file del processo in corso, quello cioè che crea la named pipe e riceve i dati. Consente di impostare nel file-device i permessi da noi prescelti.

Successivamente va invocata la funzione esterna __xmknod(), che crea appunto il nodo, il punto di contatto possibile fra il programma mittente ed il programma ricevente, ossia il file speciale, per la comunciazione dei dati.

Il primo argomento, che rappresenta la versione dell'interfaccia xmknod, per i sistemi a 64-bit va posto a zero. (Per i sistemi a 32-bit bisognerà provare il valore 0 e il valore 1).

Particolare attenzione va dato al valore da assegnare al terzo argomento __mode che è un valore intero formato dalla somma dei valori delle impostazioni dei permessi per i tre utenti. L'argomento __mode specifica, in particolare, sia la modalità del file da utilizzare sia il tipo di nodo da creare. Dovrebbe essere una combinazione (usando l'OR bit-a-bit) fra il valore della modalità prescelta e il valore della costante-direttiva S_IFIFO, pari a 4096, utile per specificare il tipo di file da creare, in questo caso il file speciale FIFO.

I valori attribuibili sono 4 per ciascuna classe di utenti considerata:

  • il primo (sempre zero) significa negazione di ogni permesso sul file;
  • il secondo consente la sola scrittura del file;
  • il terzo consente la sola lettura del file;
  • il quarto consente la lettura e la scrittura del file.

In particolare:

  • i valori riferiti all'utente Altri sono: 0, 2, 4, 6 (quindi partendo da 0 un comando ogni 2 valori). Aggiungendo 1 ad uno dei predetti valori, si consente anche l'esecuzione del file.
  • i valori riferiti all'utente Gruppo sono: 0, 16, 32, 48 (quindi partendo da 0 un comando ogni 16 valori). Aggiungendo 8 ad uno dei predetti valori, si consente anche l'esecuzione del file.
  • i valori riferiti all'utente Proprietario sono: 0, 128, 256, 384 (quindi partendo da 0 un comando ogni 128 valori). Aggiungendo 8 ad uno dei predetti valori, si consente anche l'esecuzione del file.

Il quarto argomento *__dev della funzione esterna deve essere un Puntatore ad una variabile di tipo Intero istanziata a 0 (zero) in caso di named pipe.


Mostriamo un esempio pratico.
Il programma che crea la named pipe, e dunque il file speciale di tipo FIFO, avrà il seguente codice:

Private Const FIFO As String = "/tmp/FIFO"
Private fl As File


Library "libc:6"

Private Const _MKNOD_VER As Integer = 0
Private Const S_IFIFO As Integer = 4096

' __mode_t umask (__mode_t __mask)
' Set the file creation mask of the current process to MASK.
Private Extern umask(mask As Integer) As Integer

' int __xmknod (int __ver, const char *__path, __mode_t __mode, __dev_t *__dev)
' Create a device file named PATH, with permission and special bits MODE and device number DEV.
Private Extern __xmknod(ver As Integer, path As String, mode As Integer, dev As Pointer) As Integer 'In "libarchive"


Public Sub Main()
 
 Dim err, mode As Integer
 
  mode = 0
  
  If Exist(FIFO) Then Kill "/tmp/FIFO"
  
' Crea la 'pipe' FIFO:
  umask(0)
  
  err = __xmknod(_MKNOD_VER, FIFO, S_IFIFO Or 438, VarPtr(mode))
  If err = -1 Then Error.Raise("Impossibile creare il file-device !")
  
' Apre il file speciale FIFO in scrittura e lo pone sotto 'osservazione':
  fl = Open FIFO For Write Watch
   
End


Public Sub File_Read()
 
 Dim s As String
 
  Read #fl, s, -256
  Print s
  
End

Il programma che invia i dati può essere molto semplicemente il seguente:

Private Const FIFO_FILE As String = "/tmp/FIFO"


Public Sub Main()
 
 Dim fl As File
 Dim s As String
 
  fl = Open FIFO_FILE For Write
   
  Write #fl, "Questa è una prova"
  
  fl.Close
  
End

Ovviamente va prima avviato il programma che crea il file speciale, la named pipe, e che dovrà ricevere i dati. Poi va lanciato il programma che invia i dati. Volendo, dopo aver avviato il programma che riceve i dati, si potrà anche anche utilizzare un Terminale per trasmettere semplicemente dati, scrivendo ed inviando ad esempio la seguente riga di comando:
~ $ echo testo qualsiasi ls > /tmp/FIFO
o anche una riga di comando da far eseguire dal programma ricevente, come ad esempio:
~ $ echo -ne '\n' | ls > /tmp/FIFO


Uso del file speciale /dev/pts/... e del file descriptor 1 associati al processo del programma ricevente

Sarà possibile inviare i dati anche utilizzando il file speciale (/dev/pts/...) associato al programma "A" ricevente, oppure il file descriptor n. 1 (/proc/PID_del_processo_di_A/fd/1) associato al processo del programma "A" ricevente. In questo caso si potranno avere due effetti a seconda del codice scritto nei due programmi.

Caso in cui i dati ricevuti in console/Terminale possono essere soltanto visualizzati

Inviando i dati semplicemente con le funzioni Write o Print al file-descriptor associato al programma ricevente o al file speciale pts del programma ricevente, i dati potranno essere soltanto visualizzati in console/Terminale, ma non recuperati per essere eventualmente gestiti.

Uso dei file speciali /dev/pts/...

Nel progetto "A", che riceve i dati inviati dal programma "B", avremo il seguente codice:

Public Sub Main()
 
 Dim pts As String
 
' Ricaviamo e mostriamo in console/Terminale il file speciale che rappresenta il progetto "A" (il nome del file "pts" servirà per il programma "B" inviante):
  pts = Dir("/dev/pts", "*", gb.Device)[0]
  
  Print pts
  
' Restiamo in attesa dei dati inviati:
  While True
    Wait 0.01
  Wend
  
End


Nel progetto "B", che invia i dati al programma "A", avremo il seguente codice:

Private fl As File


Public Sub Main()

 Dim pts As String

' Qui va inserito il nome numerico del file speciale (pts) che rappresenta il progetto "A":
  pts = ....

' Viene aperto il file speciale che rappresenta il progetto "A":
  fl = Open "/dev/pts/..." For Write
   
' Viene inviata la stringa di caratteri al file speciale che rappresenta il progetto "A":
  While True
    Print #fl, "Testo qualsiasi" 
    Wait 0.3
  Wend
   
End


Uso del file descriptor n. 1

In quest'altro esempio il programma "B" invia di continuo il valore progressivamente incrementato di una variabile. La stringa dei dati inviata è formattata in modo tale che nella console del programma "A" ricevente (se esso è aperto come progetto nell'IDE) ovvero nel Terminale (se il programma "A" è stato lanciato da Terminale) i dati verranno di volta in volta visualizzati sovrascritti.

Nel progamma "A", che riceve i dati, avremo il seguente codice:

Public Sub Main()
  
' Ricaviamo e mostriamo in console o in Terminale il PID del programma "A" ricevente (il PID servirà per il programma "B" inviante):
  Print Application.Id
  
' Restiamo in attesa dei dati inviati:
  While True
    Wait 0.01
  Wend
 
End


Il codice del programma "B", che invia i dati, in questo esempio sarà il seguente:

Private fl As File

 
Public Sub Main()

 Dim fl As File
 Dim i As Integer
 Dim nome_processo, PID As String

' Qui va assegnato il PID del programma "A" ricevente i dati:
  PID = .....
  
' Apriamo il "file-descriptor" n. 1 del processo del programma "A" in scrittura:
  fl = Open "/proc" &/ PID &/ "fd/1" For Write
  
' Ciclo per l'invio continuo - ogni 500 millisecondi - del valore progressivamente incrementato della variabile di tipi Intero:
  While True
' Formattiamo la stringa in modo tale che i dati, di volta in volta inviati, si sovrascrivano ai precedenti:
    Write #fl, "\r" & i
    Wait 0.5
    Inc i
  Wend
   
End

Caso in cui i dati ricevuti in console/Terminale possono essere anche recuperati ed utilizzati dal programma ricevente

Il programma che riceve i dati acquista la capacità di intercettarli, per poterli successivamente gestire direttamente, attraverso l'invio manuale dei dati dalla console/Terminale. Questa modalità, in particolare, prevede che i dati vengono scritti ed inviati manualmente dall'apposito spazio sottostante la console dell'IDE oppure nel Terminale, se il programma inviante è stato lanciato da Terminale.
In tal caso è necessario nel programma ricevente aprire in lettura e porre in osservazione con la parola chiave Watch il file speciale pts del programma che invia i dati, ed il programma inviante deve aprire in scrittura il file speciale pts del programma ricevente o, in alternativa, il proprio file speciale pts a cui esso è associato. Com già accennato, i dati dovranno essere inviati scrivendoli nell'apposito spazio sottostante la console dell'IDE oppure nel Terminale, se il programma inviante è stato lanciato da Terminale. Dopo averli scritti si dovrà inviarli premendo come di consueto il tasto "Invia" (Enter) della tastiera.

Mostriamo di seguito un semplice esempio pratico, nel quale il codice del programma "A", che riceve i dati, sarà il seguente:

Private fl As File


Public Sub Main()
 
 Dim b As Byte
 Dim pts As String
 
' Individua il file speciale 'pts' che sarà creato, associato al programma che invia i dati:
  While True
    If Not Exist("/dev/pts" &/ CStr(b)) Then Break
    Inc b
  Wend
  
  pts = CStr(b)
  
  Print "Il file spciale 'pts' del programma che invia i dati sarà:", pts
  
' Resta in attesa che il file speciale 'pts', associato al programma che invia i dati, sia generato:
  Do
    Wait 0.01
  Loop Until Exist("/dev/pts" &/ pts)
  
' Apre il file speciale 'pts', associato al programma che invia i dati, in lettura e lo sottopone in 'osservazione':
  fl = Open "/dev/pts" &/ pts For Read Watch

End


Public Sub File_read()
 
 Dim s As String
 
  Read #fl, s, -256
  
  Print s

End


Il codice del programma "B", che invia i dati, sarà il seguente:

Private i As Integer


Public Sub Main()
  
 Dim fl As File
 Dim pts As String
 
' Qui va attribuito il numero del file speciale 'pts' associato al programma che riceve i dati:
  pts = ...

' Apre in scrittura il file speciale 'pts' associato al programma che riceve i dati:
  fl = Open "/dev/pts" &/ pts For Write

  While True
    Wait 0.01
  Wend

End


Uso della Classe Clipboard

Si potranno trasmettere dati anche sfruttando gli "Appunti" di sistema con una sorta di copia-incolla, da effettuarsi mediante la Classe Clipboard di Gambas. In particolare utilizzeremo le funzioni .Copy() e .Paste() della Classe Clipboard.
Durante il trasferimento dei dati da un programma all'altro bisogna prestare attenzione a non effettuare alcun'altra copia di caratteri (per esempio con il mouse), o comunque di altri dati, altrimenti verrà trasmesso il contenuto della nuova copia di dati appena effettuata.
Ovviamente, trattandosi di "Appunti" di sistema, i dati potranno essere raccolti pure da altri programmi, anche non-Gambas.


Facciamo un esempio pratico. Avendo due programmi, A che deve trasferire dei dati al programma B, nel programma A avremo il seguente codice, capace di copiare dati negli appunti:

Public Sub Button1_Click()

 Dim s As String

  s = "Prova trasferimento dati stringa"

  Clipboard.Copy(s)
 
End

mentre nel programma B avremo il seguente codice, capace di raccogliere quanto precedentemente copiato negli appunti dal programma A:

Public Sub Button1_Click()

 Dim s As String

  s = Clipboard.Paste()

  Print s

End


Scrivere nella finestra del Terminale

Se viene aperto un Terminale virtuale distinto dal programma Gambas, e si intende scrivere al suo interno, si potranno utilizzare o il file speciale associato al Terminale aperto: il suo percorso è /dev/pts/n; oppure si potrà usare il file descriptor num. 1 associato al processo del Terminale.


Uso del file speciale della cartella pts

Volendo utilizzare il comando Shell per conoscere il nome del file speciale associato ad un certo terminale virtuale è sufficiente dare allo shell di quel terminale il comando tty:

Shell tty

Se, invece, si vuole fare a meno del comando Shell per conoscere il nome del file speciale associato, bisogna tenere in considerazione che la console dell'IDE di Gambas si comporta come un Terminale virtuale; e pertanto anche ad essa sarà associato un file speciale nella directory /dev/pts/N. Se non sarà già aperto un Terminale virtuale, il numero del file associato alla Console dell'IDE di Gambas, sarà 0, altrimenti sarà il primo numero successivo a quello dell'ultimo Terminale aperto. Identico discorso vale per i terminale virtuali aperti successivamente all'apertura dell'IDE di Gambas.


Mostriamo un codice esemplificativo, con il quale scriveremo all'interno di una finestra Terminale mediante le sole funzioni di Gambas.

E' necessario aver impostato nel progetto anche il componente "gb.desktop" e "gb.desktop.x11":

Public Sub Form_Open()

' Apre un Terminale:
 Desktop.OpenTerminal

End
 

Public Sub Button1_Click()

 Dim fl As File
 
' Apre in scrittura il file "speciale" associato al Terminale virtuale appena lanciato.
' Esso può essere individuato in questo modo:
  fl = Open "/dev/pts" &/ Dir("/dev/pts", "*")[0]" For Write     ' ...o anche “For Output

' Se si intende scrivere nel terminale ogni stringa appresso alla precedente, allora si userà “Write”:
  Write #fl, "stringa da scrivere nel terminale"

' Se si intende scrivere ogni stringa l'una sotto l'altra, allora si utilizzerà “Print”:
  Print #fl, "stringa da scrivere nel terminale"
  
' Oppure semplicemente solo "Print":
  Print #fl, "stringa da scrivere nel terminale"
  
  fl.Close
 
End


N.B. Seppure sia possibile - come abbiamo visto - scrivere all'interno della finestra del terminale, non è però possibile utilizzare tale possibilità per lanciare dalla finestra del Terminale dei comandi, magari in combinazione con l'istruzione "Desktop.Sendkeys(Chr(10))", se non in quest'altra modalità.


Uso del file descriptor associato al processo del Terminale ed utile alla scrittura dei dati

In tal caso si tratta di individuare preliminarmente il numero del PID del processo del Terminale, e quindi il file descriptor utile per la scrittura nel Terminale dei dati desiderati.

Il codice può essere il seguente:

Private fl As File


Public Sub Form_Open()

 Dim pr, s As String
 
  pr = "/proc" &/ PID_del_processo &/ "fd"
  
  For Each s In Dir(pr, "*")
    If Stat(pr &/ s).Link Like "/dev/pts/*" Then Exit
  Next
  
  fl = Open pr &/ s For Output
     
End


Public Sub Button1_Click()

' Se si vuole far scrivere nel terminale una riga "sotto" l'altra
' ogni volta che si preme il tasto, allora si utilizzerà "Print":
  Print #fl, "testo qualsiasi"

' Se si vuole far scrivere nel terminale una riga "dopo" l'altra
' ogni volta che si preme il tasto, allora si utilizzerà "Write":
  Write #fl, "testo qualsiasi"
  
  fl.Close

End