summaryrefslogtreecommitdiff
path: root/ero1/src/demo
diff options
context:
space:
mode:
authorMartial Simon <msimon_fr@hotmail.com>2025-09-15 01:07:58 +0200
committerMartial Simon <msimon_fr@hotmail.com>2025-09-15 01:07:58 +0200
commit967be9e750221ab2ab783f95df79bb26d290a45e (patch)
tree6802900a5e975f9f68b169f0f503f040056d6952 /ero1/src/demo
add: added projectsHEADmain
Diffstat (limited to 'ero1/src/demo')
-rw-r--r--ero1/src/demo/ask_variable.py26
-rw-r--r--ero1/src/demo/demo_cost.py41
-rw-r--r--ero1/src/demo/demo_exec.py229
-rw-r--r--ero1/src/demo/demo_final_report.py110
-rw-r--r--ero1/src/demo/demo_stats.py64
-rw-r--r--ero1/src/demo/print_demo.py20
6 files changed, 490 insertions, 0 deletions
diff --git a/ero1/src/demo/ask_variable.py b/ero1/src/demo/ask_variable.py
new file mode 100644
index 0000000..f2f9330
--- /dev/null
+++ b/ero1/src/demo/ask_variable.py
@@ -0,0 +1,26 @@
+from src.demo.print_demo import print_demo
+import os
+
+def ask_variable(question, answers):
+ """
+ Pose une question avec des réponses multiples et attend une réponse de l'utilisateur.
+ Parameters:
+ question : La question à afficher
+ answers : Liste de tuples contenant (réponse, valeur)
+ """
+ print("")
+ print_demo(f"{question}")
+ for i, (answer_text, _) in enumerate(answers, 1):
+ print_demo(f"{i}. {answer_text}", 1)
+
+ while True:
+ try:
+ choice = input("\n⟩ Entrez le numéro de votre choix ou 'stop' pour arrêter : ")
+ if choice == "stop":
+ raise KeyboardInterrupt
+ choice = int(choice)
+ if 1 <= choice <= len(answers):
+ return answers[choice - 1][1]
+ print_demo(f"Insérez un nombre entre 1 et {len(answers)}", 1)
+ except ValueError:
+ print_demo(f"Insérez un nombre entre 1 et {len(answers)}", 1)
diff --git a/ero1/src/demo/demo_cost.py b/ero1/src/demo/demo_cost.py
new file mode 100644
index 0000000..573e367
--- /dev/null
+++ b/ero1/src/demo/demo_cost.py
@@ -0,0 +1,41 @@
+import math
+
+def get_edge_length(graph, u, v):
+ try:
+ return graph[u][v][0]['length']
+ except KeyError:
+ try:
+ return graph[v][u][0]['length']
+ except KeyError:
+ return 0
+
+def format_time(time_seconds):
+ seconds = math.ceil(time_seconds % 60)
+ minutes = math.floor(time_seconds / 60)
+ if minutes == 0:
+ return f"{seconds} secondes"
+ return f"{minutes} minute(s) et {seconds} secondes"
+
+def export_drone(graph, time_exec, parcours):
+ total_dist = 0
+ time_exec = time_exec / 60
+ total_dist += math.ceil(sum(get_edge_length(graph, u, v) for u, v in parcours))
+
+ return {
+ "dist":total_dist,
+ "time":{
+ "format":format_time(time_exec),
+ "value":time_exec
+ }
+ }
+
+def export_deneigeuse(graph, time_exec, parcours):
+ total_dist = math.ceil(sum(get_edge_length(graph, u, v) for u, v in parcours))
+
+ return {
+ "dist":total_dist,
+ "time":{
+ "format":format_time(time_exec),
+ "value":time_exec
+ }
+ } \ No newline at end of file
diff --git a/ero1/src/demo/demo_exec.py b/ero1/src/demo/demo_exec.py
new file mode 100644
index 0000000..1242ff5
--- /dev/null
+++ b/ero1/src/demo/demo_exec.py
@@ -0,0 +1,229 @@
+# DEMO IMPORT
+from src.demo.print_demo import print_demo
+from src.demo.ask_variable import ask_variable
+from src.helper.color_suburbs import display_suburbs_with_colors
+from src.demo.demo_cost import export_deneigeuse, export_drone, get_edge_length
+from src.demo.demo_stats import stats_deneigeuses, stats_drone
+from src.demo.demo_final_report import * # nsm, flemme de trier
+
+# IMPORT FOR GENERATION
+from src.generation.graph_generation import generate_graph
+from src.generation.suburb_snowplow_generation import generate_quartiers_from_path
+
+# IMPORT FOR DRONE:
+from src.drone.postier_chinoisV2 import final_path, postier_chinois_process_v2, connect_circuits
+from src.drone.postier_chinoisV1 import postier_chinois_process_v1
+from src.drone.postier_chinoisV3 import postier_chinois_process_v3
+
+# IMPORT FOR DENEIGEUSE:
+from src.deneigeuses.hierholzer_v1 import process_hierholzer_v1
+from src.deneigeuses.hierholzer_v2 import process_hierholzer_v2
+from src.deneigeuses.directed_cpp import oriented_cpt
+from src.deneigeuses.drone_path import drone_path
+from src.helper.partitions import partition
+from src.deneigeuses.hangar_to_deneigeuse import path_hangar_to_deneigeuse, path_deneigeuse_to_hangar
+
+# IMPORT FOR YAML & KML
+from src.helper.export_to_kml import export_to_kml
+from src.helper.export_import_yaml import load_paths_from_yaml
+import os
+import time
+
+
+def demo_exec(place, drone, deneigeuse, debug):
+ """
+ Fonction principale pour le démarrage de la démo.
+ Parameters:
+ place : Liste des arrondissements à parcourir.
+ drone : Type de drone à utiliser.
+ deneigeuse : Type de déneigeuse à utiliser.
+ debug : Mode debug activé ou non.
+ """
+
+ # ---------------------- INITIALISATION ---------------------------
+
+ os.system('cls' if os.name == 'nt' else 'clear')
+ print_demo("Démarrage du programme sur :", time=True)
+ for lieu in place:
+ print_demo(lieu, indent=1)
+ print("")
+
+ # ---------------------- GENERATION DU GRAPHE ---------------------------
+
+ print_demo("Génération du graph de Montréal en cours...", time=True)
+ graph = generate_graph("Montréal, Québec, Canada", debug_mode=debug)
+ print_demo("Graph généré avec succès.", indent=1)
+
+ # ---------------------- PARCOURS - DRONE ---------------------------
+
+ start_time_drone = time.time()
+ if drone == "postier_chinoisV1":
+ print_demo("Parcours du drone sur Montréal avec la version 1.", time=True)
+ parcours_drone, finalPath = postier_chinois_process_v1(graph, debug)
+
+ elif drone == "postier_chinoisV2":
+ print_demo("Parcours du drone sur Montréal avec la version 2.", time=True)
+ parcours_drone, finalPath = postier_chinois_process_v2(graph, debug)
+
+ elif drone == "postier_chinoisV3":
+ print_demo("Parcours du drone sur Montréal avec la version 3.", time=True)
+ parcours_drone, finalPath = postier_chinois_process_v3(graph, debug)
+ else:
+ print_demo(f"Récupération du parcours de Montréal sur le fichier : {drone}", time=True)
+ parcours_drone = load_paths_from_yaml(graph, drone)
+ finalPath = final_path(graph, parcours_drone)
+
+ end_time_drone = time.time()
+ print_demo("Parcours du drone effectué avec succès.", indent=1)
+
+ # ---------------------- AFFICHAGE DU GRAPHE ---------------------------
+
+ Q_display = "Voulez-vous afficher le graph du parcours du drone ?"
+ A_display = [("Oui", True), ("Non", False)]
+ display = ask_variable(Q_display, A_display)
+ if display:
+ connections = connect_circuits(graph, parcours_drone)
+ display_suburbs_with_colors(graph, parcours_drone, connections)
+
+ # ---------------------- RECUPERATION DU PARCOURS DU DRONE ---------------------------
+
+ parcours_drone_arrondissements = []
+ for elt in place:
+ if elt in parcours_drone:
+ parcours_drone_arrondissements.extend(parcours_drone[elt])
+ else:
+ raise ValueError(f"{elt} introuvable dans le parcours du drone.")
+
+ # ---------------------- CALCUL DES COUTS - DRONE ---------------------------
+
+ cost_drone = export_drone(
+ graph, end_time_drone-start_time_drone, finalPath)
+ stat_drone = stats_drone(cost_drone)
+
+ # ---------------------- CALCUL DU NOMBRE OPTIMAL DE DÉNEIGEUSES ---------------------------
+
+ if deneigeuse == "naive":
+ print_demo("/!\\ L'algorithme de déneigeuse choisi ne dispose pas de la possibilité d'avoir plusieurs déneigeuses.", time=True)
+ number_of_deneigeuses = [1]
+ else:
+ optimal_number_deneigeuses = max(1, round((sum(get_edge_length(
+ graph, u, v) for u, v in parcours_drone_arrondissements)) / 200000))
+
+ Q_deneigeuse_number = "Avec quel nombre de déneigeuses voulez-vous comparer les coûts ?"
+ A_deneigeuse_number = [
+ ("1 déneigeuse", 1),
+ (f"{optimal_number_deneigeuses} déneigeuses - Version Optimale", optimal_number_deneigeuses),
+ (f"{optimal_number_deneigeuses * 2} déneigeuses", optimal_number_deneigeuses * 2), (f"{optimal_number_deneigeuses * 3} déneigeuses", optimal_number_deneigeuses * 3),
+ ("Faire toutes les partitions et comparer les coûts", 0)]
+ number_reply = ask_variable(Q_deneigeuse_number, A_deneigeuse_number)
+ if number_reply == 0:
+ number_of_deneigeuses = [optimal_number_deneigeuses, 1,
+ optimal_number_deneigeuses * 2, optimal_number_deneigeuses * 3]
+ else:
+ number_of_deneigeuses = [number_reply]
+ print("")
+
+ # ---------------------- GENERATION DU GRAPHE DE PARTITIONS ---------------------------
+
+ G_partition = generate_quartiers_from_path(
+ parcours_drone, graph, suburb_list=place, debug_mode=debug)
+
+ # ---------------------- PARCOURS - DÉNEIGEUSES ---------------------------
+
+ if deneigeuse == "hierholzer_v1" or deneigeuse == "hierholzer_v2":
+ print_demo("Parcours de la déneigeuse à partir de l'algorithme de Hierholzer", time=True)
+ elif deneigeuse == "naive":
+ print_demo("Parcours de la déneigeuse à partir de l'implémentation naïve", time=True)
+ else:
+ print_demo("Parcours de la déneigeuse à partir de l'algorithme du Postier Chinois Orienté", time=True)
+
+ cost_deneigeuses = {}
+ parcours_deneigeuses = {}
+ for i in number_of_deneigeuses:
+ cost_deneigeuses[i] = []
+ parcours_deneigeuses[i] = []
+
+ if i == 1 and (deneigeuse == "naive" or "hierholzer" in deneigeuse):
+ temp = []
+ for arrondissement in place:
+ temp.extend(parcours_drone[arrondissement])
+ partitions = [temp]
+ else:
+ partitions = partition(G_partition, i, debug_mode=debug)
+
+ for part in partitions:
+ start_time_deneigeuse = time.time()
+ drone_edges = [(u, v) for u, v in part.edges()] if i != 1 else part
+
+ if "hierholzer" in deneigeuse:
+ if deneigeuse == "hierholzer_v2":
+ parcours_deneigeuse = process_hierholzer_v2(
+ graph, drone_edges, debug)
+ else:
+ parcours_deneigeuse = process_hierholzer_v1(
+ graph, drone_edges, debug)
+
+ start_node = parcours_deneigeuse[0][0]
+ parcours_HangarDeneigeuse = path_hangar_to_deneigeuse(
+ graph, start_node)
+ end_node = parcours_deneigeuse[-1][1]
+ parcours_DeneigeuseHangar = path_deneigeuse_to_hangar(
+ graph, end_node)
+
+ parcours_deneigeuse = parcours_HangarDeneigeuse + parcours_deneigeuse + parcours_DeneigeuseHangar
+ if deneigeuse == "oriented_cpt":
+ parcours_deneigeuse = oriented_cpt(part, graph, debug)
+
+ start_node = parcours_deneigeuse[0][0]
+ parcours_HangarDeneigeuse = path_hangar_to_deneigeuse(graph, start_node)
+ end_node = parcours_deneigeuse[-1][1]
+ parcours_DeneigeuseHangar = path_deneigeuse_to_hangar(graph, end_node)
+
+ parcours_deneigeuse = parcours_HangarDeneigeuse + parcours_deneigeuse + parcours_DeneigeuseHangar
+ if deneigeuse == "naive":
+ parcours_deneigeuse = drone_path(drone_edges, graph)
+
+ start_node = parcours_deneigeuse[0][0]
+ parcours_HangarDeneigeuse = path_hangar_to_deneigeuse(graph, start_node)
+ end_node = parcours_deneigeuse[-1][1]
+ parcours_DeneigeuseHangar = path_deneigeuse_to_hangar(graph, end_node)
+
+ parcours_deneigeuse = parcours_HangarDeneigeuse + parcours_deneigeuse + parcours_DeneigeuseHangar
+
+ parcours_deneigeuses[i].append(parcours_deneigeuse)
+ end_time_deneigeuse = time.time()
+
+ cost_deneigeuse = export_deneigeuse(
+ graph, end_time_deneigeuse-start_time_deneigeuse, parcours_deneigeuse)
+ cost_deneigeuses[i].append(cost_deneigeuse)
+
+ stat_déneigeuses = {}
+ for i in number_of_deneigeuses:
+ stat_déneigeuses[i] = stats_deneigeuses(cost_deneigeuses[i])
+
+ print_demo("Parcours de la déneigeuse effectué avec succès.", indent=1)
+
+ # ---------------------- CALCUL DES STATS - DÉNEIGEUSES ---------------------------
+
+ os.system('cls' if os.name == 'nt' else 'clear')
+
+ print_recap_stat_drone(stat_drone, 0)
+
+ print("")
+
+ print_all_stat(stat_déneigeuses, 0)
+
+ print("")
+ print("--------------------")
+ print("")
+
+ # ---------------------- EXPORTATION DU PARCOURS EN KML ---------------------------
+
+ print_demo(
+ "Exportation des parcours des déneigeuses avec le parcours du drone au format KML.")
+ for i in parcours_deneigeuses:
+ print_demo(
+ f"{i} déneigeuses - Fichier KML : temp/parcours_{i}_deneigeuses.kml", indent=1)
+ export_to_kml(parcours_deneigeuses[i], finalPath, graph, debug, name=f"parcours_{i}_deneigeuses")
+
+ return True
diff --git a/ero1/src/demo/demo_final_report.py b/ero1/src/demo/demo_final_report.py
new file mode 100644
index 0000000..59badda
--- /dev/null
+++ b/ero1/src/demo/demo_final_report.py
@@ -0,0 +1,110 @@
+from src.demo.print_demo import print_demo
+from src.demo.ask_variable import ask_variable
+from src.helper.color_suburbs import display_suburbs_with_colors
+from src.demo.demo_cost import export_deneigeuse, export_drone, get_edge_length
+from src.demo.demo_stats import stats_deneigeuses, stats_drone
+
+# IMPORT FOR GENERATION
+from src.generation.graph_generation import generate_graph
+from src.generation.suburb_snowplow_generation import generate_quartiers_from_path
+
+# IMPORT FOR DRONE:
+from src.drone.postier_chinoisV2 import final_path, postier_chinois_process_v2, connect_circuits
+from src.drone.postier_chinoisV1 import postier_chinois_process_v1
+from src.drone.postier_chinoisV3 import postier_chinois_process_v3
+
+# IMPORT FOR DENEIGEUSE:
+from src.helper.partitions import partition
+from src.deneigeuses.hangar_to_deneigeuse import path_hangar_to_deneigeuse, path_deneigeuse_to_hangar
+
+# IMPORT FOR YAML & KML
+from src.helper.export_to_kml import export_to_kml
+from src.helper.export_import_yaml import load_paths_from_yaml
+import os
+import time
+
+
+def line():
+ print("----------------------------------")
+
+
+def print_cost_from_list(lst, base_indent=0):
+ """
+ affiche la distance et le temps approximatif de chaque déneigeuses
+ """
+
+ # print_demo(f"Récap avec {len(lst)} déneigeuses:", base_indent)
+ print("")
+ print_demo("Récapitulatif des déneigeuses:", base_indent)
+
+ for i, o in enumerate(lst):
+ print_demo(f"Déneigeuse numéro {i+1} :", base_indent + 1)
+ print_demo(f"Distance = {(o['dist']/1000):.2f} km", base_indent + 2)
+ print_demo(f"Durée approximative = {o['time']['format']}", base_indent + 2)
+
+
+def print_recap_stat_target(stat_deneigeuses, nb_deneigeuse, most_opti=False, base_indent=0):
+ """
+ Affiche dans le terminal sous un format organisé les statistiques
+ avec nb_deneigeuse déneigueses
+ """
+ if (nb_deneigeuse not in stat_deneigeuses):
+ raise ValueError(f'Données pour {nb_deneigeuse} non calculées')
+
+ data = stat_deneigeuses[nb_deneigeuse]
+
+ if most_opti:
+ print_demo(f"Récapitulatif global (solution opti): {data['number']} déneigeuses", base_indent)
+ else:
+ print_demo(f"Récapitulatif global: {data['number']} déneigeuses", base_indent)
+
+ # print_demo(f"nombre de déneigeuses = {data['number']}", base_indent + 1)
+
+ print_demo("Distance parcourue par les déneigeuses :", base_indent + 1)
+ print_demo(f"Maximale = {data['dist']['max']:.2f} km", base_indent + 2)
+ print_demo(f"Minimale = {data['dist']['min']:.2f} km", base_indent + 2)
+ print_demo(f"Totale = {data['dist']['total']:.2f} km", base_indent + 2)
+ print_demo(f"Moyenne = {data['dist']['avg']:.2f} km", base_indent + 2)
+
+ print_demo("Temps réel de parcours pour les déneigeuses:", base_indent + 1)
+ print_demo("Type 1 : ", base_indent+2)
+ print_demo(f"Maximale = {data['time']['real']['t1']['max']:.2f} heures", base_indent + 3)
+ print_demo(f"Moyenne = {data['time']['real']['t1']['avg']:.2f} heures", base_indent + 3)
+ print_demo("Type 2 : ", base_indent+2)
+ print_demo(f"Maximale = {data['time']['real']['t2']['max']:.2f} heures", base_indent + 3)
+ print_demo(f"Moyenne = {data['time']['real']['t2']['avg']:.2f} heures", base_indent + 3)
+
+ print_demo("Coût total :", base_indent + 1)
+ print_demo(f"Type 1 = {data['cost']['t1']:.2f} euros", base_indent + 2)
+ print_demo(f"Type 2 = {data['cost']['t2']:.2f} euros", base_indent + 2)
+
+ print_cost_from_list(data['list'], base_indent)
+
+
+def print_recap_stat_drone(stat_drone, base_indent=0):
+ print("##################")
+ print("# Recap du drone #")
+ print("##################")
+ # dist en km plus bas print_demo(f"distance parcourue par le drone: {stat_drone["dist"]}", base_indent+1)
+ print_demo("Temps écoulé :", base_indent)
+ print_demo(f"Durée de l'exécution du programme : {stat_drone['time']['format']}", base_indent+1)
+ print_demo(f"Durée réelle du parcours :", base_indent+1)
+ print_demo(f"{stat_drone['time']['real']:.2f} minutes", base_indent+2)
+ print_demo(f"{(stat_drone['time']['real'] / 60):.2f} heures", base_indent+2)
+ print_demo(f"Distance parcourue : {stat_drone['distKM']:.2f} km", base_indent)
+ print_demo(f"Coût : {stat_drone['cost']:.2f} euros", base_indent)
+
+
+def print_all_stat(stat_list, base_indent):
+ print("#########################")
+ print("# Recap des déneigeuses #")
+ print("#########################")
+ # line()
+
+ best = True
+ for elem in stat_list:
+ print("")
+ line()
+ print("")
+ print_recap_stat_target(stat_list, elem, best, base_indent)
+ best = False
diff --git a/ero1/src/demo/demo_stats.py b/ero1/src/demo/demo_stats.py
new file mode 100644
index 0000000..d8dc78c
--- /dev/null
+++ b/ero1/src/demo/demo_stats.py
@@ -0,0 +1,64 @@
+import math
+import parameters
+
+def stats_deneigeuses(all_costs):
+ '''
+ Calcule les statistiques des déneigeuses.
+ Parameters:
+ all_costs : Liste des coûts des déneigeuses.
+ Returns:
+ Un dictionnaire contenant les statistiques des déneigeuses.
+ '''
+ result = {
+ "list":all_costs
+ }
+ result["number"] = len(all_costs)
+ result["dist"] = {
+ "max": max(d["dist"] for d in all_costs) / 1000,
+ "min": min(d["dist"] for d in all_costs) / 1000,
+ "total": sum(d["dist"] for d in all_costs) / 1000
+ }
+ result["dist"]["avg"] = result["dist"]["total"] / result["number"]
+ result["time"] = {}
+ result["time"]["real"] = {"t1": {}, "t2": {}}
+ result["time"]["real"]["t1"]["avg"] = result["dist"]["avg"] / parameters.TYPE_I_SPEED
+ result["time"]["real"]["t2"]["avg"] = result["dist"]["avg"] / parameters.TYPE_II_SPEED
+ result["time"]["real"]["t1"]["max"] = result["dist"]["max"] / parameters.TYPE_I_SPEED
+ result["time"]["real"]["t2"]["max"] = result["dist"]["max"] / parameters.TYPE_II_SPEED
+
+
+ def coutHoraireTypeI(heures):
+ return heures * parameters.TYPE_I_COST_HOUR_UNDER_8 if heures <= 8 else 8 * parameters.TYPE_I_COST_HOUR_UNDER_8 + (heures - 8) * parameters.TYPE_I_COST_HOUR_ABOVE_8
+
+ def coutHoraireTypeII(heures):
+ return heures * parameters.TYPE_II_COST_HOUR_UNDER_8 if heures <= 8 else 8 * parameters.TYPE_II_COST_HOUR_UNDER_8 + (heures - 8) * parameters.TYPE_II_COST_HOUR_ABOVE_8
+
+ coutTotalTypeI = (
+ parameters.TYPE_I_COST_FIXED * (result["number"]) +
+ parameters.TYPE_I_COST_KILO * result["dist"]["total"] +
+ coutHoraireTypeI(result["time"]["real"]["t1"]["avg"] * result["number"]))
+
+
+ coutTotalTypeII = (
+ parameters.TYPE_II_COST_FIXED * result["number"] +
+ parameters.TYPE_II_COST_KILO * result["dist"]["total"] +
+ coutHoraireTypeII(result["time"]["real"]["t2"]["avg"] * result["number"])
+ )
+ result["cost"] = {
+ "t1":coutTotalTypeI,
+ "t2":coutTotalTypeII
+ }
+ return result
+
+def stats_drone(cost):
+ '''
+ Calcule les statistiques du drone.
+ Parameters:
+ cost : Dictionnaire contenant les coûts du drone.
+ Returns:
+ Un dictionnaire contenant les statistiques du drone.
+ '''
+ cost["distKM"] = cost["dist"] / 1000
+ cost["time"]["real"] = (cost["distKM"] / parameters.SUPER_DRONE_SPEED) * 60
+ cost["cost"] = parameters.SUPER_DRONE_COST_FIXED + parameters.SUPER_DRONE_COST_KILO * (cost["dist"] / 1000)
+ return cost \ No newline at end of file
diff --git a/ero1/src/demo/print_demo.py b/ero1/src/demo/print_demo.py
new file mode 100644
index 0000000..f44d98b
--- /dev/null
+++ b/ero1/src/demo/print_demo.py
@@ -0,0 +1,20 @@
+from datetime import datetime
+
+
+def print_demo(message, indent=0, time=False):
+ """
+ Affiche dans la console un message de démo formaté.
+ Parameters:
+ message : Le message à afficher.
+ indent : Le nombre d'espaces à ajouter devant le message.
+ time : Si True, affiche le timestamp.
+ """
+ # Récupérer le timestamp actuel en format string
+ str_time = datetime.now().strftime('%H:%M:%S.%f')[:-3]
+ time_in_str= f"[{str_time}] " if time else ""
+ space_in_str= " " * indent
+
+ if indent == 0:
+ print(f"{time_in_str}» {message}")
+ else:
+ print(f"{space_in_str} {time_in_str}› {message}")