• Non ci sono risultati.

Caratteristiche tecniche della programmazione in jES OF Una descrizione minuziosa del codice di programmazione di jES OF esula

Simulazione della genesi ed evoluzione di un sistema economico con jES Open Foundation

3.8 Caratteristiche tecniche della programmazione in jES OF Una descrizione minuziosa del codice di programmazione di jES OF esula

dagli scopi del presente lavoro55, ma di seguito se ne illustreranno gli aspetti più rilevanti per consentire la comprensione dello schema generale della simulazione sul piano tecnico.

La struttura tipica di un’applicazione Swarm prevede che la classe Start, che nel caso di jES OF è denominata StartESFrame56, contenga il metodo main() che si colloca al livello più alto della scala gerarchica del codice ed è l’elemento da cui prende avvio l’intera simulazione: la classe genera l’Observer (cioè un’instance di

ESFrameObserverSwarm) attraverso il lisp archiver e con riferimento ai dati

contenuti nel file con estensione scm, che possono essere modificati direttamente senza ricompilare il codice:

eSFrameObserverSwarm =(ESFrameObserverSwarm) Globals.env.lispAppArchiver.getWithZone$key(

Globals.env.globalZone, "eSFrameObserverSwarm");

Successivamente sono richiamati i metodi buildObjects che genera il

Model57 e crea gli oggetti dell’Observer che consentono di seguire l’evoluzione della

55 La versione integale del codice è disponibile all’indirizzo: http://web.econ.unito.it/terna/jes/

56 Le classi sono contenute in un file omonimo (es. StartESFrame si trova all’interno di

StartESFrame.java).

57 Al termine dell’attività l’Observer ed il Model saranno eliminati attraverso una procedura detta drop.

simulazione, buildActions che genera gli eventi, sincronizzati attraverso l’orologio interno alla simulazione, necessari al controllo del Model ed activateIn che permette il funzionamento dell’Observer:

eSFrameObserverSwarm.buildObjects (); eSFrameObserverSwarm.buildActions (); eSFrameObserverSwarm.activateIn (null);

L’ impiego degli stessi tre metodi consente la creazione degli oggetti e degli eventi del Model che permettono il funzionamento della simulazione; dal momento che jES OF si presenta come un’applicazione multistrato, la costruzione contemporanea di più Model ha richiesto la definizione della classe

InformationRepository che facilita la gestione delle informazioni relative ai diversi

modelli; la classe permette di trattare i dati di matrici, vettori ed elementi che caratterizzano ciascun Model (lista delle unità, strato di appartenenza, …) e contiene una serie di metodi per prelevare ed impostare dati in varie condizioni (esempi):

getUnitListFromModelSequence(int stratum), getUnitListFromReorderedModelSequence(int stratum), getInterVisibilityMinLevel(int stratum), getESFrameModelSwarm(int stratum), getStratumNumber(), getModelNumbersRandomlyReordered(int stratum), getComputationalStepsInUse(), setComputationalStepsInUse(boolean s)

La creazione di ciascun Model avviene, come quella dell’Observer, con riferimento al file scm attraverso il lisp archiver, ma in questo caso c’è un riferimento allo strato di appartenenza:

eSFrameModelSwarm =(ESFrameModelSwarm) Globals.env.lispAppArchiver.getWithZone$key( getZone(), "eSFrameModelSwarm"+ln); eSFrameModelSwarm.setStratum(ln, stratumNumber); eSFrameModelSwarmArray.atOffset$put(ln, eSFrameModelSwarm);

Per ogni Model l’Observer crea una sonda (probe) che consente all’utente di modificare i singoli parametri; ciascun Model crea i propri oggetti (la mappa dei colori (Colormap) che permette di disegnare gli elementi della simulazione, le matrici relative allo spazio delle unità (RasterArray, DisplayArray,

ActivityDisplayArray) e la finestra di visualizzazione della simulazione

(UnitSpace) con i relativi parametri (dimensioni, fattore di zoom, …). Un oggetto

UnitDisplay (di tipo Object2dDisplay) mostra le unità sul raster e permette

all’utente di aprire le sonde agli agenti con un click destro del mouse.

La classe dell’Observer gestisce anche la creazione dei grafici della produzione, dei ratio e del numero di unità e contiene lo scheduling della simulazione58 per ogni strato (frequenza di aggiornamento dello UnitSpace e del

raster, dei grafici ed in generale della visualizzazione della simulazione).

I Model contengono le unità (aUnit) che sono gestite attraverso liste (unitList) a cui sono inviati i messaggi destinati a tutti gli elementi che le compongono. La classe UnitActivitySpace utilizza una matrice di rotazione (rotationMatrix) ed un vettore direzionale (directionVector) per creare l’area di visibilità che circonda le unità e che si sviluppa a spirale:

{ ...

xSize=xS; ySize=yS;

rotationMatrix = new int [2][2];

rotationMatrix [0][0]=0;

rotationMatrix [0][1]=1;

rotationMatrix [1][0]=-1;

rotationMatrix [1][1]=0;

directionVector = new int [2]; directionVector0 = new int [2]; aSpace = new int [xSize][ySize]; bSpace = new int [xSize][ySize];

}

public void showVisibility(int x, int y, int v) {

int i, xx, yy, step, doingStep; step=1; doingStep=0; directionVector[0] = 0; directionVector[1] = 1; xx=x; yy=y; for (i=1;i<=v;i++) { doingStep++; xx+=directionVector[0]; yy+=directionVector[1]; xx=CoordinateManager.check(getSizeX(), xx); yy=CoordinateManager.check(getSizeY(), yy); if(getValueAtX$Y(xx,yy)==0) putValue$atX$Y(2, xx, yy);

if (doingStep == step || doingStep == 2*step) rotate(); if (doingStep == 2*step) { doingStep=0; step++; }

} }

public void rotate (){

directionVector0[0]=directionVector[0]; directionVector0[1]=directionVector[1]; directionVector[0]=rotationMatrix[0][0]*directionVector0 [0] + rotationMatrix[0][1]*directionVector0[1]; directionVector[1]=rotationMatrix[1][0]*directionVector0 [0] + rotationMatrix[1][1]*directionVector0[1]; }

Il controllo della visibilità reciproca fra le unità è effettuato con un ciclo for che verifica la presenza di intersezioni fra gli spazi di coppie di unità (aSpace e bSpace):

/** the measure of the common positions in the visibility areas of two units */

public int interVisibility(Unit a, Unit b) {

int interVisibility, i, j, xx, yy, step, doingStep; interVisibility=0; for (i=0;i<xSize;i++) for (j=0;j<ySize;j++) { aSpace [i][j]=0; bSpace [i][j]=0; } // Unit a step=1; doingStep=0; directionVector[0] = 0;

directionVector[1] = 1; xx=a.getXPos(); yy=a.getYPos(); for (i=1;i<=a.getVisibility();i++) { doingStep++; xx+=directionVector[0]; yy+=directionVector[1]; xx=CoordinateManager.check(getSizeX(), xx); yy=CoordinateManager.check(getSizeY(), yy); aSpace[xx][yy]=1;

if (doingStep == step || doingStep == 2*step) rotate(); if (doingStep == 2*step) { doingStep=0; step++; } } // end Unit a // Unit b step=1; doingStep=0; directionVector[0] = 0; directionVector[1] = 1; xx=b.getXPos(); yy=b.getYPos(); for (i=1;i<=b.getVisibility();i++) { doingStep++; xx+=directionVector[0]; yy+=directionVector[1];

xx=CoordinateManager.check(getSizeX(), xx);

yy=CoordinateManager.check(getSizeY(), yy);

bSpace[xx][yy]=1;

if (doingStep == step || doingStep == 2*step) rotate(); if (doingStep == 2*step) { doingStep=0; step++; } } // end Unit b for (i=0;i<xSize;i++) for (j=0;j<ySize;j++) { if(aSpace[i][j]==1 && bSpace[i][j]==1)interVisibility++; } return interVisibility; }

I parametri delle unità sono letti dalla classe UnitParameters dai file unitBasicData.txt59.

Nel Model sono creati sia l’AssigningTool che presiede all’assegnazione degli ordini alle unità produttive, sia l’orderGenerator e l’orderDistiller. Il

Generator simula il mercato creando gli ordini in modo casuale; il contenuto di

ciascun ordine è memorizzato in un vettore di tipo integer (orderRecipe) che contiene i passi della ricetta; dal punto di vista tecnico ogni fase produttiva di una ricetta occupa un tick della simulazione, quindi, per simulare la durata variabile delle diverse fasi, la dimensione ed il contenuto del vettore sono ridefiniti all’interno del codice duplicando il valore che identifica la fase per ogni tick aggiuntivo di durata

59 I file sono contenuti in cartelle denominate unitData e identificate dal numero dello strato di appartenenza.

(es. un ordine che contenga le fasi 3, 10, 9, di durata rispettiva 2, 1, 3 tick, sarà rappresentato internamente come 3, 3, 10, 9, 9, 9). Le tipologie di fasi produttive che possono essere variamente combinate a formare le ricette sono definite in un dizionario di codici (dictionary); a prescindere dal fatto che si decida di creare le unità con probabilità pari a 1 o inferiore, i valori che identificano le fasi produttive non potranno essere superiori al numero massimo di tipi di unità generabili.

L’orderDistiller ricava gli ordini da un elenco predefinito; le classi

orderDistiller e Recipe sono infatti in grado di leggere dati contenuti in fogli di

lavoro e relativi alla sequenza di ordini produttivi da lanciare ed alla lista di ricette. La classe AssigningTool permette di determinare a quale unità debba essere trasferito l’ordine dopo che l’unità precedente ne abbia completato la fase di propria competenza ed abbia collocato l’ordine nella propria lista delle produzione giornaliera; se non è richiesto un livello minimo di visibilità reciproca o nel caso in cui l’unità tenti di riassegnare l’ordine a se stessa, il trasferimento della ricetta è diretto; in caso contrario si effettua un controllo per verificare se l’unità ricevente rientri nell’area di visibilità dell’unità che invia l’ordine e si verifica se sia rispettato il vincolo del minimo livello di intervisibilità; se la ricerca non va a buon fine l’ordine è inserito nella lista d’attesa (waiting list) dell’unità in cui si trova:

...

if (interVisibilityMinLevel == 0 || sendingUnit == aUnit) unitNotFound = false;

else if (aUnit.getXPos()<unitActivitySpace.getXSize() && aUnit.getYPos()<unitActivitySpace.getYSize()) if(unitActivitySpace.interVisibility(sendingUnit, aUnit) >= interVisibilityMinLevel)unitNotFound = false; } } myUnitListIndex.next(); } }

if(! unitNotFound) {

firstOrder.setTheUnitTheOrderIsIn(aUnit);

aUnit.setWaitingList(firstOrder);

myUsedUnitList.addLast(aUnit);

return (Order) null;

}

else return firstOrder; }

Il valore di intervisibilità preso a riferimento è quello dell’unità che invia l’ordine, dal momento che è possibile definire parametri diversi per ciascuno strato, ma il primo assegnamento dell’ordine, successivo alla sua creazione da parte dell’orderGenerator, non tiene conto dell’intervisibilità; nel caso di passi a tempo zero60 che utilizzino la stessa fase produttiva o la stessa unità in cui si trova l’ordine si trascura il valore che è stato impostato per il parametro

uniqueAssignmentInEachCycle e l’ordine è riassegnato alla stessa unità ed

eseguito secondo il criterio LIFO (Last In First Out).

Altre classi svolgono specifiche funzioni tecniche: la classe ExcelReader permette la lettura di dati da file Excel, sia in modo sequenziale, quando si prelevino valori da una tabella o si conosca la struttura sottostante dei dati, sia specificando la posizione della cella da leggere: i valori sono letti utilizzando il metodo getValue() e nella lettura sequenziale, se skipEmptyCells è impostato true, le celle vuote sono omesse, mentre nel caso sia false sarà inserito un valore 0 nel punto della sequenza corrispondente ad una cella vuota.

La classe CoordinateManager effettua un controllo di coerenza delle coordinate riferite al raster rispetto ai limiti dello spazio delle unità: le classi

SwarmUtils, MyExit e MyReader hanno ulteriori funzioni tecniche specifiche.

60 Cioè di attività che debbano essere svolte contemporaneamente ad altre, ma che, dovendo essere gestite in modo sequenziale all’interno del codice di programmazione, sono trattate con un espediente; è assegnato loro un tempo di esecuzione nullo e, dopo il loro completamento, il ciclo produttivo è ripetuto nella stessa unità per eseguire un passo, questa volta a tempo non nullo.

La Figura 3.14 riprende, con riferimento al codice di programmazione, lo schema generale della simulazione visto in precedenza.

Figura 3.14 – Rappresentazione della simulazione dal punto di vista del

codice di programmazione. Adattamento al caso di jES OF dello schema di Terna [2002]

Outline

Documenti correlati