<style>
p, li{
font-size : 13px;
font-weight: 100;
}
.reveal p, .reveal ul li{
font-size : 25px;
font-weight: 100;
}
.reveal ul li ul li{
font-size : 18px;
font-weight: 100;
}
.reveal section img{
border : none;
box-shadow: 0px 4px 20px 2px rgba(0, 0, 0, 0.1);
}
.reveal pre{
box-shadow: 0px 4px 20px 2px rgba(0, 0, 0, 0.1) !important;
font-size: 13px;
}
.reveal code{
background : whitesmoke;
font-size: 20px;
}
.reveal code.hljs{
background : #2d2d2d;
font-size: 13px;
}
.reveal section img[src$='#img-techno'] {
height : 200px;
max-width : 100%;
}
.reveal section img[src$='#full-screen'] {
max-height : 50vh;
}
.reveal section img[src$='#max-height'] {
max-height : 50vh;
}
.reveal section img[src$='#max-height-small'] {
max-height : 35vh;
}
.reveal section img[src$='#logo'] {
width : 400px;
background: transparent;
box-shadow : none;
}
.mermaid svg{
max-height: 80vh;
font-size: 12px;
}
/* white */
.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6, .reveal p, .reveal ul li, .reveal ul li ul li{
color : #586D72 !important;
}
body {
background: #fff !important;
background-color: #fff !important;
}
</style>

**Revue de code 12/06/2019**
Damien DABERNAT - Pauline SCHNEE
---
## Pitch projet
# 🗑 + 🎮 = ❤️
#### [Présentation du concept](https://docs.google.com/presentation/d/14yvVJ8w3OxX9ML_d3vUXgdomBawuxkcCXQHcY9ANuVg/edit?usp=sharing)
---
## 1. Revue Globale
---
### Architecture du projet
-----------------
**Dispositif global / interfaces:**
Poubelle connectée à un jeu sur mobile via un serveur.

---
### Architecture du projet
-----------------
**Découpe fonctionnelle :**
- De déchets à ressources 🗑➜💸
- *reconnaissance des déchets*
- *connection poubelle-smarthone*
- *collecte des ressources disponibles*
- *gestion des ressources virtuelles*
- Gestion des utilisateurs et des équipes 👨👨👧👧
- CRUD users
- CRUD team
- Connection
- Chat & notifications
- Gestion du territoire 🌍
- Règles d'attributions des parcelles
- Placement 3D des parcelles
- Déplacement de la caméra
---
### Architecture du projet
-----------------
**Découpe fonctionnelle :**
- Défis 💬⚔️
- CRUD des questions
- Gestion
- Lancer un défis
- Répondre
- Feedback
- Régles du jeux
- Constructions & améliorations 🏘🔨
- CRUD des constructions
- Biblithèque de constructions
- Attribution des points
- 3D
- Instancitiation des assets 3D
- Rendu des assets 3D
---
### Choix des technologies
-----------------
Reconnaissance des déchets - **HARDWARE** 🔩
**Version 1**

Arduino : Open source + Dev interne
---
### Choix des technologies
-----------------
Reconnaissance des déchets - **HARDWARE** 📷
**Version 2**

Raspberry Pi : Open software / Close hardware
---
### Choix des technologies
-----------------
Reconnaissance des déchets - **IA** 🧠

Keras : Open source
Tensorflow : Open source
---
### Choix des technologies
-----------------
Module de connection - **Système d'identification** 🔐

---
### Choix des technologies
-----------------
Application et Gameplay - **Moteur de jeu** ⚙️

---
### Choix des technologies
-----------------
Herbement, bdd et gestion - **Serveur** 🗄

---
### Schéma haut-niveau
-----------------

---
### Démonstration
-----------------
---
### Gestion de projet
-----------------
**Planning 📅**
```mermaid
gantt
section Poubelle
Proto test arduino :a1, 2018-12-01, 2019-01-18
Dev IA : a11, after a1, 20d
Proto final rasp & hardware : a12, after a11, 2019-04-26
section Jeu
Dev démo test :a2, 2018-12-01, 2019-01-18
Module NFC : a22, 2019-04-10, 2019-04-26
Dev Gameplay : a23, 2019-05-10, 2019-06-10
Rendu 3D : a24, after a23, 2019-06-20
section Serveur
Mise en place et configuration: a3, 2018-12-01, 10d
Dev back-end: a31, after a3 2018-12-01, 2019-06-01
```
---
### Gestion de projet
-----------------
## 💃🕺
- Répartition des taches : Par affinité
- Mise en place du travail collaboratif :
- Trello
- [API Documentation](http://game.dump-it.io)
- [Where to find this service ?](https://hackmd.io/@damiendabernat/HkjbM710V)
- Versioning :
- Logiciel de gestion de version : [Bitbucket](https://bitbucket.org/%7B941266c8-7d7c-4f15-81a2-9b50cc39af58%7D/)
- Règles de commit : Utilisation de Gitflow
---
### CI/CD
-----------------
- [Pipeline](https://bitbucket.org/teamdumpit/dump-it-server/addon/pipelines/home#!/)
- [Systeme déploiement](https://bitbucket.org/teamdumpit/dump-it-server/addon/pipelines/deployments)
```bash
git pull
sudo docker-compose build #Build and Testing
sudo docker-compose up -d #If build succeeded
sudo docker system prune -f #Clean
```
---
### Monitoring & Alerting
-----------------
[]((https://dumpit.loggly.com/))
#### [Loggly](https://dumpit.loggly.com/) + [Portainer](http://game.dump-it.io:9000/)
---
### Coding rules
-------
*[Règles misent en place détaillé](https://hackmd.io/@damiendabernat/B1cuLuTTV)*
#### Objects Calisthenics 🤸♂️
* Seulement un niveau d’indentation
* Éviter l’usage du mot-clé else
* Encapsuler les types primitifs
* Maximum un opérateur d’objet par ligne
* Ne pas abréger
* Développer des petites classes
* Limiter le nombre de propriétés d’instance dans une classe
* Éviter les classes de collection de premier ordre
* Limiter l’usage d’accesseurs et mutateurs publiques
---
### Coding rules
-------
*[Règles misent en place détaillé](https://hackmd.io/@damiendabernat/B1cuLuTTV)*
#### Démarche Solid ⛰
- **S**ingle Responsability Principle ou « principe de responsabilité unique »
- **O**pen Close Principle ou « principe Ouvert / Fermé »
- **L**iskov Substitution Principle ou « principe de substitution de Liskov »
- **I**nterface Segregation Principle ou « principe de ségrégation d’interface »
- **D**ependency Inversion Principle ou « principe d’inversion des dépendances »
---
### Tests utilisateurs
-------
## 👥
1er tests utilisateurs

---
### Tests utilisateurs
-------
## 👀
Eye tracking

---
### Tests utilisateurs
-------
## 👶
Workshop Lycée

---
## 2. Revue détaillée d'un module
---
### Module de défis
-----------------
**Description du module :** Conquérir les parcelles des autres compagnies.
- Relever des défis permettra de conquérir les parcelles des autres compagnies et les points leur correspondant.
- Le joueur d'une équipe peut lancer un défi à l’équipe adverse qu’il souhaite.
- Une parcelle sera mise en jeu.
- Les défis ont un but pédagogique ou poussent à réaliser des actions écologiques.
- Si le joueur gagne, sa compagnie récupère la parcelle ennemie (distribué au hasard) sinon, il perd une parcelle qui sera donnée à la compagnie adverse.
---
### Module de défis
-----------------

---
### Module de défis
-----------------
**Découpe fonctionnelle :**
- Lancer un défis
- Recevoir un défis
- Répondre au défis
- Feedbacks
- Défis en attente
- Défis gagné
- Défis perdu
- Gestion égalité
- Update du territoire
- Update 3D
- Update points
---
```mermaid
sequenceDiagram
participant P1 as Player1
participant C1 as SocketClient1
participant S as Server
participant C2 as SocketClient2
participant P2 as Player2
P1 ->> C1 : ClickOnAdverseParcel()
C1 ->> S: send("challenge", teamId)
activate S
S -->> C1: send("question", qId)
activate C1
C1 -->> P1 : DisplayQuestion()
activate P1
P1 ->> C1 : Answer()
deactivate P1
C1 ->> S : send("answer", id)
S -->> C1 : send("ongoing", teamId)
C1 -->> P1 : DisplayOnGoingBand()
deactivate C1
S -->> C2: send("challenged", qId, teamId)
activate C2
C2 -->> P2 : DisplayQuestion()
activate P2
P2 ->> C2 : Answer()
deactivate P2
C2 ->> S : send("answer", id)
deactivate C2
S ->> S : GetWinner()
S ->> C1 : send("winner", teamId)
activate C1
Note left of C1: If ( teamId == playerTeamId )
C1 -->> P1 : DisplayYouWin()
deactivate C1
S ->> C2 : send("winner", teamId)
activate C2
Note right of C2: If ( teamId != playerTeamId )
C2 -->> P2 : DisplayYouLoose()
deactivate C2
S ->> C1 : send("parcelUpdate", parcel)
C1 -->> P1 : Update3DParcel()
S ->> C2 : send("parcelUpdate", parcel)
C2 -->> P2 : Update3DParcel()
deactivate S
```
---
### Module de défis
-----------------
**Ktor :** Class `Manager`
```kotlin
object Manager {
//...
val challenge = mutableListOf<OngoingChallenge>()
val challengeWebSocketSessions = HashMap<Int, DefaultWebSocketServerSession>()
//...
}
```
---
### Module de défis
-----------------
**Ktor :** Class `Challenge`
```kotlin
data class OngoingChallenge(val challengerList: MutableMap<Team, Int>, val challenge: Challenge)
data class Challenge(val id:Int, val question: String, val answerList: List<String>, val solution: Int)
object ChallengesDao {}
```
---
### Module de défis
-----------------
**Ktor :** Class `WebSocketChallengeController`
```kotlin
data class EventType(val type: String, val id:Int?, val message: String?)
webSocket("ws/challenge/team/{teamId}") {
call.parameters["teamId"]?.toInt()?.let { teamId ->
teamsDao.findById(teamId)?.let { team ->
Manager.challengeWebSocketSessions[team.id] = this
// ...
}
close(CloseReason(1003, "Team deconnected"))
}
}
```
---
### Module de défis
-----------------
**Unity :** Class `Server`
- Singleton
- Never Destroyed
```csharp
private static Server instance;
public static Server Instance
{
get
{
if (instance != null)
{
return instance;
}
else
{
instance = new GameObject("Server").AddComponent<Server>();
return Instance;
}
}
private set { instance = value; }
}
````
---
### Module de défis
-----------------
**Unity :** Class `Server`
- Gère tous les appels serveur (http & socket)
- Parse json & create models
- Garde les données de la session en cours (`currentUser`, `currentWorld`)
```csharp
public void InitChallengeWatcher()
{
string uri = "ws://" + serverUrl + "/ws/challenge/team/" + currentUser.team.id;
challengeWebSocket = WebSocketFactory.CreateInstance(uri);
// ....
challengeWebSocket.OnMessage += (data) => {
GotNewChallengeEvent(data);
};
// ....
challengeWebSocket.Connect();
}
private void GotNewChallengeEvent(byte[] data)
{
Error error = JsonHelper.FromJsonCheckError(Encoding.UTF8.GetString(data));
if(error == null)
{
ChallengeEvent evt = JsonUtility.FromJson<ChallengeEvent>(Encoding.UTF8.GetString(data));
OnNewChallengeEvent?.Invoke(evt);
}
else
{
OnErrorChallengeEvent?.Invoke(error);
}
}
```
---
### Module de défis
-----------------
**Unity :** Class `GameManager`
- Singleton
- Gère user interactions
- Initialise les différentes features

---
### Module de défis
-----------------
**Unity :** Class `ChallengeManager`
- Ouvre & écoute le socket du server
- Instancie les écrans en fonction des évenements reçu
- Gère l'UI & affiches les données
```csharp
private void NewChallengeEventHandler(ChallengeEvent evt)
{
if (evt.type == ChallengeEventType.question.ToString())
{
questionId = evt.id;
UnityMainThreadDispatcher.Instance().Enqueue(DoOnEvent(DisplayQuestion));
}
// ....
if (evt.type == ChallengeEventType.winner.ToString())
{
UnityMainThreadDispatcher.Instance().Enqueue(DoOnEvent(() => {
GetChallengeResult(evt.id, int.Parse(evt.message));
}));
}
}
```
---
### De déchets à ressources 🗑➜💸
-------
---
### De déchets à ressources 🗑➜💸
-------
- Reconnaissance des déchets
- Dev hardware sur raspberry Pi
- Dev IA
- Connection poubelle-smarthone
- module NFC
- Collecte des ressources disponibles
- Back-end
- Front-end : animation 3D dans Unity
- Gestion des ressources virtuelles
- Back-end
- Front-end
---
### Reconnaissance des déchets
-------
Dev hardware - **Raspberry python script**
```python
if(GPIO.output(led,True)):
camera.start_preview()
sleep(3)
camera.capture('/home/pi/image.jpg')
camera.stop_preview()
GPIO.output(led,False)
try:
connection.request('POST', '/predict', open('/home/pi/image.jpg', 'rb'))
response = connection.getresponse().read().decode()
if(response == "\"metal\"" or response == "\"paper\"" or response == "\"plastic\""):
move(10.5)
```
---
### Reconnaissance des déchets
-------
Dev IA - **TrashVGG**
```python
input_shape = (512,512,3)
model = Sequential()
model.add(Conv2D(16, kernel_size=(3,3), activation='relu', input_shape=input_shape))
model.add(Conv2D(16, kernel_size=(3,3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(BatchNormalization())
model.add(Conv2D(32, kernel_size=(3,3), activation='relu'))
model.add(Conv2D(32, kernel_size=(3,3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(BatchNormalization())
for n_input in [64,128,256]:
model.add(Conv2D(n_input, kernel_size=(3,3), activation='relu'))
model.add(Conv2D(n_input, kernel_size=(3,3), activation='relu'))
model.add(Conv2D(n_input, kernel_size=(3,3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(BatchNormalization())
model.add(Conv2D(256, kernel_size=(3,3), activation='relu'))
model.add(Conv2D(256, kernel_size=(3,3), activation='relu'))
model.add(Conv2D(256, kernel_size=(3,3), activation='relu'))
model.add(Conv2D(256, kernel_size=(3,3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(GlobalAveragePooling2D())
model.add(Dense(n_classes, activation='softmax'))
```
---
### Reconnaissance des déchets
-------
Dev IA - **TrashVGG**

**[TensorBoard](http://0.0.0.0:6006/#scalars)**
---
### Connection poubelle-smarthone
-------
Module NFC - **Unity AndroidJavaObject**
`Plugins/Android/AndroidManifest.xml`
```xml
<activity android:name="com.unity3d.player.UnityPlayerActivity" android:label="@string/app_name">
...
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<action android:name="android.nfc.action.TECH_DISCOVERED" />
<action android:name="android.nfc.action.TAG_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="dumpit" />
</intent-filter>
<meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech_filter" android:value="true" />
</activity>
```
`Scripts/Modules/NFC.cs`
```csharp
// Get ID of tag
AndroidJavaObject mNdefMessage = mIntent.Call<AndroidJavaObject>("getParcelableExtra", "android.nfc.extra.TAG");
if (mNdefMessage != null)
{
byte[] payLoad = mNdefMessage.Call<byte[]>("getId");
string text = System.Convert.ToBase64String(payLoad);
tagID = text;
Connected();
}
---
### Récupération serveurs
-------
Back-end Ktor - **Kotlin**
```kotlin
fun increaseResource(trashId: Int, resourceName: ResourceName): Boolean {
trashesDao.findById(trashId)?.let { trashInBdd ->
if(!trashes.contains(trashInBdd)) {
trashes.add(trashInBdd)
}
val trash = trashes.elementAt(trashes.indexOf(trashInBdd))
trash.getTemporaryResource(resourceName)?.let {(resource, amount) ->
trash.temporaryResource.put(resource, amount+1)
return true
} ?: run {
resourcesDao.findById(resourceName.id)?.let {
trash.temporaryResource.put(it, 1)
return true
} ?: run {
return false
}
}
} ?: run {
return false
}
}
```
---
### Collecte des ressources disponibles
-------
Front end : animation - **Unity**
```csharp
IEnumerator AnimateFactory(System.Action callback = null)
{
foreach (Ressource ressource in newRessources)
{
for(int i= 0; i< ressource.value; i++)
{
float animLenght = LaunchAnim(ressource.idResource);
yield return new WaitForSeconds(animLenght);
IncrementCount(i + 1, ressource.idResource);
}
}
callback?.Invoke();
}
```
---
## Exemple d'optimisation
-----------
Loading de parcelles d'un monde - **Kotlin**
```kotlin
fun loadParcels(parcelsDao: ParcelsDao, basicData: Boolean) {
for(x in 0 until dimension) {
for(y in 0 until dimension) {
val parcel = findParcel(parcelsDao, x, y, basicData)
parcel?.let {
territory[x, y] = parcel
}
}
}
isTerritoryLoaded = true
}
```
---
## Exemple d'optimisation
-----------
- Optimisation des exports 3D
- Chargement des scènes "en mode additive" pour éviter d'interroger le serveur pour charger le monde
---
# Merci 👋
{"metaMigratedAt":"2023-06-14T22:01:32.261Z","metaMigratedFrom":"YAML","title":"Revue de code Dump It","breaks":true,"description":"Revue de code 12/06/2019 -- Damien DABERNAT - Pauline SCHNEE","contributors":"[{\"id\":\"343c2f42-aa2e-4178-8275-344c6834deaf\",\"add\":19856,\"del\":11073},{\"id\":\"61a01a9c-26ce-4777-be70-784f88871d78\",\"add\":10658,\"del\":3428}]"}