• Non ci sono risultati.

Server di comunicazione di J-Communicator con J Manager

A.2 Il Server di SIRENE

A.2.3 Server di comunicazione di J-Communicator con J Manager

All’avvio del Server Device, J-Communicator inizialmente crea un Server tcp nella porta 5000 del localhost, ovvero nell’indirizzo ’127.0.0.1’. Questo permette di poter comunicare soltanto con applicazioni in esecuzione nella stessa macchina ed evita che applicazioni esterne possano collegarsi ad esso. Nello specifico, l’unica applicazione permessa ad accedere a questo Server `e J-Manager. Questo Server

`e stato realizzato per la comunicazione con il client del modulo J-Manager. Per il Server localhost vengono implementate diverse funzionalit`a, ognuna di queste viene eseguita alla ricezione di una determinata stringa all’interno di un messag- gio inviato da J-Manager. Questa stringa `e all’interno di un attributo chiamato Comando e pu`o avere i seguenti valori: “Collegati ”, “Inviato Output ”, “Richie- sta Input ”, “Terminato”.

Alla ricezione del comando Collegati, il Server istanzia un client che si collega al Server di J-Manager per stabilire una comunicazione bidirezionale, “Server J-Communicator - Client J-Manager ” e “Server J-Manager - Client J-Commu- nicator ”. Tale comunicazione permetter`a di poter scambiare messaggi tra i due moduli. Il client di J-Communicator si collegher`a alla porta specificata all’interno del messaggio che contiene la stringa Collegati.

Alla ricezione del comando Inviato Output, J-Communicator esegue la funzione Invia Output a Clients per inviare a tutti i Client connessi la stringa contenu- ta all’interno del messaggio ricevuto. Questa stringa contiene l’output generato dai programmi che deve essere inviata a tutti i Client che seguono quell’attivit`a o quella lezione. Eseguita la funzione Invia Output a Clients viene inviata una stringa a J-Manager affinch´e faccia continuare l’esecuzione del programma. Alla ricezione del comando Richiesta Input, J-Communicator invia un messaggio a tutti i Client per metterli a conoscenza che un programma richiede un input per continuare la sua esecuzione. I Client riceveranno questo testo e renderanno editabile l’area designata della GUI per l’invio di un input.

Alla ricezione del comando Terminato, J-Communicator invia un messaggio a tutti i Client per avvertirli che il programma `e terminato, e quindi saranno invitati a chiudere il loro Terminal di simulazione del prompt.

La funzione Avvia Programma, di tipo booleano, crea il processo del program- ma il cui nome `e il valore della stringa Nome e avvia il processo ponendolo nello stato sospeso. Questa funzione restituisce il valore True se non `e avvenuto alcun errore, diversamente restituir`a False.

La funzione Invia Input, di tipo booleano, ha come parametro di input un te- sto inviato dai Client Device e lo invia al processo in pausa. In questa funzione, J-Manager scrive il valore del testo ricevuto, Testo, nella pipe dello standard in- put del processo figlio, quindi se non `e avvenuto alcun errore allora la funzione restituisce True, altrimenti restituisce False. Il processo sar`a risvegliato dopo la terminazione di tale funzione.

A.2.4

Componente J-Manager

J-Manager `e un componente sviluppato interamente in Pascal utilizzando l’I- DE1 Lazarus. Il suo funzionamento `e simile ad un Terminal o ad una shell, con la differenza che invia i dati di visualizzazione e riceve i dati di input tramite socket e protocollo TCP. Ci si pu`o domandare il perch´e `e stato scelto un linguaggio di programmazione diverso da Javascript e C per implementare questa componente e perch´e `e stato necessario crearla. La necessit`a consiste nel risolvere tre problemi, e le loro soluzioni rispondono alle precedenti domande. Il primo problema consi- ste nell’impossibilit`a di poter gestire adeguatamente la comunicazione utilizzando delle “pipe”. Le pipe sono canali di comunicazione interprocesso monodirezionali utili allo scambio di dati e quindi di informazioni. Node.js non riesce a gestire adeguatamente il flusso di esecuzione di un programma utilizzando le pipe. In- fatti, il flusso di esecuzione di un programma `e asincrono tra gli output ed input. In un terminal generalmente il flusso di esecuzione di un programma appare ben definito nella sua esecuzione. Ci`o `e dovuto alla sincronizzazione delle pipe di input ed output del programma. Ogni programma, in qualunque sistema operativo, usa 3 pipe di comunicazione: uno per lo standard output, uno per lo standard input e l’ultimo per lo standard error. La pipe di output contiene i dati che il processo invia ad un altro processo, generalmente `e una stringa. La pipe di input contiene i dati utili al programma per la sua corretta esecuzione, questi dati generalmente sono numeri o stringhe. La pipe di error contiene gli errori che il processo solleva durante la sua esecuzione, simile alla pipe di output ma contiene solo messaggi di errore. Node.js non riesce a sincronizzare correttamente la gestione delle tre pipe in maniera tale da rendere interattiva l’esecuzione dei programmi. Si `e dunque reso necessario utilizzare un linguaggio di programmazione di basso livello per risolve- re il problema, infatti la soluzione consiste nel riuscire a sospendere il processo del programma ogni qualvolta che il programma invia un messaggio di output e di errore, e richiede un input per continuare correttamente la sua esecuzione. Generalmente, i programmi che riescono a gestire correttamente l’esecuzione delle applicazioni sono i terminal o le shell a riga di comando fornite dai sistemi opertivi. I terminal o le shell sono applicazioni che permettono di eseguire altri programmi sincronizzandone il flusso delle loro pipe, creando una tecnica simile a quella di debugging, ovvero fermano l’esecuzione del programma quando questo invia un messaggio, di output o di errore, o richiede un input. Per eseguire questo approc- cio, i terminal o le shell (nel prosieguo per brevit`a si parler`a di terminal o shell in maniera indifferente per il solo scopo di esplicitazione) devono utilizzare alcune funzioni del sistema operativo messe a disposizione tramite chiamate di sistema di tipo API (Application Programmable Interfaces). Queste funzioni permettono di

inviare segnali tra processi al fine di eseguire vari comportamenti. Tipicamente, un sistema operativo possiede funzioni per l’invio di segnali tra processi. Queste funzioni richiedono l’identificativo del processo univocamente assegnato dal siste- ma operativo, tipicamente un numero intero definito “PID ” (Process IDentifier), ed un valore numerico che specifica il tipo di segnale inviato. Vi sono vari tipi di segnali che `e possibile inviare tra processi, i pi`u comuni sono i segnali di debugging, di pausa, di “wake” (ovvero di risveglio dallo stato, ad esempio, da una pausa), di stop, di kill, di interruzione e due segnali “custom” utili all’implementazione di segnali propri usati dagli sviluppatori. Quelli utili per i nostri scopi sono: due tipi di segnale custom chiamati segnale “User1 ” e “User2 ”, che servono a distinguere il tipo di di dato, output se inviato o input se richiesto dal programma; il segnale di pausa che permette ad un processo di addormentarsi in attesa di essere sveglia- to e che permette di sospendere l’esecuzione del programma; il segnale di wake che sveglia il processo di un programma, se dormiente, per permettere ad esso di continuare la sua normale esecuzione; il segnale di kill che chiude il programma immediatamente a prescindere se il processo ha terminato la sua esecuzione o no. Il Pascal `e un linguaggio di programmazione di basso livello che pu`o agevolmente risolvere questo problema, infatti esso pu`o effettuare le corrette chiamate di siste- ma per la corretta gestione dei segnali da ricevere ed inviare ai processi.

Il secondo problema consiste nel rendere il codice sorgente di J-Manager compilabi- le, ed eseguibile, in qualsiasi sistema operativo. Il linguaggio C permette di essere compilato in qualsiasi sistema operativo, tuttavia `e necessario modificare alcune porzioni del codice sorgente quando si cerca di compilare questo codice sorgente su sistemi operativi diversi, es. Windows e Linux. Questo `e dovuto alla naturale defi- nizione delle funzioni del sistema operativo per la gestione dei segnali interprocesso che non risultano equivalenti, in termini implementativi e d’uso, tra i vari sistemi operativi. Lazarus risolve questo problema, in quanto `e un applicativo eseguibile in tantissimi sistemi operativi, vedi il paragrafo 3.4.3, e pu`o compilare facilmente il codice sorgente scritto in Pascal. Inoltre, Lazarus mette a disposizione una serie di funzioni per la corretta gestione dei segnali senza la necessit`a di modificare il codice sorgente. Il compilatore di Lazarus gestisce le condizioni di compilazione su sistemi operativi diversi, garantendo l’esecuzione del modulo J-Manager. Il terzo problema consiste nell’instaurare un canale di comunicazione tra J-Com- municator e J-Manager. La comunicazione tra questi due componenti del Server Device `e fondamentale per una delle principali caratteristiche di SIRENE: permet- tere a tutti gli utenti di interagire con i programmi. Per fare questo `e necessario che J-Communicator e J-Manager possano dialogare al fine di inviare i relativi output dei programmi ai Client Device e di ricevere tutti gli input dai Client Device e mandarli correttamente ai programmi attivi. Anche in questo caso sa- rebbe possibile utilizzare il linguaggio C per sviluppare J-Manager e permettergli

di comunicare con J-Communicator tramite socket con protocollo TCP, tuttavia anche in questo caso sarebbe necessario modificare porzioni di codice per rendere compilabile il codice sorgente, da un sistema operativo all’altro, a causa della non uniformit`a delle funzioni che i sistemi operativi mettono a disposizione tramite le proprie API. Lazarus risolve anche questo problema in quanto le librerie di Indy Project sono compilabili in tutti i sistemi operativi compatibili con Lazarus, vedi il paragrafo 3.4.3, ed i componenti “TidTCPClient ” e “TidTCPServer ” di Indy Project permettono di realizzare facilmente un client (usando il componente TidT- CPClient ) che possa collegarsi al server di comunicazione di J-Communicator e di realizzare un server (usando il componente TidTCPServer ) che permetta al client, di comunicazione, di J-Communicator di dialogare con J-Manager, questo senza eseguire alcuna modifica del codice sorgente di J-Manager.

J-Manager gestisce l’output dei processi e l’invio degli input ai processi. Per sincro- nizzare il flusso degli standard output ed input dei programmi, ogni programma, che deve inviare un output, inizialmente deve scrivere nella pipe dello standard out- put e successivamente mandano un segnale con valore numerico 10 (“SigUser1 ”, o segnale User1 ) a J-Manager, ed immediatamente ogni programma si pone in stato di pausa. Invece, quando un processo richiede un input, manda un segnale a J-Manager con valore numerico 12 (“SigUser2 ”, o segnale User2 ) e si pone nello stato di pausa finch´e J-Manager non ha scritto nel pipe dello standard input del processo e risveglia il processo dormiente, quindi il processo pu´continuare la sua esecuzione prendendo il valore di input ricevuto.

In sintesi, quando un processo ha inviato un output questo si mette subito in stato di Wait e quando un processo deve ricevere un input, questo invia un segnale a J-Manager emettendo la richiesta di un input e subito si mette in stato di Wait. Questa metodologia garantisce che il flusso di esecuzione dei programmi sia cor- retto e che vi sia una corretta sincronia tra standard output ed input. J-Manager gestisce questo ciclo di pause e di segnali e realizza quello che pu`o essere definito un “Web-Terminal ” o una “Web-Shell ”.