UNIVERSITÀ DEGLI STUDI DI BOLOGNA
FACOLTÀ DI INGEGNERIA
Corso di Laurea in Ingegneria Informatica
Progetto per il corso “Reti di calcolatori LS” tenuto dal Prof. Antonio Corradi
Sistema distribuito per il controllo remoto di
Software SCADA HMI
Paolo di Francia
Anno Accademico 2005
Indice
 Introduzione………………………………………..………………3
 Teconologie utilizzate………………………………………...……………...3
o Remoting .NET…………………………………………...…………...3
o Oggetti remoti…………………………………………...…………….4
o Canali……………………………………………………...…………..5
 Canale TCP
o Politiche di gestione………………………………………………..…5
o Esempio di remoting con un canale TCP……………………………..7
 Architettura dell’ applicazione……………………………………………....9
 Implementazione dell’ algoritmo two phase lock………………………….10
 Aspetti di sincronizzazione………………………………………………...13
 Request Engine…………………………………………………………….14
 Response Engine…………………………………………………………...16
 Politiche di scheduling delle richieste……………………………………...17
 Gestioni Errori……………………………………………………………...17
 Conclusioni………………………………………………………………...18
 Bibliografia………………………………………………………………...18
Introduzione
Il progetto si pone l’obiettivo di sviluppare mediante costrutti messi a disposizione dalla
piattaforma .NET un’applicazione distribuita che consenta di interagire con un software
SCADA commerciale in particolare Citect.L’applicazione sarà composta da un’entità Server
e più entità Client che attraverso un oggetto condiviso mediante il Remoting.NET possano
inviare e ricevere istruzioni al software SCADA.L’applicazione Server deve fornire l’istanza
principale dell’oggetto condiviso e occuparsi della gestione di tutte le richieste che vengono
inviate dai Client, a sua volta dovrà gestire le richieste pendenti comunicando verso Citect e
rendendo disponibili i risultati delle istruzioni a tutti i Client connessi.L’entità Server avrà la
possibilità di inviare messaggi con informazioni di stato mediante modalità broadcast ai
Client attualmente connessi.
Il Server avrà a disposizione un archivio dati in cui verranno inserite le richieste e le relative
risposte in modo che in caso di temporanea inabilitazione del Server queste possano poi
essere processate in tempi successivi.Le richieste verranno organizzate in modo da
mantenere le indicazioni temporali e l’identificativo del Client richiedente.Le richieste
saranno processate con politica di tipo Fifo.Entrambe le entità potranno esportare i dati
relativi a richieste e risposte su file xml.Il Server mantiene una lista degli identificativi dei
Client connessi e sarà in grado di visualizzare le risorse di sistema attualmente utilizzate.
Al fine di consentire una corretta sincronizzazione tra richieste e risposte verranno utilizzati
un Request Engine e un Response Engine in modo da accedere all’archivio dati condiviso.
L’interfaccia verso Citect verrà sviluppata attraverso l’implementazione di un oggetto con
tecnologia Microsoft COM.
Tecnologie utilizzate
Remoting .NET
.NET remoting fornisce una struttura che consente agli oggetti di interagire fra loro su
domini di applicazione. La struttura fornisce una serie di servizi, tra cui supporto di
attivazione e durata, oltre a canali di comunicazione che si occupano del trasporto di
messaggi verso e da applicazioni remote. I formattatori vengono utilizzati per la codifica e
decodifica dei messaggi, prima che il canale li trasmetta. Le applicazioni possono utilizzare
la codifica binaria in caso di prestazioni critiche, oppure la codifica XML laddove è
essenziale l'interoperabilità con altre strutture remote.
La gestione della durata degli oggetti remoti senza un supporto della struttura sottostante
non è sempre facile.
3
.
Fig.1
.NET remoting mette a disposizione una serie di modelli di durata tra cui scegliere, suddivisi
in due categorie:

Oggetti attivati da client

Oggetti attivati da server
Gli oggetti attivati da client sono controllati da un programma di gestione durata basato su
lease, che assicura che l'oggetto venga sottoposto a garbage collection alla scadenza del
lease. Nel caso di oggetti attivati da server, gli sviluppatori possono selezionare un modello
"single call" o "singleton".
Oggetti remoti
Uno degli obiettivi principali di una struttura di remoting è fornire l'infrastruttura in grado di
nascondere le complessità dei metodi di chiamata per oggetti remoti e riportarne i risultati.
Qualsiasi oggetto esterno all' application domain del mittente dovrebbe essere considerato
remoto, anche se gli oggetti vengono eseguiti sullo stesso computer. All'interno dell'
application domain, tutti gli oggetti vengono passati in base al riferimento mentre i tipi di
dati primitivi in base al valore. Poiché i riferimenti a oggetti locali sono validi solo
all'interno dell'application domain in cui vengono creati, non è possibile spostarli o riportarli
dalle chiamate remote di metodi in questa forma. Tutti gli oggetti locali che devono
attraversare il confine dell' application domain devono passare in base al valore e
dovrebbero essere contrassegnati dall'attributo personalizzabile [serializable], oppure
devono implementare l'interfaccia ISerializable.
È possibile modificare qualsiasi oggetto in un oggetto remoto facendolo derivare da
MarshalByRefObject. Quando un client attiva un oggetto remoto, riceve un proxy per
questo oggetto. Tutte le operazioni su questo proxy vengono indirizzate in modo da
consentire all'infrastruttura remota di intercettare e inoltrare correttamente le chiamate.
Questa operazione di indirizzamento influisce sulle prestazioni, ma il compilatore JIT ed EE
(execution engine) sono stati ottimizzati per evitare inutili penalizzazioni delle prestazioni,
quando gli oggetti proxy e remoti si trovano nello stesso application domain. Nei casi in cui
invece questi oggetti si trovano in domini di applicazione diversi, tutti i parametri di
chiamata del metodo nello stack vengono convertiti in messaggi e spostati nel application
domain remoto, dove i messaggi vengono messi nuovamente in un frame dello stack e il
4
metodo viene richiamato. La stessa procedura viene utilizzata per riportare i risultati della
chiamata del metodo.
Canali
I canali vengono utilizzati per trasmettere messaggi verso e da oggetti remoti. Quando un
client richiama un metodo su un oggetto remoto, i parametri e altri dettagli relativi alla
chiamata vengono trasmessi all'oggetto remoto attraverso il canale. I risultati della chiamata
tornano poi al client seguendo la stessa procedura. Un client è in grado di selezionare
qualsiasi canale registrato sul "server" per comunicare con l'oggetto remoto. In questo modo
gli sviluppatori possono selezionare i canali più adatti alle proprie esigenze. È possibile
inoltre personalizzare tutti i canali esistenti o creare nuovi canali che utilizzino protocolli di
comunicazione diversi. La scelta dei canali avviene secondo le regole descritte di seguito:
È necessario registrare almeno un canale con la struttura di remoting prima di poter
richiamare un oggetto remoto. I canali devono essere registrati prima degli oggetti.
I canali vengono registrati per application domain. È possibile avere diversi domini di
applicazione in un solo processo. Quando un processo termina, tutti i canali registrati
vengono automaticamente distrutti.
Non è consentito registrare lo stesso canale in ascolto su una stessa porta più di una volta.
Anche se i canali vengono registrati per application domain, più domini di applicazione
sullo stesso computer non possono registrare lo stesso canale in ascolto sulla stessa porta.
I client possono comunicare con un oggetto remoto utilizzando qualsiasi canale registrato.
La struttura di remoting assicura che l'oggetto remoto sia collegato al canale adatto quando
un client tenta di stabilire una connessione ad esso. Il client si occupa di richiamare
RegisterChannel sulla classe ChannelService prima di provare a comunicare con un oggetto
remoto.
Canale TCP
Il canale TCP utilizza un formattatore binario per serializzare i messaggi in un flusso binario
e trasportare il flusso all'URI di destinazione tramite il protocollo TCP.
Politiche di gestione
La struttura di remoting supporta l'attivazione da server e client di oggetti remoti.
L'attivazione da server si utilizza in genere quando gli oggetti remoti non sono necessari per
la conservazione di uno stato tra le chiamate del metodo; si utilizza anche nei casi in cui più
client richiamano metodi sulla stessa istanza di oggetto e l'oggetto conserva lo stato tra le
chiamate delle funzioni. Allo stesso tempo, gli oggetti attivati da client vengono istanziati
dal client, che gestisce la durata dell'oggetto remoto utilizzando un sistema basato su lease
fornito a tale scopo.
Le informazioni riportate di seguito sono necessarie per la registrazione di un oggetto
remoto con la struttura:


Il nome del tipo di oggetto remoto.
L'oggetto URI che i client utilizzeranno per individuare l'oggetto.
5

La modalità oggetto richiesta per l'attivazione da server, che può essere SingleCall o
Singleton.
Un oggetto SingleCall crea un'istanza di classe per ogni chiamata del client, anche se le
chiamate provengono dallo stesso client. L'invocazione successiva, sarà sempre servita da
una differente istanza del server anche se la precedente non è stata ancora riciclata dal
sistema.
Un oggetto Singleton invece non presenta mai più di una istanza contemporaneamente. Se
una istanza è già esistente la richiesta viene soddisfatta da quella istanza. Se l'istanza non
esiste, alla prima richiesta viene creata dal server e tutte le successive richieste vengono
soddisfatte da quella istanza.
È possibile registrare un oggetto remoto richiamando RegisterWellKnownType,
passando le informazioni riportate sopra come parametri, oppure memorizzarle in un file di
configurazione, richiamare ConfigureRemoting e passare il nome del file di configurazione
come parametro. Queste due funzioni possono essere entrambe utilizzate per registrare
oggetti remoti perché svolgono esattamente la stessa operazione. La seconda è più adatta
perché il contenuto del file di configurazione può essere alterato senza dover compilare di
nuovo l'applicazione host. Il frammento di codice seguente mostra come registrare la classe
HelloService come oggetto remoto SingleCall.
RemotingServices.RegisterWellKnownType(
"server",
"Samples.HelloServer",
"SayHello",
WellKnownObjectMode.SingleCall);
In questo caso "server" è il nome del assembly, HelloServer è il nome della classe e
SayHello è l'oggetto URI
Quando l'oggetto viene registrato, la struttura crea un riferimento per questo oggetto remoto
ed estrae i metadati necessari per l'oggetto dall'assembly. Queste informazioni, insieme
all'URI e al nome dell'assembly, vengono poi memorizzate nel riferimento dell'oggetto
all'interno di una tabella della struttura di remoting, utilizzata per la registrazione degli
oggetti remoti registrati. Si noti che l'oggetto remoto stesso non viene istanziato dal processo
di registrazione, ma solo quando un client tenta di richiamare un metodo sull'oggetto oppure
attiva l'oggetto dal lato client.
A questo punto, qualsiasi client che conosca l'URI di questo oggetto può ottenere un proxy
registrando il canale preferito con ChannelServices e attivare l'oggetto richiamando new,
GetObject o CreateInstance. Il frammento di codice seguente costituisce un esempio di
questa operazione:
ChannelServices.RegisterChannel(new TCPChannel);
HelloServer obj = (HelloServer)Activator.GetObject(
typeof(Samples.HelloServer),
"tcp://localhost:8085/SayHello");
In questo caso "tcp://localhost:8085/SayHello" specifica che l'utente intende stabilire una
connessione all'oggetto remoto nell'endpoint SayHello utilizzando TCP sulla porta 8085. È
possibile utilizzare GetObject o new per l'attivazione del server. L'oggetto non viene
6
istanziato quando viene effettuata una di queste chiamate. In pratica non viene generata
alcuna chiamata di rete. La struttura ottiene le informazioni sufficienti dai metadati per
creare il proxy senza connettersi affatto all'oggetto remoto. Viene solo stabilita una
connessione di rete quando il client richiama un metodo sul proxy. Quando la chiamata
arriva al server, la struttura estrae l'URI dal messaggio, esamina le tabelle della struttura di
remoting per individuare il riferimento per l'oggetto corrispondente all'URI, quindi istanzia
l'oggetto se necessario, inoltrando la chiamata del metodo all'oggetto. Se l'oggetto è
registrato come SingleCall, viene eliminato al termine della chiamata del metodo. Per
ciascun metodo invocato viene creata una nuova istanza dell'oggetto. L'unica differenza tra
GetObject e new è che il primo consente di specificare un URL come parametro, mentre il
secondo lo ottiene dalla configurazione.
È possibile utilizzare CreateInstance o new per gli oggetti attivati da client. Entrambi
consentono di istanziare un oggetto utilizzando i costruttori con dei parametri. La durata
degli oggetti attivati da client è controllata dal servizio di leasing, fornito dalla struttura di
remoting.
Esempio di remoting con un canale TCP
Questa appendice indica come scrivere un'applicazione remota semplice "Hello World". Il
client passa una stringa all'oggetto remoto, che allega le parole "Hi There" alla stringa e
riporta il risultato al client.
Questo codice deve essere salvato come server.cs. Di seguito è riportato il codice per il
server:
using
using
using
using
System;
System.Runtime.Remoting;
System.Runtime.Remoting.Channels;
System.Runtime.Remoting.Channels.Tcp;
namespace RemotingSamples {
public class Sample {
public static int Main(string [] args) {
// Crea e registra un nuovo canale Tcp
TcpChannel chan = new TcpChannel(8085);
ChannelServices.RegisterChannel(chan);
// Il metodo RegisterWellKnownServiceType permette di
registrare l’oggetto per la
// futura attivazione [PARAMETRI (Tipo, URI, metodo di
attivazione)]
RemotingConfiguration.RegisterWellKnownServiceType
(Type.GetType("RemotingSamples.HelloServer,object"),
"SayHello", WellKnownObjectMode.SingleCall);
System.Console.WriteLine("Hit to exit...");
System.Console.ReadLine();
return 0;
}
}
}
7
Questo codice deve essere salvato come client.cs:
using
using
using
using
System;
System.Runtime.Remoting;
System.Runtime.Remoting.Channels;
System.Runtime.Remoting.Channels.Tcp;
namespace RemotingSamples {
public class Client
{
public static int Main(string [] args)
{
TcpChannel chan = new TcpChannel();
ChannelServices.RegisterChannel(chan);
HelloServer obj =
(HelloServer)Activator.GetObject(typeof(RemotingSamples.Hello
Server)
, "tcp://localhost:8085/SayHello");
if (obj == null)
System.Console.WriteLine("Could not locate server");
else Console.WriteLine(obj.HelloMethod("Carlo"));
return 0;
}
}
}
Questo codice deve essere salvato come object.cs:
using
using
using
using
System;
System.Runtime.Remoting;
System.Runtime.Remoting.Channels;
System.Runtime.Remoting.Channels.Tcp;
namespace RemotingSamples {
public class HelloServer : MarshalByRefObject {
public HelloServer() {
Console.WriteLine("HelloServer activated");
}
public String HelloMethod(String name) {
Console.WriteLine("Hello.HelloMethod : {0}", name);
return "Hi there " + name;
}
}
}
8
Architettura dell’ applicazione
Il sistema è composto di due entità che interagiscono fra loro al fine di eseguire alcune
funzionalità su un software esterno ad esse. Ci si rende quindi conto che le problematiche da
affrontare sono legate al corretto funzionamento dell’ applicazione distribuita in se e del
software esterno all’applicazione, si è cercato quindi di sviluppare due entità client e server
che siano il più possibile disaccoppiate, in modo che se anche il nodo in cui è in esecuzione
lo Scada non è attivo, le richieste possano essere mantenute ed eseguite il prima possibile.
Fig.2
Al fine di poter gestire in modo sicuro la comunicazione fra la varie parti che compongono
il sistema in caso di errori, disservizi e altri inconvegnenti, l’applicazione è strutturata su
un archivio dati cioè un database. Tale archivio è composto da due tabelle, una contenente
i record delle richieste e una contenente i record delle risposte. Visti i costrutti disponibili
per l’interazione con database presenti in ADO.NET tale scelta permette di ricavare in
modo rapido e semplice le informazione desiderate.
In questo modo una eventuale caduta del server non provoca particolari danni in quanto al
successivo riavvio le richieste risiederanno ancora nell’ archivio e potranno di nuovo
essere gestite.
Al fine di lasciare l’archivio in uno stato sempre consistente i comandi su esso vengono
eseguiti attraverso il protocollo Two Phase Lock, tale scelta deriva dalla semplicità di
implementazione di una tale strategia attraverso ADO.NET, di seguito viene riportata una
breve descrizione di tale protocollo e la relativa implementazione tramite codice.
I comandi che si possono eseguire attraverso Client remoti sono:
9
1.
2.
3.
lettura di un Tag
scrittura di un Tag
esecuzione di una funzione CiCode (linguaggio proprietario di Citect)
Implementazione dell’ algoritmo two phase lock
Two Phase Lock è composto da tre stati:
1.
Stato working
Durante l’esecuzione del corpo dell’azione atomica. Quando il processo è in questo stato
gli oggetti sono inconsistenti.
Se l’elaboratore cade, il meccanismo di recupero deve abortire l’azione atomica.
2.
Stato committing
Durante la terminazione corretta dell’azione.Gli oggetti sono al loro stato finale. Il
processo commuta in tale stato tramite la commit. Se l’elaboratore cade il meccanismo di
recupero deve completare l’azione (valori finali già disponibili).
3.
Stato aborting
Durante la terminazione anomala dell’azione atomica.Gli oggetti devono essere ripristinati
al loro valore iniziale. Il processo commuta in tale stato tramite abort.Se l’elaboratore
cade durante l’azione di aborto,il meccanismo di recupero deve garantire il completamento
del ripristino dei valori iniziali degli oggetti.
Fig.3
10
L’archivio richieste e risposte deve essere utilizzato in modo da garantire un certo livello
di sicurezza sulle operazioni di inserimento e prelievo di record, in particolare se durante
una operazione di inserimento si verifica un errore, l’azione deve essere abortita e
l’archivio riportato allo stato precedente. ADO .NET semplifica notevolmente la soluzione
di inconvegnenti di questo tipo, attraverso la classe OleDBTransaction.
Essa dispone di tre metodi che consentono la rapida ed efficace implementazione
dell’algoritmo Two Phase Lock, in particolare:
1. BeginTransaction();
Inizia una transazione di database nidificata.
2. Commit();
Esegue il commit della transazione di database.
3. RollBack();
Esegue il rollback di una transazione da uno stato in sospeso.
Se il DataBase genera un errore, verrà eseguito il RollBack della transazione e non verrà
registrata alcuna modifica. Se tuttavia non viene segnalato alcun errore, sarà possibile
eseguire il commit della transazione per consentire l’aggiornamento dei dati.
La struttura delle chiamate è riportata di seguito:
//Create a connection and open the connection to the database.
OleDBConnection conn = new OleDBConnection(sConnString);
Conn.Open();
OleDBTransaction trans = conn.BeginTransaction();
try
{
//Execute SQL commands or other database transaction.
:
:
trans.Commit();
}
catch(Exception e)
{
//Error handling, logging, reporting, and so on
:
:
trans.RollBack();
}
finally
{
//Always close connection
conn.Close();
}
Nell’ applicazione i due metodi che inseriscono e prelevano richieste e risposte sono
implementati nel seguente modo:
11
public void insertRequest(string table,
string IP,
string RequestTime,
string Client,
string Kind,
string NewValue,
string RequestText, OleDbConnection
connection )
{
string strInsert = "INSERT INTO "+table+"( IP, RequestTime, Client,
Kind,
NewValue, RequestText )" + " VALUES (
'"+IP+"',
'"+RequestTime+"', '"+Client+"',
'"+Kind+"',
'"+NewValue+"',
'"+RequestText+"' )";
OleDbCommand inst = new OleDbCommand(strInsert,connection);
OleDbTransaction localTrans =
connection.BeginTransaction(IsolationLevel.ReadCommitted);
inst.Transaction = localTrans;
try
{
inst.ExecuteNonQuery();
localTrans.Commit();
}
catch(OleDbException o)
{
localTrans.Rollback();
}
}
public void insertResponse(string table,
string IP,
string RequestTime,
string Client,
string Kind,
string ResponseText, OleDbConnection
connection )
{
string strInsert = "INSERT INTO "+table+"( IP, RequestTime, Client,
Kind,
ResponseText )"+" VALUES ( '"+IP+"',
'"+RequestTime+"',
'"+Client+"', '"+Kind+"',
'"+ResponseText+"' )";
OleDbCommand inst = new OleDbCommand(strInsert,connection) ;
OleDbTransaction localTrans =
connection.BeginTransaction(IsolationLevel.ReadCommitted);
inst.Transaction = localTrans;
try
{
inst.ExecuteNonQuery();
localTrans.Commit();
}
catch
{
localTrans.Rollback();
}
}
12
Si noti l’esecuzione dell’azione di commit() al termine del comando SQL, se si verifica
qualche errore viene generata un’eccezione che provoca la chiamata al metodo
Rollback()
Aspetti di sincronizzazione
Tutti i client connessi al server utilizzano la stessa istanza dell’oggetto remoto, si rende
quindi necessario definire una politica di accesso all’oggetto in modo che le applicazioni
distribuite possano condividere la risorsa in modo trasparente per l’utente.
Di fatto da client le uniche operazioni che devono essere controllate sono l’aggiunta di una
richiesta o il prelievo delle risposte alle chiamate eseguite dal server.
Si pone poi un altro problema, il server deve eseguire comandi su un software SCADA e
non deve “stressarlo” in modo eccessivo, in quanto esso ha il compito primario di
mantenere una moltitudine di informazioni online, e di aggiornarle attraverso letture o
scritture su PLC ogni 250 mSec. Da ciò si è pensato di eseguire una singola operazione ad
intervalli regolari in modo da non provocare un eccessivo overhead. Va poi detto che
Citect non dispone di un meccanismo di schedulazione dei comandi esterni, da ciò si
evince che operazioni eseguite in modo concorrente non possono essere gestite da Citect
in modo autonomo.
La politica implementata per acquisire la risorsa e non renderne possibile l’utilizzo da
parte di altri client, avviene attraverso la creazione di un thread ad ogni richiesta, tale
thread deve controllare che la risorsa sia disponibile e in tal caso prenderne l’uso
esclusivo, aggiungere la richiesta e quindi rilasciare la risorsa.
In particolare per semplificare l’accesso all’archivio delle richieste i client si interfacciano
con un ArrayList che le contiene in modo cronologicamente ordinato, l’ArrayList assume
quindi il comportamento di una coda FIFO, è poi compito del server andare a prelevare
tali richieste e inserirle nell’archivio.
Il server deve quindi disporre di due meccanismi di accesso all’archivio dati, uno per
prelevare le richieste e uno per inserire le risposte.
13
Request Engine
Fig.4
E’ costituito da un thread che alla partenza del servizio inizia la propria esecuzione e
periodicamente controlla se sono presenti richieste pendenti, in tal caso deve inserirle del
database delle richieste, a tal fine deve essere sincronizzato con il meccanismo di
inserimento delle risposte (Request Engine), in modo da lasciare l’archivio in uno stato
consistente.
Come si può notare dal codice riportato di seguito prima di eseguire qualsiasi operazione
esso deve attraversare un semaforo di mutua esclusione, nel caso che ciò non possa
avvenire il thread rimane in attesa che la risorsa venga liberata e solo a tal punto può
continuare la propria esecuzione.
protected void getRequest()
{
while(true)
{
try
{
…
threadMutex.WaitOne();
mutexIsFree = false;
…
if(serviceIsOnLine)
{
if(scadaRemoting.getRemoteObject())
{
requestList = scadaRemoting.getAllRequest;
scadaRemoting.clearRequest();
scadaRemoting.releaseRemoteObject();
insertRequest(requestList);
}
}
14
}
catch(ThreadInterruptedException t)
{
getRequestThread.Abort();
MessageBox.Show(t.Message);
}
finally
{
threadMutex.ReleaseMutex();
mutexIsFree = true;
}
}
}
Per prima cosa viene controllato che il servizio sia attivo, quindi al fine di prevedere un
ulteriore livello di sicurezza per l’accesso alla sezione critica viene invocato un metodo
implementato sull’oggetto remoto:
public bool getRemoteObject()
{
bool _bRet = false;
_bRet = objMutex.WaitOne(10000,true);
isFree = false;
return _bRet;
}
In questo l’accesso alla sezione critica risulta controllato da un altro mutex presente
all’interno dell’oggetto remoto condiviso.
A questo punto ogni singola richiesta pendente viene prelevata dall’Array List e inserita in
modo persistente nell’archivio, una volta completata questa procedura si è certi che la
richiesta verrà schedulata dal server.
La sezione critica viene poi liberata attraverso il seguente metodo:
public void releaseRemoteObject()
{
isFree = true;
objMutex.ReleaseMutex();
}
15
Response Engine
Fig.5
Al contrario del precedente deve prelevare le richieste, eseguirle e inserire le relative
risposte nell’archivio delle risposte.
Come il request engine è costruito da un thread che periodicamente deve analizzare
l’archivio alla ricerca di nuove richieste:
protected void getResponse()
{
while(true)
{
try
{
Thread.Sleep(30000);
threadMutex.WaitOne();
mutexIsFree = false;
if(serviceIsOnLine)
{
16
insertResponse();
}
}
catch(ThreadInterruptedException t)
{
getResponseThread.Abort();
MessageBox.Show(t.Message);
}
finally
{
threadMutex.ReleaseMutex();
mutexIsFree = true;
}
}
}
Attraverso una serie di comandi SQL il metodo insertResponse() verifica la presenza
di nuove richieste e in tal caso le estrae, le esegue e ne espone il risultato, la richiesta
soddisfatta viene poi eliminata dall’archivio.
Ogni richiesta viene gestita mediante la creazione di un nuovo thread “figlio” che si
occupa di comunicare con citect e riportare i risultati del comando e eventuali errori di
comunicazione. Il thread “padre” rimane in attesa della terminazione attraverso un
comando di join().
Politiche di scheduling delle richieste
L’utilizzo di database come archivio dati permette la facile implementazione di un
qualsiasi algoritmo in modo molto semplice, la strategia di scheduling prevista è quella
dell’algoritmo FIFO cioè di eseguire sempre la richiesta pendente che da più tempo è
presente nell’archivio.
Gestioni Errori
Si può presentare la situazione in cui un client cerca di effettuare una o più delle seguenti
operazioni:
1.
aggiunta di una richiesta nel caso in cui Citect sia offline.
In questo caso il server tenterà di eseguire la richiesta e risponderà con un messaggio
indicante l’impossibilità di eseguire tale richiesta.
2.
lettura/scrittura di un Tag inesistente.
Nel caso sopra indicato il server sarà in grado di eseguire la richiesta ma risponderà con un
messaggio indicante il fallimento
3.
richiesta di esecuzione di una funzione CiCode inesistente
Come nel caso precedente
17
Conclusioni
I costrutti messi a disposizione dall’architettura .NET hanno notevolmente semplificato lo
sviluppo del sistema. Le problematiche incontrate durante l’implementazione sono state
risolte in modo più o meno ottimale attraverso la notevole documentazione disponibile in
rete riguardo temi di questo tipo. Alla luce delle prove effettuate fino ad ora il sistema
presenta buone caratteristiche di stabilità anche se una stima più esatta potrà essere
effettuata solo dopo la distribuzione del sistema a più utenti. L’applicazione è stata
concepita fin dall’inizio in modo da sperimentare le varie tecnologie attualmente a
disposizione per sistemi di questo tipo, è ovvio che il sistema potrebbe essere ulteriormente
esteso andando ad incontrare anche tecnologie diverse.
Sviluppi futuri:
 Migliorare la sicurezza utilizzando protocolli di cifratura dei messaggi
 Replicazione di entità Server per una migliore qualità di servizio
Bibliografia







Programmare Visual C#.NET Mickey Williams Mondatori Informatica
Visual C++ 6 J. Bates T. Tompkins McGrawHill
Lucidi del Corso di Calcolatori LS Prof. Antonio Corradi
MSDN.Microsoft.com
www.codeprojects.com
www.mastercsharp.com
www.codeguru.com
18