44 Commits

Author SHA1 Message Date
9c5f7d01e8 Refactor error calculation in Plot and ConfidenceIndices; update index delimiter in SimulationBuilder 2025-06-16 17:43:22 +02:00
846b33ad43 Update README.md with additional comparison data and images for JMT simulation results 2025-04-18 23:00:27 +02:00
Giacomo Bertolazzi
40751f40c2 Update README.md
fixed broken link
2025-04-18 21:20:33 +02:00
039cc1104c Update README.md to clarify example networks and add detailed experimental results; include new plot image for better visualization 2025-04-18 12:10:16 +02:00
Giacomo Bertolazzi
158976bb71 Merge pull request #4 from Berack96/arguments
Arguments
2025-04-16 10:55:30 +02:00
f8f7f72eea Update README.md with additional simulation result images for better visualization and understanding 2025-04-16 10:47:09 +02:00
37e0b57c36 Enhance ConsoleTable initialization; allow custom max column length and adjust border calculation for better formatting in Result statistics 2025-04-16 10:46:40 +02:00
44cf714840 Update README.md to enhance clarity of simulation results; format output for better readability 2025-04-09 08:04:42 +02:00
719c6b353a Add argparse4j dependency; remove Parameters class and update simulation argument handling 2025-04-02 10:44:32 +02:00
4b9c40f894 Remove Rvgs.java class for random variate generation; obsolete code cleanup 2025-04-01 09:31:44 +02:00
78dcac0a56 Enhance simulation options in InteractiveConsole; add confidence index handling and new seed generation; update Plot behavior to dispose on close 2025-03-26 14:54:55 +01:00
57c9dd9733 Update README.md to include detailed analysis of service distribution changes and their impact on queue metrics 2025-03-23 16:20:58 +01:00
3844a46379 Add new images and update README with network modifications; improve ConsoleTable formatting 2025-03-22 09:39:23 +01:00
e48bddf94d Update README.md references; moved example*.jsimg in the correct folder 2025-03-16 20:13:21 +01:00
8f30f07c36 Update README.md formatting and examples; remove outdated image and add new example image 2025-03-16 19:56:36 +01:00
e4df5dab73 Add launch configuration for NetExamples; enhance NetExamples class with main method and improved network creation; refactor Result class for better stats handling 2025-03-16 19:42:33 +01:00
Giacomo Bertolazzi
68687fa595 Merge pull request #2 from Berack96/net-creation
Net creation interactive
2025-03-16 18:02:38 +01:00
Giacomo Bertolazzi
9b51562469 Merge branch 'main' into net-creation 2025-03-16 18:02:04 +01:00
e11bb3f419 Update launch configurations to use absolute paths for example network and CSV files; remove unused Maven build plugins 2025-03-16 17:54:47 +01:00
2ed8149110 Rename runBuilder method to run in InteractiveConsole; update interactive console commands and tests for improved clarity and functionality 2025-03-16 17:38:16 +01:00
f4d3262cb7 Rename run method to runBuilder in InteractiveConsole and update references; refactor file handling in Plot and SimulationBuilder for improved clarity and consistency 2025-03-16 16:59:12 +01:00
60ef40c0ab Add example network files and update interactive console commands for improved usability 2025-03-16 16:32:11 +01:00
6cb87be89b Add example networks and update tests to utilize new NetExamples class 2025-03-16 16:00:51 +01:00
cd3ff3b699 Renamed NetBuilder to InteractiveConsole for interactive net building and update Main to use it 2025-03-16 12:44:27 +01:00
1687154c62 Add new images and update README with examples and additional information 2025-03-13 12:40:33 +01:00
76adc91a43 Refactor input handling in NetBuilderInteractive to improve error management and streamline user prompts 2025-02-19 12:14:53 +01:00
744df75d41 Rename sourceLimited method to terminal and update related logic for improved clarity; adjust spawn behavior to use -1 for infinite arrivals 2025-02-19 11:49:31 +01:00
8fe2f9d781 Remove default values from input prompts in NetBuilderInteractive for improved user input handling 2025-02-19 11:49:06 +01:00
611e14e6fa Refactor SimulationBuilder and SimulationMultiple to accept PrintStream for output; update tests to use null output stream 2025-02-19 11:22:52 +01:00
121d4cd44a Refactor Event comparison logic for clarity and add getRandomChild method to ServerNodeState for improved child selection; update simulation tests for null checks and event processing 2025-02-19 11:06:07 +01:00
c2b9d350aa Add copyOf method to Net class for creating deep copies of nets 2025-02-14 16:01:40 +01:00
1cab669fd9 Remove redundant exception for connecting to source nodes in addConnection method 2025-02-14 15:47:19 +01:00
64e135810a Replace ArrayList with List.copyOf in getFutureEventList method for improved immutability 2025-02-14 15:46:29 +01:00
fbd1fab9fb Enhance NetBuilderInteractive to support interactive node creation and connection management, updating input/output handling and adding unit tests for functionality 2025-02-12 21:26:02 +01:00
e97703e23a Refactor toString method in Net class for improved output formatting and update unit tests accordingly 2025-02-12 21:25:20 +01:00
3a769d6ae3 Implement toString method for ServerNode class and add unit tests for its output 2025-02-12 21:23:43 +01:00
6b3b5fea2f Add toString method for Distribution class and unit tests for various distributions 2025-02-12 21:22:26 +01:00
f0d9c59350 Implement toString method for Net class and add unit tests for its functionality 2025-02-12 17:57:30 +01:00
65e8f378f5 Refactor connection weight calculation to use ArrayList for fixing bug 2025-02-12 17:57:21 +01:00
cb8b2d2913 Merge branch 'main' of https://github.com/Berack96/upo-valpre 2025-02-12 15:21:15 +01:00
2331e42e55 Fixes 2025-02-12 15:20:42 +01:00
d7bf804313 Merge branch 'main' of https://github.com/Berack96/upo-valpre 2025-02-12 15:19:34 +01:00
762a1d1d0a Fix
- loop condition in SimulationMultiple to ensure correct run iterations
2025-02-12 15:18:50 +01:00
f5d70140d7 Fix
- loop condition in SimulationMultiple to ensure correct run iterations
2025-02-12 15:16:26 +01:00
47 changed files with 3295 additions and 4930 deletions

32
.vscode/launch.json vendored
View File

@@ -4,61 +4,61 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "NetExamples",
"request": "launch",
"mainClass": "net.berack.upo.valpre.NetExamples",
"projectName": "valpre"
},
{
"type": "java",
"name": "Run1k Simple",
"request": "launch",
"mainClass": "net.berack.upo.valpre.Main",
"args": "simulation -net example1.net -runs 1000 -p"
"args": "simulation -net src/test/resources/example1.net -runs 1000 -p"
},
{
"type": "java",
"name": "Run1k Complex",
"request": "launch",
"mainClass": "net.berack.upo.valpre.Main",
"args": "simulation -net example2.net -runs 1000 -p"
},
{
"type": "java",
"name": "Run1k Complex EXP",
"request": "launch",
"mainClass": "net.berack.upo.valpre.Main",
"args": "simulation -net example3.net -runs 1000 -p"
"args": "simulation -net src/test/resources/example2.net -runs 1000 -p"
},
{
"type": "java",
"name": "Run Incremental",
"request": "launch",
"mainClass": "net.berack.upo.valpre.Main",
"args": "simulation -net example3.net -runs 1000 -i \"[Service1:throughput=0.98:0.01],[Service2:utilization=0.98:0.01],[Service2:unavailable=0.98:0.01]\""
"args": "simulation -net src/test/resources/example2.net -runs 1000 -indices \"[Service1:throughput=0.98:0.01],[Service2:utilization=0.98:0.01],[Service2:unavailable=0.98:0.01]\""
},
{
"type": "java",
"name": "Run10",
"request": "launch",
"mainClass": "net.berack.upo.valpre.Main",
"args": "simulation -net example1.net -runs 10 -seed 2007539552"
"args": "simulation -net src/test/resources/example1.net -runs 10 -seed 2007539552"
},
{
"type": "java",
"name": "Plot Simple",
"request": "launch",
"mainClass": "net.berack.upo.valpre.Main",
"args": "plot -csv example1.csv"
"args": "plot -csv src/test/resources/example1.csv"
},
{
"type": "java",
"name": "Plot Complex",
"request": "launch",
"mainClass": "net.berack.upo.valpre.Main",
"args": "plot -csv example2.csv"
"args": "plot -csv src/test/resources/example2.csv"
},
{
"type": "java",
"name": "Build Net",
"name": "Interactive Net Builder",
"request": "launch",
"mainClass": "net.berack.upo.valpre.Main",
"args": "net"
},
"args": "interactive"
}
]
}

143
README.md
View File

@@ -1,53 +1,118 @@
# Valutazione delle Prestazioni
Il progetto riguarda un simulatore ad eventi discreti.
Il simulatore è iniziato con il lavoro trovato sul libro di testo [Discrete-Event System Simulation](https://www.pearson.com/en-us/subject-catalog/p/discrete-event-system-simulation/P200000003161/9780136062127) al Capitolo 4, per poi esser personalizzato e modificato radicalmente.
Questo progetto consiste in un simulatore ad eventi discreti. Il simulatore è stato inizialmente sviluppato seguendo il materiale del libro [Discrete-Event System Simulation](https://www.pearson.com/en-us/subject-catalog/p/discrete-event-system-simulation/P200000003161/9780136062127) (Capitolo 4), per poi essere personalizzato e profondamente modificato.
Il risultato è la creazione in una libreria per la simulazione di eventi discreti nella quale si può scegliera la topologia e la quantità di nodi nella rete da simulare.
Il risultato finale è una libreria per la simulazione di eventi discreti, che consente di configurare la topologia e il numero di nodi della rete da simulare.
> [!IMPORTANT]
> Il JAR risultante che si trova nelle [Releases](https://github.com/Berack96/upo-valpre/releases).\
> La versione di Java usata è la 23 (precisamente la [23.0.1](https://www.oracle.com/java/technologies/javase/jdk23-archive-downloads.html)).
> Il file JAR generato è disponibile nella sezione [Releases](https://github.com/Berack96/upo-valpre/releases).\
> La libreria è stata sviluppata utilizzando Java 23, versione [23.0.1](https://www.oracle.com/java/technologies/javase/jdk23-archive-downloads.html).
### Comandi Jar
## Comandi Jar
Il JAR viene invocato tramite il classico comando java: `java -jar upo-valpre.jar` al quale si aggiungono vari argomenti successivi in base a cosa si vuole fare:
Il file JAR può essere eseguito tramite il comando standard `java -jar upo-valpre.jar`, seguito da specifici argomenti in base all'operazione desiderata:
* `java -jar upo-valpre.jar interactive`\
Avvia una sessione interattiva per creare una rete. Questa modalità è consigliata per reti di dimensioni ridotte; per reti più complesse, è preferibile utilizzare il codice della libreria per generare la rete.\
Una volta configurata la rete, è necessario salvarla in un file per eseguire simulazioni e analisi successive.
* `java -jar upo-valpre.jar net`\
Usato per avviare una sessione interattiva per la creazione di una rete. Da usare se la rete è relativamente breve da descrivere, altrimenti è più comodo usare il codice della libreria per la generazione della rete.\
Una volta scelta la rete è necessario salvarla in un file per la successiva simulazione e analisi.
* `java -jar upo-valpre.jar simulation -net <file> [other]`\
Usato per avviare una simulazione della rete. Nel caso la rete non abbia eventuali limiti nella generazione di arrivi, viene restituito un errore.
Esistono vari tipi di argomenti per scegliere come fare la simulazione:
* `-runs <N>` per fare la simulazione N volte
* `-seed <value>` per dare un seed iniziale scelto
* `-csv <file>` per salvare i risultati delle run in un file csv
* `-p` per fare le simulazioni in parallelo (ovvero su più thread)
* `-end <criteria>` per scegliere quando la simulazione finisce nel caso non ci siano dei source limitati nella creazione di arrivi. La tipologia di fine simulazione la si può trovare dentro `EndCriteria` (ovvero MaxArrivals, MaxDepartures, MaxTime) e la formattazione da usare per passare il parametro è la seguente:\
**\[Tipo1:param1,..,paramN\];\[..\];\[TipoN:param1,..,paramN\]**
Esegue una simulazione della rete specificata. Se la rete non include limiti per la generazione degli arrivi, verrà restituito un errore. Gli argomenti disponibili per personalizzare la simulazione includono:
* `-runs <N>`: Esegue la simulazione N volte.
* `-seed <value>`: Imposta un seed iniziale per la generazione casuale.
* `-i <confidences>`: Specifica gli indici di terminazione delle simulazioni basati sugli intervalli di confidenza. Ignora l'opzione `-p` se attiva. Formato:\
**\[nodo:statistica=confidenza:errore%\];\[..\]**
* `-csv <file>`: Salva i risultati delle simulazioni in un file CSV.
* `-p`: Esegue simulazioni in parallelo (su più thread).
* `-end <criteria>`: Definisce i criteri di terminazione della simulazione per reti senza limiti di arrivi. I criteri disponibili sono definiti in `EndCriteria` (MaxArrivals, MaxDepartures, MaxTime). Formato:\
**\[tipo:param1,..,paramN\];\[..\]**
* `java -jar upo-valpre.jar plot -csv <file>`\
Mostra (con un ambiente grafico) una finestra nella quale si può scegliere quale nodo vedere e ogni statistica associata ad esso. Di seguito un'immagine di esempio:\
Avvia un'interfaccia grafica per visualizzare i risultati. È possibile selezionare un nodo e analizzare le statistiche associate. Esempio di visualizzazione:\
![1738603552417](image/README/1738603552417.png)
Esistono dei file prefatti per vedere eventuali simulazioni che, nel caso vengano passati come parametri, automaticamente vengono usati:
* `example1.net`, `example2.net` e `example3.net` per `simulation -net`
* `example1.csv`, `example2.csv` e `example3.csv` per `plot -csv`
## Classi Interne
### Classi Interne
Il progetto include diverse classi interne per supportare la simulazione e la visualizzazione dei risultati. Le classi nel package [net.berack.upo.valpre](src/main/java/net/berack/upo/valpre) sono principalmente utili per l'uso del JAR e non sono essenziali per la simulazione. Le classi principali per la simulazione si trovano nei seguenti package:
Esistono molteplici classi interne che vengono usate per supportare la simulazione e/o mostrare i risultati. In generale le classi dentro il percorso [net.berack.upo.valpre](https://github.com/Berack96/upo-valpre/tree/main/src/main/java/net/berack/upo/valpre) sono usate per l'utilizzo del jar e quindi non sono essenziali per la simulazione.
I percorsi che invece sono direttamente responsabili per la simulazione sono:
- [net.berack.upo.valpre.rand](https://github.com/Berack96/upo-valpre/tree/main/src/main/java/net/berack/upo/valpre/rand) All'interno del quale si possono trovare:
- **Rng** che viene usato per il calcolo di numeri pseudo-casuali tramite un seed iniziale e la generazione di molteplici stream di generazione di numeri casuali
- **Distribution** interfaccia usata per la generazione di un numero casuale di una distribuzione. In questo file esistono molteplici classi interne che implementano l'interfaccia; per esempio: Exponential, Normal, Uniform
- [net.berack.upo.valpre.sim](https://github.com/Berack96/upo-valpre/tree/main/src/main/java/net/berack/upo/valpre/sim) Package che contiene tutte le parti utili alla simulazione; per esempio la creazione della rete o la simulazione si più thread:
- **Net** che viene usato per rappresentare una rete da simulare.
- **ServerNode** che viene usato per rappresentare un singolo nodo della rete.
- **Event** che viene usato per rappresentare un evento della simulazione.
- **EndCriteria** interfaccia che viene implementata dalle classi interne usata per controllare se e quando la simulazione debba finire.
- **Simulation** e **SimulationMultiple** che vengono usate per far partire la simulazione; la versione multiple serve ad organizzare molteplici simulazioni su più thread o su un singolo core.
- [net.berack.upo.valpre.sim.stats](https://github.com/Berack96/upo-valpre/tree/main/src/main/java/net/berack/upo/valpre/sim/stats) Package che contiene tutte le classi utili per la raccolta e l'analisi statistica dei vari valori generati dalla simulazione:
- **Result** il risultato di una run e la sua classe interna **Result.Summary** che contiene molteplici risultati di run già analizzati.
- **NodeStats** contiene indici statistici di un nodo e la sua classe interna **NodeStats.Summary** che contiene molteplici indici statistici già analizzati.
- **ConsoleTable** utile per mostrare i risultati in console sottoforma di tabella
- **CsvResult** utile per la lettura/scrittura dei risultati in formato csv
- [net.berack.upo.valpre.rand](src/main/java/net/berack/upo/valpre/rand): Contiene:
- **Rng**: Generatore di numeri pseudo-casuali con supporto per stream multipli.
- **Distribution**: Interfaccia per la generazione di numeri casuali secondo diverse distribuzioni (es. Exponential, Normal, Uniform).
- [net.berack.upo.valpre.sim](src/main/java/net/berack/upo/valpre/sim): Include:
- **Net**: Rappresenta una rete da simulare.
- **ServerNode**: Modella un singolo nodo della rete.
- **Event**: Rappresenta un evento della simulazione.
- **EndCriteria**: Interfaccia per definire i criteri di terminazione della simulazione.
- **Simulation** e **SimulationMultiple**: Avviano la simulazione; la seconda consente l'esecuzione su più thread.
- [net.berack.upo.valpre.sim.stats](src/main/java/net/berack/upo/valpre/sim/stats): Contiene classi per l'analisi statistica:
- **Result**: Rappresenta i risultati di una simulazione.
- **NodeStats**: Contiene statistiche relative ai nodi.
- **ConsoleTable**: Mostra i risultati in formato tabellare.
- **CsvResult**: Gestisce la lettura/scrittura dei risultati in formato CSV.
## Esempi
Il JAR include due reti di esempio utili per esperimenti o per verificare il corretto funzionamento del tool. Per eseguire una simulazione con questi esempi, avviare il tool in modalità interattiva e caricare gli esempi:\
`java -jar upo-valpre.jar interactive`
Oltre ad una verifica semplice che il tool funzioni, sono stati condotti ulteriori esperimenti sulle due reti di esempio, modificando progressivamente il numero di clienti nel sistema e variando la distribuzione di servizio di un nodo, mantenendo invariata la media (μ). Ogni esperimento è stato eseguito con un campione di 1000 simulazioni. I risultati dettagliati sono disponibili a questo [link](https://docs.google.com/spreadsheets/d/1yM1fvlpc2mIIpRe8M7_ry8m3DC3ZxNA204mM60O2hoQ/edit?usp=sharing) nei fogli colorati di verde (net1_incremental, net2_incremental, pivot1, pivot2).\
Le distribuzioni usate hanno tutte la stessa media μ e sono state create con questi parametri:
- Normale(μ, 0.6)
- Uniforme(μ - (μ\*0.1), μ + (μ\*0.1))
- Esponenziale(1/μ)
- Erlang(5, 5/μ)
- Iperesponenziale(p=\[0.5, 0.5\], e=\[1/(μ\*0.5), 1/(μ\*1.5)\])
---
### Primo esempio
![1741862746304](image/README/1741862746304.png)\
`example1` è una rete con una fonte di clienti (Source) che arrivano con tasso esponenziale (λ=0.222, media 4.5) e un centro di servizio (Queue) con tasso di servizio distribuito come una normale (μ=3.2, σ=0.6).\
Esempio di risultati in console:\
![1741860064265](image/README/1741860064265.png)
Risultati grafici con incertezza:\
![1744786768084](image/README/1744786768084.png)
Modificando la distribuzione di servizio del nodo "Queue", si osservano variazioni nei valori medi della coda, del tempo di attesa e del tempo di risposta, mentre l'utilizzo e il throughput rimangono invariati.\
![1742150755381](image/README/1742150755381.png)
Di seguito si può vedere il cambiamento del tempo medio di attesa, il numero medio della coda e l'utilizzazione al variare del numero di clienti nel sistema.\
![1742556358341](image/README/1742556358341.png)
---
### Secondo esempio
![1741863043733](image/README/1741863043733.png)\
`example2` è una rete con una fonte di clienti (Source) che arrivano con tasso esponenziale (λ=1.5, media 0.666), un centro di servizio (Service1) con tasso esponenziale (λ=2.0, media 0.5) e un altro centro di servizio (Service2) con tasso esponenziale (λ=3.5, media 0.2857) e un tempo di indisponibilità distribuito esponenzialmente (λ=10.0, media 0.1) con probabilità 10%.\
Esempio di risultati in console:\
![1741862486547](image/README/1741862486547.png)
Risultati grafici con incertezza:\
![1744786814771](image/README/1744786814771.png)
Anche in questo caso, modificando la distribuzione di servizio del nodo "Service2", si osservano variazioni nei valori medi della coda e del tempo di attesa, mentre l'utilizzo e il throughput rimangono invariati.\
![1744793029362](image/README/1744793029362.png)
Una particolarità di questa rete è il basso valore atteso per il tempo di servizio. Questo, in concomitanza con il resample in caso di valori negativi, fa si di aumentare la media della Distribuzione Normale. Nei grafici seguenti è stata inclusa la Normale solo nell'ultimo per mostrare la differenza rispetto le altre distribuzioni.\
![1742632017987](image/README/1742632017987.png)
## Confronto con JMT
Il tool JMT con le stesse reti di base (ovvero senza cambiare la Distribuzione) ottiene risultati simili al simulatore. Le reti usate per fare il confronto si possono trovare dentro la cartella delle [risorse di test](src/test/resources). I risultati ottenuti dal simulatore e da JMT si possono trovare al seguente [link](https://docs.google.com/spreadsheets/d/1yM1fvlpc2mIIpRe8M7_ry8m3DC3ZxNA204mM60O2hoQ/edit?usp=sharing) nei fogli colorati di blu (example1, example2, Riassunto) oppure direttamente dalla seguente tabella:\
![1744966407854](image/README/1744966407854.png)
Si possono notare due note messe nella tabella che servono ad indicare da dove vengono presi alcuni dati: La response, l'utilization% e l'unavailable% di Service2.\
Infatti essi si possono derivare da alcuni dati presenti dentro il simulatore JMT:
- Response: è il totale dato da Queue2 + Busy2 dato che il primo indica l'attesa della coda, mentre il secondo indica il tempo medio di servizio.
- Utilization%: è il valore preso dal numero medio di customer di Busy2. In questo caso indica proprio quanto la stazione (essendo single server) è occupata.
- Unavailable%: è il valore preso dal numero medio di customer di Calibration. In questo caso indica proprio quanto la stazione (essendo single server) è ferma e non può servire i clienti.
---
Per quanto riguarda il confronto con l'aumentare dei clienti nel sistema, JMT non permette di fermare la simulazione una volta elaborati N clienti, perciò è stata solo modificata la distribuzione e sono stati salvati i dati. Il risultato è la seguente tabella:\
![1745009420186](image/README/1745009420186.png)
Notare che, le osservazioni precedenti per la rete2, valgono anche in questo caso per il calcolo del tempo di risposta.

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 902 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

29
pom.xml
View File

@@ -13,30 +13,6 @@
<maven.compiler.target>23</maven.compiler.target>
</properties>
<build>
<plugins>
<!-- Compilazione Java con Maven -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>23</source>
<target>23</target>
</configuration>
</plugin>
<!-- Maven plugin per eseguire l'applicazione JavaFX -->
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version>
<configuration>
<mainClass>net.berack.upo.valpre.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
@@ -65,5 +41,10 @@
<artifactId>jfreechart</artifactId>
<version>1.5.5</version>
</dependency>
<dependency>
<groupId>net.sourceforge.argparse4j</groupId>
<artifactId>argparse4j</artifactId>
<version>0.9.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,267 @@
package net.berack.upo.valpre;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Scanner;
import java.util.function.Function;
import com.esotericsoftware.kryo.KryoException;
import net.berack.upo.valpre.rand.Distribution;
import net.berack.upo.valpre.rand.Rng;
import net.berack.upo.valpre.sim.Net;
import net.berack.upo.valpre.sim.ServerNode;
/**
* Interactive net builder. This class allows the user to build a net by adding
* nodes and connections. The user can also save the net to a file.
*/
public class InteractiveConsole {
private Net net = new Net();
private final PrintStream out;
private final Scanner scanner;
/**
* Create a new interactive net builder. Uses System.in and System.out.
*/
public InteractiveConsole() {
this(System.out, System.in);
}
/**
* Create a new interactive net builder.
*
* @param out the output stream
* @param in the input stream
*/
public InteractiveConsole(PrintStream out, InputStream in) {
this.out = out;
this.scanner = new Scanner(in);
}
/**
* Run the interactive net builder.
*/
public Net run() {
while (true) {
try {
var choice = choose(this.net + "\nChoose the next step to do:",
"Add a node", "Add a connection", "Save the net", "Load net", "Clear", "Run", "Exit");
switch (choice) {
case 1 -> this.buildNode();
case 2 -> this.buildConnection();
case 3 -> this.net.save(ask("Enter the filename: "));
case 4 -> this.loadNet();
case 5 -> this.net = new Net();
case 6 -> this.simpleRuns();
default -> {
this.scanner.close();
return this.net;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* Build a node as a source, terminal, queue, or queue with unavailable time.
*/
private void buildNode() {
var choice = choose("Choose the type of node to create:", "Source", "Terminal", "Queue",
"Queue with unavailable time");
var name = ask("Node name: ");
var distribution = askDistribution("Service distribution");
var node = switch (choice) {
case 1 -> ServerNode.Builder.source(name, distribution);
case 2 -> {
var limit = ask("Arrivals limit (0 for Int.Max): ", Integer::parseInt);
if (limit <= 0)
limit = Integer.MAX_VALUE;
yield ServerNode.Builder.terminal(name, limit, distribution);
}
case 3 -> {
var servers = ask("Number of servers: ", Integer::parseInt);
yield ServerNode.Builder.queue(name, servers, distribution, null);
}
case 4 -> {
var servers = ask("Number of servers: ", Integer::parseInt);
var unavailable = askDistribution("Unavailable distribution");
yield ServerNode.Builder.queue(name, servers, distribution, unavailable);
}
default -> null;
};
if (node != null)
this.net.addNode(node);
}
/**
* Build a connection.
*/
private void buildConnection() {
var source = ask("Enter the source node: ");
var target = ask("Enter the target node: ");
var weight = ask("Enter the weight: ", Double::parseDouble);
var sourceNode = this.net.getNode(source);
var targetNode = this.net.getNode(target);
this.net.addConnection(sourceNode, targetNode, weight);
}
/**
* Load a net from a file or from examples.
*/
private void loadNet() throws KryoException, IOException {
var choice = choose("Choose the type of net to load:", "From file", "From examples");
this.net = switch (choice) {
case 1 -> Net.load(ask("Enter the filename: "));
case 2 -> {
var choice2 = choose("Choose the example to load:",
"Example 1: Source -> Queue",
"Example 2: Source -> Queue -> Queue");
yield switch (choice2) {
case 1 -> NetExamples.getNet1();
case 2 -> NetExamples.getNet2();
default -> null;
};
}
default -> null;
};
}
/**
* Run the simulation with the net.
*/
private void simpleRuns() throws Exception {
var choice = choose("Choose what to do:", "100 Run", "1K Runs", "Run max 10K with confidence", "Back");
var seed = Rng.newSeed();
var csv = "run" + seed + ".csv";
switch (choice) {
case 1 -> new SimulationBuilder(net).setSeed(seed).setMaxRuns(100).setParallel(true).setCsv(csv).run();
case 2 -> new SimulationBuilder(net).setSeed(seed).setMaxRuns(1000).setParallel(true).setCsv(csv).run();
case 3 -> {
var indices = ask("Confidence indices with format [node:stat=confidence:relativeError];[..]\n");
new SimulationBuilder(net).setSeed(seed).setMaxRuns(10000).parseConfidenceIndices(indices).setCsv(csv)
.run();
}
default -> {
}
}
}
/**
* Ask the user for a distribution.
*
* @return the distribution
*/
private Distribution askDistribution(String ask) {
var choice = choose(ask + ":", "Exponential", "Uniform", "Erlang",
"UnavailableTime", "Normal", "NormalBoxMuller", "None");
return switch (choice) {
case 1 -> {
var lambda = ask("Lambda: ", Double::parseDouble);
yield new Distribution.Exponential(lambda);
}
case 2 -> {
var min = ask("Min: ", Double::parseDouble);
var max = ask("Max: ", Double::parseDouble);
yield new Distribution.Uniform(min, max);
}
case 3 -> {
var k = ask("K: ", Integer::parseInt);
var lambda = ask("Lambda: ", Double::parseDouble);
yield new Distribution.Erlang(k, lambda);
}
case 4 -> {
var probability = ask("Probability: ", Double::parseDouble);
var unavailable = askDistribution("Unavailable distribution");
yield new Distribution.UnavailableTime(probability, unavailable);
}
case 5 -> {
var mean = ask("Mean: ", Double::parseDouble);
var stdDev = ask("Standard deviation: ", Double::parseDouble);
yield new Distribution.Normal(mean, stdDev);
}
case 6 -> {
var mean = ask("Mean: ", Double::parseDouble);
var stdDev = ask("Standard deviation: ", Double::parseDouble);
yield new Distribution.NormalBoxMuller(mean, stdDev);
}
default -> null;
};
}
/**
* Ask the user a question.
*
* @param ask the question to ask
* @return the answer
*/
private String ask(String ask) {
return ask(ask, Function.identity());
}
/**
* Ask the user a question.
*
* @param ask the question to ask
* @param parser the parser to use
* @param defaultValue the default value
* @return the answer
*/
private <T> T ask(String ask, Function<String, T> parser) {
var totalRows = ask.chars().filter(ch -> ch == '\n').count();
var ask2 = "\033[" + totalRows + "A" + ask;
var first = true;
try {
while (true) {
this.out.print(first ? ask : ask2);
var line = this.scanner.nextLine();
first = false;
try {
var value = parser.apply(line);
return value;
} catch (Exception e) {
this.out.print("\033[A\033[K"); // clear the line
}
}
} catch (Exception e) {
return null; // normally when the scanner is closed
}
}
/**
* Ask the user to choose an option.
*
* @param ask the question to ask
* @param options the options to choose from
* @return the choice
*/
private int choose(String ask, String... options) {
var builder = new StringBuilder();
builder.append(ask).append("\n");
for (int i = 0; i < options.length; i++) {
builder.append(i + 1).append(". ").append(options[i]).append("\n");
}
builder.append("> ");
var string = builder.toString();
var choice = 0;
choice = ask(string, val -> {
var x = Integer.parseInt(val);
if (x < 1 || x > options.length)
throw new NumberFormatException(); // retry the question
return x;
});
return choice;
}
}

View File

@@ -2,98 +2,93 @@ package net.berack.upo.valpre;
import java.io.File;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.HashMap;
import net.sourceforge.argparse4j.ArgumentParsers;
import net.sourceforge.argparse4j.impl.Arguments;
import net.sourceforge.argparse4j.inf.Namespace;
public class Main {
public static void main(String[] args) {
if (args.length == 0)
exit("No program specified!");
private final static String NAME;
/**
* The name of the program, used for the help message.
*/
static {
var name = "valpre";
try {
var program = args[0];
var subArgs = Arrays.copyOfRange(args, 1, args.length);
switch (program) {
var uri = Main.class.getProtectionDomain().getCodeSource().getLocation().toURI();
name = new File(uri).getName();
} catch (URISyntaxException e) {
}
NAME = name;
}
/**
* The main method of the program. It parses the arguments and runs the
* simulation or the plotter.
*
* @param args the arguments to parse
*/
public static void main(String[] args) {
try {
var param = Main.getParameters(args);
var command = param.getString("command");
switch (command) {
case "simulation" -> {
var param = Main.getParameters(program, subArgs);
new SimulationBuilder(param.get("net"))
.setCsv(param.get("csv"))
.setMaxRuns(param.getOrDefault("runs", Integer::parseInt, 100))
.setSeed(param.getOrDefault("seed", Long::parseLong, 0L))
.setParallel(param.get("p") != null)
.parseEndCriteria(param.get("end"))
.parseConfidenceIndices(param.get("i"))
new SimulationBuilder(param.getString("net"))
.setCsv(param.getString("csv"))
.setMaxRuns(param.getInt("runs"))
.setSeed(param.getLong("seed"))
.setParallel(param.getBoolean("p"))
.parseEndCriteria(param.getString("end"))
.parseConfidenceIndices(param.getString("indices"))
.run();
}
case "plot" -> {
var param = Main.getParameters(program, subArgs);
var csv = param.get("csv");
var csv = param.getString("csv");
var plot = new Plot(csv);
plot.show();
}
case "net" -> {
var net = new NetBuilderInteractive();
net.run();
}
default -> exit("Invalid program!");
case "interactive" -> new InteractiveConsole().run();
default -> throw new RuntimeException("Invalid program!"); // Should never happen
}
} catch (Exception e) {
exit(e.getMessage());
}
}
/**
* Get the parameters from the arguments.
*
* @param program the program to run
* @param args the arguments to parse
* @return the parameters
*/
private static Parameters getParameters(String program, String[] args) {
var arguments = new HashMap<String, Boolean>();
arguments.put("p", false);
arguments.put("seed", true);
arguments.put("runs", true);
arguments.put("net", true);
arguments.put("end", true);
arguments.put("csv", true);
arguments.put("i", true);
var descriptions = new HashMap<String, String>();
descriptions.put("p", "Add this if you want the simulation to use threads (one each run).");
descriptions.put("seed", "The seed of the simulation.");
descriptions.put("runs", "How many runs the simulator should run.");
descriptions.put("end", "When the simulation should end. Format is \"[ClassName:param1,..,paramN];[..]\"");
descriptions.put("net", "The file net to use. Use example1.net or example2.net for the provided ones.");
descriptions.put("i", "The confidence indices to use for the simulation. If active then p is ignored."
+ " Format is \"[node:stat=confidence:relativeError];[..]\"");
var csvDesc = switch (program) {
case "simulation" -> "The filename for saving every run statistics.";
case "plot" -> "The filename that contains the previous saved runs.";
default -> "";
};
descriptions.put("csv", csvDesc);
return Parameters.getArgsOrHelper(args, "-", arguments, descriptions);
}
/**
* Exit the program with an error message.
*/
public static void exit(String message) {
try {
var uri = Main.class.getProtectionDomain().getCodeSource().getLocation().toURI();
var name = new File(uri).getName();
System.err.println(message);
System.out.println("Usage: java -jar " + name + ".jar [simulation|plot|net] [args]");
System.out.println("simulation args: -net <net> [-csv <csv>] [-runs <runs>] [-seed <seed>]"
+ "[-p] [-end <end>] [-i <indices>]");
System.out.println("plot args: -csv <csv>");
System.out.println("net args: none");
System.err.println(e.getMessage());
System.exit(1);
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
/**
* Parses the arguments of the program. It uses the argparse4j library to parse
* the arguments and return a Namespace object with the parsed arguments.
*
* @param args the arguments to parse
* @return a Namespace object with the parsed arguments
*/
private static Namespace getParameters(String[] args) {
var parser = ArgumentParsers.newFor(NAME).build()
.defaultHelp(true)
.description("Build a network simulation and/or plot the results of a simulation.");
var subparser = parser.addSubparsers().title("commands").description("valid commands").help("subcommand help");
var sim = subparser.addParser("simulation").help("Run a simulation of the network.");
sim.addArgument("-net").help("The file net to use.").required(true);
sim.addArgument("-csv").help("The filename for saving every run statistics.");
sim.addArgument("-runs").type(Integer.class).help("How many runs the simulator should run.").setDefault(100);
sim.addArgument("-seed").type(Long.class).help("The seed of the simulation.").setDefault(0L);
sim.addArgument("-p").action(Arguments.storeTrue()).help("Parallel (one thread each run).").setDefault(false);
sim.addArgument("-end").help("When the simulation should end. Format:\n\"[ClassName:param1,..,paramN];[..]\"");
sim.addArgument("-indices").help("The confidence indices to use for the simulation. If active -p is ignored."
+ " Format:\n\"[node:stat=confidence:relativeError];[..]\"");
var plot = subparser.addParser("plot").help("Plot the results of a simulation.");
plot.addArgument("-csv").help("The filename for the csv file to plot.").required(true);
var _ = subparser.addParser("interactive").help("Run the interactive console.");
// Interactive console does not need any arguments
var namespace = parser.parseArgsOrFail(args);
namespace.getAttrs().put("command", args[0]);
return namespace;
}
}

View File

@@ -1,186 +0,0 @@
package net.berack.upo.valpre;
import java.util.function.Function;
import net.berack.upo.valpre.rand.Distribution;
import net.berack.upo.valpre.sim.Net;
import net.berack.upo.valpre.sim.ServerNode;
public class NetBuilderInteractive {
private final Net net = new Net();
/**
* Run the interactive net builder.
*
* @param args the arguments
*/
public void run() {
while (true) {
try {
var choice = choose("Choose the next step to do:",
"Add a node", "Add a connection", "Print Nodes", "Save the net", "Exit");
switch (choice) {
case 1 -> {
var node = this.buildNode();
this.net.addNode(node);
}
case 2 -> {
var source = ask("Enter the source node: ");
var target = ask("Enter the target node: ");
var weight = ask("Enter the weight: ", Double::parseDouble, 0.0);
var sourceNode = this.net.getNode(source);
var targetNode = this.net.getNode(target);
this.net.addConnection(sourceNode, targetNode, weight);
}
case 3 -> this.printNodes();
case 4 -> this.net.save(ask("Enter the filename: "));
case 5 -> System.exit(0);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* Print the nodes in the net.
*/
private void printNodes() {
var builder = new StringBuilder();
builder.append("Nodes:\n");
for (var i = 0; i < this.net.size(); i++) {
var name = this.net.getNode(i).name;
builder.append(name).append(" -> ");
for (var connection : this.net.getChildren(i)) {
var child = this.net.getNode(connection.index);
builder.append(child.name).append("(").append(connection.weight).append("), ");
}
builder.delete(builder.length() - 2, builder.length());
builder.append("\n");
}
System.out.print(builder.toString());
}
/**
* Build a node.
*
* @return the node
*/
private ServerNode buildNode() {
var choice = choose("Choose the type of node to create:", "Source", "Queue");
var name = ask("Node name: ");
var distribution = askDistribution("Service distribution");
return switch (choice) {
case 1 -> {
var limit = ask("Arrivals limit (0 for Int.Max): ", Integer::parseInt, 1);
if (limit <= 0)
limit = Integer.MAX_VALUE;
yield ServerNode.Builder.sourceLimited(name, limit, distribution);
}
case 2 -> {
var servers = ask("Number of servers: ", Integer::parseInt, 1);
var unavailable = askDistribution("Unavailable distribution");
yield ServerNode.Builder.queue(name, servers, distribution, unavailable);
}
default -> null;
};
}
/**
* Ask the user for a distribution.
*
* @return the distribution
*/
public static Distribution askDistribution(String ask) {
var choice = choose(ask + ":", "Exponential", "Uniform", "Erlang",
"UnavailableTime", "Normal", "NormalBoxMuller", "None");
return switch (choice) {
case 1 -> {
var lambda = ask("Lambda: ", Double::parseDouble, 1.0);
yield new Distribution.Exponential(lambda);
}
case 2 -> {
var min = ask("Min: ", Double::parseDouble, 0.0);
var max = ask("Max: ", Double::parseDouble, 1.0);
yield new Distribution.Uniform(min, max);
}
case 3 -> {
var k = ask("K: ", Integer::parseInt, 1);
var lambda = ask("Lambda: ", Double::parseDouble, 1.0);
yield new Distribution.Erlang(k, lambda);
}
case 4 -> {
var probability = ask("Probability: ", Double::parseDouble, 0.0);
var unavailable = askDistribution("Unavailable distribution");
yield new Distribution.UnavailableTime(probability, unavailable);
}
case 5 -> {
var mean = ask("Mean: ", Double::parseDouble, 0.0);
var stdDev = ask("Standard deviation: ", Double::parseDouble, 1.0);
yield new Distribution.Normal(mean, stdDev);
}
case 6 -> {
var mean = ask("Mean: ", Double::parseDouble, 0.0);
var stdDev = ask("Standard deviation: ", Double::parseDouble, 1.0);
yield new Distribution.NormalBoxMuller(mean, stdDev);
}
default -> null;
};
}
/**
* Ask the user a question.
*
* @param ask the question to ask
* @return the answer
*/
private static String ask(String ask) {
return ask(ask, Function.identity(), "");
}
/**
* Ask the user a question.
*
* @param ask the question to ask
* @param parser the parser to use
* @param defaultValue the default value
* @return the answer
*/
private static <T> T ask(String ask, Function<String, T> parser, T defaultValue) {
System.out.print(ask);
try {
var line = System.console().readLine();
return parser.apply(line);
} catch (Exception e) {
System.out.println("Invalid input: " + e.getMessage());
return defaultValue;
}
}
/**
* Ask the user to choose an option.
*
* @param ask the question to ask
* @param options the options to choose from
* @return the choice
*/
private static int choose(String ask, String... options) {
var builder = new StringBuilder();
builder.append(ask).append("\n");
for (int i = 0; i < options.length; i++) {
builder.append(i + 1).append(". ").append(options[i]).append("\n");
}
builder.append("> ");
var string = builder.toString();
var choice = 0;
while (choice < 1 || choice > options.length)
choice = ask(string, Integer::parseInt, 0);
return choice;
}
}

View File

@@ -0,0 +1,171 @@
package net.berack.upo.valpre;
import java.util.List;
import java.util.function.BiFunction;
import net.berack.upo.valpre.rand.Distribution;
import net.berack.upo.valpre.sim.Net;
import net.berack.upo.valpre.sim.ServerNode;
import net.berack.upo.valpre.sim.SimulationMultiple;
import net.berack.upo.valpre.sim.stats.CsvResult;
import net.berack.upo.valpre.sim.stats.Result;
/**
* This class provides two example networks.
* The first network is composed of a terminal node and a queue node.
* The second network is composed of a terminal node and two queue nodes.
*/
public final class NetExamples {
/**
* Main method to test the networks.
* The first network will have the distribution changed but the mean will be the
* same. The second network will have the distribution changed but the mean will
* be the same. The results will be saved to a csv file.
*
* @param args not needed
* @throws Exception if the simulation fails or the file is not saved
*/
public static void main(String[] args) throws Exception {
var seed = 123456789L;
runNet(seed, 3.2, 1, "net1.csv", (spawn, dist) -> {
var name = dist.getClass().getSimpleName() + "_" + spawn;
return NetExamples.getNet1(spawn, name, dist);
});
runNet(seed, 1 / 3.5, 2, "net2.csv", (spawn, dist) -> {
var name = dist.getClass().getSimpleName() + "_" + spawn;
return NetExamples.getNet2(spawn, name, dist);
});
}
/**
* Method to test whatever network you input.
* The network will have the distribution changed but the mean will be the same.
* The bifunction requested is to get the network you want to test passing the
* spawn and the distribution with the same mean.
* The network will be tested with spawn totals of 1, 2, 5, 7, 10, 25, 50, 75,
* 100, 250, 500, 750, 1000, 1500, 2000.
* The results will be saved to a csv passed as argument.
*
* @param seed the seed for the simulation
* @param avg the mean of the distribution
* @param nodeToWatch the node to watch
* @param csv the file to save the results
* @param getNet the bifunction to get the network
* @throws Exception if the simulation fails or the file is not saved
*/
public static void runNet(long seed, double avg, int nodeToWatch, String csv,
BiFunction<Integer, Distribution, Net> getNet) throws Exception {
var build = new Result.Builder().seed(seed);
var spawnTotals = new int[] { 1, 2, 5, 7, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 1500, 2000 };
var normal = new Distribution.NormalBoxMuller(avg, 0.6);
var exponential = new Distribution.Exponential(1 / avg);
var erlang = new Distribution.Erlang(5, 5 / avg);
var uniform = new Distribution.Uniform(avg - (avg * 0.1), avg + (avg * 0.1));
var hyper = new Distribution.HyperExponential(
new double[] { 1 / (avg * 0.5), 1 / (avg * 1.5) },
new double[] { 0.5f, 0.5f });
for (var spawn : spawnTotals) {
System.out.println("Spawn: " + spawn);
var nets = new Net[] {
getNet.apply(spawn, normal),
getNet.apply(spawn, exponential),
getNet.apply(spawn, erlang),
getNet.apply(spawn, uniform),
getNet.apply(spawn, hyper),
};
for (var net : nets) {
var summary = new SimulationMultiple(net).runParallel(build.seed, 1000);
var name = net.getNode(nodeToWatch).name;
var stat = summary.getSummaryOf(name).average;
build.addNode(name, stat);
}
}
var result = build.build();
new CsvResult(csv).saveResults(List.of(result));
System.out.println("Results saved to " + csv);
}
/**
* Return the first example network.
* The net is composed of a terminal node and a queue node.
* The terminal node generates 10000 jobs with an exponential distribution 4.5.
* The queue node has a capacity of 1 and a service time of 3.2 with a standard
* deviation of 0.6.
*
* @return the first example network
*/
public static Net getNet1() {
var norm3_2 = new Distribution.NormalBoxMuller(3.2, 0.6);
return getNet1(10000, "Queue", norm3_2);
}
/**
* Return the first example network.
* The net is composed of a terminal node and a queue node.
* The terminal node is connected to the queue node.
* The terminal node generates N jobs with an exponential distribution 4.5.
*
* @param spawn the number of jobs to generate
* @param name the name of the queue node
* @param queue the distribution of the queue node
* @return the first example network
*/
public static Net getNet1(int spawn, String name, Distribution queue) {
var source = new Distribution.Exponential(1.0 / 4.5);
var net1 = new Net();
net1.addNode(ServerNode.Builder.terminal("Source", spawn, source));
net1.addNode(ServerNode.Builder.queue(name, 1, queue));
net1.addConnection(0, 1, 1.0);
return net1;
}
/**
* Return the second example network.
* The net is composed of a terminal node and two queue nodes.
* The terminal node generates 10000 jobs with an exponential distribution 1.5.
* The first queue node has a capacity of 1 and a service time of 2.0.
* The second queue node has a capacity of 1 and a service time of 3.5.
* The second queue node has an unavailable time of 0.1 with an exponential
* distribution of 10.0.
* The terminal node is connected to the first queue node.
* The first queue node is connected to the second queue node.
*
* @return the second example network
*/
public static Net getNet2() {
var exp3_5 = new Distribution.Exponential(3.5);
return getNet2(10000, "Service2", exp3_5);
}
/**
* Return the second example network.
* The net is composed of a terminal node and two queue nodes.
* The terminal node is connected to the first queue node.
* The first queue node is connected to the second queue node.
*
* @param spawn the number of jobs to generate
* @param name the name of the second queue node
* @param service2 the distribution of the second queue node
* @return the second example network
*/
public static Net getNet2(int spawn, String name, Distribution service2) {
var exp1_5 = new Distribution.Exponential(1.5);
var exp2 = new Distribution.Exponential(2.0);
var exp10 = new Distribution.Exponential(10.0);
var unExp = new Distribution.UnavailableTime(0.1, exp10);
var net3 = new Net();
net3.addNode(ServerNode.Builder.terminal("Source", spawn, exp1_5));
net3.addNode(ServerNode.Builder.queue("Service", 1, exp2));
net3.addNode(ServerNode.Builder.queue(name, 1, service2, unExp));
net3.addConnection(0, 1, 1.0);
net3.addConnection(1, 2, 1.0);
return net3;
}
}

View File

@@ -1,216 +0,0 @@
package net.berack.upo.valpre;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
/**
* Class that helps with parsing the parameters passed as input in the console.
*/
public class Parameters {
private final Map<String, Boolean> arguments;
private final String prefix;
private Map<String, String> parameters;
/**
* Constructs a new Parameters object with the specified prefix and arguments.
* The arguments can be with value, in that case in the map the boolean should
* be true, otherwise it is only an argument that is a flag
*
* @param prefix the prefix to be used
* @param arguments a map of arguments where the key is a string and if the
* boolean is true then the argument expect a value
* @throws IllegalArgumentException if the arguments map is null or empty
*/
public Parameters(String prefix, Map<String, Boolean> arguments) {
if (arguments == null || arguments.size() == 0)
throw new IllegalArgumentException();
this.arguments = arguments;
this.prefix = prefix;
}
/**
* Get the size of the parameters.
*
* @return the size of the parameters
*/
public int size() {
return this.parameters.size();
}
/**
* Get the value of the argument passed as input.
*
* @param key the key of the argument
* @return the value of the argument
*/
public String get(String key) {
if (this.parameters == null)
return null;
return this.parameters.get(key);
}
/**
* Get the value from the arguments or the default value if it is not present.
*
* @param key The key to get the value from.
* @param parse The function to parse the value.
* @param value The default value if the key is not present.
* @return The value from the arguments or the default value if it is not
* present.
*/
public <T> T getOrDefault(String key, Function<String, T> parse, T value) {
var arg = this.get(key);
return arg != null ? parse.apply(arg) : value;
}
/**
* Return a string with the standard <arggument> <description> spaced enough
*
* @param eventualDescription the description for the argument, if not present
* the argument will be shown anyway/
* @return a string of arguments
*/
public String helper(Map<String, String> eventualDescription) {
var size = 0;
var parameters = new HashMap<String, String>();
for (var param : this.arguments.entrySet()) {
var string = this.prefix + param.getKey();
if (param.getValue())
string += " <value>";
parameters.put(param.getKey(), string);
size = Math.max(size, string.length());
}
size += 2; // spacing
var builder = new StringBuilder();
for (var param : parameters.entrySet()) {
var key = param.getKey();
var args = param.getValue();
builder.append(" ");
builder.append(args);
var desc = eventualDescription.get(key);
if (desc != null) {
builder.append(" ".repeat(size - args.length()));
builder.append(desc);
}
builder.append("\n");
}
return builder.toString();
}
/**
* Parse the arguments passed and build a map of Argument --> Value that can
* be used to retrieve the information. In the case that the arguments are not
* in the correct format then an exception is thrown.
* To get the arguments use the {@link #get(String)} method.
*
* @param args the arguments in input
* @throws IllegalArgumentException if the arguments are not formatted correctly
* or if there is an unknown argument or there
* are not arguments in the input
*/
public void parse(String[] args) {
if (args == null || args.length == 0)
throw new IllegalArgumentException("No arguments passed");
var result = new HashMap<String, String>();
for (var i = 0; i < args.length; i += 1) {
var current = args[i];
var next = i + 1 < args.length ? args[i + 1] : null;
var updateI = this.parseSingle(current, next, result);
if (updateI)
i += 1;
}
this.parameters = result;
}
/**
* Parse one single argument and put it into the map.
*
* @param current the current argument
* @param next the next argument if present
* @param result the map where to insert the value
* @throws IllegalArgumentException if the arguments are not formatted correctly
* or if there is an unknown argument
* @return true if the next argument is used
*/
private boolean parseSingle(String current, String next, Map<String, String> result) {
if (!current.startsWith(this.prefix))
throw new IllegalArgumentException("Missing prefix [" + current + "]");
current = current.substring(this.prefix.length());
var value = this.arguments.get(current);
if (value != null) {
result.put(current, value ? next : "");
return value;
}
var finalSize = result.size() + current.length();
for (var letter : current.split(""))
if (this.arguments.get(letter) != null)
result.put(current, "");
if (finalSize != result.size())
throw new IllegalArgumentException("Unknown argument [" + current + "]");
return false;
}
/**
* Parse the arguments passed and returns a map of Argument --> Value that can
* be used to retrieve the information. In the case that the arguments are not
* in the correct format then an exception is thrown and the helper is printed.
* If the arguments passed are 0 then the helper is printed.
*
* @param args the arguments in input
* @param prefix the prefix to be used
* @param arguments a map of arguments where the key is a string and if the
* boolean is true then the argument expect a value
* @param descriptions a map of descriptions for the arguments
* @throws IllegalArgumentException if the arguments are not formatted correctly
* or if there is an unknown argument
* @return a map of the values
*/
public static Parameters getArgsOrHelper(String[] args, String prefix, Map<String, Boolean> arguments,
Map<String, String> descriptions) {
var param = new Parameters(prefix, arguments);
try {
param.parse(args);
return param;
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
System.out.println(param.helper(descriptions));
throw new IllegalArgumentException("Invalid arguments");
}
}
/**
* Get the file or the example file if it is present.
*
* @param file the file to get
* @return the file or the example file
* @throws FileNotFoundException if the file is not found
*/
public static InputStream getFileOrExample(String file) throws FileNotFoundException {
if (file == null)
return null;
if (file.startsWith("example")) {
var resource = Parameters.class.getClassLoader().getResourceAsStream(file);
if (resource != null)
return resource;
}
return new FileInputStream(file);
}
}

View File

@@ -41,15 +41,10 @@ public class Plot {
* @throws IOException if anything happens while reading the file
*/
public Plot(String csv) throws IOException {
var stream = Parameters.getFileOrExample(csv);
if (stream == null)
throw new IllegalArgumentException("CSV file needed!");
var results = CsvResult.loadResults(stream);
stream.close();
var results = new CsvResult(csv).loadResults();
this.summary = new Result.Summary(results);
var nodes = this.summary.getNodes().toArray(new String[0]);
var nodes = this.summary.getNodes();
this.panelBarChart = new ChartPanel(null);
this.nodeComboBox = new JComboBox<>(nodes);
@@ -118,7 +113,7 @@ public class Plot {
var frame = new JFrame("Graph of the Simulation");
frame.add(rootPane);
frame.setSize(800, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setVisible(true);
});
}

View File

@@ -2,6 +2,7 @@ package net.berack.upo.valpre;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.util.concurrent.ExecutionException;
import com.esotericsoftware.kryo.KryoException;
@@ -13,6 +14,7 @@ import net.berack.upo.valpre.sim.EndCriteria.MaxTime;
import net.berack.upo.valpre.sim.Net;
import net.berack.upo.valpre.sim.SimulationMultiple;
import net.berack.upo.valpre.sim.stats.CsvResult;
import net.berack.upo.valpre.sim.stats.Result;
/**
* This class is responsible for running the simulation. It parses the arguments
@@ -35,10 +37,8 @@ public class SimulationBuilder {
*/
public SimulationBuilder(String netFile) throws IOException {
try {
var file = Parameters.getFileOrExample(netFile);
this.net = Net.load(file);
this.net = Net.load(netFile);
this.confidences = new ConfidenceIndices(this.net);
file.close();
} catch (FileNotFoundException e) {
throw new IllegalArgumentException("Net file needed!");
} catch (KryoException e) {
@@ -201,7 +201,7 @@ public class SimulationBuilder {
if (indices == null)
return this;
for (var index : indices.split(",")) {
for (var index : indices.split(";")) {
var parts = index.split("=");
if (parts.length != 2)
throw new IllegalArgumentException("Invalid confidence index: " + index);
@@ -230,23 +230,38 @@ public class SimulationBuilder {
* @throws ExecutionException If the simulation has an error.
* @throws IOException If the CSV file has a problem.
*/
public void run() throws InterruptedException, ExecutionException, IOException {
public Result.Summary run() throws InterruptedException, ExecutionException, IOException {
return this.run(System.out);
}
/**
* Run the simulation with the given parameters.
* At the end it prints the results and saves them to a CSV file if requested.
*
* @param out the output stream to print the results
* @throws InterruptedException If the simulation is interrupted.
* @throws ExecutionException If the simulation has an error.
* @throws IOException If the CSV file has a problem.
*/
public Result.Summary run(PrintStream out) throws InterruptedException, ExecutionException, IOException {
var nano = System.nanoTime();
var sim = new SimulationMultiple(this.net);
var summary = switch (this.type) {
case Incremental -> sim.runIncremental(this.seed, this.runs, this.confidences, this.endCriteria);
case Incremental -> sim.runIncremental(this.seed, this.runs, out, this.confidences, this.endCriteria);
case Parallel -> sim.runParallel(this.seed, this.runs, this.endCriteria);
case Normal -> sim.run(this.seed, this.runs, this.endCriteria);
};
nano = System.nanoTime() - nano;
System.out.print(summary);
System.out.println("Final time " + nano / 1e6 + "ms");
out.print(summary);
out.println("Final time " + nano / 1e6 + "ms");
if (csv != null) {
new CsvResult(this.csv).saveResults(summary.getRuns());
System.out.println("Data saved to " + this.csv);
out.println("Data saved to " + this.csv);
}
return summary;
}
/**

View File

@@ -32,6 +32,36 @@ public interface Distribution {
return sample;
}
/**
* Returns a string representation of the distribution.
* In case the distribution is null, an empty string is returned.
*
* @param distribution The distribution to represent.
* @return A string representation of the distribution.
* @throws IllegalArgumentException if the field cannot be accessed
* @throws IllegalAccessException if the field cannot be accessed
*/
public static String toString(Distribution distribution) throws IllegalArgumentException, IllegalAccessException {
if (distribution == null)
return "";
var builder = new StringBuilder();
var dist = distribution.getClass();
builder.append(dist.getSimpleName()).append('(');
for (var param : dist.getFields()) {
var paramValue = param.get(distribution);
var str = paramValue instanceof Distribution
? toString((Distribution) paramValue)
: paramValue;
builder.append(str).append(", ");
}
builder.delete(builder.length() - 2, builder.length()).append(')');
return builder.toString();
}
/**
* Represents an exponential distribution.
*/

View File

@@ -131,4 +131,13 @@ public class Rng {
var t = multiplier * (seed % Q) - R * (seed / Q);
return t > 0 ? t : (t + modulus);
}
/**
* This creates a new seed based on the current time.
*
* @return a new seed
*/
public static long newSeed() {
return System.currentTimeMillis() % MODULUS;
}
}

View File

@@ -1,209 +0,0 @@
package net.berack.upo.valpre.rand;
/* --------------------------------------------------------------------------
* This is a Java library for generating random variates from six discrete
* distributions
*
* Generator Range (x) Mean Variance
*
* bernoulli(p) x = 0,1 p p*(1-p)
* binomial(n, p) x = 0,...,n n*p n*p*(1-p)
* equilikely(a, b) x = a,...,b (a+b)/2 ((b-a+1)*(b-a+1)-1)/12
* Geometric(p) x = 0,... p/(1-p) p/((1-p)*(1-p))
* pascal(n, p) x = 0,... n*p/(1-p) n*p/((1-p)*(1-p))
* poisson(m) x = 0,... m m
*
* and seven continuous distributions
*
* uniform(a, b) a < x < b (a + b)/2 (b - a)*(b - a)/12
* exponential(m) x > 0 m m*m
* erlang(n, b) x > 0 n*b n*b*b
* normal(m, s) all x m s*s
* logNormal(a, b) x > 0 see below
* chiSquare(n) x > 0 n 2*n
* student(n) all x 0 (n > 1) n/(n - 2) (n > 2)
*
* For the a Lognormal(a, b) random variable, the mean and variance are
*
* mean = exp(a + 0.5*b*b)
* variance = (exp(b*b) - 1) * exp(2*a + b*b)
*
* Name : Rvgs.java (Random Variate GeneratorS)
* Authors : Steve Park & Dave Geyer
* Translated by : Richard Dutton & Jun Wang
* Language : Java
* Latest Revision : 7-1-04
* --------------------------------------------------------------------------
*/
public class Rvgs {
private final Rng rng;
// public Rvgs() {
// this.rngs = new Rngs(Rng.DEFAULT);
// }
public Rvgs(Rng rng) {
if (rng == null)
throw new NullPointerException();
this.rng = rng;
}
/**
* Returns 1 with probability p or 0 with probability 1 - p.
* NOTE: use 0.0 < p < 1.0
*/
public long bernoulli(double p) {
return ((this.rng.random() < (1.0 - p)) ? 0 : 1);
}
/**
* Returns a binomial distributed integer between 0 and n inclusive.
* NOTE: use n > 0 and 0.0 < p < 1.0
*/
public long binomial(long n, double p) {
long i, x = 0;
for (i = 0; i < n; i++)
x += bernoulli(p);
return (x);
}
/**
* Returns an equilikely distributed integer between a and b inclusive.
* NOTE: use a < b
*/
public long equilikely(long a, long b) {
return (a + (long) ((b - a + 1) * this.rng.random()));
}
/**
* Returns a geometric distributed non-negative integer.
* NOTE: use 0.0 < p < 1.0
*/
public long geometric(double p) {
return ((long) (Math.log(1.0 - this.rng.random()) / Math.log(p)));
}
/**
* Returns a Pascal distributed non-negative integer.
* NOTE: use n > 0 and 0.0 < p < 1.0
*/
public long pascal(long n, double p) {
long i, x = 0;
for (i = 0; i < n; i++)
x += geometric(p);
return (x);
}
/**
* Returns a Poisson distributed non-negative integer.
* NOTE: use m > 0
*/
public long poisson(double m) {
double t = 0.0;
long x = 0;
while (t < m) {
t += exponential(1.0);
x++;
}
return (x - 1);
}
/**
* Returns a uniformly distributed real number between a and b.
* NOTE: use a < b
*/
public double uniform(double a, double b) {
return (a + (b - a) * this.rng.random());
}
/**
* Returns an exponentially distributed positive real number.
* NOTE: use m > 0.0
*/
public double exponential(double m) {
return (-m * Math.log(1.0 - this.rng.random()));
}
/**
* Returns an Erlang distributed positive real number.
* NOTE: use n > 0 and b > 0.0
*/
public double erlang(long n, double b) {
long i;
double x = 0.0;
for (i = 0; i < n; i++)
x += exponential(b);
return (x);
}
/**
* Returns a normal (Gaussian) distributed real number.
* NOTE: use s > 0.0
*
* Uses a very accurate approximation of the normal idf due to Odeh & Evans,
* J. Applied Statistics, 1974, vol 23, pp 96-97.
*/
public double normal(double m, double s) {
final double p0 = 0.322232431088;
final double q0 = 0.099348462606;
final double p1 = 1.0;
final double q1 = 0.588581570495;
final double p2 = 0.342242088547;
final double q2 = 0.531103462366;
final double p3 = 0.204231210245e-1;
final double q3 = 0.103537752850;
final double p4 = 0.453642210148e-4;
final double q4 = 0.385607006340e-2;
double u, t, p, q, z;
u = this.rng.random();
if (u < 0.5)
t = Math.sqrt(-2.0 * Math.log(u));
else
t = Math.sqrt(-2.0 * Math.log(1.0 - u));
p = p0 + t * (p1 + t * (p2 + t * (p3 + t * p4)));
q = q0 + t * (q1 + t * (q2 + t * (q3 + t * q4)));
if (u < 0.5)
z = (p / q) - t;
else
z = t - (p / q);
return (m + s * z);
}
/**
* Returns a lognormal distributed positive real number.
* NOTE: use b > 0.0
*/
public double logNormal(double a, double b) {
return (Math.exp(a + b * normal(0.0, 1.0)));
}
/**
* Returns a chi-square distributed positive real number.
* NOTE: use n > 0
*/
public double chiSquare(long n) {
long i;
double z, x = 0.0;
for (i = 0; i < n; i++) {
z = normal(0.0, 1.0);
x += z * z;
}
return (x);
}
/**
* Returns a student-t distributed real number.
* NOTE: use n > 0
*/
public double student(long n) {
return (normal(0.0, 1.0) / Math.sqrt(chiSquare(n) / n));
}
}

View File

@@ -138,7 +138,7 @@ public class ConfidenceIndices {
for (var stat : statistics) {
var err = error.of(stat);
if (!Double.isFinite(err))
if (!Double.isFinite(err) || err == 0.0d)
continue;
retValues.add("%s:%s=%.3f".formatted(this.nodes[i], stat, err));

View File

@@ -23,11 +23,7 @@ public class Event implements Comparable<Event> {
@Override
public int compareTo(Event other) {
if (this.time < other.time)
return -1;
if (this.time == other.time)
return 0;
return 1;
return Double.compare(this.time, other.time);
}
/**

View File

@@ -75,7 +75,6 @@ public final class Net implements Iterable<ServerNode> {
* @param weight The probability of the child node.
* @throws IndexOutOfBoundsException if one of the two nodes are not in the net
* @throws IllegalArgumentException if the weight is negative or zero
* @throws IllegalArgumentException if the child is a source node
*/
public void addConnection(int parent, int child, double weight) {
if (weight <= 0)
@@ -85,9 +84,6 @@ public final class Net implements Iterable<ServerNode> {
if (parent < 0 || child < 0 || parent > max || child > max)
throw new IndexOutOfBoundsException("One of the nodes does not exist");
if (this.servers.get(child).spawnArrivals > 0)
throw new IllegalArgumentException("Can't connect to a source node");
var list = this.connections.get(parent);
list.removeIf(conn -> conn.index == child);
list.add(new Connection(child, weight));
@@ -170,14 +166,13 @@ public final class Net implements Iterable<ServerNode> {
for (var conn : list)
sum += conn.weight;
var newOne = new Connection[list.size()];
for (var i = 0; i < list.size(); i++) {
var conn = list.get(i);
var newOne = new ArrayList<Connection>();
for (var conn : list) {
var newWeight = conn.weight / sum;
newOne[i] = new Connection(conn.index, newWeight);
newOne.add(new Connection(conn.index, newWeight));
}
this.connections.set(node, List.of(newOne));
this.connections.set(node, newOne);
}
}
@@ -249,6 +244,56 @@ public final class Net implements Iterable<ServerNode> {
}
}
/**
* Create a copy of the net passed as input.
* The new net will have the same nodes and connections.
*
* @param net the net to copy
* @return a new Net object
*/
public static Net copyOf(Net net) {
var newNet = new Net();
for (var index = 0; index < net.size(); index++) {
var node = net.servers.get(index);
var conn = net.connections.get(index);
conn = new ArrayList<>(conn);
newNet.indices.put(node, index);
newNet.servers.add(node);
newNet.connections.add(conn);
}
return newNet;
}
@Override
public String toString() {
var builder = new StringBuilder();
try {
for (var node : this.servers) {
builder.append(node)
.append(" -> ");
for (var child : this.getChildren(this.indices.get(node))) {
var childNode = this.servers.get(child.index);
builder.append(childNode.name)
.append("(")
.append(child.weight)
.append("), ");
}
builder.delete(builder.length() - 2, builder.length())
.append("\n");
}
} catch (Exception e) {
e.printStackTrace();
}
return builder.toString();
}
/**
* A Static inner class used to represent the connection of a node
*/

View File

@@ -18,7 +18,7 @@ public class ServerNode {
/**
* Creates a generic node with the given name and distribution.
* The servers number must be 1 or higher; if lower will be put to 1.
* The spawn number must be 0 or higher; if lower will be put to 0.
* The spawn number must be 0 or higher; if lower will be put to -1 (infinite).
* The queue number must be equal or higher than the servers number; if lower
* will be put to the servers number.
* The service distribution can't be null, otherwise an exception is thrown.
@@ -37,7 +37,7 @@ public class ServerNode {
if (servers <= 0)
servers = 1;
if (spawn < 0)
spawn = 0;
spawn = -1;
if (queue < servers)
queue = servers;
@@ -71,6 +71,28 @@ public class ServerNode {
return Distribution.getPositiveSample(this.unavailable, rng);
}
@Override
public String toString() {
try {
var build = new StringBuilder()
.append(this.name)
.append("[servers:")
.append(this.maxServers)
.append(", queue:")
.append(this.maxQueue)
.append(", spawn:")
.append(this.spawnArrivals)
.append(", ")
.append(Distribution.toString(this.service));
if (this.unavailable != null)
build.append(", u:").append(Distribution.toString(this.unavailable));
return build.append(']').toString();
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ServerNode))
@@ -183,26 +205,27 @@ public class ServerNode {
/**
* Creates a source node with the given name and distribution.
* It swpawns infinite arrivals (Integer.MAX_VALUE).
* It swpawns infinite arrivals (-1).
*
* @param name The name of the node.
* @param distribution The distribution of the inter-arrival times.
* @return The created source node.
*/
public static ServerNode source(String name, Distribution distribution) {
return new Builder(name, distribution).spawn(Integer.MAX_VALUE).build();
return new Builder(name, distribution).spawn(-1).build();
}
/**
* Creates a source node with the given name, distribution, and number of
* Creates a terminal node with the given name, distribution, and number of
* arrivals to spawn.
* Once it has finished spawning the arrivals, it will not spawn anymore.
*
* @param name The name of the node.
* @param service The distribution of the inter-arrival times.
* @param spawnArrivals The number of arrivals to spawn.
* @return The created source node.
*/
public static ServerNode sourceLimited(String name, int spawnArrivals, Distribution service) {
public static ServerNode terminal(String name, int spawnArrivals, Distribution service) {
return new Builder(name, service).spawn(spawnArrivals).build();
}

View File

@@ -69,7 +69,7 @@ public class ServerNodeState {
* @return True if the node should spawn an arrival, false otherwise.
*/
public boolean shouldSpawnArrival() {
return this.node.spawnArrivals > this.stats.numArrivals;
return this.node.spawnArrivals < 0 || this.node.spawnArrivals > this.stats.numArrivals;
}
/**
@@ -158,6 +158,23 @@ public class ServerNodeState {
return null;
}
/**
* Get a random child based on the weights of the children and the random number
* generator passed as input
*
* @param rng the random number generator
* @return the index of the child or -1 if no child is selected
*/
public int getRandomChild(Rng rng) {
var random = rng.random();
for (var child : this.children) {
random -= child.weight;
if (random <= 0)
return child.index;
}
return -1;
}
/**
* Create an arrival event to a child node based on the node and the time passed
* as input
@@ -168,12 +185,7 @@ public class ServerNodeState {
* otherwise
*/
public Event spawnArrivalToChild(double time, Rng rng) {
var random = rng.random();
for (var child : this.children) {
random -= child.weight;
if (random <= 0)
return Event.newArrival(child.index, time);
}
return null;
var child = this.getRandomChild(rng);
return child > -1 ? Event.newArrival(child, time) : null;
}
}

View File

@@ -1,6 +1,5 @@
package net.berack.upo.valpre.sim;
import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
@@ -98,8 +97,13 @@ public final class Simulation {
case DEPARTURE -> {
state.updateDeparture(time);
// Spawn unavailability if has unavailable time
this.addToFel(state.spawnUnavailableIfPossible(time, this.rng));
// Spawn departure if has requests and server is available
this.addToFel(state.spawnDepartureIfPossible(time, this.rng));
// Spawn arrival to self if is source node
this.addToFel(state.spawnArrivalIfPossilbe(time));
// Spawn arrival to child node if queue is not full otherwise drop
@@ -151,7 +155,7 @@ public final class Simulation {
* @return a list of future events.
*/
public List<Event> getFutureEventList() {
return new ArrayList<>(this.fel);
return List.copyOf(this.fel);
}
/**

View File

@@ -1,5 +1,6 @@
package net.berack.upo.valpre.sim;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
@@ -99,16 +100,21 @@ public class SimulationMultiple {
* Run the simulation multiple times with the given seed and end criteria. The
* simulation runs will stop when the relative error of the confidence index is
* less than the given value.
* The results are printed on the console.
* The results are printed on the PrintStream.
*
* @param seed The seed to use for the random number generator.
* @param criterias The criteria to determine when to end the simulation. If
* null then the simulation will run until there are no more
* events.
* @param seed The seed to use for the random number generator.
* @param runs The maximum number of runs to perform.
* @param stream The PrintStream to print the results.
* @param confidences The confidence indices to use to determine when to stop
* the simulation.
* @param criterias The criteria to determine when to end the simulation. If
* null then the simulation will run until there are no more
* events.
* @return The statistics the network.
* @throws IllegalArgumentException If the confidence is not set.
*/
public Result.Summary runIncremental(long seed, int runs, ConfidenceIndices confidences, EndCriteria... criterias) {
public Result.Summary runIncremental(long seed, int runs, PrintStream stream, ConfidenceIndices confidences,
EndCriteria... criterias) {
if (confidences == null)
throw new IllegalArgumentException("Confidence must be not null");
@@ -116,7 +122,8 @@ public class SimulationMultiple {
var results = new Result.Summary(rng.getSeed(), nodes);
var output = new StringBuilder();
var stop = false;
for (int i = 0; !stop; i++) {
for (int i = 0; !stop && runs > i; i++) {
var sim = new Simulation(this.net, rng, criterias);
var result = sim.run();
results.add(result);
@@ -132,12 +139,11 @@ public class SimulationMultiple {
var oneSting = String.join("], [", errString);
output.append('[').append(oneSting).append("]");
System.out.print(output);
stream.print(output);
}
}
System.out.println(); // remove last printed line
stream.println(); // remove last printed line
return results;
}
}

View File

@@ -21,13 +21,27 @@ public class ConsoleTable {
* @throws NullPointerException if the array is null
*/
public ConsoleTable(String... header) {
var max = 0;
this(-1, header);
}
/**
* Create a new table with the header passed as input.
* The table will have as many columns as the length of the header array.
* Each column will have the same size and will be the max length of all the
* headers string or the maxLen passed as input.
*
* @param maxLen the max length of the columns
* @param header an array of strings
* @throws NullPointerException if the array is null
*/
public ConsoleTable(int maxLen, String... header) {
var max = Math.max(0, maxLen);
for (var name : header)
max = Math.max(max, name.length());
this.columns = header.length;
this.maxLen = max + 2;
this.border = ("+" + "".repeat(maxLen)).repeat(header.length) + "+\n";
this.border = ("+" + "".repeat(this.maxLen)).repeat(header.length) + "+\n";
this.builder.append(border);
this.addRow(header);
}
@@ -45,11 +59,11 @@ public class ConsoleTable {
for (var val : values) {
var diff = maxLen - val.length();
var first = (int) Math.ceil(diff / 2.0);
var first = Math.max((int) Math.ceil(diff / 2.0), 0);
builder.append('║');
builder.append(" ".repeat(first));
builder.append(val);
builder.append(" ".repeat(diff - first));
builder.append(" ".repeat(Math.max(diff - first, 0)));
}
builder.append("\n");

View File

@@ -298,7 +298,8 @@ public class NodeStats implements Cloneable, Iterable<Double> {
public NodeStats calcError(NodeStats alpha) {
var n = this.stats.size();
var distr = new TDistribution(null, n - 1);
var tValue = alpha.clone().apply(a -> distr.inverseCumulativeProbability(a));
var prob = alpha.clone().apply(a -> 1 - (1 - a) / 2.0d);
var tValue = prob.apply(p -> distr.inverseCumulativeProbability(p));
return this.stdDev().merge(tValue, (std, t) -> t * (std / Math.sqrt(n)));
}

View File

@@ -1,6 +1,7 @@
package net.berack.upo.valpre.sim.stats;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -36,6 +37,11 @@ public class Result implements Iterable<Entry<String, NodeStats>> {
this.stats = stats;
}
/**
* Gets the stats of a node.
*
* @return the stats of a node
*/
public NodeStats getStat(String node) {
for (var i = 0; i < this.nodes.length; i++)
if (this.nodes[i].equals(node))
@@ -45,7 +51,7 @@ public class Result implements Iterable<Entry<String, NodeStats>> {
@Override
public String toString() {
return buildPrintable(this.seed, this.simulationTime, this.timeElapsedMS, this.nodes, this.stats);
return getResultString(this.seed, this.simulationTime, this.timeElapsedMS, this.nodes, this.stats);
}
@Override
@@ -80,9 +86,8 @@ public class Result implements Iterable<Entry<String, NodeStats>> {
* @param stats the stats of each node
* @return a string representation of the result
*/
private static String buildPrintable(long seed, double simTime, double timeMS, String[] nodes, NodeStats[] stats) {
public static String getResultString(long seed, double simTime, double timeMS, String[] nodes, NodeStats[] stats) {
var size = (int) Math.ceil(Math.max(Math.log10(simTime), 1));
var iFormat = "%" + size + ".0f";
var fFormat = "%" + (size + 4) + ".3f";
var builder = new StringBuilder();
@@ -90,9 +95,35 @@ public class Result implements Iterable<Entry<String, NodeStats>> {
builder.append(String.format("Seed: \t%d\n", seed));
builder.append(String.format("Simulation: \t" + fFormat + "\n", simTime));
builder.append(String.format("Elapsed: \t" + fFormat + "ms\n", timeMS));
builder.append(getResultString(nodes, stats));
return builder.toString();
}
var table = new ConsoleTable("Node", "Departures", "Avg Queue", "Avg Wait", "Avg Response", "Throughput",
"Utilization %", "Unavailable %", "Last Event");
/**
* Create a string representation of the result. It only display the stats of
* each node in a table format.
*
* @param nodes the names of the nodes
* @param stats the stats of each node
* @return a string representation of the result
* @throws AssertionError if the nodes and stats do not match
*/
public static String getResultString(String[] nodes, NodeStats[] stats) {
assert nodes.length == stats.length;
var size = (int) Math.ceil(Math.max(Math.log10(stats[0].lastEventTime), 1));
var iFormat = "%" + size + ".0f";
var fFormat = "%" + (size + 4) + ".3f";
var builder = new StringBuilder();
var maxNameLen = 0;
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
maxNameLen = Math.max(node.length(), maxNameLen);
}
var table = new ConsoleTable(maxNameLen, "Node", "Departures", "Avg Queue", "Avg Wait", "Avg Response",
"Throughput", "Utilization %", "Unavailable %", "Last Event");
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
@@ -206,12 +237,24 @@ public class Result implements Iterable<Entry<String, NodeStats>> {
}
/**
* Gets the nodes of the summary.
* Gets a copy of the nodes of the summary.
*
* @return the nodes of the summary
*/
public List<String> getNodes() {
return List.of(this.nodes);
public String[] getNodes() {
return Arrays.copyOf(this.nodes, this.nodes.length);
}
/**
* Gets the statistics of the nodes of the summary.
*
* @return the statistics of the nodes of the summary
*/
public NodeStats[] getStats() {
var stats = new NodeStats[this.nodes.length];
for (var i = 0; i < this.nodes.length; i++)
stats[i] = this.stats[i].average;
return stats;
}
/**
@@ -239,11 +282,8 @@ public class Result implements Iterable<Entry<String, NodeStats>> {
@Override
public String toString() {
var stats = new NodeStats[this.nodes.length];
for (var i = 0; i < this.nodes.length; i++)
stats[i] = this.stats[i].average;
return buildPrintable(this.seed, this.avgSimulationTime, this.avgTimeElapsedMS, this.nodes, stats);
return getResultString(this.seed, this.avgSimulationTime, this.avgTimeElapsedMS, this.nodes,
this.getStats());
}
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -55,4 +55,23 @@ public class TestRandom {
assertTrue("Standard Dev must be less than [" + expected + "] -> [" + stdDev + "]", stdDev < expected);
}
@Test
public void testMean() {
var rng = new Rng();
var normal = new Distribution.NormalBoxMuller(1 / 3.5, 0.6);
var mean = 0.0;
for (var i = 0; i < 100000; i++) {
var sample = Distribution.getPositiveSample(normal, rng);
mean = (mean * (i + 1) + sample) / (i + 2);
}
assertEquals(0.6, mean, 0.01);
for (var i = 0; i < 100000; i++) {
var sample = Math.max(0, normal.sample(rng));
mean = (mean * (i + 1) + sample) / (i + 2);
}
assertEquals(0.41, mean, 0.01);
}
}

View File

@@ -0,0 +1,187 @@
package net.berack.upo.valpre.sim;
import static org.junit.Assert.assertEquals;
import java.io.ByteArrayInputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import org.junit.Test;
import net.berack.upo.valpre.InteractiveConsole;
import net.berack.upo.valpre.rand.Distribution;
public class TestInteractions {
@Test
public void distributionToString() throws Exception {
var exp = new Distribution.Exponential(1.0);
assertEquals("Exponential(1.0)", Distribution.toString(exp));
var uniform = new Distribution.Uniform(0.0, 1.0);
assertEquals("Uniform(0.0, 1.0)", Distribution.toString(uniform));
var erlang = new Distribution.Erlang(2, 1.0);
assertEquals("Erlang(2, 1.0)", Distribution.toString(erlang));
var normal = new Distribution.Normal(3.2, 0.6);
assertEquals("Normal(3.2, 0.6)", Distribution.toString(normal));
var normalBoxMuller = new Distribution.NormalBoxMuller(3.2, 0.6);
assertEquals("NormalBoxMuller(3.2, 0.6)", Distribution.toString(normalBoxMuller));
var unavailable = new Distribution.UnavailableTime(0.1, exp);
assertEquals("UnavailableTime(0.1, Exponential(1.0))", Distribution.toString(unavailable));
}
@Test
public void nodeToString() {
var exp = new Distribution.Exponential(1.0);
var normal = new Distribution.Normal(3.2, 0.6);
var unavailable = new Distribution.UnavailableTime(0.1, exp);
var node = new ServerNode.Builder("Source", exp).build();
assertEquals("Source[servers:1, queue:100, spawn:0, Exponential(1.0)]", node.toString());
node = new ServerNode.Builder("Queue", normal).build();
assertEquals("Queue[servers:1, queue:100, spawn:0, Normal(3.2, 0.6)]", node.toString());
node = new ServerNode.Builder("Queue", normal).queue(10).servers(5).spawn(100).build();
assertEquals("Queue[servers:5, queue:10, spawn:100, Normal(3.2, 0.6)]", node.toString());
node = new ServerNode.Builder("Queue", normal).queue(10).servers(5).spawn(100).unavailable(unavailable).build();
assertEquals(
"Queue[servers:5, queue:10, spawn:100, Normal(3.2, 0.6), u:UnavailableTime(0.1, Exponential(1.0))]",
node.toString());
}
@Test
public void netToString() {
var net = new Net();
assertEquals("", net.toString());
var node1 = new ServerNode.Builder("Source", new Distribution.Exponential(1)).build();
net.addNode(node1);
assertEquals(node1 + " -\n", net.toString());
var node2 = new ServerNode.Builder("Server", new Distribution.Normal(3.2, 0.6)).build();
net.addNode(node2);
assertEquals(node1 + " -\n" + node2 + " -\n", net.toString());
net.addConnection(0, 1, 1.0);
assertEquals(node1 + " -> Server(1.0)\n" + node2 + " -\n", net.toString());
var node3 = new ServerNode.Builder("Server2", new Distribution.Normal(4.1, 0.1)).build();
net.addNode(node3);
assertEquals(node1 + " -> Server(1.0)\n" + node2 + " -\n" + node3 + " -\n", net.toString());
net.addConnection(0, 2, 1.0);
net.normalizeWeights();
assertEquals(node1 + " -> Server(0.5), Server2(0.5)\n" + node2 + " -\n" + node3 + " -\n", net.toString());
net.addConnection(1, 2, 1.0);
assertEquals(node1 + " -> Server(0.5), Server2(0.5)\n"
+ node2 + " -> Server2(1.0)\n"
+ node3 + " -\n",
net.toString());
var const0 = new Distribution.Uniform(0, 0);
var unavailable = new Distribution.UnavailableTime(0.1, new Distribution.Exponential(1));
var other = ServerNode.Builder.queue("Other", 1, const0, unavailable);
net.addNode(other);
assertEquals(node1 + " -> Server(0.5), Server2(0.5)\n"
+ node2 + " -> Server2(1.0)\n"
+ node3 + " -\n"
+ other + " -\n",
net.toString());
}
@Test(timeout = 1000)
public void netBuilderInteractive() {
// Test the interactive console EXIT
var net = runInteraction("7");
assertEquals("", net.toString());
// Test the interactive console ADD NODE
net = runInteraction("1", "1", "Source", "1", "1.0", "7");
assertEquals("Source[servers:1, queue:100, spawn:-1, Exponential(1.0)] -\n", net.toString());
// Test the interactive console ADD SECOND NODE
net = runInteraction("1", "2", "Terminal", "1", "2.0", "500",
"1", "3", "Queue", "5", "3.2", "0.6", "1",
"7");
assertEquals("Terminal[servers:1, queue:100, spawn:500, Exponential(2.0)] -\n"
+ "Queue[servers:1, queue:100, spawn:0, Normal(3.2, 0.6)] -\n", net.toString());
// Test the interactive console ADD CONNECTION
net = runInteraction("1", "1", "Source", "1", "2.0",
"1", "3", "Queue", "5", "3.2", "0.6", "1",
"2", "Source", "Queue", "1.0",
"7");
assertEquals("Source[servers:1, queue:100, spawn:-1, Exponential(2.0)] -> Queue(1.0)\n"
+ "Queue[servers:1, queue:100, spawn:0, Normal(3.2, 0.6)] -\n", net.toString());
// Test the interactive console CLEAR
net = runInteraction("1", "1", "Source", "1", "2.0",
"1", "3", "Queue", "5", "3.2", "0.6", "1",
"2", "Source", "Queue", "1.0",
"2", "Queue", "Queue", "1.0",
"5", "7");
assertEquals("", net.toString());
// Test the interactive console LOAD
net = runInteraction("4", "1", "src/test/resources/example1.net", "7");
assertEquals("Source[servers:1, queue:100, spawn:10000, Exponential(0.2222222222222222)] -> Queue(1.0)\n"
+ "Queue[servers:1, queue:100, spawn:0, NormalBoxMuller(3.2, 0.6)] -\n",
net.toString());
// Test the interactive console LOAD EXAMPLE 1
net = runInteraction("4", "2", "1", "7");
assertEquals("Source[servers:1, queue:100, spawn:10000, Exponential(0.2222222222222222)] -> Queue(1.0)\n"
+ "Queue[servers:1, queue:100, spawn:0, NormalBoxMuller(3.2, 0.6)] -\n",
net.toString());
// Test the interactive console LOAD EXAMPLE 2
net = runInteraction("4", "2", "2", "7");
assertEquals("Source[servers:1, queue:100, spawn:10000, Exponential(1.5)] -> Service(1.0)\n"
+ "Service[servers:1, queue:100, spawn:0, Exponential(2.0)] -> Service2(1.0)\n"
+ "Service2[servers:1, queue:100, spawn:0, Exponential(3.5), u:UnavailableTime(0.1, Exponential(10.0))] -\n",
net.toString());
}
private static Net runInteraction(String... commands) {
var out = new PrintStream(OutputStream.nullOutputStream());
var inputs = String.join("\n", commands);
var bytes = inputs.getBytes();
var in = new ByteArrayInputStream(bytes);
return new InteractiveConsole(out, in).run();
}
/*
* An interaction example is like this:
* 1. Add a node
* - Choose the type of node to create:
* - 1. Source
* - 2. Queue
* - 3. Queue with unavailable time
* - Node name: Name
* - Arrivals limit (0 for Int.Max) / Number of servers: 1
* - Choose the type of service distribution:
* - - 1. Exponential
* - - 2. Uniform
* - - 3. Erlang
* - - 4. UnavailableTime
* - - 5. Normal
* - - 6. NormalBoxMuller
* - - 7. None
* 2. Add a connection
* - Enter the source node: Source
* - Enter the target node: Queue
* - Enter the weight: 1.0
* 3. Save the net
* 4. Load net
* 5. Clear
* 6. Exit
*/
}

View File

@@ -4,6 +4,8 @@ import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.HashSet;
import org.junit.Test;
import org.junit.jupiter.api.AfterAll;
@@ -11,61 +13,26 @@ import org.junit.jupiter.api.BeforeAll;
import com.esotericsoftware.kryo.KryoException;
import net.berack.upo.valpre.NetExamples;
import net.berack.upo.valpre.SimulationBuilder;
import net.berack.upo.valpre.rand.Distribution;
import net.berack.upo.valpre.rand.Rng;
import net.berack.upo.valpre.sim.stats.CsvResult;
import net.berack.upo.valpre.sim.stats.NodeStats;
public class TestSaveExamplesNet {
private static final Distribution exp0_22 = new Distribution.Exponential(1.0 / 4.5);
private static final Distribution exp2 = new Distribution.Exponential(2.0);
private static final Distribution exp1_5 = new Distribution.Exponential(1.5);
private static final Distribution exp3_5 = new Distribution.Exponential(3.5);
private static final Distribution exp10 = new Distribution.Exponential(10.0);
private static final Distribution norm3_2 = new Distribution.NormalBoxMuller(3.2, 0.6);
private static final Distribution norm4_2 = new Distribution.NormalBoxMuller(4.2, 0.6);
private static final Distribution unNorm = new Distribution.UnavailableTime(0.2, norm4_2);
private static final Distribution unExp = new Distribution.UnavailableTime(0.1, exp10);
private static final int spawn = 10000;
private static final String path = "src/main/resources/example%d.%s";
private static final String path = "src/test/resources/example%d.%s";
private static final String netFile1 = path.formatted(1, "net");
private static final String netFile2 = path.formatted(2, "net");
private static final String netFile3 = path.formatted(3, "net");
private static final String csv1 = path.formatted(1, "csv");
private static final String csv2 = path.formatted(2, "csv");
private static final String csv3 = path.formatted(3, "csv");
private static final Net net1 = new Net();
private static final Net net2 = new Net();
private static final Net net3 = new Net();
static {
net1.addNode(ServerNode.Builder.sourceLimited("Source", spawn, exp0_22));
net1.addNode(ServerNode.Builder.queue("Queue", 1, norm3_2));
net1.addConnection(0, 1, 1.0);
net2.addNode(ServerNode.Builder.sourceLimited("Source", spawn, exp0_22));
net2.addNode(ServerNode.Builder.queue("Queue", 1, norm3_2));
net2.addNode(ServerNode.Builder.queue("Queue Wait", 1, norm3_2, unNorm));
net2.addConnection(0, 1, 1.0);
net2.addConnection(1, 2, 1.0);
net3.addNode(ServerNode.Builder.sourceLimited("Source", spawn, exp1_5));
net3.addNode(ServerNode.Builder.queue("Service1", 1, exp2));
net3.addNode(ServerNode.Builder.queue("Service2", 1, exp3_5, unExp));
net3.addConnection(0, 1, 1.0);
net3.addConnection(1, 2, 1.0);
}
private static final Net net1 = NetExamples.getNet1();
private static final Net net2 = NetExamples.getNet2();
@BeforeAll
public void saveAll() throws IOException {
net1.save(netFile1);
net2.save(netFile2);
net3.save(netFile3);
}
@Test
@@ -85,20 +52,6 @@ public class TestSaveExamplesNet {
public void loadExample2() throws KryoException, IOException {
var sim = new Simulation(Net.load(netFile2), new Rng());
var res = sim.run();
var time = 45417.0;
var maxErr = time / 1000.0;
assertEquals(Rng.DEFAULT, res.seed);
assertEquals(time, res.simulationTime, maxErr);
testNode(res.getStat("Source"), 10000, time, 1.0, 4.5, 0.0, 0.0);
testNode(res.getStat("Queue"), 10000, time, 2.6, 7.2, 4.0, 0.0);
testNode(res.getStat("Queue Wait"), 10000, time, 5.8, 22.3, 19.1, 8497.7);
}
@Test
public void loadExample3() throws KryoException, IOException {
var sim = new Simulation(Net.load(netFile3), new Rng());
var res = sim.run();
var time = 6736.0;
var maxErr = time / 1000.0;
@@ -153,33 +106,26 @@ public class TestSaveExamplesNet {
@Test
@AfterAll
public void multiSimulation1() throws Exception {
new SimulationBuilder(net1)
.setCsv(csv1)
.setMaxRuns(1000)
.setSeed(2007539552L)
.setParallel(true)
.run();
try (var newOut = new PrintStream(OutputStream.nullOutputStream())) {
new SimulationBuilder(net1)
.setCsv(csv1)
.setMaxRuns(1000)
.setSeed(2007539552L)
.setParallel(true)
.run(newOut);
}
}
@Test
@AfterAll
public void multiSimulation2() throws Exception {
new SimulationBuilder(net2)
.setCsv(csv2)
.setMaxRuns(1000)
.setSeed(2007539552L)
.setParallel(true)
.run();
}
@Test
@AfterAll
public void multiSimulation3() throws Exception {
new SimulationBuilder(net3)
.setCsv(csv3)
.setMaxRuns(1000)
.setSeed(2007539552L)
.setParallel(true)
.run();
try (var newOut = new PrintStream(OutputStream.nullOutputStream())) {
new SimulationBuilder(net2)
.setCsv(csv2)
.setMaxRuns(1000)
.setSeed(2007539552L)
.setParallel(true)
.run(newOut);
}
}
}

View File

@@ -5,6 +5,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.HashSet;
@@ -23,7 +24,7 @@ public class TestSimulation {
private static final ServerNode node0;
private static final ServerNode node1;
static {
node0 = ServerNode.Builder.sourceLimited("First", 0, const1);
node0 = ServerNode.Builder.terminal("First", 0, const1);
node1 = ServerNode.Builder.queue("Second", 1, const1);
simpleNet = new Net();
@@ -56,10 +57,10 @@ public class TestSimulation {
node = ServerNode.Builder.source("Source", const1);
assertEquals("Source", node.name);
assertEquals(1, node.maxServers);
assertEquals(Integer.MAX_VALUE, node.spawnArrivals);
assertEquals(-1, node.spawnArrivals);
assertEquals(1.0, node.getServiceTime(null), DELTA);
node = ServerNode.Builder.sourceLimited("Source", 50, const1);
node = ServerNode.Builder.terminal("Source", 50, const1);
assertEquals("Source", node.name);
assertEquals(1, node.maxServers);
assertEquals(50, node.spawnArrivals);
@@ -196,7 +197,7 @@ public class TestSimulation {
@Test
public void nodeStatsUpdates() {
var net = new Net();
net.addNode(ServerNode.Builder.sourceLimited("Source", 50, const1));
net.addNode(ServerNode.Builder.terminal("Source", 50, const1));
net.addNode(node1);
net.addConnection(0, 1, 1.0);
@@ -393,13 +394,23 @@ public class TestSimulation {
@Test
public void simulation() {
var start = System.nanoTime();
assertThrows(NullPointerException.class, () -> new Simulation(null, rigged));
assertThrows(NullPointerException.class, () -> new Simulation(simpleNet, null));
var sim = new Simulation(simpleNet, rigged);
assertTrue(sim.hasEnded());
assertEquals(0, sim.getEventsProcessed());
assertEquals(0.0, sim.getTime(), DELTA);
var fel = sim.getFutureEventList();
assertEquals(0, fel.size());
var start = System.nanoTime();
sim = new Simulation(simpleNet, rigged);
// knowing that it takes time to allocate the object
// we can use the average time
var endAllocation = System.nanoTime();
var time = (endAllocation + start) / 2;
var diff = 0.5e-6 * (endAllocation - start); // getting the error margin in ms
var diff = 1e-6 * (endAllocation - start); // getting the error margin in ms
assertTrue(sim.hasEnded());
assertEquals(0, sim.getEventsProcessed());
@@ -410,7 +421,7 @@ public class TestSimulation {
assertEquals(0, sim.getNodeState(node0.name).numServerUnavailable);
assertEquals(0, sim.getNodeState(node1.name).numServerBusy);
assertEquals(0, sim.getNodeState(node1.name).numServerUnavailable);
var fel = sim.getFutureEventList();
fel = sim.getFutureEventList();
assertEquals(0, fel.size());
sim.addToFel(Event.newArrival(0, sim.getTime()));
@@ -489,6 +500,8 @@ public class TestSimulation {
assertEquals(0, sim.getNodeState(node1.name).numServerUnavailable);
fel = sim.getFutureEventList();
assertEquals(0, fel.size());
final var s = sim;
assertThrows(NullPointerException.class, () -> s.processNextEvent());
var elapsed = (double) (System.nanoTime() - time);
var result = sim.endSimulation();
@@ -529,7 +542,7 @@ public class TestSimulation {
@Test
public void simulationStats() {
var net = new Net();
net.addNode(ServerNode.Builder.sourceLimited("Source", 50, const1));
net.addNode(ServerNode.Builder.terminal("Source", 50, const1));
var sim = new Simulation(net, rigged);
var result = sim.run();
@@ -581,7 +594,7 @@ public class TestSimulation {
@Test
public void simulationDrop() {
var net = new Net();
net.addNode(ServerNode.Builder.sourceLimited("Source", 50, const1));
net.addNode(ServerNode.Builder.terminal("Source", 50, const1));
net.addNode(new ServerNode.Builder("Queue", _ -> 2.0).queue(20).build());
net.addConnection(0, 1, 1.0);

View File

@@ -0,0 +1,146 @@
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<archive xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="example1.jsimg" timestamp="Thu Mar 13 11:14:34 CET 2025" xsi:noNamespaceSchemaLocation="Archive.xsd">
<sim disableStatisticStop="false" logDecimalSeparator="," logDelimiter=";" logPath="C:\Users\giaco\JMT\" logReplaceMode="0" maxEvents="-1" maxSamples="1000000" name="example1.jsimg" polling="1.0" xsi:noNamespaceSchemaLocation="SIMmodeldefinition.xsd">
<userClass name="Class1" priority="0" referenceSource="Source " softDeadline="0.0" type="open"/>
<node name="Source ">
<section className="RandomSource">
<parameter array="true" classPath="jmt.engine.NetStrategies.ServiceStrategy" name="ServiceStrategy">
<refClass>Class1</refClass>
<subParameter classPath="jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy" name="ServiceTimeStrategy">
<subParameter classPath="jmt.engine.random.Exponential" name="Exponential"/>
<subParameter classPath="jmt.engine.random.ExponentialPar" name="distrPar">
<subParameter classPath="java.lang.Double" name="lambda">
<value>0.2222222222222222</value>
</subParameter>
</subParameter>
</subParameter>
</parameter>
</section>
<section className="ServiceTunnel"/>
<section className="Router">
<parameter array="true" classPath="jmt.engine.NetStrategies.RoutingStrategy" name="RoutingStrategy">
<refClass>Class1</refClass>
<subParameter classPath="jmt.engine.NetStrategies.RoutingStrategies.RandomStrategy" name="Random"/>
</parameter>
</section>
</node>
<node name="Sink">
<section className="JobSink"/>
</node>
<node name="Queue">
<classSoftDeadlines>
<softDeadline>0.0</softDeadline>
</classSoftDeadlines>
<quantumSize>
<quantaSize>0.0</quantaSize>
</quantumSize>
<quantumSwitchoverTime>
<quantumSwitchoverTime>0.0</quantumSwitchoverTime>
</quantumSwitchoverTime>
<section className="Queue">
<parameter classPath="java.lang.Integer" name="size">
<value>-1</value>
</parameter>
<parameter array="true" classPath="java.lang.String" name="dropStrategies">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.String" name="dropStrategy">
<value>drop</value>
</subParameter>
</parameter>
<parameter classPath="jmt.engine.NetStrategies.QueueGetStrategies.FCFSstrategy" name="FCFSstrategy"/>
<parameter array="true" classPath="jmt.engine.NetStrategies.QueuePutStrategy" name="QueuePutStrategy">
<refClass>Class1</refClass>
<subParameter classPath="jmt.engine.NetStrategies.QueuePutStrategies.TailStrategy" name="TailStrategy"/>
</parameter>
</section>
<section className="Server">
<parameter classPath="java.lang.Integer" name="maxJobs">
<value>1</value>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="numberOfVisits">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="numberOfVisits">
<value>1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="jmt.engine.NetStrategies.ServiceStrategy" name="ServiceStrategy">
<refClass>Class1</refClass>
<subParameter classPath="jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy" name="ServiceTimeStrategy">
<subParameter classPath="jmt.engine.random.Normal" name="Normal"/>
<subParameter classPath="jmt.engine.random.NormalPar" name="distrPar">
<subParameter classPath="java.lang.Double" name="mean">
<value>3.2</value>
</subParameter>
<subParameter classPath="java.lang.Double" name="standardDeviation">
<value>0.6000000000000001</value>
</subParameter>
</subParameter>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="classParallelism">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="serverParallelism">
<value>1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.String" name="serverNames">
<subParameter classPath="java.lang.String" name="serverTypesNames">
<value>Queue - Server Type 1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="serversPerServerType">
<subParameter classPath="java.lang.Integer" name="serverTypesNumOfServers">
<value>1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.Object" name="serverCompatibilities">
<subParameter array="true" classPath="java.lang.Boolean" name="serverTypesCompatibilities">
<subParameter classPath="java.lang.Boolean" name="compatibilities">
<value>true</value>
</subParameter>
</subParameter>
</parameter>
<parameter classPath="java.lang.String" name="schedulingPolicy">
<value>ALIS (Assign Longest Idle Server)</value>
</parameter>
</section>
<section className="Router">
<parameter array="true" classPath="jmt.engine.NetStrategies.RoutingStrategy" name="RoutingStrategy">
<refClass>Class1</refClass>
<subParameter classPath="jmt.engine.NetStrategies.RoutingStrategies.RandomStrategy" name="Random"/>
</parameter>
</section>
</node>
<measure alpha="0.01" name="Queue_Class1_Response Time" nodeType="station" precision="0.03" referenceNode="Queue" referenceUserClass="Class1" type="Response Time" verbose="false"/>
<measure alpha="0.01" name="Queue_Class1_Utilization" nodeType="station" precision="0.03" referenceNode="Queue" referenceUserClass="Class1" type="Utilization" verbose="false"/>
<measure alpha="0.01" name="Queue_Class1_Throughput" nodeType="station" precision="0.03" referenceNode="Queue" referenceUserClass="Class1" type="Throughput" verbose="false"/>
<connection source="Source " target="Queue"/>
<connection source="Queue" target="Sink"/>
</sim>
<jmodel xsi:noNamespaceSchemaLocation="JModelGUI.xsd">
<userClass color="#FF0000FF" name="Class1"/>
<station name="Source ">
<position angle="0.0" rotate="false" x="73.0" y="139.0"/>
</station>
<station name="Sink">
<position angle="0.0" rotate="false" x="354.0" y="139.0"/>
</station>
<station name="Queue">
<position angle="0.0" rotate="false" x="191.0" y="136.0"/>
</station>
</jmodel>
<results elapsedTime="932" logDecimalSeparator="," logDelimiter=";" pollingInterval="1.0" xsi:noNamespaceSchemaLocation="Results.xsd">
<measure alpha="0.99" analyzedSamples="245760" discardedSamples="390" finalValue="7.30224421202774" name="Queue_Class1_Response Time" nodeType="station" precision="0.03" referenceClass="Class1" referenceStation="Queue" state="1" type="2">
<sample lastIntervalAvgValue="7.171346385053164" lowerBound="6.925342616994286" meanValue="7.2654952908722095" simulationTime="349360.1710046531" upperBound="7.6056479647501325"/>
<sample lastIntervalAvgValue="7.340886944129393" lowerBound="7.145587638668616" meanValue="7.30224421202774" simulationTime="1105542.162869484" upperBound="7.458900785386865"/>
</measure>
<measure alpha="0.99" analyzedSamples="56320" discardedSamples="1755" finalValue="0.7110892727677632" name="Queue_Class1_Utilization" nodeType="station" precision="0.03" referenceClass="Class1" referenceStation="Queue" state="1" type="6">
<sample lastIntervalAvgValue="0.7121434625959978" lowerBound="0.6959357730746889" meanValue="0.7110892727677632" simulationTime="202809.54195132584" upperBound="0.7262427724608376"/>
<sample lastIntervalAvgValue="0.7121434625959978" lowerBound="0.6959357730746889" meanValue="0.7110892727677632" simulationTime="202809.54195132584" upperBound="0.7262427724608376"/>
</measure>
<measure alpha="0.99" analyzedSamples="51200" discardedSamples="180" finalValue="0.22256617058473832" name="Queue_Class1_Throughput" nodeType="station" precision="0.03" referenceClass="Class1" referenceStation="Queue" state="1" type="5">
<sample lastIntervalAvgValue="0.22277397312289457" lowerBound="0.21822871906729738" meanValue="0.22256617058473832" simulationTime="230637.3553415772" upperBound="0.22707953845333897"/>
<sample lastIntervalAvgValue="0.22277397312289457" lowerBound="0.21822871906729738" meanValue="0.22256617058473832" simulationTime="230637.3553415772" upperBound="0.22707953845333897"/>
</measure>
</results>
</archive>

View File

@@ -0,0 +1,997 @@
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<archive xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="example2.jsimg" timestamp="Wed Apr 02 14:09:03 CEST 2025" xsi:noNamespaceSchemaLocation="Archive.xsd">
<sim disableStatisticStop="false" logDecimalSeparator="," logDelimiter=";" logPath="C:\Users\giaco\JMT" logReplaceMode="0" maxEvents="-1" maxSamples="100000000" name="example2.jsimg" polling="1.0" xsi:noNamespaceSchemaLocation="SIMmodeldefinition.xsd">
<userClass name="Class1" priority="0" referenceSource="Source 1" softDeadline="0.0" type="open"/>
<userClass customers="1" name="Class2" priority="0" referenceSource="IdleServer2" softDeadline="0.0" type="closed"/>
<node name="Service1">
<classSoftDeadlines>
<softDeadline>0.0</softDeadline>
<softDeadline>0.0</softDeadline>
</classSoftDeadlines>
<quantumSize>
<quantaSize>0.0</quantaSize>
</quantumSize>
<quantumSwitchoverTime>
<quantumSwitchoverTime>0.0</quantumSwitchoverTime>
</quantumSwitchoverTime>
<section className="Queue">
<parameter classPath="java.lang.Integer" name="size">
<value>-1</value>
</parameter>
<parameter array="true" classPath="java.lang.String" name="dropStrategies">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.String" name="dropStrategy">
<value>drop</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.String" name="dropStrategy">
<value>drop</value>
</subParameter>
</parameter>
<parameter classPath="jmt.engine.NetStrategies.QueueGetStrategies.FCFSstrategy" name="FCFSstrategy"/>
<parameter array="true" classPath="jmt.engine.NetStrategies.QueuePutStrategy" name="QueuePutStrategy">
<refClass>Class1</refClass>
<subParameter classPath="jmt.engine.NetStrategies.QueuePutStrategies.TailStrategy" name="TailStrategy"/>
<refClass>Class2</refClass>
<subParameter classPath="jmt.engine.NetStrategies.QueuePutStrategies.TailStrategy" name="TailStrategy"/>
</parameter>
</section>
<section className="Server">
<parameter classPath="java.lang.Integer" name="maxJobs">
<value>1</value>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="numberOfVisits">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="numberOfVisits">
<value>1</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="numberOfVisits">
<value>1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="jmt.engine.NetStrategies.ServiceStrategy" name="ServiceStrategy">
<refClass>Class1</refClass>
<subParameter classPath="jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy" name="ServiceTimeStrategy">
<subParameter classPath="jmt.engine.random.Exponential" name="Exponential"/>
<subParameter classPath="jmt.engine.random.ExponentialPar" name="distrPar">
<subParameter classPath="java.lang.Double" name="lambda">
<value>2.0</value>
</subParameter>
</subParameter>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy" name="ServiceTimeStrategy">
<subParameter classPath="jmt.engine.random.Exponential" name="Exponential"/>
<subParameter classPath="jmt.engine.random.ExponentialPar" name="distrPar">
<subParameter classPath="java.lang.Double" name="lambda">
<value>1.0</value>
</subParameter>
</subParameter>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="classParallelism">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="serverParallelism">
<value>1</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="serverParallelism">
<value>1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.String" name="serverNames">
<subParameter classPath="java.lang.String" name="serverTypesNames">
<value>Service1 - Server Type 1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="serversPerServerType">
<subParameter classPath="java.lang.Integer" name="serverTypesNumOfServers">
<value>1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.Object" name="serverCompatibilities">
<subParameter array="true" classPath="java.lang.Boolean" name="serverTypesCompatibilities">
<subParameter classPath="java.lang.Boolean" name="compatibilities">
<value>true</value>
</subParameter>
<subParameter classPath="java.lang.Boolean" name="compatibilities">
<value>true</value>
</subParameter>
</subParameter>
</parameter>
<parameter classPath="java.lang.String" name="schedulingPolicy">
<value>ALIS (Assign Longest Idle Server)</value>
</parameter>
</section>
<section className="Router">
<parameter array="true" classPath="jmt.engine.NetStrategies.RoutingStrategy" name="RoutingStrategy">
<refClass>Class1</refClass>
<subParameter classPath="jmt.engine.NetStrategies.RoutingStrategies.RandomStrategy" name="Random"/>
<refClass>Class2</refClass>
<subParameter classPath="jmt.engine.NetStrategies.RoutingStrategies.RandomStrategy" name="Random"/>
</parameter>
</section>
</node>
<node name="Queue2">
<section className="Storage">
<parameter classPath="java.lang.Integer" name="totalCapacity">
<value>-1</value>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="capacities">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="capacity">
<value>-1</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="capacity">
<value>-1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.String" name="dropRules">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.String" name="dropRule">
<value>drop</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.String" name="dropRule">
<value>drop</value>
</subParameter>
</parameter>
<parameter classPath="jmt.engine.NetStrategies.QueueGetStrategies.FCFSstrategy" name="getStrategy"/>
<parameter array="true" classPath="jmt.engine.NetStrategies.QueuePutStrategy" name="putStrategies">
<refClass>Class1</refClass>
<subParameter classPath="jmt.engine.NetStrategies.QueuePutStrategies.TailStrategy" name="putStrategy"/>
<refClass>Class2</refClass>
<subParameter classPath="jmt.engine.NetStrategies.QueuePutStrategies.TailStrategy" name="putStrategy"/>
</parameter>
</section>
<section className="ServiceTunnel"/>
<section className="Linkage"/>
</node>
<node name="Busy2">
<section className="Storage">
<parameter classPath="java.lang.Integer" name="totalCapacity">
<value>-1</value>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="capacities">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="capacity">
<value>-1</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="capacity">
<value>-1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.String" name="dropRules">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.String" name="dropRule">
<value>drop</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.String" name="dropRule">
<value>drop</value>
</subParameter>
</parameter>
<parameter classPath="jmt.engine.NetStrategies.QueueGetStrategies.FCFSstrategy" name="getStrategy"/>
<parameter array="true" classPath="jmt.engine.NetStrategies.QueuePutStrategy" name="putStrategies">
<refClass>Class1</refClass>
<subParameter classPath="jmt.engine.NetStrategies.QueuePutStrategies.TailStrategy" name="putStrategy"/>
<refClass>Class2</refClass>
<subParameter classPath="jmt.engine.NetStrategies.QueuePutStrategies.TailStrategy" name="putStrategy"/>
</parameter>
</section>
<section className="ServiceTunnel"/>
<section className="Linkage"/>
</node>
<node name="IdleServer2">
<section className="Storage">
<parameter classPath="java.lang.Integer" name="totalCapacity">
<value>-1</value>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="capacities">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="capacity">
<value>-1</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="capacity">
<value>-1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.String" name="dropRules">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.String" name="dropRule">
<value>drop</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.String" name="dropRule">
<value>drop</value>
</subParameter>
</parameter>
<parameter classPath="jmt.engine.NetStrategies.QueueGetStrategies.FCFSstrategy" name="getStrategy"/>
<parameter array="true" classPath="jmt.engine.NetStrategies.QueuePutStrategy" name="putStrategies">
<refClass>Class1</refClass>
<subParameter classPath="jmt.engine.NetStrategies.QueuePutStrategies.TailStrategy" name="putStrategy"/>
<refClass>Class2</refClass>
<subParameter classPath="jmt.engine.NetStrategies.QueuePutStrategies.TailStrategy" name="putStrategy"/>
</parameter>
</section>
<section className="ServiceTunnel"/>
<section className="Linkage"/>
</node>
<node name="CheckCalibration">
<section className="Storage">
<parameter classPath="java.lang.Integer" name="totalCapacity">
<value>-1</value>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="capacities">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="capacity">
<value>-1</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="capacity">
<value>-1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.String" name="dropRules">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.String" name="dropRule">
<value>drop</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.String" name="dropRule">
<value>drop</value>
</subParameter>
</parameter>
<parameter classPath="jmt.engine.NetStrategies.QueueGetStrategies.FCFSstrategy" name="getStrategy"/>
<parameter array="true" classPath="jmt.engine.NetStrategies.QueuePutStrategy" name="putStrategies">
<refClass>Class1</refClass>
<subParameter classPath="jmt.engine.NetStrategies.QueuePutStrategies.TailStrategy" name="putStrategy"/>
<refClass>Class2</refClass>
<subParameter classPath="jmt.engine.NetStrategies.QueuePutStrategies.TailStrategy" name="putStrategy"/>
</parameter>
</section>
<section className="ServiceTunnel"/>
<section className="Linkage"/>
</node>
<node name="StartService2">
<section className="Enabling">
<parameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="enablingConditions">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="enablingCondition">
<subParameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="enablingVectors">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="enablingVector">
<subParameter classPath="java.lang.String" name="stationName">
<value>Queue2</value>
</subParameter>
<subParameter array="true" classPath="java.lang.Integer" name="enablingEntries">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="enablingEntry">
<value>1</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="enablingEntry">
<value>0</value>
</subParameter>
</subParameter>
</subParameter>
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="enablingVector">
<subParameter classPath="java.lang.String" name="stationName">
<value>IdleServer2</value>
</subParameter>
<subParameter array="true" classPath="java.lang.Integer" name="enablingEntries">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="enablingEntry">
<value>0</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="enablingEntry">
<value>1</value>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</parameter>
<parameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="inhibitingConditions">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="inhibitingCondition">
<subParameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="inhibitingVectors">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="inhibitingVector">
<subParameter classPath="java.lang.String" name="stationName">
<value>Queue2</value>
</subParameter>
<subParameter array="true" classPath="java.lang.Integer" name="inhibitingEntries">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="inhibitingEntry">
<value>0</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="inhibitingEntry">
<value>0</value>
</subParameter>
</subParameter>
</subParameter>
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="inhibitingVector">
<subParameter classPath="java.lang.String" name="stationName">
<value>IdleServer2</value>
</subParameter>
<subParameter array="true" classPath="java.lang.Integer" name="inhibitingEntries">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="inhibitingEntry">
<value>0</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="inhibitingEntry">
<value>0</value>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</parameter>
</section>
<section className="Timing">
<parameter array="true" classPath="java.lang.String" name="modeNames">
<subParameter classPath="java.lang.String" name="modeName">
<value>Mode1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="numbersOfServers">
<subParameter classPath="java.lang.Integer" name="numberOfServers">
<value>1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="jmt.engine.NetStrategies.ServiceStrategy" name="timingStrategies">
<subParameter classPath="jmt.engine.NetStrategies.ServiceStrategies.ZeroServiceTimeStrategy" name="timingStrategy"/>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="firingPriorities">
<subParameter classPath="java.lang.Integer" name="firingPriority">
<value>0</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.Double" name="firingWeights">
<subParameter classPath="java.lang.Double" name="firingWeight">
<value>1.0</value>
</subParameter>
</parameter>
</section>
<section className="Firing">
<parameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="firingOutcomes">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="firingOutcome">
<subParameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="firingVectors">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="firingVector">
<subParameter classPath="java.lang.String" name="stationName">
<value>Busy2</value>
</subParameter>
<subParameter array="true" classPath="java.lang.Integer" name="firingEntries">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="firingEntry">
<value>1</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="firingEntry">
<value>0</value>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</parameter>
</section>
</node>
<node name="Service2">
<section className="Enabling">
<parameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="enablingConditions">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="enablingCondition">
<subParameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="enablingVectors">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="enablingVector">
<subParameter classPath="java.lang.String" name="stationName">
<value>Busy2</value>
</subParameter>
<subParameter array="true" classPath="java.lang.Integer" name="enablingEntries">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="enablingEntry">
<value>1</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="enablingEntry">
<value>0</value>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</parameter>
<parameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="inhibitingConditions">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="inhibitingCondition">
<subParameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="inhibitingVectors">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="inhibitingVector">
<subParameter classPath="java.lang.String" name="stationName">
<value>Busy2</value>
</subParameter>
<subParameter array="true" classPath="java.lang.Integer" name="inhibitingEntries">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="inhibitingEntry">
<value>0</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="inhibitingEntry">
<value>0</value>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</parameter>
</section>
<section className="Timing">
<parameter array="true" classPath="java.lang.String" name="modeNames">
<subParameter classPath="java.lang.String" name="modeName">
<value>Mode1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="numbersOfServers">
<subParameter classPath="java.lang.Integer" name="numberOfServers">
<value>1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="jmt.engine.NetStrategies.ServiceStrategy" name="timingStrategies">
<subParameter classPath="jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy" name="timingStrategy">
<subParameter classPath="jmt.engine.random.Exponential" name="Exponential"/>
<subParameter classPath="jmt.engine.random.ExponentialPar" name="distrPar">
<subParameter classPath="java.lang.Double" name="lambda">
<value>3.5</value>
</subParameter>
</subParameter>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="firingPriorities">
<subParameter classPath="java.lang.Integer" name="firingPriority">
<value>-1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.Double" name="firingWeights">
<subParameter classPath="java.lang.Double" name="firingWeight">
<value>1.0</value>
</subParameter>
</parameter>
</section>
<section className="Firing">
<parameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="firingOutcomes">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="firingOutcome">
<subParameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="firingVectors">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="firingVector">
<subParameter classPath="java.lang.String" name="stationName">
<value>CheckCalibration</value>
</subParameter>
<subParameter array="true" classPath="java.lang.Integer" name="firingEntries">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="firingEntry">
<value>0</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="firingEntry">
<value>1</value>
</subParameter>
</subParameter>
</subParameter>
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="firingVector">
<subParameter classPath="java.lang.String" name="stationName">
<value>Sink 2</value>
</subParameter>
<subParameter array="true" classPath="java.lang.Integer" name="firingEntries">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="firingEntry">
<value>1</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="firingEntry">
<value>0</value>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</parameter>
</section>
</node>
<node name="StartCalibration">
<section className="Enabling">
<parameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="enablingConditions">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="enablingCondition">
<subParameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="enablingVectors">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="enablingVector">
<subParameter classPath="java.lang.String" name="stationName">
<value>CheckCalibration</value>
</subParameter>
<subParameter array="true" classPath="java.lang.Integer" name="enablingEntries">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="enablingEntry">
<value>0</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="enablingEntry">
<value>1</value>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</parameter>
<parameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="inhibitingConditions">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="inhibitingCondition">
<subParameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="inhibitingVectors">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="inhibitingVector">
<subParameter classPath="java.lang.String" name="stationName">
<value>CheckCalibration</value>
</subParameter>
<subParameter array="true" classPath="java.lang.Integer" name="inhibitingEntries">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="inhibitingEntry">
<value>0</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="inhibitingEntry">
<value>0</value>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</parameter>
</section>
<section className="Timing">
<parameter array="true" classPath="java.lang.String" name="modeNames">
<subParameter classPath="java.lang.String" name="modeName">
<value>Mode1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="numbersOfServers">
<subParameter classPath="java.lang.Integer" name="numberOfServers">
<value>1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="jmt.engine.NetStrategies.ServiceStrategy" name="timingStrategies">
<subParameter classPath="jmt.engine.NetStrategies.ServiceStrategies.ZeroServiceTimeStrategy" name="timingStrategy"/>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="firingPriorities">
<subParameter classPath="java.lang.Integer" name="firingPriority">
<value>0</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.Double" name="firingWeights">
<subParameter classPath="java.lang.Double" name="firingWeight">
<value>0.1</value>
</subParameter>
</parameter>
</section>
<section className="Firing">
<parameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="firingOutcomes">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="firingOutcome">
<subParameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="firingVectors">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="firingVector">
<subParameter classPath="java.lang.String" name="stationName">
<value>Calibration</value>
</subParameter>
<subParameter array="true" classPath="java.lang.Integer" name="firingEntries">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="firingEntry">
<value>0</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="firingEntry">
<value>1</value>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</parameter>
</section>
</node>
<node name="Sink 2">
<section className="JobSink"/>
</node>
<node name="EndService">
<section className="Enabling">
<parameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="enablingConditions">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="enablingCondition">
<subParameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="enablingVectors">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="enablingVector">
<subParameter classPath="java.lang.String" name="stationName">
<value>CheckCalibration</value>
</subParameter>
<subParameter array="true" classPath="java.lang.Integer" name="enablingEntries">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="enablingEntry">
<value>0</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="enablingEntry">
<value>1</value>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</parameter>
<parameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="inhibitingConditions">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="inhibitingCondition">
<subParameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="inhibitingVectors">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="inhibitingVector">
<subParameter classPath="java.lang.String" name="stationName">
<value>CheckCalibration</value>
</subParameter>
<subParameter array="true" classPath="java.lang.Integer" name="inhibitingEntries">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="inhibitingEntry">
<value>0</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="inhibitingEntry">
<value>0</value>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</parameter>
</section>
<section className="Timing">
<parameter array="true" classPath="java.lang.String" name="modeNames">
<subParameter classPath="java.lang.String" name="modeName">
<value>Mode1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="numbersOfServers">
<subParameter classPath="java.lang.Integer" name="numberOfServers">
<value>1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="jmt.engine.NetStrategies.ServiceStrategy" name="timingStrategies">
<subParameter classPath="jmt.engine.NetStrategies.ServiceStrategies.ZeroServiceTimeStrategy" name="timingStrategy"/>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="firingPriorities">
<subParameter classPath="java.lang.Integer" name="firingPriority">
<value>0</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.Double" name="firingWeights">
<subParameter classPath="java.lang.Double" name="firingWeight">
<value>0.9</value>
</subParameter>
</parameter>
</section>
<section className="Firing">
<parameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="firingOutcomes">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="firingOutcome">
<subParameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="firingVectors">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="firingVector">
<subParameter classPath="java.lang.String" name="stationName">
<value>IdleServer2</value>
</subParameter>
<subParameter array="true" classPath="java.lang.Integer" name="firingEntries">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="firingEntry">
<value>0</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="firingEntry">
<value>1</value>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</parameter>
</section>
</node>
<node name="Calibration">
<section className="Storage">
<parameter classPath="java.lang.Integer" name="totalCapacity">
<value>-1</value>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="capacities">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="capacity">
<value>-1</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="capacity">
<value>-1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.String" name="dropRules">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.String" name="dropRule">
<value>drop</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.String" name="dropRule">
<value>drop</value>
</subParameter>
</parameter>
<parameter classPath="jmt.engine.NetStrategies.QueueGetStrategies.FCFSstrategy" name="getStrategy"/>
<parameter array="true" classPath="jmt.engine.NetStrategies.QueuePutStrategy" name="putStrategies">
<refClass>Class1</refClass>
<subParameter classPath="jmt.engine.NetStrategies.QueuePutStrategies.TailStrategy" name="putStrategy"/>
<refClass>Class2</refClass>
<subParameter classPath="jmt.engine.NetStrategies.QueuePutStrategies.TailStrategy" name="putStrategy"/>
</parameter>
</section>
<section className="ServiceTunnel"/>
<section className="Linkage"/>
</node>
<node name="Source 1">
<section className="RandomSource">
<parameter array="true" classPath="jmt.engine.NetStrategies.ServiceStrategy" name="ServiceStrategy">
<refClass>Class1</refClass>
<subParameter classPath="jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy" name="ServiceTimeStrategy">
<subParameter classPath="jmt.engine.random.Exponential" name="Exponential"/>
<subParameter classPath="jmt.engine.random.ExponentialPar" name="distrPar">
<subParameter classPath="java.lang.Double" name="lambda">
<value>1.5</value>
</subParameter>
</subParameter>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy" name="ServiceTimeStrategy">
<value>null</value>
</subParameter>
</parameter>
</section>
<section className="ServiceTunnel"/>
<section className="Router">
<parameter array="true" classPath="jmt.engine.NetStrategies.RoutingStrategy" name="RoutingStrategy">
<refClass>Class1</refClass>
<subParameter classPath="jmt.engine.NetStrategies.RoutingStrategies.RandomStrategy" name="Random"/>
<refClass>Class2</refClass>
<subParameter classPath="jmt.engine.NetStrategies.RoutingStrategies.RandomStrategy" name="Random"/>
</parameter>
</section>
</node>
<node name="PerformCalibration">
<section className="Enabling">
<parameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="enablingConditions">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="enablingCondition">
<subParameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="enablingVectors">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="enablingVector">
<subParameter classPath="java.lang.String" name="stationName">
<value>Calibration</value>
</subParameter>
<subParameter array="true" classPath="java.lang.Integer" name="enablingEntries">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="enablingEntry">
<value>0</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="enablingEntry">
<value>1</value>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</parameter>
<parameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="inhibitingConditions">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="inhibitingCondition">
<subParameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="inhibitingVectors">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="inhibitingVector">
<subParameter classPath="java.lang.String" name="stationName">
<value>Calibration</value>
</subParameter>
<subParameter array="true" classPath="java.lang.Integer" name="inhibitingEntries">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="inhibitingEntry">
<value>0</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="inhibitingEntry">
<value>0</value>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</parameter>
</section>
<section className="Timing">
<parameter array="true" classPath="java.lang.String" name="modeNames">
<subParameter classPath="java.lang.String" name="modeName">
<value>Mode1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="numbersOfServers">
<subParameter classPath="java.lang.Integer" name="numberOfServers">
<value>1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="jmt.engine.NetStrategies.ServiceStrategy" name="timingStrategies">
<subParameter classPath="jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy" name="timingStrategy">
<subParameter classPath="jmt.engine.random.Exponential" name="Exponential"/>
<subParameter classPath="jmt.engine.random.ExponentialPar" name="distrPar">
<subParameter classPath="java.lang.Double" name="lambda">
<value>10.0</value>
</subParameter>
</subParameter>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.Integer" name="firingPriorities">
<subParameter classPath="java.lang.Integer" name="firingPriority">
<value>-1</value>
</subParameter>
</parameter>
<parameter array="true" classPath="java.lang.Double" name="firingWeights">
<subParameter classPath="java.lang.Double" name="firingWeight">
<value>1.0</value>
</subParameter>
</parameter>
</section>
<section className="Firing">
<parameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="firingOutcomes">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix" name="firingOutcome">
<subParameter array="true" classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="firingVectors">
<subParameter classPath="jmt.engine.NetStrategies.TransitionUtilities.TransitionVector" name="firingVector">
<subParameter classPath="java.lang.String" name="stationName">
<value>IdleServer2</value>
</subParameter>
<subParameter array="true" classPath="java.lang.Integer" name="firingEntries">
<refClass>Class1</refClass>
<subParameter classPath="java.lang.Integer" name="firingEntry">
<value>0</value>
</subParameter>
<refClass>Class2</refClass>
<subParameter classPath="java.lang.Integer" name="firingEntry">
<value>1</value>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</subParameter>
</parameter>
</section>
</node>
<measure alpha="0.02" name="Class1_System Throughput" nodeType="" precision="0.01" referenceNode="" referenceUserClass="Class1" type="System Throughput" verbose="false"/>
<measure alpha="0.02" name="Service1_Class1_Utilization" nodeType="station" precision="0.01" referenceNode="Service1" referenceUserClass="Class1" type="Utilization" verbose="false"/>
<measure alpha="0.02" name="Busy2_Number of Customers" nodeType="station" precision="0.01" referenceNode="Busy2" referenceUserClass="" type="Number of Customers" verbose="false"/>
<measure alpha="0.02" name="Calibration_Number of Customers" nodeType="station" precision="0.01" referenceNode="Calibration" referenceUserClass="" type="Number of Customers" verbose="false"/>
<measure alpha="0.01" name="Service1_Class1_Response Time" nodeType="station" precision="0.03" referenceNode="Service1" referenceUserClass="Class1" type="Response Time" verbose="false"/>
<measure alpha="0.01" name="Busy2_Response Time" nodeType="station" precision="0.03" referenceNode="Busy2" referenceUserClass="" type="Response Time" verbose="false"/>
<measure alpha="0.01" name="Queue2_Response Time" nodeType="station" precision="0.03" referenceNode="Queue2" referenceUserClass="" type="Response Time" verbose="false"/>
<connection source="Service1" target="Queue2"/>
<connection source="Queue2" target="StartService2"/>
<connection source="Busy2" target="Service2"/>
<connection source="IdleServer2" target="StartService2"/>
<connection source="CheckCalibration" target="StartCalibration"/>
<connection source="CheckCalibration" target="EndService"/>
<connection source="StartService2" target="Busy2"/>
<connection source="Service2" target="CheckCalibration"/>
<connection source="Service2" target="Sink 2"/>
<connection source="StartCalibration" target="Calibration"/>
<connection source="EndService" target="IdleServer2"/>
<connection source="Calibration" target="PerformCalibration"/>
<connection source="Source 1" target="Service1"/>
<connection source="PerformCalibration" target="IdleServer2"/>
<preload>
<stationPopulations stationName="IdleServer2">
<classPopulation population="1" refClass="Class2"/>
</stationPopulations>
</preload>
</sim>
<jmodel xsi:noNamespaceSchemaLocation="JModelGUI.xsd">
<userClass color="#FF0000FF" name="Class1"/>
<userClass color="#FFFF0000" name="Class2"/>
<station name="Service1">
<position angle="0.0" rotate="false" x="130.0" y="200.0"/>
</station>
<station name="Queue2">
<position angle="0.0" rotate="false" x="277.0" y="203.0"/>
</station>
<station name="Busy2">
<position angle="0.0" rotate="false" x="560.0" y="203.0"/>
</station>
<station name="IdleServer2">
<position angle="0.0" rotate="true" x="411.0" y="328.0"/>
</station>
<station name="CheckCalibration">
<position angle="0.0" rotate="false" x="738.0" y="203.0"/>
</station>
<station name="StartService2">
<position angle="0.0" rotate="false" x="394.0" y="203.0"/>
</station>
<station name="Service2">
<position angle="0.0" rotate="false" x="635.0" y="203.0"/>
</station>
<station name="StartCalibration">
<position angle="0.0" rotate="true" x="739.0" y="328.0"/>
</station>
<station name="Sink 2">
<position angle="0.0" rotate="false" x="753.0" y="113.0"/>
</station>
<station name="EndService">
<position angle="0.0" rotate="false" x="877.0" y="328.0"/>
</station>
<station name="Calibration">
<position angle="0.0" rotate="true" x="638.0" y="328.0"/>
</station>
<station name="Source 1">
<position angle="0.0" rotate="false" x="28.0" y="203.0"/>
</station>
<station name="PerformCalibration">
<position angle="0.0" rotate="true" x="505.0" y="328.0"/>
</station>
</jmodel>
<results elapsedTime="22985" logDecimalSeparator="," logDelimiter=";" pollingInterval="1.0" xsi:noNamespaceSchemaLocation="Results.xsd">
<measure alpha="0.98" analyzedSamples="163840" discardedSamples="60" finalValue="1.501221406545825" name="Class1_System Throughput" nodeType="" precision="0.01" referenceClass="Class1" referenceStation="" state="1" type="16">
<sample lastIntervalAvgValue="1.5106622983340463" lowerBound="1.468486049955798" meanValue="1.50631035481992" simulationTime="13876.695025167392" upperBound="1.546134682461421"/>
<sample lastIntervalAvgValue="1.4986167692697017" lowerBound="1.4786226101201805" meanValue="1.5021490600601366" simulationTime="64253.81714744975" upperBound="1.5264362760056998"/>
<sample lastIntervalAvgValue="1.5036451787298382" lowerBound="1.4886800249805132" meanValue="1.501221406545825" simulationTime="109105.4889075181" upperBound="1.5139758930959133"/>
</measure>
<measure alpha="0.98" analyzedSamples="327680" discardedSamples="65" finalValue="0.7471951139971086" name="Service1_Class1_Utilization" nodeType="station" precision="0.01" referenceClass="Class1" referenceStation="Service1" state="1" type="6">
<sample lastIntervalAvgValue="0.7580797914843828" lowerBound="0.7284349220101368" meanValue="0.758341372366939" simulationTime="13876.28919854346" upperBound="0.7882478227237412"/>
<sample lastIntervalAvgValue="0.7437399633757887" lowerBound="0.7338157319545954" meanValue="0.7479059359761921" simulationTime="64255.194361328504" upperBound="0.7619961399977889"/>
<sample lastIntervalAvgValue="0.7481274953188917" lowerBound="0.7385472374512729" meanValue="0.7465206013494042" simulationTime="112954.31879089898" upperBound="0.7544939652475355"/>
<sample lastIntervalAvgValue="0.7484087729914563" lowerBound="0.7385472374512729" meanValue="0.7465206013494042" simulationTime="161441.3383818988" upperBound="0.7544939652475355"/>
<sample lastIntervalAvgValue="0.7449583611117644" lowerBound="0.7410310642295588" meanValue="0.7471951139971086" simulationTime="174394.58870580947" upperBound="0.7533591637646584"/>
</measure>
<measure alpha="0.98" analyzedSamples="491520" discardedSamples="20" finalValue="0.4285284158768284" name="Busy2_Number of Customers" nodeType="station" precision="0.01" referenceClass="" referenceStation="Busy2" state="1" type="0">
<sample lastIntervalAvgValue="0.4327278274081417" lowerBound="0.4209685663151158" meanValue="0.43604958177996045" simulationTime="13876.695025167392" upperBound="0.4511305972448051"/>
<sample lastIntervalAvgValue="0.4277717437256055" lowerBound="0.4246046190172446" meanValue="0.4316330740668569" simulationTime="64255.194361328504" upperBound="0.4386615291164692"/>
<sample lastIntervalAvgValue="0.4275252965369865" lowerBound="0.42421446359628734" meanValue="0.428573485460139" simulationTime="112953.74188414749" upperBound="0.4329325073239907"/>
<sample lastIntervalAvgValue="0.4283866284569466" lowerBound="0.42421446359628734" meanValue="0.428573485460139" simulationTime="161441.69294713807" upperBound="0.4329325073239907"/>
<sample lastIntervalAvgValue="0.4302070978372558" lowerBound="0.42484998277284614" meanValue="0.4285284158768284" simulationTime="203719.94497095025" upperBound="0.43220684898081063"/>
</measure>
<measure alpha="0.98" analyzedSamples="327680" discardedSamples="160" finalValue="0.014924666640854697" name="Calibration_Number of Customers" nodeType="station" precision="0.01" referenceClass="" referenceStation="Calibration" state="1" type="0">
<sample lastIntervalAvgValue="0.014296267465728714" lowerBound="0.011322752016847876" meanValue="0.014850303903029771" simulationTime="13874.061196402345" upperBound="0.018377855789211667"/>
<sample lastIntervalAvgValue="0.014868345345791742" lowerBound="0.013988219806503858" meanValue="0.014827742149762239" simulationTime="64253.42721042049" upperBound="0.015667264493020618"/>
<sample lastIntervalAvgValue="0.014812215932949374" lowerBound="0.014092261369804996" meanValue="0.014674223347077174" simulationTime="112953.02447111536" upperBound="0.015256185324349353"/>
<sample lastIntervalAvgValue="0.014559361071869054" lowerBound="0.014247388150725647" meanValue="0.014709234534974254" simulationTime="161433.99304923858" upperBound="0.015171080919222861"/>
<sample lastIntervalAvgValue="0.014995041507434526" lowerBound="0.014247388150725647" meanValue="0.014709234534974254" simulationTime="208953.24224685304" upperBound="0.015171080919222861"/>
<sample lastIntervalAvgValue="0.014949846023837393" lowerBound="0.014247388150725647" meanValue="0.014709234534974254" simulationTime="257841.91701157822" upperBound="0.015171080919222861"/>
<sample lastIntervalAvgValue="0.015408671090531872" lowerBound="0.014497445222088935" meanValue="0.01488811143629382" simulationTime="307232.2999839692" upperBound="0.015278777650498704"/>
<sample lastIntervalAvgValue="0.014666101118067975" lowerBound="0.014497445222088935" meanValue="0.01488811143629382" simulationTime="356122.65243281063" upperBound="0.015278777650498704"/>
<sample lastIntervalAvgValue="0.014751113494851873" lowerBound="0.014497445222088935" meanValue="0.01488811143629382" simulationTime="404635.01388552965" upperBound="0.015278777650498704"/>
<sample lastIntervalAvgValue="0.014672454292437586" lowerBound="0.014497445222088935" meanValue="0.01488811143629382" simulationTime="453685.40696788987" upperBound="0.015278777650498704"/>
<sample lastIntervalAvgValue="0.015094578158137126" lowerBound="0.014497445222088935" meanValue="0.01488811143629382" simulationTime="502604.4424242108" upperBound="0.015278777650498704"/>
<sample lastIntervalAvgValue="0.0147073503392127" lowerBound="0.014627249197317147" meanValue="0.014870847342668999" simulationTime="551597.2510570573" upperBound="0.01511444548802085"/>
<sample lastIntervalAvgValue="0.014849138821895225" lowerBound="0.014627249197317147" meanValue="0.014870847342668999" simulationTime="600424.9280403093" upperBound="0.01511444548802085"/>
<sample lastIntervalAvgValue="0.015292616237036798" lowerBound="0.014627249197317147" meanValue="0.014870847342668999" simulationTime="649308.6124621176" upperBound="0.01511444548802085"/>
<sample lastIntervalAvgValue="0.014377886778078027" lowerBound="0.014627249197317147" meanValue="0.014870847342668999" simulationTime="698192.541974876" upperBound="0.01511444548802085"/>
<sample lastIntervalAvgValue="0.01497014247619875" lowerBound="0.014627249197317147" meanValue="0.014870847342668999" simulationTime="746741.2640130178" upperBound="0.01511444548802085"/>
<sample lastIntervalAvgValue="0.014858699882670481" lowerBound="0.014627249197317147" meanValue="0.014870847342668999" simulationTime="795533.0825674769" upperBound="0.01511444548802085"/>
<sample lastIntervalAvgValue="0.01517207994251526" lowerBound="0.014627249197317147" meanValue="0.014870847342668999" simulationTime="844151.4035294583" upperBound="0.01511444548802085"/>
<sample lastIntervalAvgValue="0.014861073878907496" lowerBound="0.014627249197317147" meanValue="0.014870847342668999" simulationTime="892762.1492607896" upperBound="0.01511444548802085"/>
<sample lastIntervalAvgValue="0.015266432294084805" lowerBound="0.014627249197317147" meanValue="0.014870847342668999" simulationTime="941109.1109068348" upperBound="0.01511444548802085"/>
<sample lastIntervalAvgValue="0.014682845962729731" lowerBound="0.014627249197317147" meanValue="0.014870847342668999" simulationTime="989071.5846569396" upperBound="0.01511444548802085"/>
<sample lastIntervalAvgValue="0.014978189055859124" lowerBound="0.014627249197317147" meanValue="0.014870847342668999" simulationTime="1037509.0030904149" upperBound="0.01511444548802085"/>
<sample lastIntervalAvgValue="0.015254413177080007" lowerBound="0.014627249197317147" meanValue="0.014870847342668999" simulationTime="1086040.183054915" upperBound="0.01511444548802085"/>
<sample lastIntervalAvgValue="0.014605848518467818" lowerBound="0.014782350210118329" meanValue="0.014924666640854697" simulationTime="1096716.662202565" upperBound="0.015066983071591065"/>
</measure>
<measure alpha="0.99" analyzedSamples="778240" discardedSamples="3005" finalValue="2.0114910663794854" name="Service1_Class1_Response Time" nodeType="station" precision="0.03" referenceClass="Class1" referenceStation="Service1" state="1" type="2">
<sample lastIntervalAvgValue="1.94831588313056" lowerBound="1.569602317070396" meanValue="1.8932003606330974" simulationTime="13876.28919854346" upperBound="2.2167984041957984"/>
<sample lastIntervalAvgValue="1.96702004144991" lowerBound="1.8378967552023229" meanValue="2.0265293538191256" simulationTime="64255.194361328504" upperBound="2.215161952435928"/>
<sample lastIntervalAvgValue="1.9583792326739768" lowerBound="1.8419491796060967" meanValue="1.9467177321899223" simulationTime="112954.31879089898" upperBound="2.051486284773748"/>
<sample lastIntervalAvgValue="1.9920863680624197" lowerBound="1.8815084714201606" meanValue="1.9586531713516644" simulationTime="161441.3383818988" upperBound="2.0357978712831684"/>
<sample lastIntervalAvgValue="2.08577509641053" lowerBound="1.8815084714201606" meanValue="1.9586531713516644" simulationTime="208959.31741758634" upperBound="2.0357978712831684"/>
<sample lastIntervalAvgValue="2.013680282601994" lowerBound="1.8815084714201606" meanValue="1.9586531713516644" simulationTime="257854.98823458815" upperBound="2.0357978712831684"/>
<sample lastIntervalAvgValue="1.9655282858960066" lowerBound="1.9488991331825811" meanValue="2.011806401261866" simulationTime="307242.539402015" upperBound="2.0747136693411505"/>
<sample lastIntervalAvgValue="2.0760998968280298" lowerBound="1.9488991331825811" meanValue="2.011806401261866" simulationTime="356127.94247851" upperBound="2.0747136693411505"/>
<sample lastIntervalAvgValue="2.033177821938796" lowerBound="1.9488991331825811" meanValue="2.011806401261866" simulationTime="404638.22271664144" upperBound="2.0747136693411505"/>
<sample lastIntervalAvgValue="1.999037798158856" lowerBound="1.9488991331825811" meanValue="2.011806401261866" simulationTime="453687.12732063007" upperBound="2.0747136693411505"/>
<sample lastIntervalAvgValue="1.9448319477916651" lowerBound="1.9488991331825811" meanValue="2.011806401261866" simulationTime="502604.22743246035" upperBound="2.0747136693411505"/>
<sample lastIntervalAvgValue="2.1072909204942096" lowerBound="1.9670740207786732" meanValue="2.0114910663794854" simulationTime="520010.72527265665" upperBound="2.0559081119802975"/>
</measure>
<measure alpha="0.99" analyzedSamples="51200" discardedSamples="20" finalValue="0.2857712799507223" name="Busy2_Response Time" nodeType="station" precision="0.03" referenceClass="" referenceStation="Busy2" state="1" type="2">
<sample lastIntervalAvgValue="0.28644908123102863" lowerBound="0.2722201556987353" meanValue="0.28601168477942707" simulationTime="13876.695025167392" upperBound="0.2998032138601188"/>
<sample lastIntervalAvgValue="0.28510476832868925" lowerBound="0.2816272949130171" meanValue="0.2857712799507223" simulationTime="33841.364358874554" upperBound="0.28991526498842757"/>
</measure>
<measure alpha="0.99" analyzedSamples="225280" discardedSamples="565" finalValue="0.23183614777687678" name="Queue2_Response Time" nodeType="station" precision="0.03" referenceClass="" referenceStation="Queue2" state="1" type="2">
<sample lastIntervalAvgValue="0.23921408959189303" lowerBound="0.19954877885417566" meanValue="0.2302254387533936" simulationTime="13876.695025167392" upperBound="0.26090209865261155"/>
<sample lastIntervalAvgValue="0.22855069519234839" lowerBound="0.21936484900427225" meanValue="0.23297066183042345" simulationTime="64255.194361328504" upperBound="0.24657647465657465"/>
<sample lastIntervalAvgValue="0.23258007884898674" lowerBound="0.22085652239964554" meanValue="0.23127995842533797" simulationTime="112953.74188414749" upperBound="0.2417033944510304"/>
<sample lastIntervalAvgValue="0.2332468297868342" lowerBound="0.22496715167829837" meanValue="0.23183614777687678" simulationTime="150286.67327611792" upperBound="0.2387051438754552"/>
</measure>
</results>
</archive>