Sviluppo FB interprete a linea di comando

Scopo del programma

Viene sviluppato il blocco funzione CmdLineIFServer per i sistemi SlimLine basati su LogicLab. Il FB gestisce un interprete a linea di comando che permette di interagire con il sistema connettendosi in TCP/IP o in seriale ed impartire comandi. Il FB è scritto in linguaggio ST ed è modificabile in modo da adattarlo alla proprie esigenze, è possibile implementare nuovi comandi per interagire con il sistema (Download programma).

Passando al FB uno stream di comunicazione Fp, è possibile gestire comandi testuali che eseguono operazioni e ne ritornano il risultato. Come stream di comunicazione è possibile utilizzare una connessione seriale (Utilizzando il FB SysSerialPort), un canale UDP (Utilizzando i FB SysUDPServer, SysUDPClient) o un socket TCP (Utilizzando i FB SysTCPServer, SysTCPClient). Lo scopo del FB è dimostrativo quindi sono gestiti solo due comandi, ma modificando il programma sarà possibile implementare tutti i comandi desiderati. I comandi gestiti sono:

Inp<CR>: Ritorna il valore della variabile Inp di tipo REAL.
Out xxxx<CR>: Imposta il valore nella variabile Out di tipo REAL

Fp (FILEP) Stream di comunicazione.
Inp (REAL) Valore ingresso, il valore della variabile viene ritornato sul comando Inp<CR>.
Out (REAL) Valore uscita, questa variabile è valorizzata dal comando Out xxxx<CR>.

Sviluppo del FB

Il FB è sviluppato in linguaggio ST (Structured Text) ed è realizzato utilizzando lo statement CASE per gestirne l'esecuzione a stati. In base al valore della variabile CaseNr il programma esegue le operazioni richieste.

Messaggio di benvenuto

Alla connessione utente viene ritornato un messaggio di benvenuto. Ecco il listato programma che ne gestisce la visualizzazione.

IF (SysFIsOpen(Fp) <> WMsgPulse) THEN
  WMsgPulse:=SysFIsOpen(Fp); (* Welcome message pulse *)
  CaseNr:=0; (* Program cases *)

  IF (WMsgPulse) THEN
    i:=SysVarfprintf(Fp, '%s$r$n', STRING_TYPE, ADR('Welcome !'));
  END_IF;
END_IF;

La funzione SysFIsOpen(Fp) ritorna TRUE solo se lo stream di comunicazione è aperto. Se la connessione è di tipo TCP/IP la funzione ritornerà TRUE solo alla connessione client al socket server di comunicazione. In questo modo quindi quando il client si connette (Esempio utilizando un Telnet) sul terminale del cliente verrà visualizzato il messaggio di benvenuto.

Se la connessione è di tipo UDP o seriale, essendo tipi di connessione stateless è impossibile determinare quando si instaura la connessione, quindi la funzione SysFIsOpen(Fp) ritorna sempre TRUE, ed il messaggio allora verrà inviato alla prima esecuzione del FB. E se il terminale client non è connesso andrà perso.

Esecuzione sequenze

Siccome il FB viene eseguito ciclicamente l'esecuzione è gestita con sequenze. Ad ogni sequenza si esegue una certa funzione, terminata la quale si passa alla sequenza successiva. Per evitare di uscire dal FB per un loop ad ogni passaggio di sequenza la gestione delle sequenze è chiusa in un ciclo WHILE.

WHILE (TRUE) DO
  CASE (CaseNr) OF
    0: ...
    1: ...
  END_CASE;
END_WHILE;

Ricezione comando

Il comando viene digitato dall'utente carattere dopo carattere, quindi occorre ricevere tutti i caratteri in ingresso dallo stream e bufferizzarli in una stringa in modo da poterli poi elaborare.

(* -------------------------------------------------------------- *)
(* Initialize command string reception. *)

0:
i:=TO_INT(SysFIBfClear(Fp)); (* svuoto buffer ricezione *)
Ptr:=ADR(RxCommand); (* Auxiliary pointer *)
CaseNr:=CaseNr+1; (* Program case *)

(* -------------------------------------------------------------- *)
(* The command must end with a carriage return, string reception *)
(* until the character (16#0D) is received. *)

1:
IF NOT(TO_BOOL(SysFGetIChars(Fp))) THEN RETURN; END_IF;
@Ptr:=TO_USINT(Sysfgetc(Fp)); (* Character read *)
IF (@Ptr = 16#0D) THEN
  @Ptr:=0; (* Termino comando ricevuto con codice tappo *)
  i:=TO_INT(SysFIBfClear(Fp)); (* svuoto buffer ricezione *)
  CaseNr:=10; (* Program case *)
  RETURN;
END_IF;

(* Receive string pointer, increment and length control. If too *)
(* long abort reception. *)

Ptr:=Ptr+1; (* Auxiliary pointer *)
IF (TO_UDINT(Ptr) >= TO_UDINT(ADR(RxCommand))+SIZEOF(RxCommand)) THEN
  i:=SysVarfprintf(Fp, '%s$r$n', STRING_TYPE, ADR('Command too long'));
  CaseNr:=0; (* Program case *)
  RETURN;
END_IF;

Case 0: Si inizializza il puntatore Ptr al buffer di ricezione comando, in uesto modo sarà possibile utilizzarlo per memorizzare nel buffer i caratteri ricevuti.

Case 1: La funzione SysFGetIChars(Fp) ritorna il numero di caratteri ricevuti dallo stream, se non ci sono caratteri con il RETURN si termina l'esecuzione del FB e si attende il prossimo loop di esecuzione.

Se vi è almeno un carattere con la funzione Sysfgetc(Fp) si legge un carattere ricevuto che viene rimosso dallo stream e con @Ptr lo si memorizza nel buffer ricezione comando. Siccome tutti i comandi terminano con il <CR> viene controllato se il carattere ricevuto è un <CR> codice ascii 16#0D. Nel caso in cui lo sia viene sostituito nel buffer con il codice tappo di fine stringa 16#00 e si passa alla interpretazione del comando ricevuto CaseNr:=10, con il RETURN si termina l'esecuzione del FB e si attende il prossimo loop di esecuzione.

In caso contrario si incrementa il puntatore e si controlla che il comando non superi la dimensione definita per il buffer di ricezione. Nel caso di superamento viene abortita la ricezione.

Comando "Inp"

Il comando “Inp” permette di ritornare il valore della variabile di ingresso Inp.

10:
IF (SysStrFind(ADR(RxCommand),ADR('Inp'), FIND_DEFAULT) = NULL) THEN CaseNr:=20; RETURN; END_IF;

(* Returns the Inp variable value. *)

i:=SysVarfprintf(Fp, 'Inp %f$r$n', REAL_TYPE, ADR(Inp));
CaseNr:=0; (* Program case *)
RETURN;

Case 10: Con la funzione SysStrFind viene verificato se nel buffer RxCommand che contiene il comando ricevuto si trova la stringa “Inp”, se non trovata la funzione ritorna NULL e si passa a controllare un'altro comando. Se la stringa viene trovata viene ritornato il valore della variabile Inp passata in ingresso al FB, poi si ritorna al case 0 reinizializzando la gestione.

Comando "Out"

Il comando “Out” permette di impostare il valore della variabile di uscita Out.

20:
Ptr:=SysStrFind(ADR(RxCommand),ADR('Out '), FIND_GET_END); (* Auxiliary pointer *)
IF (Ptr = NULL) THEN CaseNr:=30; RETURN; END_IF;

(* Acquire the preset value. *)

IF NOT(SysVarsscanf(Ptr, '%f', REAL_TYPE, ADR(Out))) THEN
  i:=SysVarfprintf(Fp, '%s$r$n', STRING_TYPE, ADR('"Out" command error'));
  CaseNr:=0; (* Program case *)
  RETURN;
END_IF;

(* Returns the preset value. *)

i:=SysVarfprintf(Fp, 'Out %f, Ok$r$n', REAL_TYPE, ADR(Out));
CaseNr:=0; (* Program case *)
RETURN;

Case 20: Con la funzione SysStrFind viene verificato se nel buffer RxCommand che contiene il comando ricevuto si trova la stringa “Out”, se non trovata la funzione ritorna NULL e si passa a controllare un'altro comando.

Se la stringa viene trovata Ptr punta la posizione del buffer RxCommand successivo al comando, passando questo valore alla funzione SysVarsscanf sarà possibile acquisire il valore definito in linea al comando. Nel caso non sia possibile acquisire il valore viene ritornato errore e si reinizializza la gestione.

Se il valore è acquisito correttamente viene visualizzato un messaggio con il valore definito.

Errore comando

Se non sono stati trovati i comandi gestiti, si arriva al case 30 dove viene gestito il messaggio di errore.

30:
ELSE
i:=SysVarfprintf(Fp, '%s$r$n', STRING_TYPE, ADR('Wrong command'));
CaseNr:=0; (* Program case *)
RETURN;

Case 30: Qui è possibile aggiungere l'interpretazione di altri comandi


Utilizzo con Socket TCP

Per collegare l'interprete comandi ad un socket TCP si può realizzare un programma in FBD dove si istanzia il FB realizzato insieme al FB di gestione socket TCP server.

Il FB SysTCPServer è posto in ascolto sulla porta 1000, ed accetta 1 sola connessione, lo stream di comunicazione Fp valorizzato dal server TCP è passato al FB interprete. Quando un client TCP (Esempio Toolly) si connette Fp si apre e l'interprete invierà il messaggio di benvenuto gestendo i comandi ricevuti.

Was this article helpful?