NePSi Network Protocol Simulator



Documenti analoghi
Funzioni in C. Violetta Lonati

Inizializzazione, Assegnamento e Distruzione di Classi

Gestione della memoria centrale

Introduzione alla programmazione in C

costruttori e distruttori

A intervalli regolari ogni router manda la sua tabella a tutti i vicini, e riceve quelle dei vicini.

Soluzione dell esercizio del 2 Febbraio 2004

Sistema operativo: Gestione della memoria

Esercizio 1: trading on-line

Università di Torino Facoltà di Scienze MFN Corso di Studi in Informatica. Programmazione I - corso B a.a prof.


(Esercizi Tratti da Temi d esame degli ordinamenti precedenti)

PROCEDURA INVENTARIO DI MAGAZZINO di FINE ESERCIZIO (dalla versione 3.2.0)

Allocazione dinamica della memoria - riepilogo

Corso di Sistemi Operativi Ingegneria Elettronica e Informatica prof. Rocco Aversa. Raccolta prove scritte. Prova scritta

I file di dati. Unità didattica D1 1

Approccio stratificato

Excel. A cura di Luigi Labonia. luigi.lab@libero.it

1) GESTIONE DELLE POSTAZIONI REMOTE

Complemento al corso di Fondamenti di Informatica I corsi di laurea in ingegneria, settore dell informazione Università la Sapienza Consorzio Nettuno

Introduzione al Linguaggio C

Registratori di Cassa

12 - Introduzione alla Programmazione Orientata agli Oggetti (Object Oriented Programming OOP)

Fasi di creazione di un programma

Cosa è un foglio elettronico

Concetto di Funzione e Procedura METODI in Java

Strutturazione logica dei dati: i file

Fondamenti di Informatica T-1, 2009/2010 Modulo 2 Prova d Esame 5 di Giovedì 15 Luglio 2010 tempo a disposizione 2h30'

SISTEMI DI NUMERAZIONE DECIMALE E BINARIO

ARCHITETTURA DI RETE FOLEGNANI ANDREA

Comunicazione tra Computer. Protocolli. Astrazione di Sottosistema di Comunicazione. Modello di un Sottosistema di Comunicazione

Strutture. Strutture e Unioni. Definizione di strutture (2) Definizione di strutture (1)

3 - Variabili. Programmazione e analisi di dati Modulo A: Programmazione in Java. Paolo Milazzo

Progettazione : Design Pattern Creazionali

Tipi primitivi. Ad esempio, il codice seguente dichiara una variabile di tipo intero, le assegna il valore 5 e stampa a schermo il suo contenuto:

Creare una nuova spedizione personalizzata.

Oggetti Lezione 3. aspetti generali e definizione di classi I

Programmazione a Oggetti Modulo B

Scheduling della CPU. Sistemi multiprocessori e real time Metodi di valutazione Esempi: Solaris 2 Windows 2000 Linux

Invio SMS. DM Board ICS Invio SMS

Gli array. Gli array. Gli array. Classi di memorizzazione per array. Inizializzazione esplicita degli array. Array e puntatori

Dall Algoritmo al Programma. Prof. Francesco Accarino IIS Altiero Spinelli Sesto San Giovanni

La gestione di un calcolatore. Sistemi Operativi primo modulo Introduzione. Sistema operativo (2) Sistema operativo (1)

La gestione dell input/output da tastiera La gestione dell input/output da file La gestione delle eccezioni

void funzioneprova() { int x=2; cout<<"dentro la funzione x="<<x<<endl; }

Con il termine Sistema operativo si fa riferimento all insieme dei moduli software di un sistema di elaborazione dati dedicati alla sua gestione.

Prova di Laboratorio di Programmazione

Pronto Esecuzione Attesa Terminazione

Le variabili. Olga Scotti

lo PERSONALIZZARE LA FINESTRA DI WORD 2000

Modulo 4: Ereditarietà, interfacce e clonazione

INTRODUZIONE AGLI ALGORITMI INTRODUZIONE AGLI ALGORITMI INTRODUZIONE AGLI ALGORITMI INTRODUZIONE AGLI ALGORITMI

I TUTORI. I tutori vanno creati la prima volta seguendo esclusivamente le procedure sotto descritte.

Esempio: dest = parolagigante, lettere = PROVA dest (dopo l'invocazione di tipo pari ) = pprrlogvgante

2.7 La cartella Preparazioni e CD Quiz Casa

Esercizio data base "Biblioteca"

G S M C O M M A N D E R Duo S

QG Gestione Assenze. Inserimento per Classe. Per la gestione delle assenze accedere dal Menu Giornaliere->Assenze e Giustificazioni

Laboratorio di Programmazione 1. Docente: dr. Damiano Macedonio Lezione 18 31/03/2014

Esercitazione finale per il corso di Sistemi Operativi (A.A. 2004/2005)

Procedura SMS. Manuale Utente

Gestione Risorse Umane Web

EXCEL PER WINDOWS95. sfruttare le potenzialità di calcolo dei personal computer. Essi si basano su un area di lavoro, detta foglio di lavoro,

Manuale Terminal Manager 2.0

MANUALE D'USO DEL PROGRAMMA IMMOBIPHONE

Arduino: Programmazione

Esercizi su. Funzioni

GESTIONE CONTRATTI. Contratti clienti e contratti fornitori

Il Sistema Operativo. C. Marrocco. Università degli Studi di Cassino

Dispensa di Informatica I.1

Analisi e diagramma di Pareto

Il Sistema Operativo (1)

Variabili e tipi di dato

Inizializzazione degli Host. BOOTP e DHCP

Database. Si ringrazia Marco Bertini per le slides

Il memory manager. Gestione della memoria centrale

FPf per Windows 3.1. Guida all uso

Sistemi Operativi GESTIONE DELLA MEMORIA CENTRALE. D. Talia - UNICAL. Sistemi Operativi 6.1

Corso di Informatica

Reti di Telecomunicazione Lezione 6

Introduzione. Pagina: 1 di 8. Data creazione 22/12/

Programmazione a Oggetti Lezione 10. Ereditarieta

Matematica in laboratorio

MODELLO CLIENT/SERVER. Gianluca Daino Dipartimento di Ingegneria dell Informazione Università degli Studi di Siena

Il concetto di valore medio in generale

Siamo così arrivati all aritmetica modulare, ma anche a individuare alcuni aspetti di come funziona l aritmetica del calcolatore come vedremo.

Servizi Remoti. Servizi Remoti. TeamPortal Servizi Remoti

Manuale Servizio NEWSLETTER

Corso di Informatica

TECNICHE DI SIMULAZIONE

SISTEMI OPERATIVI. Prof. Enrico Terrone A. S: 2008/09

10 - Programmare con gli Array

Per scrivere una procedura che non deve restituire nessun valore e deve solo contenere le informazioni per le modalità delle porte e controlli

GHPPEditor è un software realizzato per produrre in modo rapido e guidato un part program per controlli numerici Heidenhain.

Consiglio regionale della Toscana. Regole per il corretto funzionamento della posta elettronica

Istruzioni (1): L elaborato verrà letto, compilato e fatto girare per verificare la correttezza della sintassi e delle operazioni svolte

SOMMARIO... 3 INTRODUZIONE...

MANUALE PARCELLA FACILE PLUS INDICE

Visual Basic.NET La Gestione degli Errori di Federico BARBATI

Siti web centrati sui dati Architettura MVC-2: i JavaBeans

Transcript:

NePSi Network Protocol Simulator Framework per la simulazione di sistemi Discrete Event (DE) Il sistema INeSiS (Integrated Network Signal Processing Simulator) è una piattaforma di simulazione che comprende due sottosistemi: MuDiSP(Multirate Digital Signal Processor): un simulatore che considera flussi di bit sincroni e collegamenti fra blocchi di tipo deterministico e stabilito a priori. Network Protocol Simulator (NePSi): un simulatore a eventi discreti e asincroni in cui il flusso di dati fra i vari blocchi è stabilito in maniera dinamica nel corso della simulazione (in run-time).

NePSi cos è, cosa non è Framework - offre un insieme di classi utili alla programmazione di sistemi DE Le simulazioni risultano veloci Portabile (ANSI C++) funzionamento uniforme su Linux, Windows, MacOS Non ha un front-end grafico per la costruzione di simulazioni Non ha un front-end grafico per l analisi dei risultati Non ha un front-end grafico per il debugging La documentazione è scarsa

Sistema Discrete Event (DE) Un sistema DE è un sistema che evolve in base a eventi, che vengono scambiati tra i blocchi della simulazione stessa. Gli eventi possono essere generati in modo deterministico o random, e possono contenere un informazione. Ad ogni evento è associato un tempo, che è il tempo di ricezione del segnale da parte del blocco a cui è destinato. Il blocco di destinazione dell evento deve essere deciso al run-time (non sono necessarie connessioni a priori tra blocchi). Gli eventi in generale sono scambi di messaggi fra blocchi del simulatore; un particolare tipo di evento può essere ad esempio il cambiamento di stato in una macchina a stati finiti. Ogni evento ha associato un a certa quantità di informazione, che al minimo è costituita dal tipo dell evento e dal tempo di esecuzione (ricezione dell evento). In generale è possibile associare ad un evento un qualsiasi tipo di informazione, anche complessa e strutturata. Se l evento è l arrivo di un pacchetto, l informazione associata all evento sarà ad esempio il tipo di pacchetto. Il tipo di evento può essere distinto (dal ricevitore) in base ad informazioni in esso contenute o grazie all ereditarietà C++. Il secondo metodo è preferibile in quanto risulta più veloce e meno soggetto ad errori di programmazione. Il tempo associato all evento è sempre il tempo associato alla sua ricezione, che costituisce un elemento importante per la connotazione dell evento in quanto è l istante in cui il ricevitore ha a disposizione l informazione legata all evento e può elaborarla. Si noti che, come effetto di un evento, spesso si ha la generazione di altri eventi.

Blocchi NePSi Ogni blocco NePSi è costituito da una (o più) classi C++. La programmazione di ogni blocco è totalmente lasciata all implementatore; l uso di NePSi avviene tramite ereditarietà ed offre la possibilità di gestire: tempo parametri al run-time generatori di numeri random analizzatori di dati Non è possibile l incapsulazione di blocchi, anche se è possibile ottenere qualcosa di simile. In generale un blocco NePSi corrisponde ad una classe C++, più eventuali sottoclassi di utilità. Ogni blocco ha come classi padri delle classi generiche, da cui eredita alcune funzioni fondamentali, come il concetto di tempo, la gestione dei parametri al run-time, i generatori random, etc. Ogni blocco NePSi ha caratteristiche a sé stanti, non è cioè possibile generare un blocco terminale mobile da tanti sottoblocchi generatore random, ricezione ACK, e così via. Ogni blocco avrà caratteristiche peculiari, e anche se in realtà produrrà dati che verranno usati da un macro oggetto terminale mobile rimane un entità autonoma. Ciò offre maggiore flessibilità al simulatore.

Simulazioni DE Ogni simulazione DE ha le seguenti caratteristiche: Ogni entità (terminale, base station, canale, etc.) può essere scomposto in blocchi logici autonomi (blocchi) Ogni blocco comunica i propri cambiamenti di stato ad altri blocchi (eventi) Ogni cambiamento di stato avviene ad un determinato tempo e genera un evento che verrà ricevuto ad un tempo successivo (tempo) La dimensione (in termini di numero di funzioni svolte) di un blocco deve essere scelta accuratamente. Scegliendo un blocco troppo piccolo (cioè che svolge poche funzioni) si rischia di avere un sistema con un numero di blocchi troppo elevato, che generano a loro volta troppi eventi. Occorre evitare questa situazione in quanto la velocità di simulazione è fortemente dipendente dal numero di eventi che vanno gestiti (la coda degli eventi futuri va tenuta in ordine temporale e l operazione di riordino è dipendente dalla dimensione della lista ). Scegliendo un blocco troppo grande si corre il rischio di avere blocchi troppo complessi, difficili da modificare e da verificare.

Simulazioni generiche Ogni simulazione dovrebbe poter: Leggere parametri dall esterno (flessibilità) Scrivere i risultati (generazione di statistiche) Poter essere facilmente oggetto di debugging

NePSi prerequisiti ANSI C++ Classi Ereditarietà Member Functions Classi e funzioni virtuali Standard Template Libraries (STL) Tipi e Uso Conoscere le Standard Template Libraries permette di ottimizzare la scrittura del codice, evitando di dover perdere tempo a scrivere funzioni che già esistono. Un esempio evidente è la gestione dei vettori. Con la STL <vector> si possono facilmente gestire vettori di dimensioni dinamiche, senza doversi preoccupare di allocare e deallocare memoria.

NePSi - esempio Un sistema che misuri il traffico prodotto da dei generatori di traffico (test dei generatori) GenGeom GenMMP Evento: generazione di messaggio Informazione associata: lunghezza di messaggio Terminal GenTcpIp Evento: messaggio spedito Il blocco GenGeom genera in maniera autonoma gli eventi che rappresentano l arrivo di un messaggio nel sistema. Per fare ciò manda degli eventi a sé stesso; tali eventi hanno un intertempo pari all intertempo tra due arrivi di messaggio. Ad ogni ricezione dell evento di generazione, vengono generati altri due eventi: uno di generazione del messaggio successivo e uno per il blocco Terminal, di arrivo di un messaggio. Il blocco Terminal, a sua volta, può inviare al blocco GenGeom un evento di avvenuta spedizione del messaggio. La scansione del tempo in NePSi avviene in sub-unità; il tempo viene rappresentato con un numero di tipo unsigned long long int (indicato con time_int). La minima unità di tempo rappresentabile è denominata tick; la quantità di tempo (reale) rappresentata da un tick viene decisa dal programmatore ed è in genere pari a 1 µs. In generale un tick sarà pari a 10Ex secondi, con x quantità intera scelta a piacere. La scelta di un unsigned long long int è motivata con la necessità di avere un grado di accuratezza nella determinazione del tempo anche dopo un numero elevato di eventi. Si noti che la durata della simulazione è limitata a 1.84467440737095e19 tick, normalmente pari a 1.84467440737095e13 secondi.

Classi NePSi - 1 DEDevice è una classe virtuale da cui si derivano tutti i blocchi; le classi generiche sono tutte di tipo virtual per evitare che vengano utilizzate, essendo esse implementate solo in qualità di classi padre (meccanismi di ereditarietà). Le classi DevTerminal e DevTrafficGenerator sono state dunque costruite dal programmatore, per questo particolare esempio, per ereditarietà da DEDevice. UplinkPkt è un pacchetto molto semplice (ad esempio una struttura C) che serve al sistema per scambiare le informazioni. E in sostanza l informazione associata al messaggio di uplink. DEEvent è la classe virtuale da cui derivano gli eventi. Un evento è in realtà una classe, e ciò per dare possibilità allo scheduler di sistema (l oggetto che gestisce gli eventi e l asse temporale del simulatore) di gestire più facilmente gli eventi. Se ad esempio si assegna ad ogni classe evento una variabile membro relativa al tempo, lo scheduler non si dovrà preoccupare di cosa quella classe esegua, ma potrà semplicemente leggere la variabile tempo di tutte le classi evento in coda e provvedere ad eseguirle in base a quale ha il tempo di arrivo minore. DECheckEvent è una classe che serve al sistema per auto-controllarsi durante la simulazione. Le classi indicate con colore chiaro derivate da DEEvent sono gli eventi relativi all esempio in esame, e costituiscono gli eventi che i vari blocchi mandano a sé stessi o ad altri blocchi. Ad esempio, Ev_Geom_MsgArrival è l evento che il GenGeom manda a sé stesso per scandire l intertempo di generazione eventi al Terminal, Ev_MMP_GoStatus_A è l evento utile al generatore MMP per cambiare stato, e così via. Altre classi utili, mostrate in figura, sono quelle relative ai generatori casuali, sia di numeri generici (blocco sopra) che di tempo (la coppia sottostante di classi relative a DETime).

Classi NePSi - 2 Dalla classe DESystem si ottiene per derivazione SysTest, che definisce quali classi operano nel simulatore, e che contiene il main. DETime si occupa del tempo, dei tick, e della precisione. DEScheduler si occupa di sapere qual è il prossimo evento, ed è l istanza dell oggetto Scheduler descritto in precedenza. Questa classe non deve di norma essere modificata dai programmatori utenti di NePSi. In figura sono poi da notare tre blocchi principali di classi, quelle relative ai Parametri, che si occupano di lettura e scrittura dei parametri di simulazione, quelle dell Analyser e quelle del Probe, che generano e gestiscono le statistiche da osservare. Fra questi due ultimi gruppi di classi, la principale differenza sta nel fatto che Analyser genera solo un numero limitato di parametri statistici e può decidere se interrompere o meno la simulazione analizzando tali dati, mentre Probe può generare più tipi di dati statistici ma non può agire sulla simulazione. NOTA: per problemi di copyright del codice e per la non applicabilità ad una vasta classe di simulazioni di TLC, gli Analyser saranno rimossi a breve dal sistema. Il loro uso è pertanto sconsigliato.

Blocchi funzioni speciali Durante l esecuzione di una simulazione, ogni blocco (classe) viene richiamato più volte secondo la seguente logica: Setup (funzione statica) Dichiara i parametri usati dalla classe Initialize Prepara la classe alla simulazione (lettura parametri, inizializzazione variabili, etc.) Handler degli eventi (uno per ogni tipo di evento) Risposta ad un evento Sono le funzioni membro necessarie ad ogni blocco derivante dal DEDevice. La Setup deve essere static, in quanto non appartiene a nessuna istanza della classe, ed ha effetti pertanto su tutte le istanze della classe stessa. Viene usata per dichiarare i parametri della classe, e viene chiamata una volta sola per simulazione, prima che vengano letti i parametri; deve essere eseguita per ogni classe della simulazione. La Setup dentro la casse derivata dal DESystem deve chiamare tutte le Setup delle classi (blocchi) della simulazione dall utente (le Setup delle classi interne di NePSi sono chiamate in modo automatico). Initialize viene chiamata più volte, ma sempre a tempo zero; ad esempio, se si vogliono fare simulazioni multi-run, in cui cioè si ripetono più prove di simulazione in sequenza, ogni volta che automaticamente si riavvia una nuova simulazione, tornando così al tempo zero, si effettua una nuova chiamata a Initialize, che rappresenta il costruttore degli oggetti. Nell Initialize si devono leggere i parametri e impostare lo stato dell oggetto per la simulazione. Si possono (devono) anche lanciare gli eventi iniziali. Gli Handlers degli eventi sono poi le funzioni tramite cui una classe reagisce all arrivo di un evento; Ogni classe avrà un Handler per ogni tipo di evento che deve gestire.

Blocchi funzioni speciali - 2 Setup (funzione statica) Viene eseguita UNA sola volta per esecuzione del programma e non deve contenere riferimenti non statici. Initialize Viene eseguita più volte durante l esecuzione, sempre all inizio di un RUN (durante un esecuzione si possono fare più RUN). Handler degli eventi (uno per ogni tipo di evento) E buona norma avere un handler per tipo di evento, ma nulla vieta di avere un handler che gestisce più tipi differenti. Nota: la funzione Handler per l evento viene dichiarata all interno della definizione dell evento, pertanto è possibile che più tipi di evento facciano riferimento allo stesso Handler.

Note e considerazioni: Per costruire una nuova simulazione (o un blocco) la cosa più semplice è copiare una simulazione già fatta e modificala. In questo modo si ha che: Si ha uno stile di programmazione uniforme. Si evita di dimenticarsi alcune funzioni standard (in molte classi derivate si devono ridefinire alcune funzioni) Dagli esempi si impara

Events: note Un evento non ha il tempo di esecuzione memorizzato internamente; quest ultimo viene specificato tramite la chiamata a newevent. Il tempo specificato è relativo al tempo attuale, ossia si indica tra quanto accadrà l evento, non quando accadrà l evento (notazione relativa, non assoluta). La chiamata a newevent, inoltre, può non contenere il tempo di ritardo; in questo caso l evento è sincrono rispetto all evento in corso. Gli eventi sincroni sono da preferirsi agli eventi a delay 0 (maggiore velocità). Quando si genera un evento (tipicamente all interno dell Handler di un altro evento), si comunicherà allo scheduler fra quanto tempo accadrà l evento che si sta generando, tramite la chiamata alla funzione newevent. Il tempo è indicato in DETime. Esistono diversi tipi di costruttori per la classe DETime, cosicché è possibile esprimere un tempo in tick, secondi o in Step/BigStep/SuperStep. Quest ultimo tipo di rappresentazione è utile quando esiste, all interno del sistema che si intende simulare, una sistema di divisione del tempo in sottounità di dimensione fissa (es. Slot/Frame/SuperFrame). In NePSi uno Step è formato da un numero intero di tick, un BigStep da un numero intero di Step e un SuperStep da un numero intero di BigStep, tali che: 1 SS = n BS = nm S, n, m interi. n e m sono definiti tramite il costruttore della classe derivata da DESystem e NON sono modificabili durante l esecuzione della simulazione. Pertanto NON è possibile gestire frame di lunghezza variabile tramite questo sistema. ATTENZIONE: non è possibile definire un ordine di esecuzione per eventi con lo stesso tempo. Gli eventi ad un dato tempo verranno eseguiti in ordine casuale.

DETime - classe tempo La classe DETime fornisce una notazione di tempo flessibile e generica. La base è un unsigned long long int (64bit) che misura unità di tempo, generici o sottomultipli del secondo. Le funzioni membro permettono di: Eseguire conversioni da/in secondi normali. Dividere il tempo secondo slot, frame e superframe. Gestire il tempo in base a slot, frame e superframe (somme di slot, frame e superframe). Gestire l overflow (tempo fuori scala). Il DETime può determinare quanti secondi compongano uno slot, quanti slot compongano un frame, e così via. E possibile chiedere a questo blocco in che slot/frame/superframe la simulazione si trova, oppure si può chiedere a che tempo corrisponde lo slot (o frame o superframe) successivo a quello attuale. Si noti che il tempo rappresentato da un tick è puramente convenzionale. E possibile cioè non far riferimento ai secondi ma alle ore o ai giorni. L unica cosa che dovrà essere considerata, se si fa uso di differenti basi temporali, è la corretta lettura dei risultati.

DETime - classe tempo - es. DETime( time_int step, time_int bigstep, time_int superstep ) DETime( string A ) ( const DETime& A ) ( double A ) time_int Tick() Step() BigStep() SuperStep() time_int RelativeTick() RelativeStep() RelativeBigStep() operators + - * / % += < <= ==!= => > double() DETime AddStep AddBigStep AddSuperStep DETime NextStep NextBigStep NextSuperStep NextIndexedStep NextIndexedBigStep DETime BeginOfTheNextUsefulStep DETime ToNextStep ToNextIndexedStep ToBeginOfTheNextUsefulStep bool HasOffsetZero() IsInOverFlow() Nelle prime due righe del listato in figura si possono notare vari tipi di costruttori per la classe DETime, in cui, a seconda dei parametri passati, la classe DETime viene istanziata in modo diverso. Ci sono poi gli operatori funzione, che ritornano valori di tick, Step o altro. Gli operatori definiti sono tutti definiti tramite overloading. Le funzioni in carattere rosso non vengono usate in quanto considerano valori assoluti di tempo. Ciò che interessa comunicare con lo scheduler è infatti fra quanto accadrà l evento successivo, non quando in termini assoluti (possono però essere utili per il debugging o la raccolta di dati). Le funzioni finali, che ritornano valori booleani, sono usate per sapere se si è all inizio di uno slot oppure se si è in situazione di overflow, ossia se il tempo che è stato generato è troppo elevato; in tal caso, lo scheduler gestirà ugualmente questo evento in overflow, ma lo eseguirà a fine simulazione (o mai), marcandolo come evento in overflow.

Parameters Un parameter appartiene ad una classe e va definito nella Setup(). Il parametro stesso (normalmente) viene letto nella Initialize(). I parametri possono essere DETime, double, int, string, enum o bitmask. void StatCollector::setup(ParamManager *Param, StatisticManager *) { Param->addClass ("StatCollector", "Stat Collector"); Param->addParameter ("StatCollector", new IntParameter("num_prio", "classe di priorita'", "4", "(0,10)") ); } void StatCollector::Initialize() { num_prio = get<intparameter, int>(param, "num_prio", "StatCollector", "");... I parametri possono essere usati soltanto da classi derivanti da DEDevice, e ciò perché un evento non ha bisogno di parametri. Stat collector in carattere verde è il nome della classe, mentre quello in carattere blu è il commento che apparirà sul listato dei parametri. num_prio in viola rappresenta il nome del parametro, a cui si associa ancora il commento in carattere blu; dentro Initialize si indica di che tipo è il parametro (Int, Double, etc.). Dentro Initialize, num_prio in nero è una variabile, che viene inizializzata al valore letto dal file dei parametri tramite il comando get. IntParameter rappresenta il tipo di parametro, mentre int è la conversione che viene effettuata su quel tipo. C è poi il nome del puntatore (Param), il nome del parametro, il nome della classe (StatCollector) e infine un campo che nell esempio è vuoto (non viene utilizzato). Occorre prestare attenzione alla chiamata alla funzione get. NON è possibile eseguire conversioni al volo, ossia le chiamate accettabili sono solo: get<intparameter, int>, get<doubleparameter, double>, get<stringparameter, string>, get<enumparameter, int>, get<bitmaskparameter, int>. In caso di necessità (comodità) è utile definire delle macro per tali comandi tramite opportune #define.

GenGeom.h #ifndef GenGeom_h #define GenGeom_h #include "rndgen.h" #include "derandom_time.h" #define DBG_EV_WIDTH 21 /////////////////////////////////////////////////////////////////////////////// // class GenGeom class GenGeom : public DevTrafficGenerator { protected: ExponentialDETimeRndGen msginterarrivaltime; Geometric1RndGen msglength; public: GenGeom (DESystem *_System, int _ID) : DevTrafficGenerator(_System, _ID) {}; ~GenGeom() {}; static void setup(parammanager *Param, StatisticManager *Results); virtual void Initialize(void); virtual void MessageSent(void) {}; void HEv_Geom_MsgArrival(void); }; #endif Il file mostrato in figura è un header di una classe Nepsi, deriva da DevTrafficGenerator che a sua volta deriva dalla classe DEDevice. I comandi #ifndef, #define ed #endif servono per evitare la doppia inclusione. Vengono incluse le definizioni delle classi random. Si definisce poi la classe GenGeom, con due variabili interne, una che definisce una esponenziale, e l altra una distribuzione geometrica. Nella parte public si trovano le funzioni: il costruttore, che deve contenere un riferimento a DESystem (per costruire un oggetto GenGeom devo in realtà costruire un oggetto DEDevice che richiede DESystem). C è poi il distruttore, la Setup, che deve essere dichiarata come static void, come richiesto ad ogni classe che deriva da DEDevice. Nelle classi derivate da DEEvent non servono invece né parametri né Results né Setup. Ci sono poi le funzioni virtual void Inizialize e MessageSent, che gestisce l evento ricezione Message_Sent da Terminal. L handler è HEv_Geom_MsgArrival (void), che deve restituire un void. Occorre notare che un handler di evento deve restituire un void, ma che non è detto che venga invocato senza parametri (quindi con un void). La maggior parte degli handler di eventi sono della forma void HEv_qualcosa(parametri).

GenGeom - Events /////////////////////////////////////////////////////////////////////////////// // Events class Ev_Geom_MsgArrival : public DEEvent { protected: GenGeom &thegenerator; public: Ev_Geom_MsgArrival(GenGeom &_thegenerator) : DEEvent(), thegenerator(_thegenerator) {}; virtual const char *Type() const { return "Ev_Geom_MsgArrival"; }; void Handler() { thegenerator.hev_geom_msgarrival(); }; virtual void print(ostream &os) const { os << Time << ": " << setw(dbg_ev_width) << Type() << " - " << thegenerator; }; }; Questa è la seconda parte della schermata precedente (nel listato sta prima di #endif. L evento che appartiene alla GenGeom viene detto Ev_Geom_etc.. E l evento mandato a questa classe, e viene definito nella relativa intestazione.h. E derivato da DEEvent, contiene un puntatore a GenGeom, cioè alla classe destinataria dell evento (sé stessa). Nel costruttore si indica il destinatario. C è poi una funzione di servizio che stampa il nome della classe, e si chiama *Type. C è poi la funzione print che serve al Debugger per stampare l evento. Dopo << Type ( ) si può mettere ciò che si vuole venga scritto dal debugger. void Handler ( ) è la funzione chiamata dallo scheduler di Nepsi quando si accorge che quell evento è il prossimo da eseguire, e che richiama la funzione HEV_Geom_MsgArrival ( ), che è definita nel.h come prototipo (vedi pag. precedente). Ogni evento deve avere una funzione chiamata Handler.

GenGeom.cpp #include "deevent.h" #include "DevTerminal.h" #include "GenGeom.h" void GenGeom::setup(ParamManager *Param, StatisticManager *) { Param->addClass ("GenGeom", "Message generator - interarrival expon., msg len geom. "); Param->addParameter ("GenGeom", new DETimeParameter("MsgInterarrTime", "Mean message interarrival time", "1.0", "(0,inf)" )); Param->addParameter ("GenGeom", new DoubleParameter("MsgLength", "Mean message length", "5.0", "(0,inf)" )); } void GenGeom::Initialize() { DETime MsgInterarrTime = get<detimeparameter,detime>(param, "MsgInterarrTime", "GenGeom", ""); double MsgLength = get<doubleparameter,double>(param, "MsgLength", "GenGeom", ""); msginterarrivaltime = ExponentialDETimeRndGen(MsgInterarrTime); msglength = Geometric1RndGen(MsgLength); newevent(new Ev_Geom_MsgArrival(*this), msginterarrivaltime.get()); } void GenGeom::HEv_Geom_MsgArrival(void) { owner->hev_term_msgarrival(msglength.get()); newevent(new Ev_Geom_MsgArrival(*this), msginterarrivaltime.get()); } File GenGeom.cpp. Nel Setup dichiaro due parametri, che nell Initialize leggo e metto in due variabili. Poi mi costruisco due variabili random, e metto un newevent al tempo del prossimo messaggio in arrivo, in cui comunico quale oggetto riceverà l evento (lui stesso in questo caso) e fra quanto tempo (il tempo di interarrivo dei messaggi). Poi c è l handler dell evento: prima uso owner (=proprietario del generatore), poi mi rischedulo e c è dunque un altra chiamata a newevent. Owner è il puntatore al generatore dei messaggi, a cui notifico l arrivo del messaggio. La stessa chiamata avrebbe potuto essere fatta tramite un evento immediato. Un evento è utile per fare cose nel futuro. Se ho eventi sincroni si può evitare di usare eventi, chiamando direttamente la funzione del destinatario. La differenza tra generare un evento e la chiamata diretta è: la chiamata diretta non viene stampata dal debugger, l evento si. la chiamata diretta viene eseguita immediatamente, l evento viene eseguito dopo la conclusione dell evento in corso (alla fine della funzione). newevent (new Ev_msgArrival (owner, msglength.get()) ) oppure: newevent (new Ev_msgArrival (owner, msglength.get()), DETime() ).

Analyzers Vs. Probes Analyzers Globali Q.tà osservata: double Media, varianza, istogramma Detect intervallo di confidenza: automatico (akaroa) Probes Locali Q.tà osservata: ridefinibile Qualsiasi (ridefinibile) Detect intervallo di confidenza: manuale Un Analyzer viene definito nella setup ed è comune a tutte le istanze della classe. Ogni istanza di quella classe usa lo stesso Analyzer. Automaticamente sa quando stoppare la simulazione. L intervallo di confidenza è dunque gestito in automatico. Tale feature può portare a risultati errati se la simulazione riguarda un sistema con eventi di tipo catastrofico, cioè eventi relativamente rari che però modificano radicalmente il comportamento del sistema. Le probes sono istanze di classi, quinid sono locali; se voglio utilizzare una sola probe per N terminali, occorre definire come statica, oppure definire un oggetto esterno che raccolga le statistiche dai vari oggetti. Nota: le probes possono essere usate da qualsiasi oggetto, non solo da quelli derivati da DEDevice.

Analyzers Ogni analyzer è globale e va dichiarato nella Setup(). Si deve eseguire il binding tra l analizer e una variabile int nella Inititalize(). void Dev_MM1Server::setup(ParamManager *Param, StatisticManager *Results) { Results->AddAnalyser("delay", "Request delay");... } void Dev_MM1Server::Initialize() { delay_analyser = Results->Enable("delay");... }... Results->ProcessObservation(delay_analyser, 0.5);... Un Analyzer viene definito in modo simile ad un parametro. Nell Initialize si abilita l analizzatore delay e lo si assicia all identificativo delay_analyser. Al momento dell utilizzo si deve usare l identificativo precedentemente ottenuto per poter indirizzare l analizzatore.

Probes Una probe è un normale oggetto. Normalmente si creano le probes nell Initialize(). Header file, tra le variabili membro di una classe: ProbeMean* theprobedelay NOTA: va inizializzata a 0 nel costruttore. StatCollector::StatCollector(DESystem *_System) : DEDevice(_System), theprobedelay(0) {} void StatCollector::Initialize() { if(theprobedelay) delete theprobedelay; theprobedelay = new ProbeMean("num_prio");... }... theprobedelay->observe(0.5);... Le Probe sono oggetti, si creano nell Initialize, o, se sono state definite come statiche, nel setup. Vanno inizializzate a 0 nel costruttore per sicurezza. L uso delle Probes è effettuato tramite una chiamata ad una funzione membro. Tale funzione può essere oggetto di overloading, pertanto tramite una Probe è possibile misurare qualsiasi quantità.

Probes - tipi Tipi di Probe già presenti in NePSi; si può derivare per ereditarietà dalla classe Probe. ProbeMean ProbeMinMax ProbeHistogram ProbeBand ProbeSlice Time, E[x], E[x 2 ], E[x 2 ] - E[x] 2 Time, min(x), max(x) Istogramma(x) Time, sum(x)/time Come ProbeMean, ma le statistiche sono azzerate ad ogni output

Note di utilizzo: NePSi è un framework in continua evoluzione, quindi non è perfetto e non risolve tutti i problemi di creazione di una simulazione. Si può usare in due modi: Modo cattivo: Si forza la struttura se NePSi impone limitazioni. Non si guardano gli header NePSi. Si fanno troppe domande ai gestori di NePSi ;-{)) Modo buono: Se si trovano limiti imposti, ci si chiede perché. Eventualmente si chiedono chiarimenti e/o si suggeriscono modifiche. Si esplora NePSi (le parti pubbliche ). Si scrive la documentazione ;-{))