diff --git a/README.md b/README.md index 79b7582f..3a69b85e 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,38 @@ -# VirSim - Virus Spread Simulator -![workflow-develop badge](https://github.com/VirusSpreadSimulator/PPS-22-Virsim/actions/workflows/build.yml/badge.svg?branch=develop) + \ + \ +![workflow-main-badge](https://github.com/VirusSpreadSimulator/PPS-22-Virsim/actions/workflows/build.yml/badge.svg?branch=main) +![scala-version-badge](https://img.shields.io/badge/scala-3.1.1-red) +![sbt-version-badge](https://img.shields.io/badge/sbt-1.6.2-red) +[![ScalaDoc](https://img.shields.io/badge/Scaladoc-link-red)](https://virusspreadsimulator.github.io/PPS-22-virsim/latest/api/) \ +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=VirusSpreadSimulator_PPS-22-virsim&metric=coverage)](https://sonarcloud.io/summary/new_code?id=VirusSpreadSimulator_PPS-22-virsim) +[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=VirusSpreadSimulator_PPS-22-virsim&metric=bugs)](https://sonarcloud.io/summary/new_code?id=VirusSpreadSimulator_PPS-22-virsim) +[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=VirusSpreadSimulator_PPS-22-virsim&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=VirusSpreadSimulator_PPS-22-virsim) +[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=VirusSpreadSimulator_PPS-22-virsim&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=VirusSpreadSimulator_PPS-22-virsim) \ [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) - +[![semantic-release: angular](https://img.shields.io/badge/semantic--release-angular-e10079?logo=semantic-release)](https://github.com/semantic-release/semantic-release) -**Virsim** is a Scala-based simulation tool for the spread of a Virus within a population. +**Virsim** is a Scala-based simulation tool for the spread of a Virus within a population. \ +You can create your own simulation configuring everything such as the virus or the map of the environment and then Virsim will simulate the progress of the infections inside it. \ +Take a look at our [user guide](https://github.com/VirusSpreadSimulator/PPS-22-virsim/blob/main/doc/report/07-user-guide.md) to write your first simulation configuration. -For more informations visit the [Virsim website](https://virusspreadsimulator.github.io/PPS-22-virsim) or the latest API here [![ScalaDoc](https://img.shields.io/badge/Scaladoc-link-red)](https://virusspreadsimulator.github.io/PPS-22-virsim/latest/api/) +For more information visit our [website](https://virusspreadsimulator.github.io/PPS-22-virsim). ## Usage -``` -$ git clone https://github.com/VirusSpreadSimulator/PPS-22-virsim -$ cd PPS-22-virsim -$ sbt rootJVM/run -``` -or download latest Jar [here](https://github.com/VirusSpreadSimulator/PPS-22-virsim/releases/latest) and then: +Virsim comes in two different versions: +- Desktop Application +- Web Application + +### Desktop Application +Download latest Jar [here](https://github.com/VirusSpreadSimulator/PPS-22-virsim/releases/latest) and then: ``` java -jar virsim.jar ``` - +**Launch a simulation at this [link](https://virusspreadsimulator.github.io/PPS-22-virsim/simulator/).** ## Authors Developed for Academic purpose by: diff --git a/build.sbt b/build.sbt index 5498db7e..ec15f004 100644 --- a/build.sbt +++ b/build.sbt @@ -37,15 +37,10 @@ lazy val root = crossProject(JSPlatform, JVMPlatform) scalaJSUseMainModuleInitializer := true, libraryDependencies ++= Seq("org.scala-js" %%% "scalajs-dom" % "2.2.0") ) - .jvmSettings( - libraryDependencies ++= Seq( - "org.scala-lang.modules" %% "scala-swing" % "3.0.0" - ) - ) + .jvmSettings() lazy val aggregate = (project in file(".")) .enablePlugins(ScalaUnidocPlugin) - .enablePlugins(GitHubPagesPlugin) .enablePlugins(SiteScaladocPlugin) .aggregate(root.jvm, root.js) .settings( @@ -53,5 +48,3 @@ lazy val aggregate = (project in file(".")) ScalaUnidoc / siteSubdirName := "latest/api/", addMappingsToSiteDir(ScalaUnidoc / packageDoc / mappings, ScalaUnidoc / siteSubdirName) ) - -gitHubPagesSiteDir := baseDirectory.value / "target/site" diff --git a/doc/config/style.css b/doc/config/style.css index 623a7d1d..f4dfe917 100644 --- a/doc/config/style.css +++ b/doc/config/style.css @@ -7,7 +7,8 @@ img { display: block; margin-left: auto; margin-right: auto; - width: 100%; + max-width: 100%; + max-height: 40%; } @include-when-export url(https://fonts.loli.net/css?family=Open+Sans:400italic,700italic,700,400&subset=latin,latin-ext); diff --git a/doc/process/productBacklog.md b/doc/process/productBacklog.md index 254fe1c7..b7a19ddb 100644 --- a/doc/process/productBacklog.md +++ b/doc/process/productBacklog.md @@ -2,20 +2,20 @@ | Priority | Item | Details | Initial Size Estimate | 1 | 2 | 3 | 4 | 5 | 6 | | -------- | ------------------------------------------------------------ | ------- | --------------------- | ---- | ---- | ---- | ---- | ---- | ---- | -| 1 | Bootstrap e organizzazione del progetto | | 4 | 0 | 0 | 0 | 0 | 0 | | -| 2 | Definizione dell'architettura del sistema | | 8 | 1 | 0 | 0 | 0 | 0 | | -| 3 | Definizione e implementazione del modello del dominio | | 12 | 12 | 11 | 1 | 0 | 0 | | -| 4 | Sviluppo del motore di simulazione | | 15 | 15 | 10 | 5 | 1 | 0 | | -| 5 | Realizzazione GUI JVM-Based | | 11 | 11 | 10 | 6 | 2 | 1 | | -| 6 | Implementazione del modulo per il caricamento della configurazione fornita dall'utente | | 14 | 14 | 14 | 7 | 0 | 0 | | -| 7 | Sviluppo della logica di movimento degli individui | | 7 | 7 | 7 | 7 | 0 | 0 | | -| 8 | Sviluppo della logica di interazione tra gli individui | | 5 | 5 | 5 | 5 | 4 | 0 | | -| 9 | Sviluppo della logica di interazione tra individui e strutture | | 12 | 12 | 12 | 12 | 12 | 0 | | -| 10 | Sviluppo della logica di contagio degli individui | | 15 | 15 | 15 | 14 | 14 | 0 | | -| 11 | Sviluppo della logica per l'aggiornamento dello stato degli individui | | 4 | 4 | 4 | 4 | 4 | 0 | | -| 12 | Sviluppo della logica di interazione dinamica dell'utente con la simulazione | | 10 | 10 | 10 | 10 | 8 | 0 | | -| 13 | Sviluppo del DSL per la configurazione statica della simulazione | | 8 | 8 | 8 | 5 | 1 | 0 | | -| 14 | Monitoraggio real-time della simulazione | | 5 | 5 | 5 | 5 | 4 | 0 | | -| 15 | Sviluppo del sistema di esportazioni dati | | 5 | 5 | 5 | 5 | 4 | 0 | | -| 16 | Realizzazione GUI JS-Based | | 4 | 4 | 3 | 3 | 3 | 3 | | +| 1 | Bootstrap e organizzazione del progetto | | 4 | 0 | 0 | 0 | 0 | 0 | 0 | +| 2 | Definizione dell'architettura del sistema | | 8 | 1 | 0 | 0 | 0 | 0 | 0 | +| 3 | Definizione e implementazione del modello del dominio | | 12 | 12 | 11 | 1 | 0 | 0 | 0 | +| 4 | Sviluppo del motore di simulazione | | 15 | 15 | 10 | 5 | 1 | 0 | 0 | +| 5 | Realizzazione GUI JVM-Based | | 11 | 11 | 10 | 6 | 2 | 1 | 0 | +| 6 | Implementazione del modulo per il caricamento della configurazione fornita dall'utente | | 14 | 14 | 14 | 7 | 0 | 0 | 0 | +| 7 | Sviluppo della logica di movimento degli individui | | 7 | 7 | 7 | 7 | 0 | 0 | 0 | +| 8 | Sviluppo della logica di interazione tra gli individui | | 5 | 5 | 5 | 5 | 4 | 0 | 0 | +| 9 | Sviluppo della logica di interazione tra individui e strutture | | 12 | 12 | 12 | 12 | 12 | 0 | 0 | +| 10 | Sviluppo della logica di contagio degli individui | | 15 | 15 | 15 | 14 | 14 | 0 | 0 | +| 11 | Sviluppo della logica per l'aggiornamento dello stato degli individui | | 4 | 4 | 4 | 4 | 4 | 0 | 0 | +| 12 | Sviluppo della logica di interazione dinamica dell'utente con la simulazione | | 10 | 10 | 10 | 10 | 8 | 0 | 0 | +| 13 | Sviluppo del DSL per la configurazione statica della simulazione | | 8 | 8 | 8 | 5 | 1 | 0 | 0 | +| 14 | Monitoraggio real-time della simulazione | | 5 | 5 | 5 | 5 | 4 | 0 | 0 | +| 15 | Sviluppo del sistema di esportazioni dati | | 5 | 5 | 5 | 5 | 4 | 0 | 0 | +| 16 | Realizzazione GUI JS-Based | | 4 | 4 | 3 | 3 | 3 | 3 | 0 | diff --git a/doc/process/sprints/sprint-6/productBacklogRefinement.md b/doc/process/sprints/sprint-6/productBacklogRefinement.md index 625e2c08..2e92a5f2 100644 --- a/doc/process/sprints/sprint-6/productBacklogRefinement.md +++ b/doc/process/sprints/sprint-6/productBacklogRefinement.md @@ -1 +1,4 @@ # Product Backlog Refinement - Sprint 6 + +Durante lo Sprint 6, il team ha terminato lo sviluppo della prima versione del simulatore. +Quindi l'intervento principale durante questo product backlog refinement è stato quello di controllare e quindi azzerare gli impegni residui di tutte le attività completate. diff --git a/doc/process/sprints/sprint-6/sprintBacklog.md b/doc/process/sprints/sprint-6/sprintBacklog.md index b9c6db8b..e4f566a9 100644 --- a/doc/process/sprints/sprint-6/sprintBacklog.md +++ b/doc/process/sprints/sprint-6/sprintBacklog.md @@ -2,11 +2,11 @@ | Product Backlog Item | Sprint Task | Volunteer | Initial Estimate of Effort | 1 | 2 | 3 | 4 | 5 | | ------------------------------------------- | ------------------------------------------------------------ | --------- | -------------------------- | ---- | ---- | ---- | ---- | ---- | -| Realizzazione GUI JVM-Based | raffinamento grafica | | 2 | 2 | 0 | 0 | | | -| | sviluppo della parte di log per le azioni dinamiche | | 3 | 1 | 0 | 0 | | | -| Realizzazione GUI JS-Based | sviluppo della schermata dedicata all'inizializzazione della simulazione | | 2 | 2 | 0 | 0 | | | -| | sviluppo della schermata di simulazione | | 4 | 3 | 0 | 0 | | | -| Configurazione dei parametri di simulazione | configurazione dei parametri di simulazione | | 2 | 2 | 2 | 2 | | | -| Prima release | aggiunta dello stile al report generato da CI | | 4 | 4 | 4 | 0 | | | -| | test del sistema di release automatico | | 4 | 4 | 4 | 2 | | | -| | prima release | | 1 | 1 | 1 | 1 | | | \ No newline at end of file +| Realizzazione GUI JVM-Based | raffinamento grafica | | 2 | 2 | 0 | 0 | 0 | 0 | +| | sviluppo della parte di log per le azioni dinamiche | | 3 | 1 | 0 | 0 | 0 | 0 | +| Realizzazione GUI JS-Based | sviluppo della schermata dedicata all'inizializzazione della simulazione | | 2 | 2 | 0 | 0 | 0 | 0 | +| | sviluppo della schermata di simulazione | | 4 | 3 | 0 | 0 | 0 | 0 | +| Configurazione dei parametri di simulazione | configurazione dei parametri di simulazione | | 2 | 2 | 2 | 2 | 2 | 0 | +| Prima release | aggiunta dello stile al report generato da CI | | 4 | 4 | 4 | 0 | 0 | 0 | +| | test del sistema di release automatico | | 4 | 4 | 4 | 2 | 1 | 0 | +| | prima release | | 1 | 1 | 1 | 1 | 1 | 0 | \ No newline at end of file diff --git a/doc/process/sprints/sprint-6/sprintRetrospective.md b/doc/process/sprints/sprint-6/sprintRetrospective.md index dcd5aa00..b1064976 100644 --- a/doc/process/sprints/sprint-6/sprintRetrospective.md +++ b/doc/process/sprints/sprint-6/sprintRetrospective.md @@ -1,2 +1,6 @@ # Sprint Retrospective +Alla luce della terminazione del progetto, durante l'ultimo Sprint, il team si è ritrovato e ha messo sul tavolo tutti gli aspetti positivi e negativi che hanno caratterizzato il periodo di sviluppo al fine di ottenere feedback e suggerimenti rispetto all'ambiente di lavoro in modo da migliorare le performance anche nei progetti futuri. + +L'intero team è convinto circa i vantaggi derivanti dall'utilizzo di un processo Scrum-based e di un approccio che prevede un importante processo di Continuous Integration. + diff --git a/doc/process/sprints/sprint-6/sprintReview.md b/doc/process/sprints/sprint-6/sprintReview.md index 386fdd10..6904b965 100644 --- a/doc/process/sprints/sprint-6/sprintReview.md +++ b/doc/process/sprints/sprint-6/sprintReview.md @@ -1,2 +1,6 @@ # Sprint Review +Durante l'ultima Sprint Review, si è riunito tutto il team assieme al Product Owner per mostrare al committente il risultato finale del lavoro svolto. In particolare sono state mostrate tutte le funzionalità sviluppate ed una preview della guida utente che verrà resa disponibile agli utenti finali. + +Il lavoro svolto è stato ulteriormente validato confrontandolo con i requisiti raccolti nelle fasi iniziali assieme al committente. +Il committente ha approvato il progetto, perciò lo sprint si ritiene concluso con successo. diff --git a/doc/process/sprints/sprint-6/taskBoardScreen/sprintTaskBoard_During.png b/doc/process/sprints/sprint-6/taskBoardScreen/sprintTaskBoard_During.png new file mode 100644 index 00000000..b7a835a2 Binary files /dev/null and b/doc/process/sprints/sprint-6/taskBoardScreen/sprintTaskBoard_During.png differ diff --git a/doc/process/sprints/sprint-6/taskBoardScreen/sprintTaskBoard_End.png b/doc/process/sprints/sprint-6/taskBoardScreen/sprintTaskBoard_End.png new file mode 100644 index 00000000..8b445a66 Binary files /dev/null and b/doc/process/sprints/sprint-6/taskBoardScreen/sprintTaskBoard_End.png differ diff --git a/doc/report/00-init.md b/doc/report/00-init.md index 6b17bd28..ee9afdb4 100644 --- a/doc/report/00-init.md +++ b/doc/report/00-init.md @@ -14,4 +14,12 @@ ## Introduzione +Il progetto consiste nella realizzazione di un software che simuli la diffusione di un virus all'interno di una popolazione di individui, chiamati anche entità, i quali interagiscono in un ambiente limitato. +Il simulatore permette all'utente di generare uno scenario personalizzato nel quale vengono definite tutte le caratteristiche delle entità, del virus e delle strutture, le quali potranno essere configurate e disposte emulando il più possibile scenari reali. +Ciascun individuo ha una serie di caratteristiche tra cui età, stato di salute e immunità sviluppata rispetto al virus che vengono prese in considerazione all'interno della simulazione e contribuiscono a definire i contagi. +L'obiettivo è simulare le interazioni in un contesto pseudo-reale nel quale gli individui durante il giorno si muovono liberamente all'esterno entrando e uscendo dalle strutture con la possibilità, di notte, di fare ritorno alla propria abitazione. +Il contagio avviene per contatto con individui infetti ed è influenzato dalle caratteristiche della struttura nel caso in cui l'individuo si trovi al suo interno. La gravità della malattia da esso derivata comporta un calo dello stato di salute che può portare alla morte dell'entità. +Tra le strutture configurabili dall'utente vi è una particolare tipologia, l'ospedale, dedicata alla cura degli individui malati. Inoltre, durante la simulazione l'utente ha la possibilità di vaccinare una percentuale della popolazione, di chiudere un determinato gruppo di strutture o inserire l'obbligo di portare la mascherina. +È possibile visualizzare l'intera simulazione mediante interfaccia grafica e tramite l'ausilio di grafici monitorare l'andamento dei dati aggregati e dei parametri principali. Questi ultimi dati vengono inoltre esportati in un foglio di calcolo al fine di poterli consultare al termine della simulazione. +Il software viene fornito sia come applicativo desktop che come web app.
diff --git a/doc/report/01-process.md b/doc/report/01-process.md index b452eaa2..9424ba7f 100644 --- a/doc/report/01-process.md +++ b/doc/report/01-process.md @@ -10,13 +10,13 @@ Al fine di simulare un contesto reale, i componenti del team interpretano i vari Considerando la cardinalità ridotta del team, si è adottata una versione semplificata di Scrum cercando di rimanere coerenti con la filosofia originale. Come anticipato, Andrea Acampora in veste di committente ed esperto del dominio si è occupato di garantire l'usabilità e la qualità del risultato interagendo direttamente con il Product Owner interpretato da Andrea Giulianelli. -### Meeting/interazioni pianificate +### Meeting e interazioni pianificate I meeting tra i membri del team si sono tenuti online con frequenza regolare sfruttando la piattaforma Microsoft Teams. Il meeting iniziale è stato caratterizzato dall'incontro tra il team, in particolare il Product Owner, e il committente con lo scopo di comprendere il dominio applicativo e gli obiettivi del progetto. Durante questo incontro è stata eseguita un'intervista che ha portato alla stesura dei requisiti e alla creazione del **Product Backlog**. Quest'ultimo documento è stato fondamentale per mantenere una lista con priorità delle feature customer-centric accompagnando lo sviluppo per tutta la durata del progetto. A livello operativo il documento è stato gestito mediante una tabella in un file in linguaggio Markdown mantenuta in versione all'interno della repository del progetto in modo da permetterne l'evoluzione. A seguito di questo incontro sono stati eseguiti Sprint con cadenza settimanale, in particolare ciascuno Sprint è stato caratterizzato dalle seguenti attività: -- **Sprint Planning**: cercando di rispettare la natura di Scrum, lo Sprint Planning è stato diviso in due parti. Nella prima parte, dedicata alla preparazione dello sprint, si delineano i principali task ad alta priorità da svolgere all'interno dello sprint stesso. Essi sono estratti dal Product Backlog e approfonditi. Il risultato di questa prima parte è lo **Sprint Goal**, un riassunto con gli obiettivi dello sprint mantenuto in versione in un file in linguaggio Markdown all'interno della repository del progetto. Lo Sprint Goal prodotto lascia flessibilità nella quantità e nella selezione degli elementi in preparazione della seconda parte dello Sprint Planning. Durante la seconda parte il team di sviluppo si occupa di decidere in modo effettivo, considerando i tempi a disposizione, quali item dovranno essere completati nello sprint corrente. Il risultato di questa fase è lo **Sprint Backlog**, un approfondimento della porzione di Product Backlog designata nella fase precedente. Anche questo documento, rappresentato da una tabella in un file in linguaggio Markdown, è mantenuto in versione. +- **Sprint Planning**: cercando di rispettare la natura di Scrum, lo Sprint Planning è stato diviso in due parti. Nella prima parte, dedicata alla preparazione dello sprint, si delineano i principali task ad alta priorità da svolgere all'interno dello sprint stesso. Essi sono estratti dal Product Backlog ed approfonditi. Il risultato di questa prima parte è lo **Sprint Goal**, un riassunto con gli obiettivi dello sprint mantenuto in versione in un file in linguaggio Markdown all'interno della repository del progetto. Lo Sprint Goal prodotto lascia flessibilità nella quantità e nella selezione degli elementi in preparazione della seconda parte dello Sprint Planning. Durante la seconda parte il team di sviluppo si occupa di decidere in modo effettivo, considerando i tempi a disposizione, quali item dovranno essere completati nello sprint corrente. Il risultato di questa fase è lo **Sprint Backlog**, un approfondimento della porzione di Product Backlog designata nella fase precedente. Anche questo documento, rappresentato mediante una tabella in un file in linguaggio Markdown, è mantenuto in versione. - **Daily Scrum**: durante lo sprint, con cadenza giornaliera e della durata di 15 minuti, sono stati eseguiti i Daily Scrum al fine di sincronizzare il lavoro e riportare eventuali ostacoli incontrati. - **Product Backlog Refinement**: alla fine dello sprint viene eseguito il Product Backlog Refinement, un meeting con l'obiettivo di analizzare, ri-stimare e rivedere le priorità del Product Backlog al fine di semplificare i futuri Sprint Planning. - **Sprint Review**: consiste in un meeting da svolgere alla fine dello sprint ed ha l'obiettivo di analizzarne e ispezionarne il risultato al fine di comprendere, con il Product Owner e il committente, se i task dello sprint sono stati svolti correttamente. @@ -27,19 +27,21 @@ A seguito di questo incontro sono stati eseguiti Sprint con cadenza settimanale, Al termine di ogni Sprint Planning, partendo dallo Sprint Backlog, i task vengono rappresentati all'interno di una **Sprint Task Board**, con l'ausilio del software **Trello**. La divisione dei task è stata gestita mediante la Task Board, assegnando ciascun task a uno o più componenti del team. All'interno della Task Board sono presenti molteplici liste, ciascuna delle quali rappresenta un determinato stato nell'evoluzione del task. Le tipologie di liste presenti sono: *TO-DO, Doing, Testing, Done, Waiting, Blocked*. In questo modo ogni componente del team può avere una panoramica completa sul lavoro svolto e in esecuzione. Nella repository del progetto, nella sezione relativa alla documentazione di ciascuno sprint, è possibile osservare alcuni screenshot relativi all'evoluzione della Board di Trello. -A seconda della tipologia, i task sono stati assegnati a un singolo membro del team, oppure sfruttando il *pair-programming*. Invece, i task più importanti hanno coinvolto l'intero team. -Per ogni task assegnato è stata considerata la seguente *definition of done*: un task o, in particolare, una funzionalità è da considerarsi terminato nel momento in cui è stato adeguatamente testato e documentato, ha passato una code review (automatica o manuale a seconda dell'importanza) e soddisfa le aspettative del committente. +A seconda della tipologia i task sono stati assegnati a un singolo membro del team, oppure sfruttando il *pair-programming*, mentre i task più importanti hanno coinvolto l'intero team. +Per ogni task assegnato è stata considerata la seguente *definition of done*: un task, o in particolare una funzionalità è da considerarsi terminata nel momento in cui è stata adeguatamente testata e documentata, ha passato una code review (automatica o manuale a seconda dell'importanza) e soddisfa le aspettative del committente. ### Modalità di revisione in itinere dei task -Durante lo sprint non è possibile effettuare cambiamenti agli obiettivi di esso. Ciascuna modifica, la quale viene discussa in uno dei meeting finali quali Product Backlog Refinement, Sprint Review e Sprint Retrospective, è da includere necessariamente all'interno dello sprint successivo. +Durante lo sprint non è possibile effettuare cambiamenti agli obiettivi di esso. Ciascuna modifica, la quale viene ulteriormente discussa in uno dei meeting finali quali Product Backlog Refinement, Sprint Review e Sprint Retrospective, è da includere necessariamente all'interno dello sprint successivo. L'unica revisione consentita è relativa alla modifica della stima del carico di lavoro residuo di ogni task all'interno dello Sprint Backlog. Invece, nel caso di cambi drammatici e/o critici è possibile interrompere lo Sprint in corso. ### Scelta degli strumenti di test L'intero processo di sviluppo è stato effettuato seguendo i principi del **Test-Driven Development** (TDD), un modello di sviluppo di software nel quale si porta avanti il codice di test insieme a quello di produzione, facendo sì che i test diventino una specifica di comportamento desiderato, utile per comprendere gli obiettivi e per documentare il codice. -A supporto di ciò è stato utilizzato il testing framework **ScalaTest**. Inoltre, al fine di testare l'intero sistema a fronte dei bisogni e delle aspettative del committente, sono stati effettuati degli acceptance tests utilizzando il tool **Cucumber**. Quest'ultimo permette di testare i casi d'uso dell'applicazione mediante un linguaggio dichiarativo e comprensibile anche dal committente. -Ulteriori dettagli inerenti ai test sono presenti nell'apposita sezione all'interno del capitolo Implementazione. +A supporto di ciò sono stati utilizzati due testing framework: + ++ **ScalaTest**: sfruttando **AnyFunSuite** e i **Matchers** per una maggiore dichiaratività. Ulteriori dettagli inerenti ai test sono presenti nell'apposita sezione all'interno del capitolo Implementazione. ++ **WeaverTest**: un test framework interoperabile con monix sviluppato da **[Disney](https://github.com/disneystreaming/weaver-test)**. ### Scelta degli strumenti di build e Continuous Integration @@ -59,19 +61,19 @@ I branch previsti dal workflow sono i seguenti: - *release*: branch di supporto, dedicato alla preparazione di una nuova release. - *hotfix*: branch di supporto, utilizzato per la correzione di errori nel codice di produzione rilasciato. -Inoltre, al fine di esplicitare maggiormente il significato dei commit si è scelto di utilizzare la specifica **[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)**, che ha semplificato l'utilizzo di tool automatici per il versionamento dell'applicazione. In particolare è stato adottata la specifica del **[Semantic Versioning](https://semver.org/)**. -Al fine di controllare il rispetto della specifica *Conventional Commits* è stato utilizzato un *hook* per git con lo scopo di analizzare il messaggio di commit ed eventualmente farlo fallire in caso di errore. Inoltre, è stato utilizzato un ulteriore hook pre-commit per eseguire automaticamente il pull dalla repository prima di poter effettuare qualsiasi commit in modo tale da limitare il più possibile la divergenza tra le linee di sviluppo. Per poter condividere e configurare automaticamente gli hook è stato utilizzato il plugin sbt **sbt-git-hooks** il quale al caricamento del progetto inserisce quest'ultimi all'interno della cartella *.git*. +Inoltre, al fine di esplicitare maggiormente il significato dei commit si è scelto di utilizzare la specifica **[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)**, che ha semplificato l'utilizzo di tool automatici per il versionamento dell'applicazione. In particolare è stata adottata la specifica del **[Semantic Versioning](https://semver.org/)**. +Al fine di controllare il corretto utilizzo della specifica *Conventional Commits* è stato utilizzato un *hook* per git con lo scopo di analizzare il messaggio di commit ed eventualmente farlo fallire in caso non la rispetti. Inoltre, è stato utilizzato un ulteriore hook pre-commit per eseguire automaticamente il pull dalla repository prima di poter effettuare qualsiasi commit in modo tale da limitare il più possibile la divergenza tra le linee di sviluppo. Per poter condividere e configurare automaticamente gli hook è stato utilizzato il plugin sbt **sbt-git-hooks** il quale al caricamento del progetto inserisce quest'ultimi all'interno della cartella `.git`. #### Build Automation -Come strumento per la build automation è stato utilizzato **Scala-Build Tool** (Sbt), un build tool automator che ha permesso una gestione efficiente del progetto attraverso la gestione delle dipendenze e di vari plugins utili per l'analisi statica del codice e per la configurazione cross-platform del progetto. +Come strumento per la build automation è stato utilizzato **Scala-Build Tool** (Sbt), un build tool automator che ha permesso una gestione efficiente del progetto attraverso la gestione delle dipendenze e di vari plugins utili per l'analisi statica del codice e per la configurazione cross-platform del progetto. Inoltre, per una gestione più agile del progetto cross-platform (JVM e JS) sono stati utilizzati i plugin di [sbt-crossproject](https://github.com/portable-scala/sbt-crossproject). #### Code Quality Control Un elemento centrale dell'intero processo è stata la continua ricerca di qualità nel codice sviluppato. A supporto di ciò sono stati utilizzati diversi tool automatici per l'analisi statica del codice sia a livello di plugin per l'IDE che a livello di Continuous Integration: -- **Scalafmt**: tool per una corretta formattazione del codice -- **Wartremover**: utilizzato per l'analisi del codice Scala al fine di trovare potenziali problemi in esso. +- **Scalafmt**: tool per una corretta formattazione del codice. +- **Wartremover**: utilizzato in locale per l'analisi del codice Scala al fine di trovare potenziali problemi in esso. - **Jacoco**: utilizzato per calcolare e controllare la coverage del codice rispetto ai test. - **SonarCloud**: tool per la ricerca di code smells, bugs e vulnerabilità. Esso è utilizzato per automatizzare la code review e per visualizzare i report sulla coverage generati da Jacoco. @@ -82,12 +84,31 @@ In generale, la code review è stata in parte automatizzata mediante il tool Son Per quanto riguarda lo strumento per la *Continuous Integration* è stata utilizzata la piattaforma **GitHub Actions**, la quale consente di automatizzare i flussi di lavoro dello sviluppo di software all'interno di GitHub. È possibile distribuire i flussi di lavoro nella stessa posizione in cui si archivia il codice per compilare, testare, assemblare, analizzare, rilasciare e distribuire software oltre ad automatizzare la collaborarazione tramite *issue* e *pull requests*. L'obiettivo è quello di verificare continuamente l'integrità del codice eseguendo nuovamente tutti i test presenti per ciascuna modifica effettuata evitando così situazioni di regressione. Un altro utilizzo del workflow di Continuous Integration è quello di mandare in esecuzione i tool per l'analisi statica del codice. Al fine di garantire un corretto utilizzo del software su piattaforme differenti i test verranno eseguiti su Windows, Linux e MacOs. -Inoltre, per la gestione automatica degli aggiornamenti delle dipendenze è stato integrato all'interno dei workflow il tool **Renovate**. +È stato sviluppato un workflow dedicato esclusivamente al processo di build che comprende: + ++ Check di scalafmt su tutto il codice per assicurare una corretta formattazione. ++ Esecuzione dei test per evitare situazioni di regressione. ++ Analisi statica del codice mediante Sonarcloud, a supporto del processo di code review. + +Inoltre, per la gestione automatica degli aggiornamenti delle dipendenze è stato utilizzato il tool **Renovate**, il quale si occupa di controllare la presenza di aggiornamenti nelle dipendenze, e prova ad eseguirli in un branch ad hoc che verrà poi integrato automaticamente nel branch di sviluppo qualora i test vengano eseguiti con successo. #### Continuous Deployment In aggiunta alla Continuous Integration è stato utilizzato anche un meccanismo di Continuous Deployment, il quale permette di rilasciare le *major versions* del software in maniera automatica. -Il delivery target scelto è **Github Release**. Per ciascuna release gli artefatti presenti sono un file jar dell'applicazione eseguibile ed il report in versione PDF ottenuto dal merging dei file in formato Markdown presenti in versione. -È previsto Inoltre il deploy automatico della Scaladoc sfruttando il servizio di hosting **Github Pages** integrato nella repository del progetto. +Il delivery target scelto è **Github Release**. +Considerando, come anticipato, l'utilizzo di Git-Flow come branching model, ad ogni commit sul branch *main* viene attivata la pipeline di deploy. Essa ha un duplice obiettivo: + ++ Release: al fine di ottenere una gestione automatica della versione la quale sfrutti i Conventional Commit e della pubblicazione degli artefatti è stato utilizzato il tool [Semantic Release](https://github.com/semantic-release/semantic-release). La release include: + + + Il file jar dell'applicativo JVM: generato attraverso il plugin **sbt-assembly**. + + Il file pdf del report ottenuto dal merging automatico dei file in formato Markdown presenti in versione. Per la conversione dei file è stata sviluppata una [action](https://github.com/andrea-acampora/action-md2pdf) ad hoc che sfrutta il converter [mdpdf](https://github.com/BlueHatbRit/mdpdf). + + I sample di configurazione della simulazione in formato scala e yaml. + ++ Deploy: è stato utilizzato il servizio di hosting **Github Pages** al fine di creare un sito che permetta di accedere a: + + + Scaladoc: generata automaticamente mediante il plugin **sbt-unidoc** ed **sbt-site** e pubblicata al seguente [link](https://virusspreadsimulator.github.io/PPS-22-virsim/latest/api/). + + Web App: il codice del simulatore viene transcompilato attraverso il plugin **sbt-crossproject** descritto precedentemente e ne viene eseguito il deploy all'interno del sito del simulatore al seguente [link](https://virusspreadsimulator.github.io/PPS-22-virsim/simulator/). + + Il deploy del sito avviene automaticamente al seguente [link](https://virusspreadsimulator.github.io/PPS-22-virsim/) grazie all'azione **[action-gh-pages](https://github.com/peaceiris/actions-gh-pages)** in modo tale da aggiornare la Scaladoc e la webapp ad ogni release senza interventi umani. diff --git a/doc/report/02-requirements.md b/doc/report/02-requirements.md index f43e1140..b24ea798 100644 --- a/doc/report/02-requirements.md +++ b/doc/report/02-requirements.md @@ -5,14 +5,13 @@ ## Business 1. Requisiti di Business - + 1.1. Simulazione della diffusione di un virus all’interno di una popolazione di individui che interagisce in un ambiente limitato - + 1.1.1. generazione di diversi scenari configurabili attraverso mappe personalizzate - + 1.1.1.2. presenza di diverse tipologie di strutture con caratteristiche personalizzabili legate al virus - + 1.1.2. definizione di una configurazione statica che riguarda diversi aspetti della simulazione, dell’ambiente e degli individui. - + 1.1.3. interazione con la simulazione per la modifica e l’aggiunta di ulteriori parametri o vincoli. - + 1.1.4. presenza di un tempo virtuale e di un ciclo giorno/notte. - + 1.1.5. Visualizzazione, monitoraggio ed esportazione di informazioni e statistiche riguardanti l’andamento della simulazione - + + 1.1. Simulazione della diffusione di un virus all’interno di una popolazione di individui che interagisce in un ambiente limitato. + + 1.1.1. Generazione di diversi scenari configurabili attraverso mappe personalizzate. + + 1.1.1.1. Presenza di diverse tipologie di strutture con caratteristiche personalizzabili legate al virus. + + 1.1.2. Definizione di una configurazione statica che riguarda diversi aspetti della simulazione, dell’ambiente e degli individui. + + 1.1.3. Interazione con la simulazione per la modifica e l’aggiunta di ulteriori parametri o vincoli. + + 1.1.4. Presenza di un tempo virtuale e di un ciclo giorno/notte. + + 1.1.5. Visualizzazione, monitoraggio ed esportazione di informazioni e statistiche riguardanti l’andamento della simulazione. ## Utente @@ -20,13 +19,13 @@ Per essere il più vicini possibile ad una situazione agile reale, Andrea Acampora riveste il ruolo di committente all’interno di questo progetto. Il dialogo, portato avanti tra il Product Owner e il Committente può essere riassunto nella seguente intervista: > "Faccio parte del comitato tecnico scientifico. Alla luce della recente pandemia ho la necessità di un software, preferibilmente leggero e multipiattaforma, che sia in grado di simulare la diffusione di un virus all'interno della popolazione. -> Dalle nostre recenti analisi e dalle statistiche fornite dal ministero si evince una difficoltà nel prevedere i livelli di contagio in situazioni che prevedono l'interazione tra individui in spazi aperti e/o all'interno di strutture. A fronte di ciò si richiede la possibilità di configurare la simulazione in termini di caratteristiche degli individui, delle strutture e del virus. Nello specifico deve essere possibile descrivere la popolazione in termini di età e cardinalità. Le strutture, le quali avranno pericolosità di contagio personalizzata, possono essere collocate arbitrariamente all'interno dell'ambiente. Il virus deve essere configurato considerando il tasso di diffusione, periodo medio di positività e la probabiltà di sviluppare una forma grave della malattia. -> Collaborando con vari team di ricerca internazionali, è necessario che la configurazione sia il più possibile agile ed esprimibile in un linguaggio english-like utilizzando la terminologia propria del settore. +> Dalle nostre recenti analisi e dalle statistiche fornite dal ministero si evince una difficoltà nel prevedere i livelli di contagio in situazioni che implicano l'interazione tra individui in spazi aperti e/o all'interno di strutture. A fronte di ciò si richiede la possibilità di configurare la simulazione in termini di caratteristiche degli individui, delle strutture e del virus. Nello specifico deve essere possibile descrivere la popolazione in termini di età e cardinalità. Le strutture, le quali avranno pericolosità di contagio personalizzata, possono essere collocate arbitrariamente all'interno dell'ambiente. Il virus deve essere configurato considerando il tasso di diffusione, periodo medio di positività e la probabiltà di sviluppare una forma grave della malattia. +> Collaborando con vari team di ricerca internazionali è necessario che la configurazione sia il più possibile agile ed esprimibile in un linguaggio english-like. > La simulazione deve rispecchiare nel modo più fedele possibile l'avanzare della giornata nel mondo reale in cui la maggior parte degli individui interagisce all'aperto e all'interno delle strutture prevalentemente durante il giorno con un afflusso verso le proprie abitazioni durante le ore notturne. Gli individui potranno quindi contrarre il virus muovendosi all'interno dell'ambiente e interagendo con le strutture in loro prossimità considerando strategie di ingresso personalizzate per ciascuna di esse. -> Si richiede la possiblità di introdurre una particolare tipologia di struttura, l'ospedale, necessario per la cura degli individui, simulando in questo modo la pressione del virus sul reparto sanitario. +> Si richiede la possiblità di introdurre una particolare tipologia di struttura, l'ospedale, necessaria per la cura degli individui, simulando in questo modo la pressione del virus sul reparto sanitario. > Al fine di anticipare e simulare le decisioni prese nella gestione della pandemia è richiesta la possibliltà di interagire dinamicamente con la simulazione inserendo vincoli e parametri dinamici quali obbligo di indossare la mascherina, chiusura di strutture e introduzione di una campagna vaccinale. -> Vi è inoltre la necessità di monitorare costantemente l'andamento della simulazione rispetto all'evoluzione del contagio e lo spostamento degli individui mediante interfaccia grafica. Essa deve comprendere anche grafici con le principali statistiche. Quindi è sicuramente utile la possibilità di mettere in pausa e stoppare preventivamente la simulazione ed impostarne la velocità. -> Potendo eseguire simulazioni con parametri differenti, si rende necessario un meccanismo di esportazione dei dati a fini comparativi e di archiviazione. In particolare, i dati rilevanti sono il tasso di letalità del virus, il numero di individui morti, malati e curati, il numero totale di malati gravi e il periodo medio di ospedalizzazione." +> Vi è inoltre la necessità di monitorare costantemente l'andamento della simulazione rispetto all'evoluzione del contagio e lo spostamento degli individui mediante interfaccia grafica. Essa deve comprendere anche grafici con le principali statistiche. È sicuramente utile la possibilità di mettere in pausa e stoppare preventivamente la simulazione ed impostarne la velocità. +> Potendo eseguire simulazioni con parametri differenti, si rende necessario un meccanismo di esportazione dei dati a fini comparativi e di archiviazione. In particolare, i dati rilevanti sono la pressione ospedaliera e il numero di individui sani, infetti, malati gravi e morti." Al fine di schematizzare esplicitando maggiormente i requisiti utente è stato prodotto il seguente diagramma dei casi d'uso utile anche per le fasi successive. @@ -35,62 +34,77 @@ Al fine di schematizzare esplicitando maggiormente i requisiti utente è stato p Alla luce di un approfondimento dell'intervista con il committente si evincono i seguenti requisiti: 2. Requisiti utente - + 2.1. Configurazione delle caratteristiche iniziali della simulazione - + 2.1.1. Durata della simulazione in termini di giorni - + 2.1.2. Cardinalità della popolazione di individui - + 2.1.3. Media e deviazione standard dell’età della popolazione - + 2.1.4. Caratteristiche virus - + 2.1.4.1. Tasso di diffusione del virus - + 2.1.4.2. Probabilità di sviluppo di una forma grave della malattia associata al virus - + 2.1.4.3. Periodo medio di positività al virus - + 2.2. Configurazione delle caratteristiche dell’ambiente - + 2.2.1. Dimensione della griglia che rappresenta logicamente l’ambiente - + 2.2.2. Descrizione delle strutture presenti nell’ambiente - + 2.2.2.1. Parametri della struttura - + 2.2.2.1.1. Tipologia - + 2.2.2.1.1.1. Edificio Generico: tipologia di struttura configurabile dall’utente con cui gli individui possono interagire - + 2.2.2.1.1.2. Ospedale: tipologia di struttura dedicata alla cura degli individui - + 2.2.2.1.2. Disposizione in termini di coordinate - + 2.2.2.1.3. Numero massimo di persone - + 2.2.2.1.4. Raggio di visibilità della struttura espresso in numero di celle - + 2.2.2.1.5. Media e deviazione standard del tempo di permanenza di un individuo nella struttura - + 2.2.2.1.6. Pericolosità di contagio all’interno della struttura - + 2.2.2.1.7. Strategia per discriminare l’accesso alla struttura - + 2.2.2.1.7.1. Filtro su media e deviazione standard dell’età degli individui - + 2.2.2.1.7.2. Entrata libera (default) - + 2.2.2.1.7.3. Entrata basata su probabilità di ingresso generica alla struttura - + 2.3. Interazione con la simulazione tramite GUI - + 2.3.1. Comandi stop, pausa, riprendi - + 2.3.2. Gestione della velocità di simulazione - + 2.3.3. Modifica e inserimento dinamico di vincoli e/o parametri - + 2.3.3.1. Obbligo mascherina - + 2.3.3.2. Chiusura di una tipologia di struttura - + 2.3.3.3. Vaccinazione esprimendo la percentuale di persone sottoposte - + 2.4. Visualizzazione dell’andamento della simulazione tramite GUI - + 2.4.1. Evoluzione ambiente e spostamento individui - + 2.4.2. Grafici sui dati principali della simulazione - + 2.5. Esportazione dati - + 2.5.1. Pressione ospedaliera - + 2.5.2. Individui sani - + 2.5.3. Individui infetti - + 2.5.4. Individui malati gravi - + 2.5.5. Individui morti - - -Infine, in seguito ai requisiti utente elencati, è emerso il seguente dominio modellato attraverso il diagramma delle classi di analisi qui riportato. + + 2.1. Configurazione delle caratteristiche iniziali della simulazione. + + 2.1.1. Durata della simulazione in termini di giorni. + + 2.1.2. Cardinalità della popolazione di individui. + + 2.1.3. Media e deviazione standard dell’età della popolazione. + + 2.1.4. Caratteristiche virus. + + 2.1.4.1. Tasso di diffusione del virus. + + 2.1.4.2. Probabilità di sviluppo di una forma grave della malattia associata al virus. + + 2.1.4.3. Media e deviazione standard del perdiodo di positività al virus. + + 2.2. Configurazione delle caratteristiche dell’ambiente. + + 2.2.1. Dimensione della griglia che rappresenta logicamente l’ambiente. + + 2.2.2. Descrizione delle strutture presenti nell’ambiente. + + 2.2.2.1. Parametri della struttura. + + 2.2.2.1.1. Tipologia. + + 2.2.2.1.1.1. Edificio Generico: tipologia di struttura configurabile dall’utente con cui gli individui possono interagire. + + 2.2.2.1.1.2. Ospedale: tipologia di struttura dedicata alla cura degli individui. + + 2.2.2.1.2. Disposizione in termini di coordinate. + + 2.2.2.1.3. Numero massimo di persone. + + 2.2.2.1.4. Raggio di visibilità della struttura espresso in numero di celle. + + 2.2.2.1.5. Media e deviazione standard del tempo di permanenza di un individuo nella struttura. + + 2.2.2.1.6. Pericolosità di contagio all’interno della struttura. + + 2.2.2.1.7. Strategia per discriminare l’accesso alla struttura. + + 2.2.2.1.7.1. Filtro su caratteristiche degli individui. + + 2.2.2.1.7.2. Entrata libera (default). + + 2.2.2.1.7.3. Entrata basata su probabilità di ingresso generica alla struttura. + + 2.3. Interazione con la simulazione tramite GUI. + + 2.3.1. Comandi stop, pausa, ripresa. + + 2.3.2. Gestione della velocità di simulazione. + + 2.3.3. Modifica e inserimento dinamico di vincoli e/o parametri. + + 2.3.3.1. Obbligo mascherina. + + 2.3.3.2. Chiusura di un gruppo di strutture. + + 2.3.3.3. Vaccinazione esprimendo la percentuale di persone sottoposte. + + 2.4. Visualizzazione dell’andamento della simulazione tramite GUI. + + 2.4.1. Evoluzione ambiente e spostamento individui. + + 2.4.2. Grafici sui dati principali della simulazione. + + 2.5. Esportazione dati. + + 2.5.1. Pressione ospedaliera. + + 2.5.2. Individui sani. + + 2.5.3. Individui infetti. + + 2.5.4. Individui malati gravi. + + 2.5.5. Individui morti. + + 2.6. Possibilità di eseguire la simulazione tramite WebApp. + + +In seguito ai requisiti utente elencati, è emerso il seguente dominio modellato attraverso il diagramma delle classi di analisi qui riportato. +Infine, si riportano i mockup realizzati insieme al committente per l'applicativo desktop. + + + +**Schermata iniziale** + + + +**Schermata Simulazione** + + + + + ## Funzionali 3. Requisiti funzionali + 3.1. La simulazione è basata su un modello ad eventi discreti in cui è presente un tempo virtuale che scandisce le iterazioni. - + 3.1.1. l’avanzare del tempo virtuale e quindi delle iterazioni sancisce l’alternanza del ciclio giorno/notte simulando l’avanzamento delle giornate. + + 3.1.1. L’avanzare del tempo virtuale e quindi delle iterazioni sancisce l’alternanza del ciclio giorno/notte simulando l’avanzamento delle giornate. + 3.1.2. Nel corso del giorno gli individui si muoveranno liberamente all’interno della mappa. + 3.1.3. Nel corso della notte in modo randomico alcuni individui saranno costretti a tornare nella propria casa. + 3.2. L’ambiente della simulazione è rappresentato logicamente in due dimensioni attraverso una griglia che consente la disposizione delle strutture e lo spostamento degli individui tramite coordinate. - + 3.2.1. All’inizio della simulazione vengono generate un numero ottimale di case e disposte in zone esterne alla griglia. - + 3.2.2. Ogni individuo verrà associato ad una casa. + + 3.2.1. All’inizio della simulazione vengono generate un numero ottimale di case e disposte esternamente nella parte inferiore della griglia. + + 3.2.2. Ogni individuo è associato ad una casa. + 3.3. Ciascuna struttura occupa una cella della griglia ed ha un raggio di visibilità espresso in celle. + 3.3.1. Le strutture sono definite dalle caratteristiche espresse dall’utente (2.2.2) e dal numero attuale di persone presenti al suo interno. + 3.3.2. Una volta nel raggio, ogni individuo sceglie di entrare con una probabilità che dipende dalle caratteristiche della struttura stessa. @@ -98,45 +112,45 @@ Infine, in seguito ai requisiti utente elencati, è emerso il seguente dominio m + 3.3.2.2. Assenza di un intervento dell’utente sulla chiusura della struttura (2.3.3.2). + 3.3.2.3. Strategia di accesso alla struttura (2.2.2.1.7). + 3.3.3. L’individuo rimarrà all’interno della struttura per un periodo di tempo che dipende anch’esso dalle caratteristiche della struttura (2.2.2.1.5). - + 3.3.4. Terminato il periodo di permanenza nella struttura, l’individuo sarà posto in una delle celle esterne ed adiacenti al raggio della struttura stessa. - + 3.4. Un individuo è un’entità che occupa una posizione all’interno della griglia + + 3.3.4. Terminato il periodo di permanenza nella struttura, l’individuo sarà posto in una delle celle esterne al raggio della struttura stessa. + + 3.4. Un individuo è un’entità che occupa una posizione all’interno della griglia. + 3.4.1. Ad ogni iterazione si muove in modo casuale in una qualsiasi cella adiacente a quella corrente. - + 3.4.1.1. Due individui possono trovarsi nella stessa posizione - + 3.4.2. La simulazione genera un insieme (2.1.2) di individui con diverse caratterstiche - + 3.4.2.1. Età, generata casualmente seguendo una distribuzione Gaussiana con parametri specificati eventualmente dall’utente (2.1.4). + + 3.4.1.1. Due individui possono trovarsi nella stessa posizione. + + 3.4.2. La simulazione genera un insieme (2.1.2) di individui con diverse caratterstiche. + + 3.4.2.1. Età, generata casualmente seguendo una distribuzione Gaussiana con parametri specificati eventualmente dall’utente (2.1.3). + 3.4.2.2. Stato di salute, rappresentato mediante un valore da 0 a 100. - + 3.4.2.2.1. Il valore di partenza è influenzato dall’età dell’individuo + + 3.4.2.2.1. Il valore di partenza è influenzato dall’età dell’individuo. + 3.4.2.2.2. Nel momento in cui il valore raggiunge lo 0 l’individuo è considerato deceduto. + 3.4.2.2.3. Un individuo non contagiato tende a riacquisire la propria salute. + 3.4.2.3. Ogni individuo ha un valore che rappresenta la sua immunità al virus. - + 3.4.2.3.1. Si incrementa in seguito di una guarigione dal virus o ad una somministrazione di un vaccino. + + 3.4.2.3.1. Si incrementa in seguito ad una guarigione dal virus o ad una somministrazione di un vaccino. + 3.4.2.3.2. Il valore viene decrementato con l’avanzare della simulazione. + 3.5. Il contagio tra diversi individui avviene dentro e fuori dalle strutture. + 3.5.1. Fuori dalle strutture, il contagio si verifica per prossimità di un individuo infetto ad uno sano, con una probabilità che varia in base alla distanza tra essi. + 3.5.2. All’interno delle strutture, il contagio avviene con una probabilità che dipende dalla pericolosità di contagio all’interno della struttura (2.2.2.1.6) e dal numero di infetti presenti nella struttura. - + 3.5.3. Indipendentemente dal luogo, il contagio è influenzato dal tasso di diffusione del virus (2.1.5.1), dall’immunità dell’individuo (3.4.2.3) e dalla presenza di alcuni vincoli o parametri dinamici settabili durante la simulazione.(2.3.3). + + 3.5.3. Indipendentemente dal luogo, il contagio è influenzato dal tasso di diffusione del virus (2.1.4.1), dall’immunità dell’individuo (3.4.2.3) e dalla presenza di alcuni vincoli o parametri dinamici settabili durante la simulazione (2.3.3). + 3.5.4. L’ individuo contrae il virus per un determinato periodo. - + 3.5.4.1. Il periodo viene calcolato seguendo una distribuzione Gaussiana a seconda del parametro di durata media (2.1.5.3). - + 3.5.4.2. Durante il periodo di positività al virus, lo stato di salute diminuisce in modo costante a seconda della gravità della malattia associata al virus che dipende dallo stato di salute nel momento del contagio e dal parametro probabilità di sviluppare una forma grave della malattia (2.1.5.2). + + 3.5.4.1. Il periodo viene calcolato seguendo una distribuzione Gaussiana a seconda del parametro di durata media (2.1.4.3). + + 3.5.4.2. Durante il periodo di positività al virus, lo stato di salute diminuisce in modo costante a seconda della gravità della malattia associata al virus che dipende dallo stato di salute nel momento del contagio e dal parametro probabilità di sviluppare una forma grave della malattia (2.1.4.2). + 3.6. L’unico modo per curare un individuo dalla malattia provocata dal virus è l’ospedale. + 3.6.1. Nel momento in cui la vita di un individuo scende sotto a una soglia critica esso può entrare in un ospedale al fine di ristabilire la propria salute. + 3.6.2. Nel caso in cui nell’ambiente non siano presenti ospedali oppure abbiano raggiunto la capienza massima, l’individuo continua a spostarsi all’interno dell’ambiente. + 3.6.3. Nel caso in cui sia disponibile un ospedale, l’individuo viene spostato al suo interno e, per il periodo di permanenza, la cura aumenta il suo stato di salute. + 3.7. La configurazione fornita dall’utente viene espressa mediante l’utilizzo di un DSL english-like. + 3.8. Durante la simulazione vengono visualizzati i dati principali eventualmente tramite l’ausilio di grafici. - + 3.9. Esportazione dei dati della simulazione mediante formati standard come da requisito 2.5 + + 3.9. Esportazione dei dati della simulazione mediante formati standard come da requisito 2.5. ## Non funzionali 4. Requisiti Non Funzionali + 4.1. Mantenimento della fluidità del sistema e dell’interattività anche su dispositivi con risorse limitate. - + 4.1.1. Requisito minimo: 4 GB di RAM, CPU Dual Core - + 4.2. L’applicazione deve essere cross-platform ossia funzionare correttamente su diversi sistemi operativi: Linux, Windows, Mac OS. + + 4.1.1. Requisito minimo: 4 GB di RAM, CPU Dual Core. + + 4.2. L’applicazione deve funzionare correttamente su diversi sistemi operativi: Linux, Windows, Mac OS. + 4.3. Estendibilità del sistema, in particolare delle tipologie di strutture e delle strategie di accesso ad esse. ## Implementazione 5. Implementazione - + 5.1. Il framework sarà implementato utilizzando come linguaggio principale Scala e testato mediante Scalatest. + + 5.1. Il framework sarà implementato utilizzando come linguaggio principale Scala. diff --git a/doc/report/03-architecture.md b/doc/report/03-architecture.md index 73feedba..25e5d8ad 100644 --- a/doc/report/03-architecture.md +++ b/doc/report/03-architecture.md @@ -21,7 +21,7 @@ Si applicano sulle componenti i seguenti vincoli di interazione: 1. Gli attori possono interagire solo con *boundary*. 2. I *boundary* possono interagire solo con *control* e *attori*. -3. *Entity* possono essere a conoscenza di altre *entity* ed interagire solo con i *control*. +3. Le *Entity* possono essere a conoscenza di altre *entity* ed interagire solo con i *control*. 4. I *control* possono interagire con i *boundary*, le *entity* e con altri *control*, ma non con gli attori. La scelta è ricaduta sul pattern ECB per la sua compatibilità con la *Clean Architecture*, che ci ha permesso di ottenere i seguenti vantaggi: @@ -29,13 +29,12 @@ La scelta è ricaduta sul pattern ECB per la sua compatibilità con la *Clean Ar + Separazione delle responsabilità, suddividendo il software in layers che possono essere aggiornati e mantenuti in modo indipendente. + Indipendenza dai framework. + Orientandosi ad una progettazione a componenti è possibile applicare il testing in maniera agile sostituendo alcuni di essi, ad esempio un *control*(che implementa un particolare caso d'uso) o un *boundary*, con test double. -+ I vincoli di interazione di ECB rispettano la *dependency rule* descritta dalla Clean Architecture, applicando inoltre nel caso dell'interazione tra boundary e controller la *dependency inversion*. ++ I vincoli di interazione di ECB rispettano la *dependency rule* descritta dalla Clean Architecture, applicando inoltre nel caso dell'interazione tra *boundary* e *control* la *dependency inversion*. Inoltre ECB ha facilitato il soddisfacimento dei seguenti requisiti, la cui realizzazione altrimenti sarebbe stata più complessa: -+ I requisiti utente 2.4 e 2.5 prevedono due modalità di interazione tra l'utente e il sistema: visualizzazione dell'andamento della simulazione tramite GUI ed esportazione dei dati. Entrambe necessitano degli stessi dati e in quanto boundary di ECB è stato possibile evitare ripetizioni di codice modellandoli come unico concetto astratto. -+ Dall'intervista è emersa la necessità del cliente di un progetto multipiattaforma che disponesse di una GUI JVM-based e di una JS-based. Anche in questo caso il concetto di boundary presente in ECB ha semplificato la progettazione. -+ Essendo l'engine elemento centrale dei software di simulazione, è necessario isolarlo adeguatamente al giusto livello di astrazione al fine di essere mantenibile. Grazie alla caratteristiche e ai vantaggi di ECB sopracitati, quali separazione delle responsabilità, indipendenza dai framework e testabilità, oltre alla dependency rule, è stato possibile progettarlo in maniera più agile. ++ I requisiti utente 2.4, 2.5 e 2.6 prevedono tre modalità di interazione tra l'utente e il sistema: visualizzazione dell'andamento della simulazione tramite GUI JVM-based, esportazione dei dati e GUI JS-based. In questo caso il concetto di boundary presente in ECB ha semplificato la progettazione ed ha evitato ripetizioni di codice in quanto entramble le GUI e l'esportatore non sono altro che tre boundary distinti che rispettano la stessa API nei confronti del *control*. ++ Essendo l'engine elemento centrale dei software di simulazione, è necessario isolarlo adeguatamente al giusto livello di astrazione al fine di renderlo maggiormente manutenibile. Grazie alla caratteristiche e ai vantaggi di ECB sopracitati, quali separazione delle responsabilità, indipendenza dai framework e testabilità, oltre alla dependency rule, è stato possibile progettarlo in maniera più agile. + Il requisito non funzionale 4.3 che prevede una generale estendibilità del sistema. ### Descrizione dei componenti dell'architettura @@ -43,15 +42,21 @@ Inoltre ECB ha facilitato il soddisfacimento dei seguenti requisiti, la cui real Lo schema architetturale fornito precedentemente evidenzia i seguenti componenti: + **Boundary**: rappresenta il concetto generico di *boundary* descritto in ECB, è stato scelto di non esplicitare gli specifici *boundary* presenti nel sistema in quanto espongono tutti la stessa api per l'interazione con il *control*. Inoltre, come descritto precedentemente, è necessario rappresentare il sistema mantenendo una sufficiente flessibilità che permetta l'aggiunta di diverse modalità di interazione con gli attori del sistema. -+ **Launcher**: è un componente appartenente al control e il punto di ingresso dell'applicazione. A seguito della segnalazione del boundary dell'intenzione dell'attore di avviare la simulazione comunica con il loader dopo aver eseguito controlli di validità preliminari sintattici sulla configurazione fornita. -+ **Loader**: è un componente appartenente al control. Effettua controlli rispetto alla completezza e alla validità delle specifiche incluse nella configurazione fornita dall'attore e si occupa della creazione e del caricamento dell'ambiente, poi restituito all'engine della simulazione. ++ **Launcher**: è un componente appartenente al control e il punto di ingresso dell'applicazione. A seguito della segnalazione del boundary dell'intenzione dell'attore di avviare la simulazione si occupa di coordinarne l'avviamento comunicando con il loader ed eventualmente con i boundary in caso di errori nella configurazione fornita. ++ **Loader**: è un componente appartenente al control. Coordina il caricamento ed il controllo di validità del file di configurazione fornito dall'attore sfruttando il componente Parser e si occupa della creazione e del caricamento dell'ambiente, poi restituito all'engine della simulazione. ++ **Parser**: è un componente appartente al control. Effettua controlli rispetto alla completezza ed alla validità delle specifiche incluse nella configurazione fornita dall'attore. Questa modellazione rende più flessibile l'aggiunta di diverse tipologie di parser ciascuna dedicata ad uno specifico formato di file. + **Engine**: è un componente appartenente al control. Esso si occupa di gestire il simulation loop aggiornando la simulazione e interagendo con i boundary. - + **Environment** : si tratta dell'*entity* principale che rappresenta l'ambiente simulato con tutte le caratteristiche. Nello schema architteturale sono riportate le principali *entity* che compongono l'environment: + **Virus** : *entity* che contiene le principali informazioni del virus. + **Entity** : *entity* che contiene le principali informazioni di ogni individuo all'interno della simulazione. - + **Building** : *entity* che contiene le principali informazioni di ogni struttura all'interno della simulazione. + + **Structure** : *entity* che contiene le principali informazioni di ogni struttura all'interno della simulazione. + +Di seguito un diagramma UML che mette in evidenza i *trait* principali dell'architettura descritta in questo capitolo. Il diagramma astrae dall'effettiva implementazione di alcuni tipi di dati citati esplicitando esclusivamente l'utilizzo della libreria **Monix**, la quale ha influenzato il suddetto design. -//includere a fine progetto un diagramma delle classi, indicando le interfacce dei boundary utilizzate. + + +Inoltre, al fine di rappresentare il flow derivato dall'architettura mostrata, viene fornito il seguente diagramma di sequenza. + +![Sequence-Diagram](imgs/sequence.svg) diff --git a/doc/report/04-detailed-design.md b/doc/report/04-detailed-design.md index a663514d..6fe84b5d 100644 --- a/doc/report/04-detailed-design.md +++ b/doc/report/04-detailed-design.md @@ -1,3 +1,387 @@ ## Design di dettaglio +Dopo aver descritto l'architettura del sistema, si procede con il design di dettaglio, in cui si evidenziano le scelte di progettazione dei componenti principali. In questo capitolo verranno esposti, oltre alle scelte di progettazione, i design pattern utilizzati ed una breve descrizione di come il codice è stato organizzato. + +### Obiettivi + +Il design del sistema segue un approccio che cerca di combinare i vantaggi del mondo funzionale e del mondo ad oggetti. In generale, la linea che abbiamo seguito è quella di preferire l'approccio funzionale favorendo la dichiaratività, l'immutabilità e la descrizione lazy della computazione evitando o eventualmente incapsulando i side-effects e le eccezioni. +In particolare, si è scelto di perseguire l'immutabilità evitando o incapsulando side-effects ed eccezioni in quanto nella maggior parte dei casi ciò permette di semplificare la logica del programma e soprattutto l'analisi e la comprensione del codice. Infatti, quando un elemento è mutabile, occorre ricostruire il suo stato rispetto a tutto il flow in cui esso viene utilizzato. Quindi, in questo design si è scelto di stressare l'immutabilità, anche per cercare di ottenere, ove possibile, il concetto di funzione pura, la quale a parità di input restituisce sempre lo stesso output. + +Inoltre, si è deciso di adottare, ovunque possibile, un approccio monadico. Questo approccio permette di rappresentare sequenze, anche complicate, di funzioni attraverso pipeline succinte che astraggono dal control flow e soprattutto dai side-effects. Infatti, l'approccio monadico consente di rappresentare i side-effects come effects sul quale si ha maggiore controllo ed inoltre, utilizzando la libreria Monix, è stato possibile ottenere facilmente anche il vantaggio di poter descrivere la computazione in modo lazy, limitando i side-effects nell'*end-of-the-world*. +La scelta della libreria Monix per il nostro approccio monadico è derivata dal fatto che Monix possiede la monade *Task*, la quale consente di rappresentare la specifica di una computazione lazy o asincrona che, una volta eseguita, produrrà un risultato, assieme a tutti i possibili side-effects. *Task* infatti non è *eager*, ed è *referential transparent* nel suo utilizzo safe. Questo permette di dividere l'esecuzione dalla descrizione della computazione, favorendo la dichiaratività. + +### Design a componente dell'architettura + +Il pattern architetturale *ECB* descritto nel capitolo precedente si presta facilmente alla *component-programming* in cui sostanzialmente ogni elemento appartenente all'entity, al boundary o al control lo si vede come un componente che ha dipendenze da altri (come ad esempio l'engine che necessita dei boundary per poter comunicare loro il nuovo stato). +Per questo motivo, si è scelto di progettare l'architettura a livello di design di dettaglio scegliendo il **Cake Pattern** grazie al quale è stato possibile rappresentare ogni elemento dell'architettura come un componente con una ben definita interfaccia offerta agli altri componenti e delle ben definite dipendenze. L'applicazione risultante quindi viene costruita instanziando ogni componente e collegandolo in modo da soddisfare le varie dipendenze. + +Grazie a questo pattern è stato possibile rappresentare esplicitamente le dipendenze tra i vari elementi architetturali permettendo di fare dependency injection agilmente. + +La combinazione ECB + Cake Pattern ha semplificato notevolmente il raggiungemento del requisito 2.6 il quale comporta la realizzazione di un applicazione cross-platform (JVM e JS). Infatti, ha permesso di limitare al minimo la ripetizione di codice, condividendo tra le diverse piattaforme tutto il design dei componenti core, limitando le modifiche principali ai boundaries e agli elementi platform-specific. + +Il design di ogni componente è il medesimo: + +![component_diagram](imgs/detailed_design_component_diagram.svg) + +Come si vede dal diagramma, gli elementi presenti in ciascun componente sono: + +- un trait che definisce l'interfaccia, cioè il contratto, del componente; qui chiamato per comodità **ComponentInterface** +- un trait che si occupa di fornire l'instanza del componente; qui chiamato per comodità **Provider** +- un trait, qui chiamato per comodità **Component**, che utilizza le dipendenze richieste dal componente (**Requirements**) e contiene l'implementazione del componente stesso, qui chiamata per comodità **ComponentImpl**. +- un trait che espone tutti i concetti del componente necessari per poter essere utilizzato assieme agli altri, qui chiamato per comodità **Interface**. + +Combinando tutto ciò con gli obiettivi del design descritti nella sezione precedente, *si può riassumere il design in questo modo*: + +- Ogni elemento architetturale è rappresentato come un componente. +- Le entities sono progettate favorendo un approccio funzionale. +- Le computazioni sono rappresentate con un approccio monadico attraverso l'utilizzo della libreria Monix. +- Gli eventi di ogni componente boundary sono rappresentati come stream di eventi attraverso l'uso di *Observable* di Monix. +- I side effects sono limitati all'*end-of-the-world*. + +### Boundary + +Come anticipato, ciascun *boundary* incapsula l'interazione con gli attori del sistema. +Il pattern ECB pone le sue fondamenta sul fatto che tutti i Boundary siano uguali e passivi rispetto agli altri componenti, ricevendo le stesse informazioni dai control ed incapsulando le interazioni. Le interazioni degli attori del sistema con i componenti boundary vengono rappresentate nel nostro design come stream di eventi, sfruttando **Observable** di Monix. + +Tra i boundary che possono essere iniettati all'interno del simulatore deve essere sempre essere prensente un **ConfigBoundary** dedicato al caricamento della configurazione e alla visualizzazione degli errori in essa. La necessità di un tipo speciale di Boundary è nata dal fatto che nel nostro caso abbiamo due tipologie di eventi: + +- *eventi asincroni*: sono quelli che vengono emessi dall'interazione dell'attore con il sistema +- *eventi sincroni*: rappresentano quegli eventi necessari per la configurazione della simulazione, e che quindi devono essere ricevuti in un certo ordine. + +Al fine di rispettare la *dependency rule* descritta dalla Clean Architecture e da ECB, si è deciso di modallare un ulteriore trait **ConfigBoundary** che estende il trait **Boundary** con due metodi necessari per ottenere la configurazione e segnalare errori al boundary. In questo modo, il componente Boundary rimane passivo, infatti non eseguirà mai chiamate dirette agli elementi del control rispettando la *dependency rule*. + +![config_boundary](imgs/detailed_design_config_boundary.svg) + +Perciò, tra i boundary specificati per l'applicazione ve ne sarà solamente uno di tipo *ConfigBoundary*, il quale gestirà, tra le altre cose, anche la parte di inizializzazione della simulazione con il compito di fornire la configurazione e gestire gli eventuali errori verso l'attore del sistema. + +I boundary sviluppati sono i seguenti: + +- **GUI-JVM**: si occupa della creazione di un'interfaccia grafica dell'applicazione Desktop jvm-based. +- **Esportatore**: si occupa dell'esportazione dei dati aggregati e delle statistiche riguardanti la simulazione in un file in formato `.csv`. +- **GUI-JS**: si occupa della creazione dell'interfaccia grafica della WebApp js-based. Come detto precedentemente infatti, l'applicazione sviluppata dovrà essere cross-platform e la specifica di un apposito boundary rientra tra le parti platform-specific. + +Considerando la necessità di eseguire il rendering della simulazione, nonostante solitamente i framework per gestire le GUI siano fortemente object-oriented e sfruttino principalmente side-effects, si è deciso comunque di descrivere la struttura delle view utilizzando un approccio monadico, isolando tutto ciò che non è funzionale nell'*end-of-the-world*. +A tal proposito, al fine di isolare l'approccio a side-effects tipico del disegno degli elementi su *"canvas"*, è stata creata la **type-class** **Drawable** la quale rappresenta l'estensione di un tipo generico con le capacità di disegno. Questo è un concetto comune e non platform-specific. + +![drawable_concept](imgs/detailed_design_drawable_general.svg) + +La type class è stata progettata per lavorare con gli extension methods di Scala (non facilmente rappresentabili in UML). +Grazie a questa type-class la capacità di essere disegnati può essere inserita a piacere su ogni tipo anche dopo la sua definizione. Tutto ciò grazie al pattern **type class** che ci permette di definire metodi dotati di **polimorfismo ad-hoc**. +Essendo un concetto comune a tutti i boundary, *Drawable* astrae dal tipo di grafica utilizzata e definisce al suo posto un **abstract type** (*Graphic*). +In questo modo i boundary platform-specific potranno specificare il proprio tipo ed eseguire il "pimping" di operazioni basate su di essa. + +Inoltre, al fine di rappresentare il concetto di sorgente di eventi a livello di boundary è stato modellato il trait **EventSource**. + +![event_source](imgs/detailed_design_event_source.svg) + +Il seguente concetto modella tutto ciò che è in grado di emettere eventi dovuti all'interazione dell'attore: pulsanti, text fields ecc... In questo modo i suddetti componenti possono essere integrati con maggiore facilità ed elasticità all'interno di un contesto monadico. +**Event** rappresenta gli eventi emessi dai boundary ed è modellato attraverso un *Product Type*. Ogni evento specifica il suo interesse rispetto ad un particolare stato dell'engine, esprimendo il fatto che esso, in un particolare stato dell'applicazione, potrebbe perdere di significatività. + +#### JVM + +Il boundary che gestisce la gui jvm-based si occupa di visualizzare l'interfaccia grafica del simulatore dell'applicazione Desktop. +L'applicazione è composta da due schermate principali che soddisfano i mockup sviluppati ed approvati dal committente mostrati nel capitolo dei requisiti. + +Il design di questo boundary è avvenuto considerando l'utilizzo della libreria **Java Swing**. Al fine di poter integrare agilmente il design monadico di tutto il sistema con la gui, si è deciso di adottare un approccio in cui le varie view consistono in descrizioni monadiche lazy della costruzione e del comportamento dei componenti, in modo tale da aderire al paradigma funzionale incapsulando la natura object-oriented e side-effect oriented di **Java Swing**. +Inoltre, considerando che i boundary comunicano con i control emettendo eventi, tutti i componenti di Java Swing necessari all'interazione degli attori sono stati ridefiniti attraverso dei wrapper ad-hoc che consentono di integrarli agilmente in un contesto monadico (**MonadComponents**). In particolare, ogni componente in questione (wrapper di: *JButton*, *JComboBox*, *JTextField*, ...) estende il trait **EventSource** descritto in precedenza. + +In questo modo ogni componente, il quale esprime, in Java Swing, il proprio comportamento attraverso side-effect, diventa un componente facilmente integrabile in un contesto monadico nel quale il flow è gestito attraverso stream di eventi. + +Inoltre, al fine di gestire il disegno dei concetti di simulazione è stata utilizzata la type-class *Drawable* descritta precedentemente. Maggiori dettagli verrano forniti nel capitolo *Implementazione*. + +#### JS + +Il boundary che gestisce la gui js-based si occupa di visualizzare l'interfaccia grafica del simulatore della WebApp. +In questo caso è stata sviluppata un'unica schermata in quanto non erano previste indicazioni dal committente. + +Il design di questo boundary è avvenuto considerando l'utilizzo della libreria **Scalajs**. Scalajs, per quanto riguarda l'API fornita, emula fortemente JavaScript rendendo poco agevole il suo utilizzo diretto in un approccio monadico. Perciò, al fine di poter integrare agilmente il design monadico di tutto il sistema con la gui, si è deciso di adottare un approccio in cui le varie view consistono in descrizioni monadiche lazy della costruzione e del comportamento dei componenti similmente a quanto descritto per il *boundary jvm-based*. + +Similmente a quanto descritto precedentemente, considerando che i boundary comunicano con i control emettendo eventi, tutti i componenti HTML-based di Scalajs necessari all'interazione degli attori sono stati ridefiniti attraverso dei wrapper ad-hoc che consentono di integrarli agilmente in un contesto monadico (**MonadComponents**). In particolare, ogni componente in questione (wrapper di: *Button*, *Select*, *Input*, ...) estende il trait **EventSource** descritto in precedenza. + +Al fine di gestire il disegno dei concetti di simulazione è stata utilizzata la type-class *Drawable* descritta precedentemente. Maggiori dettagli verrano forniti nel capitolo *Implementazione*. + +#### Exporter + +L'**Exporter** è un boundary con il compito di esportare alcune statistiche e dati aggregati della simulazione su un file di testo in formato *`.csv`*. Quest'ultimo viene aggiunto come componente al *launcher* dell'applicazione ed in quanto *boundary* ad ogni step della simulazione riceve *l'environment* aggiornato. + +L'esportazione dei dati su file avviene continuamente ad ogni step in modo da poter analizzare, una volta terminata la simulazione, l'andamento dei parametri principali nel corso del tempo. + +Al fine di ottenere un design che separi l'estrazione delle statistiche dal concetto di Exporter stesso, l'intero processo di estrazione dei dati viene delegato al trait **DataExtractor**. +Un DataExtractor contiene un nome utile per comprendere la tipologia di statistica che si intende calcolare ed un metodo *generico* per estrarre quest'ultima a partire dall'*environment* della simulazione. + +Al fine di elencare e riassumere tutte le tipologie di dati e statistiche che è possibile esportare è stata creata l'enum **StatisticalData**. +Inoltre, attraverso l'utilizzo del pattern **Adapter** , implementato con **Given Conversions**, si è reso possibile evitare di utilizzare ogni singola implementazione del trait DataExtractor mentre è sufficiente creare un elenco di statistiche dell'enum ed il pattern si occuperà poi di istanziare per ognuna il rispettivo estrattore. + +![Exporter](imgs/detailed_design_exporter.svg) + +Infine, l'aggiunta del trait *DataExtractor* si è rilevata utile anche nella creazione dei grafici in quanto anch'essi hanno l'obiettivo di mostrare alcuni dati aggregati durante la simulazione. In questo modo si sono evitate inutili ripetizioni di codice ed è stato relativamente semplice implementare i vari grafici presenti. + +Maggiori dettagli sulle effettive implementazioni degli estrattori e sull'esportatore verranno forniti nel capitolo *Implementazione*. + +### Launcher + +Come anticipato, il Launcher è un componente appartenente al control ed è il punto di ingresso dell'applicazione. Essendo un componente dell'architettura esso è modellato tramite il **Cake pattern** come descritto precedentemente. + +Il suo scopo è coordinare l'avviamento della simulazione comunicando con il loader ed eventualmente con i boundary in caso di errori nella configurazione fornita. + +Al fine di mantenere l'approccio funzionale, si evitano qualsiasi forma di eccezione incapsulandole nei tipi di dato ritornati. In particolare, è possibile notare **ConfigurationResult**, un Product Type restituito dal metodo *parseConfiguration* del *Loader*, che incapsula gli eventuali errori all'interno della configurazione fornita dall'utente. + +### Loader + +Il Loader, appartenente al *Control*, si occupa di caricare la configurazione fornita dall'utente, creare l'environment iniziale ed infine lanciare l'engine della simulazione. Esso è un componente dell'architettura, quindi è modellato tramite il **Cake pattern** come descritto precedentemente. + +I componenti di cui necessita a livello architetturale sono l' *Environment*, in quanto una volta caricata la configurazione dovrà essere inizializzato con i parametri definiti dall'utente, l'*Engine* il quale dovrà iniziare la simulazione con l'environment aggiornato ed infine il *Parser*, necessario per il caricamento della configurazione. + +Per quanto riguarda la configurazione della simulazione, si è scelto di rimanere coerenti con gli obiettivi di design descritti precedentemente. Infatti, si è scelto di perseguire un approccio estremamente dichiarativo considerando il file di configurazione come un file *Scala* esprimibile tramite un **DSL** implementato attraverso il pattern **Pimp my Library** con l'utilizzo di **extension methods**. + +In questo modo, anche un utente che non conosce il linguaggio Scala può creare e modificare i file di configurazione della simulazione in maniera semplice a seconda delle esigenze. +A fronte di ciò è stato definito il trait *Configuration* ed una case class *VirsimConfiguration* la quale verrà istanziata direttamente con i parametri definiti dall'utente. + +Nella creazione dell'environment viene delegata la creazione delle entità alla classe *EntityFactory*, fornita al loader tramite **given instance** ed utilizzata come parametro implicito nel metodo *createEnvironment*. Quest'ultima utilizza il pattern **Factory** per creare il Set di entità partendo dai parametri del file di configurazione. + +Nel design del Loader si è scelto di separare la responsabilità del caricamento della configurazione con la creazione dell'environment e a tal fine è stato introdotto il componente Parser a supporto del Loader. + +![Loader_architecture](imgs/detailed_design_loader.svg) + +#### Parser + +Il Parser, anch'esso appartenente al *Control*, ha il compito di effettuare controlli di validità sulla configurazione fornita dall'utente ed in seguito istanziarla e restituirla al *Loader*. Esso è un componente dell'architettura, quindi è modellato tramite il **Cake pattern** come descritto precedentemente. + +Oltre a fungere da supporto al Loader per il caricamento della configurazione, la definzione del trait *Parser* permette di aggiungere in maniera facile ulteriori componenti per caricare file di configurazione in formati diversi. +A fronte di ciò sono state create due tipologie di Parser: + +- **ScalaParser**: si occupa di caricare i file di configurazione scritti tramite il DSL in Scala utilizzando la classe *ScriptEngineManager* di Java, fornita tramite **given instance**. +- **YAMLParser**: si occupa di caricare i file di configurazione scritti in formato YAML. + +Entrambe le tipologie implementano il trait Parser il quale contiene il metodo *checkErrors* già implementato in quanto è comune a tutti i parser presenti. + +Nel design del Parser si è adottato il pattern **Pimp my library** per aumentare chiarezza e leggibilità nei metodi per il controllo dei parametri della simulazione inseriti dall'utente. + +Per quanto riguarda la lettura del file di configurazione, la quale in quanto progetto cross-platform può avvenere sia tramite applicazione desktop che tramite web app, si è reso necessario aggiungere un ulteriore componente *Reader*. + +#### Reader + +Il Reader, componente appartenente al *Control*, ha il compito di leggere il file di configurazione fornito dall'utente e restituirlo al Parser il quale poi provvederà a caricarlo. + +La motivazione dell'aggiunta di questo componente risiede nel fatto che non è possibile trovare una modalità di lettura di un file comune sia ad un'applicazione desktop che ad una web app. +Per questo motivo è stato introdotto il trait Reader con due distinte implementazioni: + +- **JVMReader**: si occupa di leggere il file utilizzando le tradizionali API di Java per l'IO. +- **JSReader**: si occupa di leggere il file fornito dal broswer utilizzando *FileReader* di Javascript. + +Nel design del Reader si è cercato comunque di generalizzare un concetto di *Path* del file e ciò è stato possibile sfruttando il **family polimorphism** grazie agli **abstract types** di Scala. + +Per quanto riguarda il *JSReader* la lettura del file viene svolta utilizzando un approccio ad eventi per rimanere coerenti con l'Event Loop di JavaScript. + +L'interazione tra Loader, Parser e Reader viene riassunta nel seguente diagramma: + +![Loader_Parser_Reader_Interaction](imgs/detailed_design_loader_diagram.svg) + +Maggiori dettagli verranno forniti nel capitolo *Implementazione*. + +### Engine + +L'Engine si occupa di gestire il simulation loop aggiornando la simulazione e interagendo con i boundary. Esso è un componente dell'architettura, quindi è modellato tramite il **Cake pattern** come descritto precedentemente. + +Nel design dell'engine e quindi nel design del simulation loop è opportuno considerare gli obiettivi di design descritti precedentemente. Infatti, si desidera creare un motore con cui gestire la simulazione che adotti un approccio funzionale e il più possibile dichiarativo. +Il simulation loop è stato espresso mediante un approccio monadico basato su Monix che ha consentito di specificare il loop attraverso una descrizione lazy della computazione rimanendo altamente dichiarativi. +Infatti, come spiegato successivamente, la gestione degli eventi, delle logiche e degli aggiornamenti in generale è stata espressa mantenendo un'elevata dichiaratività, senza preoccuparsi del control-flow e senza gestire in modo imperativo i thread di esecuzione. + +Il compito dell'Engine, oltre a raccogliere gli eventi provenienti dai boundary, è eseguire le logiche di aggiornamento della simulazione. Esse devono essere eseguite nell'ordine specificato. Inoltre, come da requisito 2.3.2 è necessario poter impostare la velocità di simulazione oltre che gestire lo stato (2.3.1) e gestire le interazioni dinamiche dell'utente (2.3.3). +Da questo ne deriva la necessità di esprimere una configurazione dell'Engine, la quale sia indipendente dalla particolare istanza di simulazione, e che possa essere impostata a livello di applicazione. A tal fine è stato creato il trait **SimulationConfig** che rappresenta la configurazione da utilizzare nell'Engine. Al fine di iniettare la configurazione nell'engine, esso è stato progettato per utilizzare un **context parameter** di tipo **SimulationConfig** a livello di costruttore. +In questo modo la configurazione viene fornita con un approccio che rivela maggiormente l'intento configurativo. + +![simulation_config](imgs/detailed_design_simulation_config.svg) + +*SimulationConfig* necessita delle seguenti configurazioni: + +- *maxEventPerIteration*: al fine di evitare una possibile starvation dell'engine dovuta all'arrivo continuo di eventi dai boundary, si considerano solamente un certo numero di eventi ad ogni tick dell'engine; questo parametro consente di impostarne il numero. +- *engineSpeed*: rappresenta la velocità di un singolo tick dell'engine. +- *engineStatus*: rappresenta lo stato corrente dell'engine. Gli stati sono *running*, *paused* e *stopped*. +- *logics*: rappresenta la sequenza di logiche da eseguire ad ogni iterazione. +- *eventLogics*: rappresenta la funzione che associa ogni evento proveniente dai boundary alla specifica logica in grado di gestirlo. + +In questa sezione si astrae da come viene effettuato l'aggiornamento di tali configurazioni (come quella della velocità e dello stato). +Come si vede, grazie alla configurazione, l'engine ha a disposizione tutto ciò che gli serve per poter adempiere ai suoi compiti. + +Precedentemente è stato accennato che l'engine provvede alla gestione di tutte le logiche e degli eventi. A tal fine è necessario che l'engine adotti una strategia per far si che allo stesso tempo sia in grado di: + +- raccogliere gli eventi dai boundary, +- eseguire il loop di simulazione aggiornando i boundary dopo aver computato il nuovo environment. + +La strategia individuata può essere riassunta nei suoi passi principali con il seguente diagramma di attività (relativo ad una singola iterazione): + +![engine_activity](imgs/detailed_design_engine_activity.svg) + +Tutto ciò, grazie all'utilizzo di Monix, è stato progettato in modo tale da poter essere espresso mediante una descrizione lazy della computazione. + +Infine, a seconda della tipologia di logica, essa deve estendere il tipo **UpdateLogic**, per le logiche di aggiornamento, o **EventLogic**, per le logiche associate agli eventi. **UpdateLogic** ed **EventLogic** sono espressi mediante **type alias** di `Environment => Task[Environment]` forzando, come da obiettivo di design, una descrizione lazy della computazione. I **type-alias** consentono di aumentare la comprensibilità del design. + +Considerando che le logiche implementano tutte lo stesso contratto e sono inserite nella configurazione dell'engine, esse possono essere applicate in modo estremamente dichiarativo mediante la tecnica del **folding**. Questo concede una buona flessibilità ed estendibilità nell'aggiunta di nuove logiche e nella gestione delle priorità. + +Ulteriori dettagli sull'*engine* e sul *simulation loop* saranno riportati nel capitolo *Implementazione*. + +### Environment + +Appartenente alle **Entity**, è un componente dell'architettura modellato tramite **Cake Pattern** come descritto in precedenza. +Si occupa di mantenere le informazioni delle Strutture, delle Entità, del Virus, oltre a quelle relative al tempo corrente, alla durata della simulazione e alla grandezza della griglia. +Ad ogni iterazione della simulazione viene aggiornato dal componente Engine. In particolare l'aggiornamento è gestito tramite il metodo `update`. +Le strutture vengono mantenute all'interno di un Set, così come le entità che si muovono liberamente all'interno della griglia, mentre ogni struttura mantiene una lista delle entità al suo interno. Questo ha evitato problemi legati all'aggiornamento, in quando ogni entità è presente in modo univoco all'interno dell'Environment. É presente anche un metodo `allEntities` il quale restituisce un Set contenente tutte le entità interne ed esterne alle strutture della simulazione. + +#### Common + +In questa sezione verranno descritti i concetti comuni presenti all'interno del modello del dominio, cioè il design di quegli elementi che trovano utilizzo nei tre macro concetti del dominio: entità, strutture e virus. +Gli elementi descritti sono i seguenti: + +- Proprietà gaussiane +- Eventi descritti da una probabilità +- Spazio +- Tempo + +##### Proprietà gaussiane + +Diverse proprietà all'interno del dominio sono descritte da una distribuzione gaussiana come ad esempio l'età degli individui oppure il tempo di permanenza all'interno delle strutture. Al fine di modellare il concetto di distribuzione gaussiana è stato creato il *trait* **Gaussian**. Esso è generico in un tipo il quale rappresenta il tipo della proprietà da generare. +La motivazione che deriva dalla modellazione di questo concetto è quello di evitare ripetizione di design e di codice nel modellare distribuzioni gaussiane. + +Infatti, considerando che tutto ciò che è necessario a generare un valore che rispetta una distribuzione gaussiana è condiviso indipendentemente da ciò che deve essere prodotto come tipo finale, si è deciso di progettare questo concetto utilizzando il pattern **Template Method**. + +![guassian_diagram](imgs/detailed_design_gaussian.svg) + +Il **Template Method** in questione è il metodo `next` definito nel trait **Gaussian** il quale contiene la logica di generazione di un valore, del tipo desiderato `A`, che rispetta la distribuzione guassiana. Al suo interno, dopo aver generato un valore numerico, esso viene convertito nel tipo desiderato grazie al metodo protetto `convert` che sarà l'unico metodo per cui verrà eseguito l'*override* nelle sottoclassi. +In questo modo si riesce ad astrarre tutto ciò che è comune, specificando solo ciò che realmente cambia a livello poi implementativo. + +Le specializzazioni che sono state progettate in quanto utili per il sistema sono: + +- *GaussianDurationTime*: è un generatore di "tempi di durata" che segue una distribuzione gaussiana. Essi sono espressi secondo un'unità di misura come ad esempio: minuti, secondi ... +- *GaussianIntDistribution*: è un generatore di interi che segue una distribuzione guassiana. + +##### Eventi descritti da una probabilità + +La simulazione prende in considerazione molti eventi che accadono con una certa probabilità la cui formula per computarla è influenzata da diversi parametri. Un esempio è sicuramente l'evento di contagio la cui probabilità di accadimento dipende da diversi fattori, ad esempio nel contagio all'esterno abbiamo: distanza tra individui, immunità sviluppata dagli individui, presenza di mascherine, ecc... che devono partecipare nel calcolo della probabilità. Dopodiché ovviamente è necessario un "algoritmo" in grado di capire e simulare, data la probabilità, se l'evento è accaduto oppure no. + +A tal fine è stata progettata la **type class** *Probable* la quale consente di estendere un tipo generico con la capacità di agire come un evento descritto da una certa probabilità di accadimento. + +Per poter aderire l'utilizzatore in questo modo dovrà solamente fornire l'implementazione della formula. + +A partire dalla **type class** *Probable* è stato definito un algoritmo aggiuntivo il quale specifica il **context-bound** *Probable* sul tipo generico accettato e che permette di simulare se l'evento, data la probabilità computata dalla formula specificata dall'utilizzatore, è avvenuto o meno. Questo metodo permette di raggiungere l'obiettivo definito in precedenza ossia definire un evento probabile andando a specificare solamente la formula per calcolare la probabilità abilitando un utilizzo altamente dichiarativo del concetto. +Il risultato viene espresso attravero il *sum type ProbabilityResult*. +Allo stesso tempo, al fine di essere compatibile anche con API che lavorano con tipi *Boolean* si è sfruttato il pattern **Adapter**, grazie alle **given Conversion** offerte da Scala, per poter convertire agilmente il tipo *ProbabilityResult* in *Boolean* e viceversa. + +Grazie a questa type-class la capacità di rappresentare un evento probabile può essere inserita a piacere su ogni tipo anche dopo la sua definizione. Tutto ciò grazie al pattern **type class** che ci permette di definire metodi dotati di **polimorfismo ad-hoc**. + +##### Spazio + +Le strutture devono essere posizionate all'interno dello spazio dell'environment e, similmente, le entità devono essere in grado di spostarsi e quindi avere una posizione all'interno di esso. Per questo motivo è necessario un concetto di posizione, di punto, all'interno dell'environment. + +Il punto all'interno dell'environment è definito dalla **case class**, o record, *Point2D*. Nel suo design si è scelto di separare la dichiarazione della struttura dal comportamento. Infatti *Point2D* è modellata solamente nelle sue componenti e tutte le funzionalità sono state aggiunte successivamente attraverso il pattern **Pimp my library** il quale è pensato anche per situazioni in cui si desidera separare struttura e funzionalità di un concetto in pieno stile funzionale. + +Inoltre, è stato definito il **type-alias** *Distance* al fine di rappresentare la distanza con una notazione più *domain-specific*. + +##### Tempo + +La simulazione prende in considerazione anche lo scorrere del tempo e le logiche che vengono eseguite sono consapevoli di esso. + +Al fine di modellare il tempo corrente all'interno dell'environment è stato creato il **trait** *TimeStamp* il quale contiene il riferimento al tempo dell'engine corrente, cioè a quanti tick e iterazioni sono trascorse dall'inizio della simulazione. Inoltre, per poter essere utilizzato all'interno delle logiche esso possiede ulteriori metodi per convertire il tempo dell'engine nel tempo logico dell'environment e per ottenere a quale periodo del giorno (*inizio giornata*, *mattina*, *inizio della notte*, *notte*) quel tempo corrisponde (*sum type Period*). + +#### Entity + +Al fine di modellare il concetto di entity all'interno della simulazione si è deciso di rendere più granulare possibile l'acquisizione di ogni singola caratteristica dell'individuo. Il **trait** *Entity* mantiene solo le caratteristiche essenziali che deve avere un'entità. Sono stati per questo definiti ulteriori trait, ai quali sono state associate singole o un numero minimale di proprietà. La definizione delle entità si riduce quindi ad una attività di composizione, la quale rende riutilizzabili in futuro astrazioni già definite, permettendo di combinare fra loro le diverse proprietà per definire nuovi tipi di entità. +`MovementGoal` definisce il tipo di movimento di un'entità in un determinato momento della simulazione. Questo può essere: + ++ *Random Movement*: nel caso in cui l'entità voglia muoversi in maniera casuale ++ *Back To Home*: l'entità deve tornare a casa. Questo significa che il movimento successivo dovrà per forza essere in direzione della casa. ++ *No Movement*: l'entità non si muove. Questo accade per esempio quando l'entità entra in una struttura. + + + +L'infezione è stata gestita a parte e contiene le informazioni circa l'infezione che ha contratto un'entità, quali la gravità, il momento dell'infezione e la durata. + + + +#### Structure + +Le strutture, assieme alle entità e al virus, sono uno dei concetti principali del modello del dominio. Esse, come anticipato nei requisiti, rappresentano gli edifici presenti all'interno di un ambiente, ad esempio una città. Sono dotate di diversi parametri e possono essere configurate in diverso modo. Inoltre, le entità possono interagirvi entrando al loro interno con una permanenza che dipende dalle caratteristiche della struttura stessa durante il quale può avvenire il contagio. + +Le tipologie di strutture previste da requisiti sono tre: *Casa*, *Struttura generica* ed *Ospedale*. Nonostante ciò, il requisito 4.3 richiede che le tipologie di strutture e le strategie di ingresso siano estendibili, perciò il design di questi concetti ha tenuto in considerazione ciò. + +Per questo motivo e per essere, nella modellazione delle Strutture, indipendente dai particolari tipi di dato (considerando che inizialmente non erano stati nemmeno progettati, quindi evitando di creare dipendenze sequenziali nella progettazione), si è scelto di seguire l'approccio **abstract modelling** sfruttando il **family polimorphism** grazie agli **abstract types** di Scala permettendo una progettazione graduale da interfaccia ad effettiva implementazione affrontando in miglior modo la complessità del concetto. + +Di seguito uno schema riassuntivo che riporta gli elementi principali (type rappresentati esternamente in quanto non direttamente descrivibili in UML): + +![structure_base_diagram](imgs/detailed_design_structure.svg) + +Il design tiene in considerazione gli obiettivi descritti precedentemente, quindi viene stressata l'immutabilità. + +*Structure*, modellato come **trait**, rappresenta la struttura di base definita tramite **abstract modelling**, in cui ogni tipo è definito tramite **abstract type**. + +I due comportamenti principali sono stati definiti attraverso l'utilizzo del pattern **Template Method**: + +- `tryToEnter`: questo template method definisce lo scheletro con cui coordinare l'ingresso di un'entità all'interno della struttura. Esso si appoggia ai seguenti metodi che dovranno essere *overridati* nelle varie implementazioni: + - `checkEnter`: è il metodo che include i vari controlli da eseguire prima di poter far entrare l'entità. Esso può prendere in considerazione la strategia e/o le caratteristiche della struttura stessa. + - `enter`: è il metodo che definisce come si comporta la struttura quando un'entità viene accettata all'interno di essa. Da notare che ritorna una nuova instanza in quanto immutabile. + - `notEntered`: è il metodo che definisce come si comporta la struttura quando un'entità non viene accettata all'interno di essa. +- `entityExit`: questo template method definisce lo scheletro con cui coordinare l'uscita di un'entità dalla struttura. Esso si appoggia al metodo `exit`, il quale definisce come gestire l'uscita dell'entità dalla struttura, che dovrà essere *overridato* nella varie implementazioni. + +A partire da ciò i **mixins** *Visible* e *Closable* rappresentano due estensioni del concetto che modellano rispettivamente la capacità della struttura di essere vista da un'entità e la capacità di essere chiusa. Quest'ultima agisce proprio da **mixin** in quanto definisce un modo per "impilare" una modifica a `checkEnter`. Essi sono stati definiti come **mixins** in quanto possono avere effetto sulle funzionalità della Struttura stessa, come nel caso di *Closable*. + +Al fine di poterle comporre, le altre caratteristiche delle Strutture sono state create attravero **trait** appositi sfruttando il pattern **self-type**. In questo modo si è evitato di rappresentarle come sottotipi dipendendo in maniera più leggera dal principio *Liskov Substitution Principle* (LSP) e rappresentadole come decorazioni componibili. +Le tre caratteristiche sviluppate sono: + +- *Groupable*: rappresenta la possibilità delle strutture di essere raggruppate. Permette di specificare il nome del gruppo di appartenenza. +- *Habitable*: rappresenta la possibilità della struttura di agire come casa. +- *Hospitalization*: rappresenta la capacità della struttura di provvedere alla cura delle entità. Ogni struttura con questa capacità viene descritta anche in termini di qualità del trattamento nella cura dei pazienti. + +Per definire i tipi di strutture è stato pensato un ulteriore **trait** *SimulationStructure* che specificasse tutti i *type* necessari alla nostra simulazione e alcuni concetti di base. + +Tutto ciò permette di definire le strutture (Casa, Edificio generico, Ospedale, ecc...) semplicemente mettendo assieme tutte le componenti e caratteristiche necessarie. In questo modo è semplice creare nuove tipologie di Strutture con nuovi componenti e/o caratteristiche lavorando con una buona flessibilità e soprattutto consentendo di progettare partendo da una definizione indipendente dal design della restante parte del simulatore (grazie all'*abstract-modelling*). + +La strategia di ingresso viene gestita all'interno della struttura attraverso il pattern **Strategy**, passando la suddetta strategia alla struttura da creare. + +Le strategie di ingresso sono di tre tipi principali: + +- *Base*: è la strategia di base in cui tutte le entità sono ammesse senza restrizioni. +- *Filter-based*: consente di specificare un filtro sulle entità per decidere la loro accettazione. +- *Probability-based*: consente di specificare una probabilità con cui le entità sono accettate all'interno della struttura. + +Come anticipato, il design deve prevedere una buona estendibilità nel tipo di strategie disponibili permettendone, inoltre, la loro composizione (ad esempio *Filter-based* assieme a *Probability-based*, *"le entità con più di 18 anni sono ammesse con una probabilità del 50%"*). Al fine di modellare tutto ciò è stato scelto di utilizzare i **mixins**. +È stato modellato un **trait** *EntranceStrategy* che rappresenta l'interfaccia della strategia di ingresso. +L'unica implementazione del trait è **BaseEntranceStrategy** che rappresenta la strategia *Base*. Dopodiché le altre vengono modellate attraverso i **mixins** *FilterBasedStrategy* e *ProbabilityBasedStrategy*. Questo permette di ottenere una buona estendibilità (nuove strategie possono essere aggiunte con facilità specificando nuovi *mixins*) e la possibilità di comporre tra di loro le diverse strategie. + +Infine, le entità che riescono ad entrare rimangono all'interno della struttura per un periodo determinato a seconda della distribuzione gaussiana del tempo di permanenza nella struttura stessa. Il concetto di *permanenza* è stato modellato attraverso il **trait** *EntityPermanence*. + +#### Virus + +*Entity* che contiene informazioni riguardo al virus presente nell'*environment*. + +I parametri principali del virus sono: + +- nome +- tasso di diffusione +- giorni medi e deviazione standard del periodo di positività +- probabilità di sviluppare una forma grave della malattia +- distanza massima entro la quale è possibile infettarsi. + +Per semplicità ognuno di questi parametri contiene un valore di *default* in modo da semplificare la configurazione del virus da parte dell'utente. + +### Pattern di progettazione + +Durante il design si è cercato di trarre vantaggio dall'utilizzo di vari design pattern tipici della programmazione ad oggetti e funzionale: + +- *Adapter* +- *Factory* +- *Strategy* +- *Template Method* +- *Builder* +- *Type class* +- *Pimp my library* +- *Family polymorphism* +- *Self type* + +### Organizzazione del codice + +Il codice è stato organizzato considerando l'utilizzo del plugin **sbt-crossproject** il quale nel caso di progetto *full* *jvm-js* richiede la creazione di tre moduli: *shared*, *jvm* e *js*. Dopodiché i package sono stati organizzati seguendo l'architettura **ECB** adottata con i package *boundary*, *entity* e *control* che rappresentano i tre macro-componenti dell'architettura all'interno dei quali vi sono i componenti descritti nel capitolo del design architetturale. + +![packages](imgs/packages.svg) + + diff --git a/doc/report/05-implementation.md b/doc/report/05-implementation.md index cb2845f6..e4304738 100644 --- a/doc/report/05-implementation.md +++ b/doc/report/05-implementation.md @@ -1,3 +1,247 @@ ## Implementazione +In questo capitolo verranno descritte le scelte più rilevanti che non sono state descritte nel precedente capitolo di design di dettaglio e che riguardano l'implementazione dei concetti. + +La documentazione del codice si può trovare nella [Scaladoc](https://virusspreadsimulator.github.io/PPS-22-virsim/latest/api/) presente nel sito del simulatore. + +### Obiettivi + +Come per il design, prima di procedere all'implementazione abbiamo deciso, a livello di team, una serie di obiettivi e di principi da seguire durante l'implementazione. Essi estendono quelli descritti per il design di dettaglio. + +Innanzitutto abbiamo considerato, come anticipato, un approccio in cui si favorisce l'immutabilità evitando side-effects e l'uso di eccezioni, incapsulandoli ove necessario. Inoltre, durante l'implementazione si è cercato di rispettare sempre i principi, tra cui: DIP, ISP, LSP, SRP, oltre che a DRY e KISS. Quest'ultimi due anche grazie all'ausilio del tool SonarCloud descritto nel processo di sviluppo. + +Di seguito alcuni accorgimenti adottati nell'implementazione. + +Nell'utilizzo di **case class** abbiamo sfruttato le **lens** (attraverso la libreria [Monocle](https://www.optics.dev/Monocle/)) per accedere e trasformare i dati immutabili in modo più agevole e soprattutto in modo maggiormente dichiarativo. + +Abbiamo preferito l'utilizzo di **Factory** attraverso il metodo `apply` nel companion object dei trait invece che esporre direttamente le implementazioni al fine di dare una modellazione più astratta del concetto, che sia indipendente dalla specifica implementazione, consentendo allo stesso tempo di modificare dettagli implementativi in modo più agevole. + +Tutto ciò cercando di utilizzare le proprietà, le funzionalità e i costrutti messi a disposizione da **Scala 3**, come ad esempio: uniform access, extension methods, currying, mixins, given instances, type alias, function type ... + +### Boundary + +Considerando la necessità di eseguire il rendering della simulazione, sono necessari dei pannelli che contengano le informazioni di simulazione. +Al fine di evitare di modellare gli stessi concetti sia per il boundary JVM che per il boundary JS, si è deciso di creare il concetto di Pannello a livello shared del Boundary. +**BasePanel** modella il concetto di pannello di base che può essere inizializzato e stoppato. Esso viene esteso da **UpdatablePanel** il quale permette di aggiornare il pannello con il nuovo stato dell'*Environment*. Infine, è stata modellata anche la capacità di emettere degli eventi attraverso l'**EventablePanel** che sfrutta il pattern **self-type** in quanto rappresenta un'estensione, una capacità, che può essere aggiunta ad un qualsiasi pannello. + +Di seguito verranno descritte le scelte implementative più rilevanti prese nello sviluppo dei tre boundary. + +#### JVM + +Come anticipato nel design di dettaglio *Java Swing* è fortemente object-oriented e segue un approccio a side-effects. +Al fine di gestire il disegno dei concetti di Simulazione è stata utilizzata la type-class *Drawable* descritta precedentemente. È stata creata l'istanza per JVM chiamata **DrawableSwing** in cui viene specificato il tipo di grafica utilizzato, *Graphics2D*, ed è stato eseguito un "pimping" specificando l'operazione aggiuntiva che consente di poter disegnare attraverso un'unica chiamata un *set* di *DrawableSwing*. + +I concetti estesi con la capacità di disegno sono: *Environment*, *SimulationEntity* e *SimulationStructure*. Ciò ha permesso di isolare il codice necessario per eseguire il disegno degli elementi e quindi di isolare l'approccio a side-effects tipico dell'API di *Graphics2D*. + +Il Boundary JVM è in particolare un **ConfigBoundary** in quanto è il boundary principale dell'applicativo Desktop dedicato alla visualizzazione dell'interfaccia grafica del simulatore. Esso è composto da due frame principali: + +- *InitGUI*: è il primo frame che viene visualizzato ed è dedicato al caricamento della configurazione e alla visualizzazione degli eventuali errori associati. +- *SimulationGUI*: è il frame che visualizza la simulazione in corso. + +La struttura di entrambi i frame è stata sviluppata attraverso una descrizione monadica lazy sfruttando i **Task** (di cui è stato creato un "alias", `io`, per una migliore leggibilità del codice lato gui) offerti dalla libreria *Monix*. In questo modo essi si integrano molto facilmente all'interno di tutto il sistema monadico predisposto e descritto precedentemente. Inoltre, il frame *SimulationGUI*, data la sua complessità, è stato suddiviso in diversi pannelli che estendono i trait **BasePanel**, **UpdatablePanel** ed **EventablePanel** descritti in precedenza. + +Il Boundary necessita di esporre verso l'esterno gli eventi generati da esso. Questo è stato realizzato andando ad eseguire il *merging* di tutte le sorgenti di eventi presenti: **EventablePanels** ed **EventSource**, attraverso l'API di **Observable** offerta dalla libreria *Monix*. + +#### JS + +Al fine di gestire il disegno dei concetti di simulazione è stata utilizzata anche qui la type-class *Drawable* descritta precedentemente. È stata creata l'istanza per JS chiamata **DrawableJS** in cui viene specificato il tipo di grafica utilizzato, *CanvasRenderingContext2D*, e, similmente a prima, è stato eseguito un "pimping" specificando l'operazione aggiuntiva che consente di poter disegnare attraverso un'unica chiamata un *set* di *DrawableJS*. + +I concetti estesi con la capacità di disegno sono: *Environment*, *SimulationEntity* e *SimulationStructure*. Ciò ha permesso di isolare il codice necessario per eseguire il disegno degli elementi e quindi di isolare l'approccio a side-effects tipico dell'API di *CanvasRenderingContext2D*. + +Anche il Boundary JS è un **ConfigBoundary** in quanto è il boundary principale della WebApp ed è anch'esso dedicato alla visualizzazione dell'interfaccia grafica del simulatore. Esso è composto da una sola schermata la quale è stata sviluppata attraverso una descrizione monadica lazy sfruttando i **Task** offerti dalla libreria *Monix*. In questo modo essi si integrano molto facilmente all'interno di tutto il sistema monadico predisposto e descritto precedentemente. Similmente a quanto accade per *SimulationGUI* del Boundary JVM, data la sua complessità, la schermata è stato suddivisa in diversi pannelli che estendono i trait **BasePanel**, **UpdatablePanel** ed **EventablePanel** descritti in precedenza. + +Il Boundary necessita di esporre verso l'esterno gli eventi generati da esso. Questo è stato realizzato andando ad eseguire il *merging* di tutte le sorgenti di eventi presenti: **EventablePanels** ed **EventSource**, attraverso l'API di **Observable** offerta dalla libreria *Monix*. + +#### Exporter + +Per quanto riguarda il metodo per esportare dati ad ogni step della simulazione esso utilizza tecniche di **folding** sugli estrattori di dati presenti. + +Inoltre gli estrattori di dati sono stati implementati attraverso *case class* immutabili le quali hanno il solo compito di implementare il metodo generico *extractData()* a seconda del tipo di dato che si intende estrarre. + +Le tipologie di estrattori presenti sono: + +- **Time**: si occupa di estrare l'orario corrente della simulazione. + +- **Days**: si occupa di estrarre il numero di giorni trascorsi. +- **Hours**: si occupa di estrarre le ore trascorse nel giorno corrente. +- **Minutes**: si occupa di estrarre i minuti trascorsi nell'ora corrente. + +- **Alive**: si occupa di estrarre il numero di entità in vita nell'environment. +- **Deaths**: si occupa di estrarre il numero di entità morte nell'environment. +- **Sick**: si occupa di estrarre il numero di entità gravemente malate nell'environment. +- **Infected**: si occupa di estrarre il numero di entità infette nell'environment. +- **Healthy**: si occupa di estrarre il numero di entità in salute nell'environment. +- **AtHome**: si occupa di estrarre il numero di entità che si trovano in casa. +- **HospitalsCapacity**: si occupa di estrarre la capacità degli ospedali presenti. +- **HospitalFreeSeats**: si occupa di estrarre il totale dei posti liberi negli ospedali presenti. +- **Hospitalized**: si occupa di estrarre il numero di entità ricoverate negli ospedali. +- **HospitalPressure**: si occupa di estrarre la pressione ospedaliera ossia il rapporto tra gli ospedalizzati ed i posti totali. + +### Parser + +Al Parser vengono aggiunti tramite **extension methods** i metodi *shouldBeWithin* e *andIfNot*. +In questo modo per ciascun parametro è possibile controllare se rientra nel range di valori possibili e in caso negativo generare un errore che verrà comunicato ai Boundary. + +Per quanto riguarda invece lo *YAMLParser*, esso è arrichito dai metodi *to* e *has* per semplificare le operazioni di look-up e di conversione sulla mappa restituita dal parsing del file YAML. + +### Reader + +Il Reader di JavaScript utilizza un *PublishSubject* di *Monix* per leggere il file caricato dall'utente sul browser. In questo modo è possibile implementare il metodo utilizzando un approccio ad eventi ed associare ad esso un Task di *Monix* per rimanere coerenti con il resto del progetto. + +### Engine + +Per implementare il flow dell'engine descritto nel capitolo del design di dettaglio, il quale prevede la necessità di raccogliere gli eventi dai boundary e di eseguire il loop di simulazione aggiornando i boundary dopo aver computato il nuovo Environment, è stato necessario prevedere una fase di setup dell'engine in cui eseguire il *dispatch* del carico di lavoro. + +In particolare, il task ritornato dal metodo `startSimulationLoop` è composto dalla descrizione monadica lazy di due task che nel momento dell'esecuzione verranno eseguiti in parallelo attraverso lo scheduler di *Monix* definito: + +- Il primo task si occupa di gestire ogni evento proveniente dagli *Observable* restituiti dai Boundary, opportunamente uniti, inserendoli in una *ConcurrentQueue* di *Monix*. +- Il secondo task esegue il vero loop di simulazione, eseguendo le logiche e comunicando l'Environment aggiornato ai Boundary. Esso considera solo i primi eventi dalla suddetta coda (il cui numero è definito dal valore associato alla configurazione *maxEventPerIteration* iniettata nell'engine). + +Nella descrizione del design di dettaglio sono presenti due tipologie di logiche: **UpdateLogic** per le logiche di aggiornamento dell'Environment da eseguire ad ogni tick ed **EventLogic** per le logiche associate agli eventi. In particolare le logiche implementate sono le seguenti: + +- *UpdateLogic*: esse sono state inserite nel seguente ordine al fine di eseguirle sequenzialmente + - **UpdateEntityStateLogic**: è la logica che provvede ad aggiornare lo stato di salute di ogni entità. + - **HospitalRecoveryLogic**: è la logica che si occupa di curare gli individui all'interno degli ospedali. + - **HospitalizeEntityLogic**: è la logica che si occupa di ricoverare gli individui malati a rischio di morte. + - **EntityGoalUpdateLogic**: è la logica che si occupa di aggiornare l'obiettivo di ogni entità sulla base del periodo della giornata simulata. + - **MovementLogic**: è la logica che gestisce il movimento delle entità all'interno dell'Environment. + - **ExitLogic**: è la logica che si occupa di controllare e gestire l'uscita degli individui dalle strutture. + - **EntranceLogic**: è la logica che si occupa di controllare e gestire l'entrata degli individui nelle strutture. + - **ExternalInfectionLogic**: è la logica che si occupa di gestire il contagio nell'Environment, esternamente alle strutture. + - **InternalInfectionLogic**: è la logica che si occupa di gestire il contagio internamente alle strutture. + - **LogicTimeUpdate**: è la logica che aggiorna il tempo virtuale della simulazione. + - **IterationLogic**: è la logica che controlla la terminazione della simulazione. +- *EventLogic*: nella configurazione (*SimulationConfig*) ogni evento appartenente ad **Event** è associato alla corrispettiva logica. All'interno della configurazione la funzione `Event => EventLogic` è implementata attraverso l'utilizzo di una **Map**. + - **PauseLogic**, **ResumeLogic**, **StopLogic**: rispettivamente associati agli eventi *Pause*, *Resume* e *Stop*, si occupano della gestione della volontà dell'utente di mettere in pausa, riprendere e stoppare preventivamente la simulazione. + - **SimulationSpeedLogic**: associato all'evento *ChangeSpeed(speed)*, si occupa di gestire la volontà dell'utente di cambiare la velocità di simulazione. + - **SwitchMaskObligationLogic**: associato all'evento *SwitchMaskObligation*, si occupa di gestire la volontà dell'utente di inserire/togliere l'obbligo di mascherina per tutte le entità dell'Environment. + - **VaccineRound**: associato all'evento *VaccineRound(percentage)*, si occupa di gestire la volontà dell'utente di vaccinare una percentuale delle entità presenti nell'Environment. + - **SwitchStructureLogic**: associato all'evento *SwitchStructure(group)*, si occupa di gestire la volontà dell'utente di aprire/chiudere uno specifico gruppo di strutture. + +### Environment + +Per quanto riguarda l'environment tutti i campi presenti sono gestiti in modo immutabile. +Il metodo `update` ha tutti i parametri come default, i quali mantengono l'Environment invariato e restituisce un nuovo Environment aggiornando le informazioni con i parametri passati. + +#### Common + +Come anticipato nel design di dettaglio, la **type class** *Probable* permette di aumentare un tipo con la capacità di rappresentare un evento dotato di una determinata probabilità di accadimento, la quale viene definita da una formula. Infatti è possibile notare che all'interno dell'*object ProbableGivenInstances* sono presenti tutte le *given instances* necessarie. In particolare si possono osservare quelle associate agli eventi di contagio. Esse sono modellate andando ad aumentare i tipi *ExternalProbableInfection* e *InternalProbableInfection* che rappresentano un contagio probabile rispettivamente esterno ed interno alle strutture. + +Le formule implementate, le quali sono semplici e non hanno alcuna volontà di essere significative in un contesto reale, sono le seguenti: + +- *Contagio esterno* (individuo *j*): + + ![external_probable_infection_formula](imgs/external_infection.svg) + +- *Contagio interno* (individuo *j*): + + ![internal_probable_infection_formula](imgs/internal_infection.svg) + + +Considerando: + +- I: infetti in prossimità dell'entità +- P0: spreadRate del virus +- PS: infectionProbability della struttura +- M: riduzione data dalla presenza della mascherina +- Mj: riduzione data dalla presenza o meno della mascherina per l'entità j +- Dji: distanza tra le entità j ed i +- Dmax: distanza massima di contagio del virus +- Ij: immunità dell'individuo j +- Imax: immunità massima +- NS: numero entità all'interno della struttura +- NSinf: numero entità infette nella struttura +- NSinfm: numero entità infette nella struttura con la mascherina + +Grazie alla type class è stato possibile isolare la rappresentazione delle formule in un concetto che rispecchia il significato di evento probabile. + +#### Entity + +Al fine di poter confrontare due Entity anche in tick della simulazione differenti è stato ridefinito il metodo `equals`, definendo che due Entity sono uguali se hanno lo stesso id. +Le Entity vengono create tramite una **Factory**, implementata utilizzando il metodo `apply` nel companion object, evitando quindi di esporne l'implementazione. +Ad ogni Entity al momento della creazione viene attribuito un valore `maxHealth` il quale rappresenta il massimo della vita che essa potrà avere per tutta la durata della simulazione. Questo valore dipende dall'età e siccome presumubilmente molte Entity avranno la stessa età, è stata utilizzata la libreria **Scalaz**, la quale fornisce una immutableHashMapMemo, per salvare le nuove maxHealth calcolate ed evitare di ricalcolarle successivamente. Il campo health assume maxHealth al momento della creazione. + +#### Structure + +Le Strutture sono state effettivamente create partendo dal trait *SimulationStructure*, il quale definisce tutti gli **abstract types** dichiarati nel trait *Structure*, e componendolo con i trait rappresentanti le caratteristiche desiderate. Questo approccio rende flessibile e agile l'estensione e l'aggiunta di nuove strutture e tipologie. +Seguendo i requisiti sono state implementate le seguenti strutture: + +- *House*: rappresenta la casa per le entità +- *GenericBuilding*: rappresenta un edificio generico +- *Hospital*: rappresenta l'ospedale + +### Test + +Al fine di garantire la correttezza e la qualità del codice il team ha sfruttato il modello di sviluppo **TDD** (*Test Driven Development*) durante lo sviluppo di tutte le parti del sistema ad eccezione delle parti di interfaccia utente. Inoltre, allo scopo di prevenire situazioni di regressione, come anticipato nella descrizione del processo di sviluppo, i test vengono eseguiti come parte della pipeline di Build del workflow di Continuous Integration. + +Si è ottenuta una coverage di linea di circa 50% ed una coverage totale di circa 30%. + +### Suddivisione del lavoro + +L'analisi del dominio del problema, la raccolta e la stesura dei requisiti come anche la progettazione e lo sviluppo del processo di Continuous Integration e dell'architettura del sistema sono state portate avanti da tutti i componenti del team. + +Durante gli Sprint Planning e in particolare durante la compilazione della Sprint task board i vari compiti da portare a termine sono stati suddivisi tra i componenti del team. Di seguito, per ciascun componente, verranno elencati i concetti progettati, sviluppati e testati. + +#### Acampora Andrea + +Nel progetto mi sono occupato del design, dello sviluppo e del testing dei seguenti concetti: + +- Caricamento della configurazione fornita dall'utente + - Loader + - Parser + - Reader + - DSL +- Esportazione dei dati e calcolo dei dati aggregati di simulazione: + - Exporter + - DataExtractor +- Creazione grafici per l'andamento della simulazione +- Virus +- Environment +- Sviluppo delle seguenti logiche: + - Logica per la vaccinazione di una percentuale di popolazione + +#### Accursi Giacomo + +Nel progetto mi sono occupato del design, dello sviluppo e del testing dei seguenti concetti: + +- Sviluppo degli individui e dei concetti legati ad esso: + - Entity + - Infection +- Sviluppo delle seguenti logiche: + - Logica di movimento + - Logica di ingresso nelle strutture + - Logica di uscita dalle strutture + - Logica per la gestione dell'obbligo di indossare la mascherina +- Environment + +#### Giulianelli Andrea + +Nel progetto mi sono occupato del design, dello sviluppo e del testing dei seguenti concetti: + +- Sviluppo delle strutture e dei concetti legati ad esse: + - Structure + - Strategie di ingresso + - Permanenza dell'individuo all'interno della struttura + +- Sviluppo dei concetti comuni: + - Distribuzione gaussiana + - Evento probabile + - Spazio + - Tempo + +- Sviluppo dell'engine e gestione delle logiche +- Sviluppo dei boundary: + - Boundary JVM escludendo la parte dei grafici + - Boundary JS + +- Sviluppo delle seguenti logiche: + - Logica per l'aggiornamento dell'obiettivo delle entità + - Logica per l'aggiornamento dello stato di salute delle entità + - Logica per l'ospedalizzazione delle entità + - Logica per la cura delle entità all'interno degli ospedali + - Logica di contagio + - Logica per la gestione dell'apertura dei gruppi di strutture + - Logica per l'aggiornamento dello stato dell'engine + + diff --git a/doc/report/06-retrospective.md b/doc/report/06-retrospective.md index cc3f980d..a2d0bc47 100644 --- a/doc/report/06-retrospective.md +++ b/doc/report/06-retrospective.md @@ -1 +1,55 @@ ## Retrospettiva + +In questo capitolo si prenderanno in considerazione tutti gli episodi di interesse che sono emersi e i principali aspetti positivi e negativi incontrati durante il progetto. + +### Processo di sviluppo + +Lo sviluppo del software è stato gestito attraverso la versione del framework **Scrum** descritta nel capitolo del processo di sviluppo. Esso ci ha concesso di sperimentare i vari ruoli presenti all'interno di un team **Scrum** e di considerare diversi punti di vista che ci hanno aiutato nella comprensione del dominio e nella risoluzione delle problematiche. + +I diversi incontri previsti da **Scrum** sono risultati molto utili al fine di definire in modo preciso i compiti da svolgere e per mantenere sempre sotto controllo lo stato di avanzamento dello sviluppo. Le riunioni a fine sprint ci hanno permesso di capire il giusto carico di lavoro che mantenesse allo stesso tempo una buona produttività e la giusta motivazione nel team. Inoltre, si è sempre cercato di avere sprint significativi cioè che portassero ad un risultato tangibile per il committente. + +Infine, l'utilizzo di tutta la documentazione prodotta, e mantenuta in versione, in particolare della Sprint task board è risultata particolarmente utile per l'organizzazione del lavoro e per avere una panoramica sullo stato di avanzamento dello Sprint corrente. + +Tutta la documentazione relativa a: + +- *Product Backlog* +- *Sprint Goal* +- *Sprint Backlog* +- *Screen della Sprint Taskboard svolta su Trello* +- *Product Backlog Refinement* +- *Sprint Review* +- *Sprint Retrospective* + +è stata prodotta e aggiornata per ogni sprint, seguendo i principi del framework **Scrum**, e mantenuta in versione nella repository del progetto sotto forma di file Markdown. In particolare si trova nella directory `doc/process/`. + +### Andamento dello sviluppo + +Durante lo sviluppo dell'applicazione sono emersi i seguenti problemi: + +- *Integrazione del progetto cross-plaftorm e IDE Intellij IDEA*: durante lo sviluppo è emerso che l'IDE utilizzato, Intellij IDEA, non supporta adeguatamente progetti cross-plaftform in cui vi è la generazione di moduli con dipendenze reciproche, come appunto svolto dall'insieme di plugin utilizzati di **sbt-crossproject**. A conferma di ciò sono presenti i seguenti post: [issue](https://youtrack.jetbrains.com/issue/SCL-18334/sbt-crossproject-shared-sources-do-not-see-jvm-js-sources?_gl=1*15jal5d*_ga*MzYyMTY0MjY3LjE2NTgyOTk3MjU.*_ga_9J976DJZ68*MTY1OTA4OTg0NS40LjEuMTY1OTA5MDY2Ni42MA..&_ga=2.79344852.1915785814.1659015060-362164267.1658299725), [discussione sul forum di Jet Brains](https://intellij-support.jetbrains.com/hc/en-us/community/posts/206633785-scala-js-IDEA-does-not-find-individual-JS-and-JVM-implementations-of-an-object-sbt-compile-sbt-test-works-fine). + + Al fine di consentire ugualmente il raggiungimento dell'obiettivo, anche se con qualche rinuncia e con un aumento della viscosità dell'ambiente di lavoro, abbiamo adottato i seguenti workaround: + + - Disabilitare il server di compilazione Scala: inizialmente ha permesso di risolvere il problema però con una rinuncia quasi completa ai suggerimenti dell'IDE. Questo workaround può essere compiuto seguendo i seguenti passi: *Setttings -> Compiler -> Scala Compiler Server -> Rimuovere flag "Use compile server"* + - Forzare Intellij IDEA a produrre i .class in target/classes: successivamente, dopo un incrontro con il prof. Aguzzi, abbiamo adottato il seguente workaround che ci ha permesso di ri-ottenere parte dei suggerimenti dell'IDE. I passi sono: *ProjectSettings -> Modules -> Si crea un nuovo module posizionato nella root del progetto cross platform e con il nome "shared" impostando tutte le versioni in modo corretto (Scala: 3.1.1, SBT: 1.6.2)*. + +- *Utilizzo di Prolog all'interno del progetto cross-platform*: inizialmente la volontà era quella di sviluppare una parte delle logiche dell'engine sfruttando i vantaggi offerti dal paradigma logico. Dopo lo sviluppo della logica di movimento però, ci siamo resi conto che la libreria utilizzata durante il corso per l'utilizzo di Prolog da Scala era integrabile con JS, quindi con ScalaJS, con uno sforzo che andava ben oltre il monte ore. Stesso discorso per le alternative trovate. Per questo motivo la scelta finale è stata quella di mantenere tutte le logiche in Scala, ma allo stesso tempo mantenere in un package separato il lavoro svolto dal componente del team Giacomo Accursi. + +Nonostante ciò, lo sviluppo e le strategie adottate hanno permesso di ottenere i seguenti aspetti positivi: + +- *Automazione offerta dal workflow di Continuous Integration e Deployment*: la scelta di dedicare uno sforzo iniziale importante nell'impostare i workflow di Continuous Integration e Deployment ha concesso di portare avanti agilmente tutto il processo di sviluppo. L'analisi statica del codice ci ha permesso di risolvere code smells ed evitare debito tecnico con più facilità. I test eseguiti in automatico hanno evitato notevoli errori di regressione. Infine, tutto il processo di deploy automatico che comprende: release, documentazione e deploy della WebApp del simulatore, ha velocizzato notevolmente la release del prodotto e il suo aggiornamento. +- *Utilizzo di Scrum come framework di riferimento*: come anticipato nella sezione precedente *Scrum* ci ha permesso di monitorare costantemente l'andamento di sviluppo e di sincronizzarci in maniera più agile. +- *Flessibilià del design*: il design prodotto ci ha permesso di effettuare cambiamenti ed adattamenti in itinere agilmente e senza introdurre troppo debito tecnico. + +La documentazione riguardante l'andamento dello sviluppo è mantenuta in versione all'interno della repository nella directory `doc/process/`. + +### Commenti finali + +Il progetto ha consentito di approfondire aspetti di design, sviluppo e gestione di un software in un contesto Scrum emulando un contesto reale. +Inoltre, ha concesso di approfondire gli argomenti trattati nel corso e di esplorare l'approccio funzionale. + +Durante tutto il progetto abbiamo seguito il consiglio di sperimentare nuovi pattern, strumenti, strategie e metodologie come ad esempio: il pattern architetturale ECB, Continuous Integration e Deployment, lo sviluppo di un progetto cross-platform, l'approccio monadico ... + +Al termine del progetto possiamo ritenerci soddisfatti in quanto abbiamo raggiunto gli obiettivi che ci eravamo imposti in fase di stesura dei requisiti con un buon bilanciamento nella suddivisione degli item e un'ottima sinergia tra i vari componenti del team. + + diff --git a/doc/report/07-user-guide.md b/doc/report/07-user-guide.md new file mode 100644 index 00000000..741703e6 --- /dev/null +++ b/doc/report/07-user-guide.md @@ -0,0 +1,133 @@ +## Guida Utente + +Il simulatore è utilizzabile sia attraverso l'applicativo Desktop, sia attraverso la WebApp. + +### Applicazione Desktop + +La schermata di avvio permette all'utente il caricamento del file di configurazione desiderato. L'applicazione Desktop lavora con configurazioni definite in Scala, in cui può essere sfruttato il DSL sviluppato. Al fine di selezionare il file, l'utente necessita solamente di cliccare sul pulsante "*Choose*" e selezionare il file dal *File Dialog* che apparirà. Dopodiché premendo il pulsante "*Start*" si potrà avviare la simulazione. + + + +In caso ci siano errori all'interno della configurazione essi vengono visualizzati attraverso una finestra di dialogo apposita: + + + + + + + +Nell'esempio all'interno della configurazione una struttura è stata posizionata al di fuori dell'environment. + + + +Invece, nel caso in cui la configurazione sia valida verrà avviata la simulazione. + +![jvm_simulation](imgs/jvm_simulation.png) + + + +La schermata di simulazione offre due pannelli che consentono la visualizzazione delle informazioni relative alla simulazione stessa: + +- il pannello a sinistra mostra lo stato corrente dell'environment mostrando le strutture presenti, il movimento delle entità e le interazioni con le strutture. In particolare: + - le entità sono rappresentate attraverso un cerchio che assume diversi colori. Nel caso di individuo sano esso sarà di colore verde, di tonalità più accesa se giovane, e più scura con l'avanzare dell'età. Nel caso di inviduo infetto esso sarà di colore rosso. In entrambi i casi qualora l'entità abbia un valore di immunità residua maggiore di zero (a seguito di vaccinazione o guarigione) allora avrà un contorno di colore blu. + - le strutture vengono rappresentate attraverso un quadrato il cui colore dipende dalla tipologia di struttura: arancione per le strutture generiche, azzurro per gli ospedali e grigio per le case. Per le strutture generiche e per gli ospedali viene mostrato il raggio di visibilità attraverso un cerchio di colore giallo con raggio più o meno ampio a seconda del valore. Nel caso in cui la struttura venga chiusa attraverso l'azione dinamica "*Switch group*" il raggio di visibilità non verrà disegnato. Sopra alla struttura stessa viene visualizzata la capacità residua. +- il panello a destra mostra invece i seguenti grafici: + - *Deads, Sick and Healthy Entities*: attraverso un grafico a barre in pila vengono mostrati per ogni ora del giorno corrente il numero di individui morti, malati e in salute. + - *Infected per Hour*: attravero un grafico a barre viene mostrato per ogni ora del giorno corrente il numero di individui infetti. + - *Hospitals Pressure*: attraverso un grafico a torta viene mostrata la pressione ospedaliera (considerando tutti gli ospedali disponibili all'interno dell'environment). + +L'utente ha la possibilità di interagire con la schermata attraverso il pannello inferiore in cui sono disponibili diverse sezioni: + +- *Commands*: racchiude i comandi disponibili all'utente sulla simulazione. Potrà mettere in pausa, riprendere e stoppare la simulazione. Inoltre, è possibile attraverso la Combo Box cambiare la velocità di simulazione scegliendo tra SLOW, NORMAL e FAST. +- *Dynamic config values*: mostra all'utente lo stato corrente delle azioni dinamiche quali obbligo di mascherina e apertura dei gruppi di strutture. +- *Dynamic config*: racchiude le interazioni dinamiche che possono essere attivate dall'utente + - *Switch mask obligation*: consente di invertire lo stato corrente di obbligo della mascherina. + - *Vaccine round*: consente di eseguire una campagna di vaccinazione specificando una percentuale di adesione (esempio: 30) nel campo di testo a fianco. + - *Switch group*: consente di invertire lo stato di apertura di un gruppo di strutture. Il gruppo è possibile specificarlo nel campo di testo a fianco. +- *Stats*: mostra all'utente le statistiche principali e i dati aggregati sulla simulazione. + +Infine, nella directory dei file temporanei del proprio sistema operativo è possibile trovare nella directory `virsim-export` un file in formato `.csv` contenente i dati aggregati raccolti durante la simulazione. + +#### Descrizione API file di configurazione Scala + +Iniziare la configurazione in un file `.scala` con **VirsimConfiguration()** e definire all'interno le keyword **simulation**, **virus** e **structures** separate da una virgola come nell'esempio seguente: `VirsimConfiguation(simulation, virus, structures)`. + +In questo caso viene istanziata una configurazione di default senza strutture nell'environment. + +Per i parametri della simulazione è possibile aggiungere in seguito alla keyword **simulation** i seguenti parametri: + + - *gridSide*: grandezza della griglia dell'environment. Deve essere compresa tra 20 e 100. + - *days*: durata massima della simulazione in termini di giorni. + - *entities*: numero delle entità presenti nell'environment. Deve essere compreso tra 2 e 1000. + - *averagePopulationAge*: età media della popolazione. + - *stdDevPopulationAge*: deviazione standard dell'età della popolazione. + - *startingInfectedPercentage*: percentuale di infetti ad inizio simulazione. + +Per i parametri del virus è possibile aggiungere alla keyword **virus** i seguenti parametri: + + - *name*: nome del virus presente nell'environment. + - *spreadRate*: tasso di diffusione del virus. Deve essere compreso tra 0 e 1. + - *averagePositivityDays*: giorni medi di positività del virus. + - *stdDevPositivityDays*: deviazione standard dei giorni di positività del virus. + - *severeDeseaseProbability*: probabilità di sviluppare una forma grave della malattia. Deve essere compreso tra 0 e 1. + - *maxInfectionDistance*: distanza massima entro la quale è possibile infettarsi. + +Per quanto riguarda le strutture è possibile aggiungere alla keyword **structures** la keyword **are** ed in seguito l'elenco di strutture. Esse possono essere di due tipologie: + +- *GenericBuilding*, con i parametri obbligatori: + + - *position*: posizione della struttura all'interno della griglia. + - *infectionProbability*: probabilità di infettarsi entrando nella struttura. Deve essere compreso tra 0 e 1. + - *capacity*: numero massimo di persone che possono essere dentro alla struttura contemporaneamente. + + ed i parametri opzionali: + + - *permanenceTimeDistribution*: tempo di permanenza dell'entità dentro la struttura. + - *entranceStrategy*: strategia di ingresso della struttura. + - *isOpen*: stato della struttura (aperta/chiusa). + - *visibilityDistance*: grandezza del raggio di visibilità della struttura. + - *group*: tipologia della struttura. + +- Hospital, con i parametri obbligatori: + + - *position*: posizione della struttura all'interno della griglia. + - *infectionProbability*: probabilità di infettarsi entrando nella struttura. Deve essere compreso tra 0 e 1. + - *capacity*: numero massimo di persone che possono essere dentro alla struttura contemporaneamente. + + ed i parametri opzionali: + + - *entranceStrategy*: strategia di ingresso della struttura. + - *isOpen*: stato della struttura (aperta/chiusa). + - *visibilityDistance*: grandezza del raggio di visibilità della struttura. + - *treatmentQuality*: efficacia dell'ospedale nella cura delle entità. + +Un esempio di configurazione si trova nei file associati alla release. [Link](https://github.com/VirusSpreadSimulator/PPS-22-virsim/releases/latest/download/configuration.scala) + +### WebApp + +La WebApp presenta un'unica schermata in cui dopo aver selezionato il file di configurazione YAML attraverso il pulsante "*Choose File*" viene visualizzata la seguente schermata: + +![web_app_screen](imgs/js_simulation.png) + +La WebApp prevede tutte le funzionalità come descritte nella versione Desktop ad eccezione della visualizzazione dei grafici e dell'esportatore di dati aggregati. + +#### Descrizione file di configurazione YAML + +Nella configurazione in formato YAML è possibile definire i parametri della simulazione, del virus e delle strutture all'interno delle keywords **simulation**, **structures**, **virus**. + +Nello specifico, la keyword structures accetta una lista di strutture di tipologia **GenericBuilding** oppure **Hospital**. + +A differenza della configurazione in formato Scala, per motivi legati alla difficoltà nell'istanziare delle classi in un formato chiave-valore, non è possibile definire i parametri **permanenceTimeDistribution**, **treatmentQuality** e **isOpen**. + +Mentre per quanto riguarda la strategia di ingresso è possibile definire le seguenti strategie: + +- *probability*: valore da 0 a 1 che rappresenta la probabilità di entrare nella struttura. + +- *ageLowerThan*: valore che rappresenta l'età massima per entrare nella struttura. +- *ageGreaterThan*: valore che rappresenta l'età minima per entrare nella struttura. + +Nel caso in cui non si volessero modificare i parametri di default è possibile creare un file di configurazione vuoto specificando solamente: + + `simulation: {}` + +Un esempio di configurazione si trova nei file associati alla release. [Link](https://github.com/VirusSpreadSimulator/PPS-22-virsim/releases/latest/download/configuration.yml) diff --git a/doc/report/diagrams/analysis_class.txt b/doc/report/diagrams/analysis_class.txt index 5c15bdcf..415917e2 100644 --- a/doc/report/diagrams/analysis_class.txt +++ b/doc/report/diagrams/analysis_class.txt @@ -43,9 +43,11 @@ class Infection { } class Virus { + name spreadRate averagePositivityPeriod severeDeseaseProb + maxInfectionDistance } class Environment { diff --git a/doc/report/diagrams/architecture.txt b/doc/report/diagrams/architecture.txt index 34065e33..9914f9f4 100644 --- a/doc/report/diagrams/architecture.txt +++ b/doc/report/diagrams/architecture.txt @@ -7,11 +7,12 @@ boundary boundary control launcher control engine control loader +control parser entity environment entity virus entity entity -entity building +entity structure actor Utente @@ -26,11 +27,14 @@ engine -up- loader engine -left- boundary +loader -- parser + engine -- environment environment -right- virus environment -- entity -environment -- building +environment -- structure + @@ -38,6 +42,7 @@ environment -- building launcher -[hidden]-> virus boundary -[hidden]-> loader boundary -[hidden]-> engine +launcher -[hidden]-> parser @enduml diff --git a/doc/report/diagrams/class_architecture.txt b/doc/report/diagrams/class_architecture.txt new file mode 100644 index 00000000..68ef266b --- /dev/null +++ b/doc/report/diagrams/class_architecture.txt @@ -0,0 +1,66 @@ +@startuml + +!theme vibrant + +interface Boundary <