<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> ![](https://i.imgur.com/t649qAa.png#logo) **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. ![](https://i.imgur.com/vLlLQVk.png) --- ### 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** ![](https://i.imgur.com/n9Kic0s.png#img-techno) Arduino : Open source + Dev interne --- ### Choix des technologies ----------------- Reconnaissance des déchets - **HARDWARE** 📷 **Version 2** ![](https://i.imgur.com/bNHmPwY.png#img-techno) Raspberry Pi : Open software / Close hardware --- ### Choix des technologies ----------------- Reconnaissance des déchets - **IA** 🧠 ![](https://i.imgur.com/juWlxcE.png#img-techno) Keras : Open source Tensorflow : Open source --- ### Choix des technologies ----------------- Module de connection - **Système d'identification** 🔐 ![](https://i.imgur.com/ooJMq7P.jpg#img-techno) --- ### Choix des technologies ----------------- Application et Gameplay - **Moteur de jeu** ⚙️ ![](https://i.imgur.com/iFar3WL.png#img-techno) --- ### Choix des technologies ----------------- Herbement, bdd et gestion - **Serveur** 🗄 ![](https://i.imgur.com/5ikRKYy.png#img-techno) --- ### Schéma haut-niveau ----------------- ![](https://i.imgur.com/70PKGul.png#full-screen) --- ### 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://i.imgur.com/1su5HvC.png#img-techno)]((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 ![](https://i.imgur.com/G5SDg5H.jpg#max-height-small) --- ### Tests utilisateurs ------- ## 👀 Eye tracking ![](https://i.imgur.com/g6KeBWC.gif#max-height-small) --- ### Tests utilisateurs ------- ## 👶 Workshop Lycée ![](https://i.imgur.com/o6PqbLC.png#max-height-small) --- ## 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 ----------------- ![](https://i.imgur.com/9iL6EH5.png#max-height) --- ### 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 ![](https://i.imgur.com/XZ7tcPy.png) --- ### 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** ![](https://i.imgur.com/ap8T2s1.png) **[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}]"}
    569 views
   Owned this note