Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
FACOLTÀ DI INGEGNERIA
Laureando: Relatore:
Indice
1 Introduzione........................................................................................4
2 Analisi..................................................................................................6
2.1 La simulazione DPD ................................................................................................. 6
2.1.1 Cenni sulla simulazione molecolare multiscala................................................................... 6
2.1.2 La simulazione mesoscala e DPD........................................................................................... 7
2.2 Analisi del simulatore.............................................................................................. 8
2.2.1 Ambiente nativo ....................................................................................................................... 8
2.2.2 Gestione dell’input/output...................................................................................................... 8
2.2.3 Implementazione ...................................................................................................................... 9
2.2.4 Panoramica sul funzionamento ........................................................................................... 10
2.2.5 Punti critici................................................................................................................................. 12
2.3 Analisi dei requisiti ................................................................................................ 12
2.3.1 Requisiti del simulatore........................................................................................................... 12
2.3.2 Requisiti del front-end............................................................................................................. 13
2.4 Specifica dei requisiti ........................................................................................... 15
2.4.1 Specifiche del simulatore ...................................................................................................... 15
2.4.2 Specifiche del front-end ........................................................................................................ 16
2.5 Portabilità futura .................................................................................................... 21
2.5.1 Linux e Mono ............................................................................................................................ 21
2.5.2 Influenze sulla progettazione e realizzazione .................................................................... 22
2.6 Casi d’uso .............................................................................................................. 22
3 Implementazione ............................................................................26
3.1 Architettura e tecnologie utilizzate...................................................................... 26
3.1.1 Microsoft .NET Framework...................................................................................................... 26
3.1.2 Fortran 90 e interfacciamento.............................................................................................. 27
3.1.3 Schema architetturale ........................................................................................................... 28
3.2 Implementazione del simulatore......................................................................... 29
3.2.1 Direttive per la compilazione in libreria .............................................................................. 29
3.2.2 Gestione dell’output............................................................................................................... 30
3.2.3 Passaggio dell’input ............................................................................................................... 31
3.2.4 Interruzione del programma ................................................................................................. 31
3.2.5 Controllo stato inizializzazione............................................................................................... 32
3.2.6 Subroutine e funzioni introdotte ........................................................................................... 32
3.2.7 Portabilità e vincoli nell’implementazione ......................................................................... 33
3.3 Classe per l’interfacciamento.............................................................................. 33
3.3.1 P/Invoke (Platform Invoke) .................................................................................................... 33
3.3.2 La classe SimulatorDLL............................................................................................................ 34
3.3.3 Approfondimenti ..................................................................................................................... 35
-2-
Indice
4 Interfaccia ........................................................................................49
4.1 Installazione / Configurazione ............................................................................. 49
4.2 Esempio di utilizzo ................................................................................................. 49
4.2.1 Immissione dell’input .............................................................................................................. 50
4.2.2 Salvataggio/caricamento dei parametri .......................................................................... 51
4.2.3 Configurazione del programma .......................................................................................... 52
4.2.4 Avvio della simulazione.......................................................................................................... 53
4.3 Testing in ambiente Linux ..................................................................................... 54
5 Conclusioni.......................................................................................56
Bibliografia.............................................................................................58
-3-
Capitolo 1 - Introduzione
Capitolo 1
Introduzione
Premessa
L’applicazione di algoritmi computazionali nel campo dell’Ingegneria Chimica è una metodologia di
lavoro al giorno d’oggi quanto mai diffusa. Il supporto offerto dalle tecnologie viene particolarmente
sfruttato in ambiti che richiedono tecniche quali la modellizzazione e la simulazione.
A tal riguardo, le soluzioni software disponibili sul mercato esistono, tuttavia possono risultare a volte
costose, a volte incomplete. Il problema nasce dall’elevato numero di scenari possibili quando si ha a
che fare con algoritmi di simulazione; per tale motivo, infatti, è facile che spesso ci si rivolga a soluzioni
custom.
4
Capitolo 1 - Introduzione
In sostanza si richiede non solo lo sviluppo di un’interfaccia grafica per la gestione dell’applicativo
esistente, ma anche uno studio sull’eventuale portabilità del progetto finale (il progetto d’origine è
infatti sviluppato in ambiente Linux, nell’ambito dell’Università si intende lavorare in ambiente Windows:
la soluzione ottimale consiste in un applicazione multipiattaforma).
La fase di analisi e sviluppo del sistema in esame, svoltasi nell’ambito dell’Università di Trieste, è stata
strutturata secondo i seguenti passi:
1) Chiarimento sugli obiettivi da raggiungere
2) Definizione dei vincoli architetturali e approfondimento sulle tecnologie in uso
3) Studio della soluzione esistente
4) Analisi e specifica dei requisiti del sistema
5) Implementazione dell’interfacciamento e risoluzione problematiche annesse
6) Realizzazione dell’interfaccia grafica
7) Testing e considerazioni sulla portabilità
5
Capitolo 2 - Analisi
Capitolo 2
Analisi
In questo capitolo si affronta la fase di analisi del progetto. Inizialmente vengono messe in luce le
caratteristiche del simulatore (considerando funzionalità, struttura e requisiti di sistema),
successivamente segue uno studio approfondito dei requisiti richiesti dal progetto (input/output,
interfaccia grafica, portabilità) sino ad ottenere le specifiche di ciascun dettaglio e uno schema
riassuntivo di ciò in cui consiste il progetto finale.
Nell’ambito della scienza dei materiali gioca un ruolo fondamentale l’applicazione di tecniche
computazionali: esse infatti permettono di ottenere una previsione sulle proprietà macroscopiche di
materiali in via di realizzazione. L’importanza di tali tecniche coinvolge principalmente il settore delle
nanotecnologie: si ha a che fare dunque con elementi che presentano almeno una caratteristica
morfologica sulla scala dei nano metri. In questo contesto si collocano le tecniche di simulazione
molecolare.
Gli algoritmi di simulazione molecolare costituiscono, infatti, uno strumento sempre più performante per
lo studio delle proprietà dei nano materiali; tuttavia, le proprietà macroscopiche di un materiale
derivano da caratteristiche su più livelli di scala, sia per quanto riguarda le dimensioni, che i tempi.
Le singole tecniche di simulazione dunque, per quanto sempre più potenti, non permettono di ottenere
un risultato completo.
La soluzione consiste nel combinare assieme più modellizzazioni (e quindi simulazioni) su scale diverse.
Questo è ciò che costituisce una tecnica di simulazione multiscala: si integrano diverse tecnologie di
simulazione, ognuna appartenente a una scala specifica (vedi figura 2.1)
6
Capitolo 2 - Analisi
La simulazione mesoscala è un tipo di simulazione che si interpone tra simulazioni a bassa scala (che
considerano i singoli atomi o addirittura i quanti) e ad alta scala (che considerano proprietà del sistema
nella sua completezza, o il processo completo). Essa si basa sui risultati delle simulazioni a scale inferiori
e al contempo fornisce informazioni che verranno utilizzate nelle modellizzazioni a livello macroscopico.
7
Capitolo 2 - Analisi
L’applicativo è stato sviluppato con il linguaggio Fortran 90 e nasce dall’unione di diversi moduli
realizzati nel corso degli anni. Esso nasce originariamente in ambiente Linux, testato sul compilatore
Fortran gfortran; al dipartimento di Ingegneria Chimica dell’Università (ente per il quale viene realizzato
questo progetto) ad ogni modo viene distribuito il codice sorgente: ciò comporta la possibilità di
compilare ed eseguire il programma anche in ambiente Windows. (Per un corretto funzionamento in
ambiente Windows è comunque consigliata la compilazione con il compilatore gfortran, disponibile
anche per tale sistema operativo)
Allo stato attuale i requisiti per la corretta esecuzione del simulatore sono dunque:
a) Sistema operativo a scelta tra Windows e Linux
b) Compilatore gfortran
Il programma si occupa di eseguire un modello di simulazione molecolare DPD (vedi paragrafo 2.1). Il
tempo all’interno della simulazione è misurato in timesteps cioè in intervalli di tempo definiti a priori.
Gestione dell’input
I dati di input sono esplicitati tramite variabili globali all’interno del codice stesso. Gli unici parametri che
possono essere modificati interattivamente dall’utente al momento dell’esecuzione, sono quelli che
vengono definiti control parameters e consistono in:
• durata in timesteps della simulazione
• numero di timesteps ogni quanto viene aggiornato il file di monitoring
• possibilità di riprendere una simulazione interrotta partendo dal file di configurazione associato
• nome del file di configurazione (dove salvare lo stato / dal quale leggere lo stato nel caso si
scelga di riprendere una precedente simulazione)
8
Capitolo 2 - Analisi
Gestione dell’output
L’output viene mostrato durante la simulazione sotto forma di:
• messaggi su console:
o valori iniziali dell’input
o valore dell’energia (iniziale, finale, corrente dopo ogni iterazione)
o numero di iterazioni
o messaggi di errore
• file esterni:
o un file di configurazione (nome scelto dall’utente) contenente lo stato attuale (utile nel
caso si volesse interrompere e riprendere la simulazione)
o un file di monitoring (“monitor.dat”) contenente informazioni sull’andamento di
energia, pressione e temperatura
o più file definiti di tipo RASMOL (estensione “.xyz”) utili all’elaborazione tramite
l’omonimo software esterno (RASMOL per l’appunto)
2.2.3 Implementazione
Segue uno schema dell’implementazione dell’applicativo (vedi figura 2.2). Come si può notare l’input
proviene da più sorgenti, inoltre è fondamentale osservare la distinzione tra le varie tipologie di output
(dall’output generico su console, all’output specifico su file esterni).
9
Capitolo 2 - Analisi
Breve panoramica sul funzionamento del simulatore. L’applicativo viene testato in ambiente Linux (il
suo ambiente originario), compilato tramite il compilatore gfortran. Il file sorgente è chiamato “rdpd-
s_gptms_zns-mptms_1.f90”.
Compilazione ed esecuzione
Il comando per la compilazione è il seguente (da console):
gfortran rdpd-s_gptms_zns-mptms_1.f90
Viene generato un file denominato “a.out” (oppure “a.exe” nel caso ci si trovi in ambiente Windows), si
esegue quindi tale file:
./a.out
Appena avviato il simulatore richiede come input i sopracitati control parameters. Successivamente
restituisce su console una serie di output, relativi alla configurazione iniziale (come si può vedere in
figura 2.3).
Gestione dell’output
L’output dei successivi dati (quali energia, iterazioni, ecc.) viene in parte gestito a livello di console
(come visto sopra) e in parte gestito su file esterni.
Conclusasi una prima fase (pochi minuti di durata) che potremmo definire di inizializzazione, vengono
generati nella cartella del programma diversi nuovi file (vedi figura 2.4), già analizzati nel capitolo 2.2.2.
10
Capitolo 2 - Analisi
Il contenuto del file “monitor.dat”, ad esempio, dopo alcuni minuti di simulazione consiste in 4 colonne
raffiguranti il timestep (colonna 1), l’energia istantanea (colonna 2), la pressione istantanea (colonna 3)
e la temperatura istantanea (colonna 4), come si può vedere in figura 2.5.
Nel caso in cui un utilizzatore del programma volesse cambiare le variabili di input del simulatore
dovrebbe aprire con un editor di testo il sorgente (“rdpd-s_gptms_zns-mptms_1.f90”), modificare i valori
delle variabili globali (vedi esempio in figura 2.6) e ricompilare il programma come precedentemente
visto.
Figura 2.6 - dati di input del programma sotto forma di variabili globali
11
Capitolo 2 - Analisi
In questa sezione, partendo dallo studio appena effettuato sul simulatore, si va ad eseguire un analisi
dei punti critici del sistema in uso che necessitano di correzioni e/o aggiunte; ciò costituisce il punto di
partenza per la fase di definizione dei requisiti che il front-end è vincolato a rispettare.
1. Immissione dell’input
Il primo problema venuto alla luce durante lo studio del simulatore riguarda l’immissione dei dati di
input; non è infatti plausibile pensare che un utente medio che voglia utilizzare il simulatore debba
conoscere il linguaggio Fortran 90 al fine di modificare i dati di input da introdurre. Inoltre, l’applicativo
va ricompilato ogni qual volta venga modificato l’input, ciò comporta l’impossibilità di prescindere dal
codice sorgente, vincolando la distribuzione di un prodotto “finito” e completo.
3. Interfaccia utente
L’interfaccia utente è basilare: è costituita dalla classica schermata a console, tipica degli applicativi
che girano sotto shell. Essa denota dunque un approccio poco user-friendly, inoltre permette poca
interattività con l’utente (quest’ultimo a parte inserire da riga di comando i valori dei control
parameters non ha altre funzionalità a sua disposizione).
1. Vincoli architetturali
Il motore dell’applicazione (l’algoritmo di simulazione) è già stato implementato e distribuito come
codice sorgente; ciò che si richiede è l’interfacciamento al simulatore mantenendo il più possibile
intatto il codice originale. In sostanza le modifiche al codice del simulatore saranno minime e solo se
estremamente necessarie.
12
Capitolo 2 - Analisi
Oltre ai requisiti richiesti è stato ritenuto opportuno, da parte dell’autore, introdurre un ulteriore requisito.
Per tale motivo si ritiene opportuna la creazione di un file di log/result che permetta all’utente di
controllare i messaggi generati durante la simulazione, nonché i risultati finali.
1. Vincoli architetturali
In accordo con l’ente committente è stato scelto di realizzare il front-end sulla piattaforma Microsoft
.NET. Questa scelta è stata in parte motivata dallo sviluppo già in passato di applicativi su tale
framework nell’ambito del Dipartimento di Ingegneria Chimica dell’Università di Trieste. (Per un
approfondimento sulle tecnologie utilizzate si rimanda al capitolo 3.1)
13
Capitolo 2 - Analisi
I seguenti requisiti sono stati introdotti dall’autore del progetto, in quanto ritenuti utili ai fini
dell’utilizzabilità e per incrementare le funzionalità dell’applicativo.
8. Configurabilità dell’interfaccia
L’utente esperto, che abbia dunque raggiunto un grado di successiva abilità nell’utilizzo
dell’applicazione, può trovare funzionale la possibilità di configurare alcuni parametri dell’interfaccia
grafica, quali i valori di default dei campi della form e la descrizione loro associata.
Per tale motivo si prevede la possibilità di modificare tali valori, andando ad adattare l’applicazione
alle proprie esigenze.
14
Capitolo 2 - Analisi
1. Vincoli architetturali
Gli algoritmi di simulazione sono già stati implementati in ambiente Fortran 90; la soluzione che si ha a
disposizione è inoltre testata e funzionante. Ciò porta alla decisione di mantenere l’applicativo in tale
ambiente (escludendo quindi un porting del codice che richiederebbe un costo oneroso in termini di
tempo e lavoro).
I nuovi moduli introdotti che riguardino direttamente la simulazione verranno dunque sviluppati in
Fortran 90.
Tabella 2.1 – Elenco dei parametri passabili per input dall’esterno al simulatore
15
Capitolo 2 - Analisi
1. Vincoli architetturali
E’ stato scelto il framework Microsoft .NET versione 3.5 (al momento l’ultima versione uscita in termini di
tempo); il linguaggio di programmazione per l’interfaccia sarà il C#, dal momento che già in passato è
stato testato l’interfacciamento tra un applicativo Fortran 90 e un applicativo C# nell’ambito del
Dipartimento.
Il sistema operativo per il quale il progetto è destinato all’uso è Microsoft Windows XP/Vista.
16
Capitolo 2 - Analisi
Si è optato per un grafico a linee con indicatori dei valori. Questo perché, in questo specifico caso, ciò
che conta è mostrare l’andamento in tempo reale di diversi valori controllando la presenza di eventuali
abbassamenti o innalzamenti non motivati. Il grafico serve principalmente per valutare se i parametri
della simulazione denotino un comportamento anomalo, per questo il grafico a linee è ciò che risulta
più utile in questo contesto.
17
Capitolo 2 - Analisi
Per ogni valore vengono memorizzati il nome del campo (name) e il valore attuale (value).
La struttura del file XML generato sarà di questo tipo:
E’ importantissimo segnalare il tipo di errore all’utente (ed utilizzare un linguaggio per quanto possibile
comprensibile). Ciò migliora l’usabilità del programma, poiché in caso di errori di input verrà segnalato
qual è il valore da modificare.
Il passaggio dei parametri avviene al momento in cui il front-end si interfaccia con il simulatore.
L’inizializzazione è la prima fase appena avviato il simulatore, non rappresenta la simulazione vera e
propria, che invece avverrà successivamente; l’inizializzazione, inoltre, può durare anche diversi minuti.
L’avvio della simulazione avviene appena finita l’inizializzazione; ci si accorge di ciò dal momento che
vengono generati il file di configurazione (contenente lo stato della simulazione appena inizializzata) e il
primo dei file RASMOL (quelli con estensione .xyz).
18
Capitolo 2 - Analisi
Se il programma non ha trovato errori esegue il ciclo principale che contiene gli algoritmi di
simulazione; questo ciclo avrà termine se viene interrotto dall’utente (vedi requisiti del simulatore –
capitolo 2.3.1) o se la simulazione è stata portata a termine.
Per ciascuna delle precedenti fasi verrà segnalato l’inizio e la fine, oltre ad un’eventuale interruzione
dell’esecuzione.
8. Configurabilità dell’interfaccia
E’ previsto che l’utente abbia la possibilità di modificare due parametri legati all’interfaccia che sta
utilizzando:
- valori di default dei campi di immissione input
- descrizione associata a tali campi
Si prevede quindi una configurazione di default tipica dell’applicazione, immutabile e compresa
all’interno del codice, e una possibile configurazione custom gestita dall’utente.
L’utente in sostanza può modificare i parametri della configurazione di default salvando le nuove
impostazioni su un file esterno “default.xml” (sempre XML quindi) che si troverà in una directory
prestabilita. Al momento di avviare il programma ci sarà un controllo sull’esistenza e la correttezza del
file default.xml: se il file esiste ed è integro (la struttura non è danneggiata ed i valori sono consistenti)
allora verrà caricata la configurazione custom, altrimenti si caricherà la configurazione di default
prevista dal programma.
Verranno dunque salvati il nome del campo di riferimento (name), il valore di default (defaultVal) e la
descrizione associata (description).
19
Capitolo 2 - Analisi
Si può abbozzare una struttura indicativa rappresentata nelle successive figure 2.8 e 2.9.
20
Capitolo 2 - Analisi
Questa scelta risulta spesso cruciale al momento di progettare e realizzare una soluzione software;
bisogna tenere in considerazione un rapporto costi/benefici poiché talvolta il costo per l’utilizzo di una
tecnologia anziché di un’altra può risultare elevato.
D’altro canto i vantaggi di una applicazione, definita portabile (riutilizzabile quindi sotto un ambiente
diverso da quello nativo), sono molteplici: si va da una maggiore diffusione (fondamentale nel caso di
soluzioni commerciali) a un maggiore supporto nello sviluppo (nel caso di soluzioni open source in via di
realizzazione o completamento). Senza considerare poi che per realizzare un progetto portabile
bisogna molto spesso (non sempre) ricorrere all’utilizzo di tecnologie standard, quindi universalmente
riconosciute e seguenti regole ben definite.
In seguito si approfondisce il tema della portabilità sotto Linux prendendo in esame le soluzioni esistenti
in merito ad ambienti di sviluppo e le strade percorribili in tale senso.
Il principale obiettivo che ci si pone è dunque la massima portabilità dell’applicativo verso sistemi
operativi di tipo Linux.
In questo caso si parla di massima portabilità intendendo la possibilità di riutilizzo del software in tale
ambiente con il minor numero di modifiche possibili (in sostanza con il minor costo): è bene precisare,
infatti, che il porting richiede quasi sempre modifiche al codice, nonostante la portabilità ideale sia
rappresentata da un’applicazione riutilizzabile senza modifiche.
Al momento di definire i vincoli architetturali del progetto è stato deciso di optare per lo sviluppo su
framework Microsoft .NET. Questa piattaforma, sebbene di tipo proprietario, permette la portabilità in
ambiente Linux sfruttando un progetto abbastanza recente (nato circa 4 anni fa) chiamato “Mono”.
Senza entrare troppo nel dettaglio, Mono (http://mono-project.com/What_is_Mono) si può definire come un
progetto open source che mette a disposizione diversi strumenti (dall’ambiente di sviluppo a una
macchina virtuale) per la creazione ed esecuzione di applicativi multipiattaforma compatibili con il
framework .NET.
La compatibilità è ottenuta grazie alla presenza di un’insieme di librerie, messe a disposizione,
compatibili con quelle del framework di casa Microsoft. Il linguaggio originariamente supportato
dall’ambiente di sviluppo incluso nel progetto (chiamato “Monodevelop”) è il C#, sebbene
recentemente sia stato introdotto il supporto ad altri linguaggi (Java e Python per citarne alcuni).
21
Capitolo 2 - Analisi
La strada appena messa in luce si traduce concretamente nella scelta di tecnologie, metodologie e
strumenti che supportino la portabilità dell’applicativo.
I vincoli architetturali sono compatibili con la direzione presa (utilizzo di piattaforma .NET, linguaggio di
programmazione C#) e la stessa fase di specifica dei requisiti del front-end tiene da conto questa
scelta.
Un esempio è rappresentato dall’utilizzo del linguaggio XML per la gestione dei parametri di input e di
configurazione dell’interfaccia, in quanto XML rappresenta di per sé uno standard riconosciuto dal
W3C (http://www.w3.org/Consortium/). La sua diffusione difatti è passata dall’ambito del web ad ambiti
più generici quali il passaggio di informazioni tra sistemi diversi, la definizione della struttura di un
documento, e molteplici altri.
La successiva fase di realizzazione (vedi capitolo 3) mostrerà concretamente in che termini la scelta
della portabilità abbia influito sulle tecniche e gli strumenti utilizzati.
La fase di progettazione evidenzierà dei progressivi test anche in ambiente di sviluppo monodevelop, in
modo da controllare passo passo che la strada intrapresa sia quella corretta ai fini degli obiettivi che ci
si è imposti di raggiungere.
22
Capitolo 2 - Analisi
Salvataggio:
- Vengono letti i valori dei campi contenenti la form
- Vengono salvati su file XML in forma (strutturata dunque) nome del campo e valore associato
Caricamento:
- Viene effettuato un controllo sull’esistenza del file
- Viene caricato il file XML tramite un parser XML preesistente
- Viene controllata l’integrità della struttura (il file non deve essere corrotto né deve aver subito
modifiche che ne hanno alterato la struttura)
23
Capitolo 2 - Analisi
- Campo per campo si controlla l’esistenza effettiva del controllo a cui si fa riferimento nel file
- Si imposta il valore del controllo trovato usando quello caricato dal file
Customizzazione dell’interfaccia
24
Capitolo 2 - Analisi
25
Capitolo 3 - Implementazione
Capitolo 3
Implementazione
La piattaforma .NET
Come precedentemente accennato in altri capitoli, è stato scelto di sviluppare il front-end sulla
piattaforma di sviluppo Microsoft .NET.
Senza entrare troppo nello specifico, il Microsoft .NET Framework è una piattaforma per lo sviluppo e
l’esecuzione di applicativi basati sulla tecnologia .NET (http://www.microsoft.com/net/). Si compone
sostanzialmente di una Base Class Library, una libreria di classi disponibili per diverse funzioni, un insieme
di compilatori per i linguaggi supportati (C#, J#, Visual Basic.Net, i principali) e un ambiente di
esecuzione virtuale, il CLR (Common Language Runtime) che fornisce le funzionalità di una Virtual
Machine (seguendo le specifiche di uno standard chiamato CLI – Common Language Infrastructure).
26
Capitolo 3 - Implementazione
In combinazione con il framework si ha a disposizione una suite di strumenti per lo sviluppo degli
applicativi su tale piattaforma: Microsoft Visual Studio.
La realizzazione del front-end è stata effettuata utilizzando la versione 2008 di Microsoft Visual Studio,
mentre la versione del framework .NET è la 3.5.
Il linguaggio di programmazione scelto è il C#, linguaggio orientato alla programmazione ad oggetti
nativamente supportato dalla piattaforma .NET.
Il linguaggio Fortran
Il Fortran 90 è un linguaggio di programmazione, evoluzione dell’originario Fortran (Formula Translation).
Il Fortran fu uno dei primi linguaggi di programmazione ad alto livello e nacque, come suggerisce il
nome stesso, per un utilizzo destinato all’ambito scientifico (implementazione di formule matematiche in
algoritmi computazionali).
27
Capitolo 3 - Implementazione
il supporto al tool di sviluppo Microsoft Visual Studio: il compilatore “Intel Visual Fortran 11.0 Professional
Edition” (http://www.intel.com/cd/software/products/asmo-na/eng/compilers/), evoluzione del predecessore
Compaq Visual Fortran.
Nonostante i benefici offerti da una compilazione in ambiente .NET (tramite la soluzione Lahey Fortran),
è stato deciso, in accordo con l’autore del simulatore, di prediligere il secondo scenario: front-end su
piattaforma .NET, simulatore compilato nativamente.
In tal modo resterà possibile per l’autore del simulatore compilare il sorgente con la soluzione originaria
(compilatore gfortran), rendendo quindi più semplici eventuali sviluppi futuri del codice stesso (in
seguito a migliorie o interventi di manutenzione).
Il vantaggio più grande nell’utilizzo di una libreria dinamica è la possibilità di modificare il simulatore
(editando il sorgente e ricompilandolo) senza essere costretti a ricompilare il front-end (questo a patto
che non vengano cambiate le interfacce delle subroutine o delle funzioni condivise da entrambi).
28
Capitolo 3 - Implementazione
Utilizzo finale
- il simulatore viene compilato in ambiente Windows utilizzando un compilatore a scelta tra Intel
Visual Fortran e Gfortran; le direttive passate al compilatore prevedono la creazione di una
libreria dinamica (file con estensione “.dll” - Dynamic Link Library)
- il front-end è sviluppato in linguaggio C#, in ambiente di sviluppo Visual Studio, ed è compilato
dal compilatore interno (che genera un oggetto in CIL)
- il front-end è collegato alla libreria del simulatore grazie alle funzionalità di una libreria delle
Base Class Library fornite dal framework (vedi capitolo 3.3 per approfondimenti)
- Al momento dell’esecuzione la libreria viene caricata in memoria dal sistema e contiene
codice eseguibile direttamente; il front-end invece viene eseguito in un ambiente virtuale, il
CLR, che si occupa della conversione da CIL a linguaggio macchina adatto all’hardware
specifico.
29
Capitolo 3 - Implementazione
Approfondimento
Le subroutine (o funzioni) esportate, infatti, presentano un nome all’esterno che raramente coincide
con quello all’interno del codice Fortran. Ciò varia da compilatore a compilatore. Se il compilatore
supporta la proprietà ALIAS è possibile definire a priori il nome che avrà la subroutine all’esterno.
NOTA: Nel caso si voglia controllare il nome delle sub e funzioni esportate all’esterno da una dll è
possibile utilizzare lo strumento “dumpbin.exe”. Per utilizzare tale strumento aprire il prompt dei comandi
di Visual Studio e digitare:
Poiché il simulatore non lavorerà più su console, è necessario reindirizzare l’output originariamente su
console in output su file di log.
Per fare ciò è necessario:
1) predisporre un file di log che viene aperto prima dell’inizializzazione e chiuso a fine simulazione
2) convertire le print su console in write su tale file
Approfondimento
1) All’inizio della subroutine principale è stata introdotta l’apertura del file di log:
open (30,file="logresult.txt",access="append",status="unknown")
Si apre il file di log in modalità append (scrivo aggiungendo righe) e unknown (se il file esiste aggiungo
righe a quello esistente, altrimenti ne creo uno nuovo); il primo parametro rappresenta un numero
associato al puntatore al file, permetterà successivamente di scrivere sul file senza dover specificare
nuovamente il nome. A fine simulazione si chiude il file con l’istruzione close.
2) Da un’attenta analisi del codice si è giunti alla conclusione che l’output su console è di due
tipologie:
a) output generico, del tipo:
print *, testo_o_valore
Le print del tipo b), poiché concentrate in un'unica porzione del codice ed essendo di tipologie diverse
(valori formattati, non formattati, testo), vengono direttamente sostituite in scritture sul file di log:
Le print del tipo a) invece, al fine di modificare meno possibile il codice, vengono sostituite da una
subroutine chiamata “myprint” che si occuperà di scrivere su file di log.
30
Capitolo 3 - Implementazione
Passaggio dell’input
Alcuni parametri di input per la simulazione vengono ricevuti dall’esterno, per gestire la ricezione
dall’esterno di parametri è necessario:
1) Trasformare i parametri in variabili globali posizionate all’interno di un modulo
2) Introdurre una subroutine che riceva come argomenti i parametri di input, e assegni alle
variabili globali appena definite il rispettivo valore ricevuto dall’esterno
3) Rimuovere la sezione in cui si leggono alcuni parametri da console (i control parameters – vedi
capitolo 2.2.2), poiché anch’essi verranno introdotti dall’esterno.
Approfondimento
1) E’ stato creato un modulo chiamato “global_data” che conterrà variabili globali usate dalle nuove
subroutine e funzioni, nonché dal programma principale.
I valori di input, definiti originariamente come parametri (con valore quindi esplicitato) diventano
variabili globali all’interno del nuovo modulo creato (senza quindi un valore di default).
2) E’ stata creata una funzione chiamata “load_input” che riceve come argomenti i valori di input,
assegna tali valori alle rispettive variabili (contenute in “global_data”) e alla fine restituisce il valore true.
3) Vengono rimosse tutte le “read”, dal momento che non vi è più interazione con la console.
Il codice sorgente presentava controlli sull’input immesso sia all’interno della subroutine principale, sia
all’interno di altre subroutine; ogni qualvolta non si verificasse una condizione stabilita il programma
veniva interrotto dall’istruzione “stop” (seguita da una descrizione dell’errore da mostrare a console).
Inoltre, la simulazione è strutturata in modo da non poter essere interrotta dall’utente, se non chiudendo
l’applicazione.
E’ necessario, ai fini dell’utilizzo come libreria del simulatore:
1) Scrivere la descrizione dell’errore su un file esterno (poiché non è più disponibile la console)
2) Modificare le istruzioni di “stop” con istruzioni di “return”.
3) Introdurre all’interno del ciclo principale di simulazione un controllo che preveda la possibilità di
interrompere il ciclo (e quindi uscire dal programma)
Approfondimento
1) E’ stata creata una nuova subroutine chiamata “mystop” che riceve come argomento un
messaggio di testo e crea un file chiamato “errore.log” dove scrive il messaggio ricevuto.
E’ stata inoltre introdotta una variabile chiamata “errorCode” che rappresenta un codice restituito
dall’applicazione al momento di terminare; essa è utile per distinguere le interruzioni dovute ad errore,
dalla terminazione dovuta alla fine effettiva della simulazione. Il valore di default di “errorCode” è 0
(simulazione terminata correttamente), la funzione mystop imposta il valore di “errorCode” a -1
(interruzione dovuta ad errori). Inoltre, è stata creata una funzione chiamata “getErrorCode” che
restituisce il valore della variabile “errorCode”.
31
Capitolo 3 - Implementazione
2) Lo statement “stop” non può essere utilizzato all’interno di una libreria, poiché una volta eseguito
restituisce il controllo al sistema operativo, interrompendo dunque non solo il codice della libreria, ma
anche dell’applicazione chiamante.
Per evitare che l’applicazione chiamante venga chiusa quando si interrompe la libreria è necessario
sostituire le “stop” con le “return” (restituisce il controllo al codice chiamante).
Tutte le stop sono state dunque sostituite da chiamate alla funzione “mystop” e successiva return.
3) E’ stata introdotta una variabile globale chiamata “simulStop”, di tipo booleano, con valore di
default settato a false. E’ stato introdotto all’interno del ciclo principale un controllo sulla variabile
simulStop, se essa viene settata a true il ciclo si interrompe e il programma esce con una chiamata a
“mystop” e successiva “return”.
E’ stata inoltre creata una funzione chiamata “AbortSimul” che permetta di settare tale variabile a true
dall’esterno.
Il codice della subroutine principale può essere suddiviso in due macrosezioni (vedi capitolo 2.2.3): una
rappresenta una fase di inizializzazione, l’altra rappresenta la simulazione vera e propria. Si ritiene utile
introdurre una funzione che restituisca l’avvenuta o meno dell’inizializzazione, in modo da distinguere
l’inizio e la fine delle due fasi.
Approfondimento
La scelta di introdurre una funzione di questo tipo è dovuta anche all’impossibilità di interrompere la
fase di inizializzazione. Poiché si è voluto modificare il meno possibile il codice originale si è preferito non
dividere la subroutine principale in due subroutine dedicate a inizializzazione e simulazione; si è optato
invece per l’introduzione di una variabile booleana “initialized”, settata di default a false, che una volta
finita la prima fase viene settata a true.
E’ stata inoltre creata una funzione chiamata “isInitialized” che restituisce il valore della sopracitata
variabile, in modo da poter controllare lo stato dell’inizializzazione anche dall’esterno.
- subroutine init_log ()
32
Capitolo 3 - Implementazione
Scrive su file di log il nome del parametro (desc) e il suo valore (val)
- subroutine abortSimul ()
Le modifiche al codice sorgente, illustrate e approfondite nei capitoli precedenti, sono il frutto oltre che
di uno studio dei requisiti, anche delle previsioni di portabilità per l’applicativo finale.
Si riporta un esempio nel paragrafo seguente.
Platform Invoke (spesso abbreviato P/Invoke) è una tecnica che permette l’esecuzione di codice
unmanaged, contenuto ad esempio all’interno di una DLL, da parte di codice managed (quali il C#
per l’appunto).
NOTA: Gli applicativi sviluppati all’interno del framework .NET contengono codice definito managed;
questo poiché il programma in assembly (cioè in CIL) ottenuto come risultato della compilazione viene
eseguito in un ambiente virtuale dove sono possibili diversi controlli (sicurezza in primis). Per contro, il
codice al di fuori del framework come le DLL (che altro non sono che codice nativo basato su API
win32, nel caso di Windows) viene detto codice unmanaged, perché eseguito senza controlli.
33
Capitolo 3 - Implementazione
using System.Runtime.InteropServices;
[DllImport("dpdsimulator.dll")]
(NOTA: la funzione che si vuole importare con DllImport deve essere definita static, inoltre bisogna
aggiungere l’attributo extern che esplicita come il metodo sia implementato all’esterno).
La classe “SimulatorDLL” è quella che si occupa dell’interfacciamento vero e proprio con il simulatore.
E’ composta dai metodi importati dalla libreria, contenenti quindi codice unmanaged, e da metodi
propri, che sono quindi metodi managed. (Vedi figura 3.3, i metodi evidenziati sono quelli importati)
Campi principali
I campi della classe sono tre (tutti private) e rappresentano:
- private int errorCode
il codice d’errore restituito al momento di interruzione del simulatore
- private bool stopped
lo stato del simulatore (avviato o non avviato?)
- private Thread thrd
il thread associato al simulatore (vedi paragrafo successivo)
34
Capitolo 3 - Implementazione
Metodi principali
- private void run()
Avvia il simulatore. Successivamente (cioè quando il codice del simulatore sarà terminato), effettua
un controllo sul codice di errore (usando “getErrorCode” – vedi capitolo 3.2.6) e in base al risultato:
a) mostra un messaggio di avvenuta simulazione
b) controlla l’esistenza del file di errore e mostra il messaggio di errore
Infine setta a true “stopped” e termina il thread.
- public bool setSimInput
Passa i parametri di input chiamando la funzione “load_input” della libreria del simulatore.
Restituisce lo stesso valore booleano restituito dalla “load_input”.
- public void start()
3.3.3 Approfondimenti
Multi
Un’applicazione MultiThread è strutturata in modo da permettere la suddivisione del programma stesso
in task eseguibili in maniera concorrenziale. Ciò risulta particolarmente utile nel caso si debba eseguire
una porzione di codice dalla durata significativamente elevata, avendo necessità di compiere nel
frattempo altro lavoro.
Il sistema che si è andati a progettare prevede l’esecuzione di algoritmi computazionali che possono
durare ininterrottamente anche per giorni (questa è una caratteristica di gran parte degli algoritmi di
simulazione). Lo sviluppo di un’applicazione strutturata in un unico thread porterebbe a conseguenze
quali l’inutilizzo del front-end durante l’esecuzione della simulazione (quindi, come detto, per tempi
decisamente lunghi). Per questo motivo è stata scelta la strada del multiThreading.
Per utilizzare la tecnologia MultiThread in ambiente .NET è sufficiente utilizzare gli strumenti forniti dalla
classe “System.Threading”. Segue un breve esempio delle funzionalità messe a disposizione:
thrd.Start();
thrd.Abort();
All’interno del metodo è possibile introdurre un’istruzione per metterlo in pausa un certo numero di ms:
thrd.Sleep(tempo_in_millisecondi);
35
Capitolo 3 - Implementazione
Passaggio di parametri
Al momento del passaggio di parametri da un codice ad un altro bisogna tenere conto di:
a) Che tipo di passaggio di parametri utilizzano (per valore o riferimento?)
b) Quali tipologie di dati definiscono (esiste il tipo float? E il tipo string?)
a) In C# ho tre tipologie di passaggio di parametri: in, out, ref. Sostanzialmente con in la variabile viene
passata per valore e non viene modificata durante la chiamata, con out la variabile viene passata per
argomento e settata all’interno della procedura chiamata e con ref la variabile viene sempre passata
per riferimento, ma va settata prima del passaggio. Di default è implicitamente usato in.
In Fortran la situazione è diversa: le variabili vengono passate generalmente (poi ci sono casi specifici)
per riferimento. Per questo motivo tutte le variabili passate da C# a Fortran dovranno essere specificate
come ref.
b) I datatypes (tipologie di dati) del Fortran, differiscono da quelli del C# (in generale del C). Per
esempio si riporta una tabella con alcune differenze:
FORTRAN C
logical bool
real float
real*8 double
Inoltre, in Fortran le stringhe sono considerate come array di caratteri. Tuttavia è comunque possibile
passare una string utilizzando un espediente che viene chiamato “hidden length”: oltre a passare il
riferimento alla string si passa anche la lunghezza della stringa. Questo parametro, sebbene non sia
presente nell’interfaccia della subroutine/funzione chiamata, è necessario al Fortran a istanziare l’array
di caratteri che conterrà la stringa.
(fonte: http://msdn.microsoft.com/en-us/library/ty8d3wta.aspx)
Per questo motivo per interrompere il simulatore non ci si è solo affidati alla “Thread.Abort” (chiamata
comunque una volta usciti dal codice), ma è stato necessario modificare il codice unmanaged
permettendo la possibilità di interrompere il ciclo principale.
36
Capitolo 3 - Implementazione
La configurazione del sistema (intendesi: i valori di default dei campi della form principale) è gestita da
tre metodi della classe Form1:
- createDefaultConfiguration
- loadDefaultConfiguration
- saveDefaultConfiguration
Questo metodo crea una configurazione di default per il programma, inizializzando i valori dei
campi di input, le loro descrizioni e il tipo di dato contenuto.
Approfondimento
Per ogni parametro di input esiste un’omonima ArrayList (una struttura simile ad un array, ma
che permette il salvataggio di dati non appartenenti alla stessa tipologia).
Questo metodo istanzia le ArrayList seguendo una struttura di questo tipo (parametro “nrun”):
Così facendo, per risalire ad un particolare ArrayList a partire da un controllo, basta usare come
il controllo stesso come chiave nel Dictionary “ctrl_paramts”.
Questo metodo carica i valori di default nei campi della form principale, impostando anche le
descrizioni associate (visibili in barra di stato) e il tipo di campo.
37
Capitolo 3 - Implementazione
Approfondimento
Quando la configurazione di default viene modificata (vedi punto 8 del capitolo 2.4.2) viene
creato un file chiamato “default.xml” in una sottocartella del programma chiamata “config”.
Anzitutto viene controllata l’esistenza di tale file: in caso negativo tutti i valori saranno presi dalle
ArrayList di default viste prima, altrimenti parte dei valori verrà presa dal file (valori di default e
descrizione) e parte dalle ArrayList.
Sul file “default.xml” viene salvato tutto tranne il tipo del controllo (che infatti viene caricato
sempre dalle ArrayList). Questo per evitare che venga modificato accidentalmente il file,
introducendo valori che potrebbero interferire col corretto funzionamento del programma.
A questo punto devo aggiungere i record. Per aggiungere un record devo definire un oggetto
del tipo “DataRow”:
DataRow dr;
foreach (ArrayList par in ctrls_paramts.Values)
{
dr = dataCfg.NewRow();
/* name and type are taken from default array par */
dr["name"] = ((Control)par[0]).Name;
dr["type"] = par[2].ToString();
[...]
Successivamente creo un record per ogni parametro di input (in pratica scorro tutti gli ArrayList
contenuti nel Dictionary creato con la “createDefaultConfiguration”) e riempio i campi “name”
e “type” prendendo i valori dall’ArrayList del parametro.
Precedentemente si era detto che nel caso dell’esistenza di un file di configurazione, parte dei
valori sarebbero stati caricati e presi da quello. Difatti, viene creata una DataTable che
conterrà i valori caricati (chiamata “dataCfg_loaded”) e in seguito viene popolata utilizzando il
metodo “ReadXml”, che permette la lettura di dati da file xml (a patto che tale file sia stato
precedentemente generato con un altro metodo: “WriteXml”):
dataCfg_loaded.ReadXml("config\\default.xml");
Tornando al caricamento dei valori, parte di essi sono stati presi dall’ArrayList del parametro, i
restanti (“defaultVal” e “description”) vengono presi dunque da questa “dataCfg_loaded”
accedendo alla riga di interesse tramite una query di selezione:
dr_loaded = dataCfg_loaded.Select("name='"+((Control)par[0]).Name+"'");
if (dr_loaded.Count() == 1)
{
dr["defaultVal"] = dr_loaded[0]["defaultVal"];
[...]
38
Capitolo 3 - Implementazione
In sostanza si applica il metodo “Select”; il criterio di ricerca è “il campo ‘name’ deve
contenere il nome del controllo associato all’ArrayList corrente” (siamo ancora dentro il ciclo
dove scorrevamo tutti gli ArrayList dei parametri).
dGV_configuration.DataSource = dataCfg;
Dopo aver verificato l’esistenza del controllo tra i controlli del panel dove si trova la form di
input, viene impostato il suo valore (ctrl.Text) e l’evento da eseguire quando si entra nel campo
(ctrl.Enter). L’evento da eseguire sarà una funzione che usa il controllo come chiave nel
Dictionary “ctrls_paramts”, ottiene l’ArrayList del parametro associato al controllo, prende il
campo con la descrizione e lo mostra nella barra di stato del front-end.
Approfondimento
I valori di default sono stati creati all’avvio del programma e si trovano, oltre che nelle ArrayList
dei parametri, nella DataGridView “dGV_configuration”.
Per salvare i parametri sul file:
- si va a riprendere la DataTable associata a tale DataGridView
- ne si fa una copia (per non modificare l’originale)
- si rimuove il campo “type” che, come è stato precedentemente detto, non verrà salvato
poiché non modificabile
- si salva su file utilizzando il metodo “WriteXml” della classe DataTable
39
Capitolo 3 - Implementazione
Il salvataggio e caricamento dei parametri di input viene effettuato tramite i due metodi:
- saveParamsToXML
- loadParamsToXML
Questo metodo della classe Form1 scorre tutti i controlli presenti nella form di input principale e
ne memorizza il valore (oltre che al nome) su un file esterno di tipo XML a scelta dell’utente.
Approfondimento
Viene creata una DataTable che conterrà i valori, viene popolata scorrendo i controlli della
Panel che contiene la form di input (filtrandoli in base a una convenzione sul nome), infine
viene salvata la DataTable utilizzando il metodo “WriteXml” precedentemente visto.
La funzione controlla l’esistenza del file e carica da esso i valori dei campi della form di input.
Nel caso qualcosa non andasse a buon fine (struttura del file xml corrotta, errore in lettura, file
non trovato) restituisce il valore false.
Se i valori sono stati correttamente caricati, vengono aggiornati i controlli.
Approfondimento
I valori vengono caricati in una DataTable mediante il metodo “ReadXml” precedentemente
visto. Sono effettuati controlli sulle eccezioni di tipo “XmlException” (struttura danneggiata) o
“InvalidOperationException” (la struttura dell’oggetto di destinazione non corrisponde a quella
presente nel file xml).
L’aggiornamento dei controlli viene fatto con un ciclo sui record della DataTable appena
popolata. I controlli sono come al solito filtrati in base a convenzioni sul nome (deve iniziare per
“tB_”).
L’utilità del valore del ritorno: serve al metodo chiamante per informare l’utente dell’avvenuto
errore, e offrire la possibilità di caricare comunque i valori di default dell’applciativo al posto di
quelli salvati.
Il controllo dei parametri immessi nella form di input è compito di due metodi (più un terzo metodo
chiamato all’interno del secondo):
- areParametersOk
- loadParametersFromControls
o setInputFromCtrl
40
Capitolo 3 - Implementazione
Effettua una semplice verifica che tutti i controlli della form di input (filtrati opportunamente in
base al nome) non siano vuoti.
Carica dai controlli della form di input i valori, controlla la correttezza del tipo e li salva in
particolari strutture dati di tipo Dictionary, che verranno utilizzate successivamente al momento
di passare i parametri alla classe SimulatorDLL.
Approfondimento
Tra le variabili globali della classe Form1 sono state predisposte tre strutture di tipo Dictionary.
Tutte e tre usano come chiave una stringa, tuttavia memorizzano valori di tipo differente:
- Stringa e intero:
Dictionary<string, int> input_i = new Dictionary<string, int>();
- Stringa e double:
Dictionary<string, double> input_d = new Dictionary<string, double>();
- Stringa e stringa:
Dictionary<string, string> input_s = new Dictionary<string, string>();
Il metodo preso in considerazione scorre tutti i controlli della form di input, controlla il tipo
associato ad ogni controllo e aggiunge al Dictionary corrispondente la coppia: (nome del
parametro, valore).
Per ottenere il tipo di un controllo si prende il controllo, lo usa come chiave nel Dictionary
“ctrls_paramts” ottenendo l’ArrayList associato, nell’ArrayList trovato si va quindi a prendere il
tipo di dato memorizzato nel controllo (può essere “int”, “double” o “string”).
La verifica che il tipo del valore presente nel controllo coincida col tipo specificato per tale
controllo viene fatta dal metodo “setInputFromCtrl (nome del parametro,valore,tipo)” che
viene analizzato nel paragrafo successivo.
41
Capitolo 3 - Implementazione
Converte la variabile “value” da stringa generica a valore in base al tipo descritto da “type”; in
seguito aggiunge la coppia (“name”, valore_convertito) a uno dei tre Dictionary “input_i”,
“input_d”, “input_s” a seconda del tipo.
Approfondimento
La conversione viene fatta utilizzando i metodi della Classe statica “Convert”. Il controllo sulla
correttezza del tipo viene fatto catturando le eventuali eccezioni sollevate al momento della
conversione:
try
{
switch (type)
{
case "int":
input_i.Add(name,Convert.ToInt32(value));
break;
case "double":
input_d.Add(name, Convert.ToDouble(value));
break;
case "string":
input_s.Add(name, value);
break;
default:
return false;
}
}
catch (FormatException)
{
MessageBox.Show([...]);
return false;
[...]
I metodi che si occupano del controllo del file di monitoring e della visualizzazione su grafico sono:
- initializeGraph
- updateGraph
Prima di passare all’analisi di questi metodi è bene introdurre la libreria utilizzata per la visualizzazione di
valori sottoforma di grafico: la libreria ZedGraph.
Libreria ZedGraph
ZedGraph (http://zedgraph.org/) è un set di classi scritte in C# per la creazione di grafici 2D a partire da
diverse tipologie di valori.
E’ stata scelta poiché essendo scritta in C# è utilizzabile anche in ambiente Linux, difatti risulta
nell’elenco delle librerie supportate e consigliate da Mono (http://www.mono-project.com/Libraries).
Il modulo “initializeGraph” è stato sviluppando a partire da un modulo già esistente nel progetto di
demo distribuito con la libreria. Pertanto per quanto riguarda l’utilizzo si rimanda alla documentazione
sul sito ufficiale.
42
Capitolo 3 - Implementazione
Inizializza i due grafici presenti nella schermata della simulazione (imposta nomi degli assi, colori,
tipologie di curve e altri parametri strettamente legati alla visualizzazione dei grafici).
Per i motivi spiegati nel paragrafo precedente non ne viene approfondita l’implementazione.
Questo è il metodo che si occupa dell’aggiornamento vero e proprio dei grafici prendendo i
valori dal file di monitoring “monitor.dat” aggiornato durante la simulazione.
è nata per la gestione dei controlli nelle Windows Form, infatti è bene ricordare come non sia
possibile modificare le proprietà di un controllo creato da un thread, da parte di un altro thread
(viene generata un eccezione del tipo “CrossThreadException”).
Approfondimento
Il metodo “updateGraph” è associato a un Timer chiamato “timer_updateGph”; tale Timer,
avviato appena finita la fase di inizializzazione da parte del simulatore, genera l’evento
updateGraph ad intervalli di tempo definiti.
L’intervallo di tempo con il quale viene controllato il file di monitoring (e quindi generato il
grafico) non è fisso, ma è calcolato in base al parametro “nprint” impostato nella simulazione.
In seguito a diversi test effettuati su un PC relativamente potente (Intel Core 2 Duo P8600 / 4GB
di RAM) si è giunti a una stima di esecuzione di 10 timesteps/sec (quindi 1 timestep corrisponde
a 100ms facendo il rapporto).
Per questo motivo è stato settato l’intervallo del “timer_updateGph” a (100 * nprint) ms.
Tornando al metodo, viene inizialmente controllata la presenza del file di monitoring (in caso
negativo si ferma il Timer e si esce con un messaggio d’errore); in seguito si controlla la
lunghezza del file e se ne memorizza il valore. Se tra due letture il valore è cambiato allora si
legge l’ultima riga e si aggiorna il grafico:
L’ultima riga è sempre a 66 bytes dalla fine, dal momento che le righe han lunghezza costante.
Importante il parametro FileShare.ReadWrite poiché il file è aperto anche dal simulatore.
43
Capitolo 3 - Implementazione
Nel caso non si aprisse il file in condivisione di lettura e scrittura si andrebbe incontro ad un
errore di accesso concorrenziale al file da parte della libreria del simulatore.
Dalla riga letta vengono estratti i valori con una semplice operazione di parsing; in seguito tali
valori sono convertiti in formato double, utilizzando i metodi della classe Convert, e vengono
passati ai metodi della libreria “ZedGraph” per l’aggiornamento dei grafici.
Istanzia un oggetto della classe SimulatorDLL (vedi capitolo 3.3.2), inizializza i grafici, passa i
parametri caricati dalla form di input al simulatore, infine avvia la simulazione e il
“timer_checkinit” che si occuperà di controllare la fine dell’inizializzazione.
Approfondimento
Il passaggio dei parametri avviene in due fasi:
1) vengono caricati i parametri dai controlli della form di input e vengono salvati nei
Dictionary predisposti “input_i”, “input_d” e “input_s” utilizzando il metodo
“loadParametersFromControls” (vedi capitolo 3.4.3)
2) viene chiamato il metodo “setSimInput” della classe SimulatorDLL passando i valori
contenuti nei tre Dictionary:
dpd.setSimInput(input_d["L"],[...],input_i["nrun"],[...],input_s["cnfile"])
Metodo chiamato ogni 6 secondi (si consideri che la fase di inizializzazione a seconda dei valori
di input può durare da una decina di secondi a diversi minuti in media).
Si occupa di controllare l’avvenuta fine della fase di inizializzazione della simulazione servendosi
del metodo “isSimInitialized” della classe SimulatorDLL.
44
Capitolo 3 - Implementazione
Una volta verificatasi tale condizione, ferma il timer associato (“timer_checkinit”) e avvia i due
timer relativi al controllo della fine simulazione (“timer_checkstop”) e all’aggiornamento dei
grafici di monitoring (“timer_updateGph”).
Metodo chiamato ogni 120 secondi (si ricorda che una simulazione può durare minuti, ma
anche ore o giorni) per non pesare sul carico di lavoro della CPU.
Questo metodo controlla l’effettiva interruzione della simulazione sfruttando il metodo
“isSimStopped” della classe SimulatorDLL.
Una volta verificatasi tale condizione ferma il timer associato (“timer_checkstop”) e il timer per
l’aggiornamento dei grafici di monitoring (“timer_updateGph”).
Inoltre, ricava il codice d’errore restituito dalla simulazione (tramite una chiamata al metodo
“getSimErrorCode” sempre della classe SimulatorDLL) e prevede due situazioni di output in base
al fatto che il simulatore sia stato interrotto manualmente o sia stato terminato in seguito alla
fine dell’algoritmo di simulazione.
3.5.1 Struttura
La struttura scelta per l’implementazione dell’interfaccia consiste in una semplice applicazione di tipo
SDI (Single Document Interface); una finestra singola, contenente però più controlli di tipo Panel; in
sostanza ogni controllo di tipo Panel è a sua volta un contenitore di altri controlli.
Ho 3 Panel principali (vedi figura 3.4):
- uno per la form di immissione input, che costituisce la schermata iniziale
- uno per la configurazione dei parametri di default del programma
- uno per lo stato della simulazione e per i grafici di monitoring
La scelta della struttura ricalca le funzionalità dell’applicativo:
- introdurre input
- configurare i parametri dell’interfaccia
- interagire con la simulazione
45
Capitolo 3 - Implementazione
L’interfaccia utente è un fattore fondamentale al giorno d’oggi, essa deve sostanzialmente adattarsi
all’utente e non viceversa. E’ importante tenere conto dei seguenti parametri al momento della
progettazione di un’interfaccia utente:
- deve adattarsi a diverse tipologie di utenti: sia principianti che esperti. Ciò si traduce in
informazioni più dettagliate e suggerimenti, ma anche in tasti di scelta rapida e pochi
messaggi di conferma (solo se necessari).
- deve prevenire l’immissione di input errato, effettuando controlli sui campi e informando
l’utente sulle modalità di compilazione di una form
Nel progetto che è stato realizzato ciò si traduce nelle seguenti scelte:
- presenza di un menu (che offre in maniera strutturata tutte le funzionalità disponibili)
- presenza di una barra di stato (che permette di mostrare all’utente messaggi utili senza però
utilizzare
- finestre di tipo MessageBox che richiedono un’interazione)
- presenza di descrizione per ogni controllo (per aiutare l’utente principiante nell’utilizzo
dell’applicativo)
- presenza di tasti di scelta rapida sui menu e sui pulsanti (per favorire l’utilizzo da parte di utenti
esperti)
46
Capitolo 3 - Implementazione
Prima di terminare il capitolo riguardante l’implementazione è bene mettere in luce una modifica
importante da apportare all’applicazione per quanto riguarda la gestione dello stack.
Il problema di fondo nasce dall’utilizzo del simulatore come libreria dinamica. Esso infatti, non essendo
nato per essere utilizzato all’interno di una libreria, è strutturato in modo da gestire lo stack a
disposizione unicamente per se stesso. Al momento, invece, della chiamata da parte di
un’applicazione esterna, lo stack a disposizione per il front-end e la libreria si esaurisce generando
un’eccezione di StackOverflow.
Il valore è in esadecimale, in decimale corrisponde a 1.048.576 (byte), cioè 1MB riservato allo stack.
Dopo un’attenta ricerca si è giunti ad una soluzione per modificare la dimensione dello stack riservato
all’applicativo, utilizzando il tool Microsoft Binary File Editor (“editbin.exe”).
Dopo diversi test, è stato possibile valutare un valore accettabile per la dimensione dello stack che si
assesta attorno ai 5MB.
editbin /stack:nuova_dimensione_dello_stack_in_byte
Requisiti di sistema
Il testing verrà effettuato su un sistema operativo Linux: la distribuzione OpenSUSE 11.0 con ambiente
Mono 2.2. Si preannuncia già che per motivi che verranno approfonditi nei prossimi paragrafi è
necessaria una versione della libreria “libgfortran” >= 4.4 (quindi una versione del compilatore “gcc”
superiore alla 4.3)
47
Capitolo 3 - Implementazione
Il sorgente viene compilato in una libreria condivisa (quindi con estensione .so – Shared Object):
A questo punto non basta collocare la libreria nella cartella dell’applicativo. Difatti, durante il testing
l’applicativo non riconosceva la libreria generando una DllNotFoundException.
Dalla documentazione di Mono si è giunti a una soluzione:
- copiare la libreria nella cartella “/usr/local/lib”
- introdurre se non già presente tale percorso nel file “/etc/ld.so.conf”
- avviare il programma “ldconfig” aggiornando la cache delle librerie condivise
(per ulteriori informazioni su ldconfig: http://linux.die.net/man/8/ldconfig)
Modifiche al front-end
Il front-end richiede pochissime modifiche (come da obiettivo preposto); è necessario:
- modificare il riferimento alla libreria nell’istruzione “DllImport” mettendo il nuovo nome
- modificare i nomi delle subroutine interfacciate (poiché il compilatore gfortran non supporta la
direttiva “alias” – vedi capitolo 3.2.1)
NOTA: in ambiente Linux non si ha a disposizione il programma “dumpbin.exe” (vedi capitolo 3.2.1),
tuttavia per ottenere il nome delle subroutine esportate dalla libreria è sufficiente utilizzare l’applicativo
“objdump”:
objdump -T libsimulatordpd.so
48
Capitolo 4 - Interfaccia
Capitolo 4
Interfaccia
49
Capitolo 4 - Interfaccia
La form risulta già compilata (fatta eccezione per il nome del file di configurazione) con i valori di
default previsti all’interno del programma (ricavati dalle specifiche – vedi capitolo 2.4.1)
Nel caso in cui si scelga di ripristinare una precedente simulazione, verrà abilitato il pulsante accanto al
campo “Configurational file” che permetterà di scegliere un file di configurazione generato in una
simulazione precedente (vedi figura 4.2)
50
Capitolo 4 - Interfaccia
E’ possibile aprire anche file contenuti in cartelle diverse da quella dell’applicazione principale, il
programma stesso si occuperà di verificare la cartella ed eventualmente di creare una copia del file
scelto nella cartella principale (ciò è necessario poiché il simulatore è nato per funzionare con un file di
configurazione presente nella propria cartella).
Nel caso si voglia salvare l’input immesso nella form è sufficiente premere il tasto “Save” o scegliere dal
menu “Parameters -> Save”, la prima volta verrà chiesto il nome del file su cui salvare, successivamente
i salvataggi avverranno sul file specificato la prima volta, senza dover ripeterne il nome.
Nel caso invece si vogliano salvare i parametri su un nuovo file è sufficiente scegliere dal menu
“Parameters -> Save As...” (vedi figura 4.3)
Per caricare dei parametri precedentemente salvati è sufficiente utilizzare il pulsante “Load” presente in
fondo alla form di input, o in alternativa selezionare dal menu “Parameters -> Load”.
Nel caso in cui il file XML sia corrotto o sia stato manualmente modificato in maniera errata verrà
comunicato l’errore richiedendo se sia necessario caricare i valori di default del programma (vedi
figura 4.4)
51
Capitolo 4 - Interfaccia
Se invece di salvare e caricare file contenenti i parametri di input si è soliti utilizzare spesso un’unica
configurazione di input, potrebbe risultare conveniente modificare la configurazione di default del
programma (i valori caricati all’avvio per intendersi).
Per fare ciò è sufficiente selezionare dal menu la voce “GUI Settings”, si accede a una sezione dove è
possibile modificare i valori di default dei campi e la loro descrizione (vedi figura 4.5); in seguito è
possibile scegliere se salvare (pulsante “Save”) o semplicemente tornare alla schermata principale
senza effettuare modifiche (pulsante “Back”).
NOTA: Le colonne “name” e “type” sono in sola lettura, questo per evitare che un utente possa
inavvertitamente modificare valori di vitale importanza per il corretto funzionamento dell’applicazione.
Tali valori non possono essere modificati, in quanto strettamente legati alla struttura del front-end.
52
Capitolo 4 - Interfaccia
Una volta completato l’input della form principale si può passare alla fase di simulazione selezionando
la voce “2. Go to simulation” nel menu di navigazione rapida a sinistra.
Se l’input immesso è incorretto viene comunicato un messaggio d’errore e si viene riportati alla
schermata di input. Vengono inoltre evidenziati i campi con valori incorretti. (vedi figura 4.7)
Figura 4.7 - gestione di input errato tramite messaggio ed evidenziazione dei campi
In caso contrario è possibile accedere a una nuova schermata contenente lo stato della simulazione,
per il momento vuoto, e i grafici di monitoring, ancora da inizializzare.
A questo punto è sufficiente avviare la simulazione utilizzando il pulsante “start” o selezionando dal
menu “Simulation -> Start”; notare come il pulsante “stop” sia disabilitato: verrà abilitato solo una volta
finita la fase di inizializzazione.
A questo punto i grafici vengono configurati e viene comunicato l’avvio della simulazione e della fase
di inizializzazione (che può durare in media diversi minuti), richiedendo la conferma da parte dell’utente
(vedi figura 4.8)
53
Capitolo 4 - Interfaccia
Una volta finita la fase di inizializzazione è possibile stoppare la simulazione in qualunque momento
tramite il pulsante “stop”. Come si può notare è possibile vedere lo stato della simulazione nella sezione
“Status”, mentre è possibile seguire l’andamento in tempo reale dei valori di monitoring nei grafici
sottostanti (vedi figura 4.9).
Infine, si ha a disposizione il log/result della simulazione nel file generato nella cartella principale,
chiamato “logresult.txt”; esso conterrà dunque i messaggi di output avvenuti durante la simulazione,
nonché, i risultati finali a fine simulazione.
54
Capitolo 4 - Interfaccia
L’unico problema rimasto è rappresentato dall’illeggibilità del file di log; ciò è molto probabilmente un
errore legato al compilatore gfortran 4.4 e alla scrittura su file in particolari contesti (c’era già stato un
bug nelle precedenti versioni, come visto nel capitolo 3.6.2).
55
Capitolo 5 - Conclusioni
Capitolo 5
Conclusioni
Capitolo conclusivo del documento nel quale si cercherà di fare il punto della situazione attuale e
valutare eventuali prospettive e sviluppi futuri; si prenderanno inoltre in considerazione gli obiettivi iniziali
per stilare un’analisi qualitativa su ciò che è stato realizzato.
Seguiranno, infine, le conclusioni personali da parte dell’autore del documento.
Il sistema risultato di questo processo di analisi e sviluppo rappresenta una soluzione soddisfacente gli
obiettivi funzionali preposti. Il risultato, infatti, consiste in un front-end grafico interfacciato, secondo più
prospettive, con la soluzione di partenza.
Si può affermare di aver soddisfatto in sostanza i requisiti di usabilità che rendevano per l’appunto il
progetto originario incompleto. Si è lavorato non solo sull’interfaccia grafica, ma sui punti di debolezza
intrinseci al simulatore stesso; le modifiche apportate sono il frutto non solo di necessità meramente
tecniche di interfacciamento, ma anche di un’attenta analisi sull’usabilità dell’applicativo esistente.
La nuova interfaccia, inoltre, è stata progettata da un lato per l’adattabilità al motore di simulazione,
dall’altro per fornire le funzionalità utili ai futuri utilizzatori nell’ambito del contesto universitario.
Il front-end realizzato è implementato sul framework Microsoft .NET 3.5 ed è composto di 1007 righe di
codice in linguaggio C#. Il core del sistema è costituito dal simulatore, rimasto nel linguaggio Fortran90
e modificato in maniera mirata; esso è implementato in ambiente nativo e si interfaccia al front-end
sotto forma di libreria dinamica. Sono state introdotte 7 subroutine e funzioni per un totale di 136 righe
aggiunte al progetto iniziale (senza considerare le modifiche apportate).
Il sistema, inoltre, è stato testato in ambiente Linux per soddisfare il requisito (non vincolante, ma utile ai
fini del progetto in questione) di portabilità. Il risultato si è rilevato positivo: il codice richiede un numero
considerevolmente minimo di modifiche, inoltre le funzionalità sono quasi tutte rispettate (ad eccezione
del file di log/result che per via di un bug non risulta leggibile).
56
Capitolo 5 - Conclusioni
Il sistema attualmente è in fase di testing all’esterno dell’Università. Prima del suo effettivo utilizzo
nell’ambiente di destinazione sarà necessario però definire gli sviluppi futuri del progetto e, cosa di
maggiore importanza, le tempistiche associate.
Sviluppi futuri
Sicuramente i punti sui quali si intende lavorare in previsione di scenari futuri sono:
- porting del simulatore all’interno del framework
- aggiunta di nuovi parametri di gestione della simulazione
- portabilità completa in ambiente Linux
Il primo punto è relativo alla possibilità di compilare il simulatore internamente al framework .NET
(tramite la soluzione commerciale Lahey Visual Fortran). Le motivazioni che muovono in questa
direzione riguardano vantaggi in campo di sicurezza (tutto il codice diventa managed ed eseguito
nell’ambiente virtuale messo a disposizione) e nel campo delle prestazioni (il codice compilato in tal
modo è ottimizzato per l’esecuzione all’interno del framework). Questa scelta era già stata affrontata
nelle fasi di analisi e implementazione, tuttavia necessita dell’assenso da parte dello sviluppatore del
codice originale.
Il secondo punto riguarda la possibilità di gestire ulteriori valori di input al di fuori di quelli standard
richiesti nei requisiti; questo può essere utile per utilizzatori più esperti che vogliono avere a disposizione
più parametri sui quali intervenire. E’ richiesta tuttavia una modifica strutturale al programma che
potrebbe richiedere tempi di realizzazione significativi.
Il terzo punto prende in considerazione la risoluzione delle problematiche presenti allo stato attuale
nella fase di porting (il bug nella generazione del file di log) e prevede un’ulteriore fase di testing per
verificare l’effettivo funzionamento in ambiente Mono. I tempi previsti per questo scenario sono da
definirsi.
Considerazioni finali
Il processo di sviluppo del sistema in esame ha visto il susseguirsi delle tipiche fasi studiate nell’ambito
dell’Ingegneria del Software. L’applicazione concreta dei metodi di studio ed analisi affrontati nel
contesto di molte materie universitarie risulta una risorsa quanto mai preziosa per un laureato in
Ingegneria. Da un punto di vista strettamente personale si prende in grande considerazione non tanto
la realizzazione in sé del progetto, che comunque richiede tempo e buona dose di impegno, quanto
piuttosto ciò che vi è dietro, ossia l’analisi delle problematiche, la ricerca di soluzioni operative, lo studio
del contesto e delle tecnologie con le quali si lavora. Tutto ciò, difatti, porta al consolidarsi di una
metodologia di lavoro, risultando dunque un’esperienza basilare in previsione di un futuro percorso di
miglioramento professionale.
E’ per questo motivo che si ritiene il documento qui presente non solo un’approfondita
documentazione del lavoro fatto, ma anche il frutto di riflessioni, ricerca e studio riguardo
problematiche sempre più diffuse quali l’interfacciamento tra applicativi in ambienti eterogenei, e la
portabilità su piattaforme diverse.
57
Bibliografia
Bibliografia
58
Bibliografia
dotNetHell.it - Come sfruttare le funzioni di una DLL unmanaged esterna creata in C/C++
http://www.dotnethell.it/articles/UnmanagedDLL.aspx
59
Bibliografia
Introduction to Multithreading in C#
http://www.c-
sharpcorner.com/UploadFile/mgold/MultithreadingIntro10062005000439AM/MultithreadingIntro.aspx?Ar
ticleID=920ecafc-e83b-4a9c-a64d-0b39ad885705
CodeProject: Exception Handling Best Practices in .NET. Free source code and programming help
http://www.codeproject.com/KB/architecture/exceptionbestpractices.aspx
CodeProject: A flexible charting library for .NET. Free source code and programming help
http://www.codeproject.com/KB/graphics/zedgraph.aspx
Bug 19363 - List directed write of Infinity and NaN has regressed (gfortran)
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=19363
DllNotFoundException - Mono
http://www.mono-project.com/DllNotFoundException
60
Ringraziamenti
Ringraziamenti
...alla mia famiglia in particolare:
...a mio padre,
per la fiducia riposta in me e perché
mi sta insegnando ad affrontare tutto senza paure nella vita
...a mia madre,
perché mi ha sempre sostenuto in questi ultimi anni,
per la sua pazienza e importanza
...a mia sorella.
...a Luca
Grazie.
Filippo
61