Jalil LAHRACH 2024-2025
Note d’application : IHM et Communication avec l’alimentation bidirectionnelle
Client industriel – GCK Battery Département Génie Électrique
Représenté par Tuteur GE Jacques LAFFONT
Arnaud VOYER - Ingénieur systèmes embarqués Tuteur industriel Julian LAURENCE
Conception d’un banc de test de
batteries
Note d’application
par
Jalil LAHRACH
Rayan REZKI
Département Génie Électrique
POLYTECH Clermont
Table des matières
Intro : .................................................................................................................. 4
1. Communication avec l’alimentation bidirectionnelle : ................................. 4
1.1. Communication Ethernet avec l’alimentation : ....................................................4
1.2. Contrôle et cycles de charge/décharge d'une alimentation via SCPI : ...................6
1.2.1. Objectif : ....................................................................................................................... 6
1.2.2. Structure du programme : .............................................................................................. 6
1.2.3. Ouverture : .................................................................................................................. 12
2. L’Interface Homme-Machine (IHM) ........................................................... 13
2.1. Description générale ........................................................................................ 13
2.2. Fonctionnalités principales .............................................................................. 13
2.3. Description des fonctions................................................................................. 16
2.4. Fonctionnement de l'IHM.................................................................................. 20
2.5. Aspects techniques.......................................................................................... 21
2.6. Points d'amélioration possibles ........................................................................ 21
Conclusion : ...................................................................................................... 21
Annexe.............................................................................................................. 21
Annexe 1 : Code de communication ETH avec l’alimentation bidirectionnelle : ............. 22
Annexe 2 : Code de l’IHM :........................................................................................... 24
Figure 1: Image de la datasheet de l'alimentation et le logo de la marque _____________________________ 4
Figure 2 : Port LAN de l'alimentation bidirectionnelle EA-PSB 10000 __________________________________ 5
Figure 3 : Exemple de paramétrage réseaux de l’ordinateur fonctionnel _______________________________ 6
Figure 4 : Import des bibliothèques utiles __________________________________________________________ 6
Figure 5 : Paramètres globaux ____________________________________________________________________ 7
Figure 6 : Fonction d'envoie des commandes ______________________________________________________ 7
Figure 7 : Fonction de connexion initiale ___________________________________________________________ 8
Figure 8 : Fonction de reconnexion ________________________________________________________________ 8
Figure 9 : Commande de récupération des erreurs d’après le « Guide de Programmation » de
l’alimentation ___________________________________________________________________________________ 8
Figure 10 : Fonction de diagnostic des erreurs ______________________________________________________ 9
Figure 11 : Décodage des erreurs d'après le "Guide de Programmation" ______________________________ 9
Figure 12 : Description des commandes et requêtes standards IEEE _________________________________ 9
Figure 13 : Passage en mode "remote control"_____________________________________________________ 10
Figure 14 : Fonction d'initialisation de l'appareil ___________________________________________________ 10
Figure 15 : Validation de la connexion avec l'appareil ______________________________________________ 10
Figure 16 : Commande de "preset" de tension pour effectuer un premier test_________________________ 10
Figure 17 : Commande d'activation et de désactivation de la sortie de l'alimentation__________________ 11
Figure 18 : Logique de commande de l’alimentation _______________________________________________ 11
Figure 19 : Script principal d'exécution du code____________________________________________________ 12
Figure 20 : Bibliothèque à importer pour l’exécution de cette IHM ___________________________________ 13
Figure 21 : Affichage de l'IHM ____________________________________________________________________ 13
Figure 22 : Ouverture de la recherche de fichier suite à l'appui sur le bouton "Load Profile" ____________ 14
Figure 23 : Message si aucun profile n'a été chargé avant l'acquisition _______________________________ 14
Figure 24 : Affichage de l'acquisition des données _________________________________________________ 15
Figure 25 : Message si aucune donnée ne peut être affichée ________________________________________ 15
Figure 26 : Affichage des données ________________________________________________________________ 15
Figure 27 : Message si aucune donnée ne peut être sauvegardée____________________________________ 16
Figure 28 : Sauvegarde des données une fois acquises _____________________________________________ 16
Figure 29 : Variables principales _________________________________________________________________ 16
Figure 30 : Fonction d'initialisation de l'IHM _______________________________________________________ 17
Figure 31 : Fonction de création de l'affichage opérationnel de l'IHM ________________________________ 17
Figure 32 : Fonction de récupération de fichiers .csv _______________________________________________ 17
Figure 33 : Fonction de gestion du bouton "Start" __________________________________________________ 18
Figure 34 : Fonction de gestion du bouton "Stop" __________________________________________________ 18
Figure 35 : Fonction d'acquisition des données en mode "Auto" _____________________________________ 18
Figure 36 : Fonction d'acquisition des données en mode "Manual" __________________________________ 18
Figure 37 : Fonction d'acquisition des données ____________________________________________________ 19
Figure 38 : Fonction d'écriture dans la fenêtre _____________________________________________________ 19
Figure 39 : Fonction de sauvegarde des données __________________________________________________ 19
Figure 40 : Fonction d'affichage des données acquises _____________________________________________ 20
Intro :
Ce document se veut être une note d’application d’une partie de ce qui a été réalisé lors du projet
de conception d’un banc de test de batterie pour la branche GCK batterie l’entreprise GCK sous
la supervision des enseignants de l’école Polytech’Clermont. Vous verrez comment implémenter
et comprendre le programme de communication Ethernet avec l’alimentation bidirectionnelle
chargée de réaliser les cycles de charge/décharge ainsi que celui de l’interface utilisateur (IHM)
qui permet de faire le lien entre l’appareil et l’humain qui souhaite s’en servir.
1. Communication avec l’alimentation bidirectionnelle :
1.1. Communication Ethernet avec l’alimentation :
Comme stipulé dans le cahier des charges, l’alimentation qui sera utilisée tout au long
de ce processus est de la gamme Elektro-Automatik EA-PSB 10000.
Figure 1: Image de la datasheet de l'alimentation et le logo de la marque
Le mode de communication que l’on utilisera sera un mode Ethernet. Pour cela, il est
nécessaire de se connecter à l’alimentation en deux phases :
- De manière filaire, donc à l’aide d’un câble RJ45 branché au port LAN (Ethernet) de
l’alimentation.
Figure 2 : Port LAN de l'alimentation bidirectionnelle EA-PSB 10000
- De manière numérique, aux mêmes réseaux que l’alimentation, donc en configurant sur
l’ordinateur qui communique : le numéro du port TCP (fourni par l’alimentation), le type
de configuration IP (ici, « Manuellement », pour pouvoir modifier le reste des paramètres),
l’adresse IP (propre à l’ordinateur), le masque de sous-réseau et le routeur (qui doivent
être les mêmes que ceux de l’alimentation).
Figure 3 : Photo du paramétrage de la communication Ethernet de l'alimentation
D’après l’écran d’affichage de l’alimentation, l’adresse IP utilisée par défaut par celle-ci
est « [Link] », le routeur est « [Link] », le numéro de port est « 5025 » et le
masque de sous-réseau est « [Link] ». Il est alors nécessaire de choisir une
adresse IP pour notre ordinateur de la forme « 192.168.0.X » car le masque indique que
l’identifiant de l’appareil (ce qui le différencie des autres appareils sur ce réseau) est la
dernière valeur de son adresse IP (seul le dernier numéro du masque est différent de 255).
Ce choix permettra à l’alimentation de reconnaitre l’ordinateur et inversement.
Figure 3 : Exemple de paramétrage réseaux de l’ordinateur fonctionnel
1.2. Contrôle et cycles de charge/décharge d'une alimentation via SCPI :
Dans le cadre de la programmation de l’alimentation, il est nécessaire de télécharger la
bibliothèque « socket » pour utiliser la syntaxe SCPI et envoyer les commandes. De plus,
la bibliothèque « time » est requise pour insérer des délais entre les commandes,
permettant ainsi de gérer les cycles de charge et de décharge.
Figure 4 : Import des bibliothèques utiles
1.2.1. Objectif :
Ce programme vise à établir une connexion avec une alimentation programmable via le
protocole SCPI (Standard Commands for Programmable Instruments). Il permet
d’effectuer des cycles de charge et de décharge en utilisant une interface en Python. La
note fournit une analyse approfondie des fonctions, de la logique des cycles, et des
étapes d’initialisation.
1.2.2. Structure du programme :
[Link]. Paramètres globaux :
Figure 5 : Paramètres globaux
- « IP_ADDRESS » et « PORT » : Spécifient l’adresse IP et le port TCP de l’alimentation.
- « NUM_CYCLES » : Nombre de cycles charge/décharge à exécuter.
[Link]. Fonctions utilitaires :
• Envoi de commandes :
Figure 6 : Fonction d'envoie des commandes
- Cette fonction transmet une commande SCPI au dispositif via une connexion existante.
- Gestion d’exceptions :
- Si le socket est fermé, une reconnexion automatique est tentée.
- Les erreurs courantes (e.g., BrokenPipeError) sont capturées pour assurer la
robustesse.
- Réponse : La fonction retourne la réponse du dispositif à la commande envoyée.
• Connexion initiale :
Figure 7 : Fonction de connexion initiale
- Établit une connexion TCP avec l’alimentation en utilisant les paramètres IP et port.
- Active l’option TCP « Keep-Alive » pour maintenir la connexion active.
- « Timeout » : La fonction inclut un délai de 10 secondes pour gérer les pannes de
connexion.
• Rétablissement de connexion :
Figure 8 : Fonction de reconnexion
- En cas de perte de connexion, cette fonction ferme le socket existant et tente une
reconnexion automatique.
• Lecture des journaux d’erreur :
Figure 9 : Commande de récupération des erreurs d’après le « Guide de
Programmation » de l’alimentation
Figure 10 : Fonction de diagnostic des erreurs
- Utilise la commande SCPI « SYST:ERR? » pour récupérer les logs d’erreur du dispositif.
Figure 11 : Décodage des erreurs d'après le "Guide de Programmation"
• Initialisation :
Figure 12 : Description des commandes et requêtes standards IEEE
Figure 13 : Passage en mode "remote control"
Figure 14 : Fonction d'initialisation de l'appareil
- Identification : Utilise la requête « *IDN? » pour vérifier que l’appareil est connecté et
répond correctement.
Figure 15 : Validation de la connexion avec l'appareil
- Configuration :
- Active le contrôle à distance (« SYST:LOCK ON »).
- Désactive les timeouts SCPI (« SYST:TIMEOUT 0 »).
- Efface les erreurs précédentes avec « *CLS ».
- Réinitialise l’appareil à son état par défaut avec « *RST ».
• Logique des cycles :
Figure 16 : Commande de "preset" de tension pour effectuer un premier test
Figure 17 : Commande d'activation et de désactivation de la sortie de l'alimentation
Figure 18 : Logique de commande de l’alimentation
- Cette fonction exécute plusieurs cycles de charge et décharge selon le paramètre
« NUM_CYCLES ».
- Cycle typique :
1. Charge : Configure la tension à 10V avec la commande « VOLT 10 » et attend un délai
(10 secondes par défaut).
2. Gestion des erreurs :
- En cas de perte de connexion pendant un cycle, la fonction tente une reconnexion et
consigne les erreurs.
3. Désactivation de la sortie : Après tous les cycles, la sortie de l’alimentation est
désactivée via « OUTP:STAT OFF ».
• Script principal
Figure 19 : Script principal d'exécution du code
- Connexion et initialisation :
1. Une connexion est établie avec l’appareil via « connect_to_device() ».
2. L’appareil est initialisé avec « initialize_device(sock) ».
3. La sortie de l’alimentation est activée via « OUTP ON ».
- Exécution des cycles :
- Les cycles charge/décharge sont effectués avec
« cycle_charge_discharge(sock) ».
- Gestion des erreurs critiques :
- En cas de perte de connexion majeure (BrokenPipeError), une tentative de
reconnexion est effectuée.
- Nettoyage final :
- La sortie de l’alimentation est désactivée (à l’aide de « OUTP OFF » et
« OUTP:STAT OFF »).
- Le socket est fermé proprement.
1.2.3. Ouverture :
1. Essayer le programme sur l’alimentation du client.
2. Utiliser les commandes consacrées aux batteries du « Programming Guide »
3. Lire avec attention la datasheet Hardware de l’alimentation afin de définir un
algorithme pour effectuer les cycles. (Puis modifier la fonction
« cycle_charge_discharge(sock) »)
Conclusion : Ce programme constitue une base solide pour l’interaction avec une
alimentation programmable via SCPI. Il peut être adapté et étendu à d’autres besoins en
ajoutant des commandes SCPI ou en modifiant la logique des cycles.
2. L’Interface Homme-Machine (IHM)
2.1. Description générale
Le script fournit une interface graphique (IHM) pour gérer un banc de cyclage de batteries.
Il utilise la bibliothèque « tkinter » pour l'IHM, « csv » pour manipuler des fichiers de profil,
et « matplotlib » pour visualiser les données collectées.
Figure 20 : Bibliothèque à importer pour l’exécution de cette IHM
2.2. Fonctionnalités principales
Figure 21 : Affichage de l'IHM
1. Modes d'opération
- Mode automatique : acquisition continue de données sur une durée définie. (Que des
0 en l’absence de valeurs capteur)
- Mode manuel : suivi d'un profil de cycle de charge/décharge prédéfini.
2. Gestion des profils
- Chargement d'un profil de cycle à partir d'un fichier CSV contenant des paramètres
(voltage, courant, etc.).
Figure 22 : Ouverture de la recherche de fichier à la suite de l'appui sur le bouton "Load
Profile"
3. Acquisition de données
- Simule la capture de paramètres de la batterie (tension, courant, température, etc.) à
intervalles réguliers.
Figure 23 : Message si aucun profile n'a été chargé avant l'acquisition
Figure 24 : Affichage de l'acquisition des données
4. Visualisation des données
- Graphiques de tension, courant, température et autres paramètres.
Figure 25 : Message si aucune donnée ne peut être affichée
Figure 26 : Affichage des données
5. Exportation des données
- Enregistrement des mesures dans un fichier CSV.
Figure 27 : Message si aucune donnée ne peut être sauvegardée
Figure 28 : Sauvegarde des données une fois acquises
2.3. Description des fonctions
Classe « BatteryCyclingApp » :
a) Attributs principaux :
- « data_log » : liste contenant les mesures collectées.
- « is_running » : booléen indiquant si le cycle est actif.
- « profile_dat » : liste des paramètres chargés depuis un profil CSV.
- « mode » : mode de fonctionnement (manuel ou automatique).
Figure 29 : Variables principales
b) Méthodes principales :
• « __init__(self, root) »
- Initialise la fenêtre principale et les composants de l'IHM.
Figure 30 : Fonction d'initialisation de l'IHM
• « create_widgets(self) »
- Crée et place les widgets dans l'IHM.
Figure 31 : Fonction de création de l'affichage opérationnel de l'IHM
• « load_profile(self) »
- Permet de charger un fichier CSV contenant les paramètres des cycles.
- Les données sont stockées dans « profile_data » sous forme de dictionnaire.
Figure 32 : Fonction de récupération de fichiers .csv
• « start_cycling(self )»
- Démarre le processus de cyclage dans un thread séparé (mode automatique ou
manuel).
Figure 33 : Fonction de gestion du bouton "Start"
• « stop_cycling(self )»
- Arrête le processus de cyclage.
Figure 34 : Fonction de gestion du bouton "Stop"
• « run_auto_mode(self) »
- Simule un mode automatique avec acquisition continue des données pendant 10
secondes.
Figure 35 : Fonction d'acquisition des données en mode "Auto"
• « run_manual_mode(self) »
- Exécute un cycle manuel basé sur les paramètres chargés depuis le profil.
Figure 36 : Fonction d'acquisition des données en mode "Manual"
• “acquire_data(self, profile=None)”
- Simule l'acquisition de données (temps, tension, courant, etc.).
Figure 37 : Fonction d'acquisition des données
• « log_message(self, message) »
- Affiche des messages dans la fenêtre de log.
Figure 38 : Fonction d'écriture dans la fenêtre
• « save_data(self) »
- Enregistre les mesures collectées dans un fichier CSV.
Figure 39 : Fonction de sauvegarde des données
• « show_graphs(self) »
- Affiche les graphiques des différents paramètres mesurés.
Figure 40 : Fonction d'affichage des données acquises
2.4. Fonctionnement de l'IHM
1. L'utilisateur choisit le mode d'opération (« Auto » ou « Manual »).
2. En mode manuel, un fichier CSV de profil doit être chargé.
3. Le processus de cyclage est démarré (« Start »).
4. Les données sont acquises et affichées dans le log.
5. L'utilisateur peut arrêter le processus (« Stop ») à tout moment.
6. Les données peuvent être visualisées et/ou sauvegardées.
2.5. Aspects techniques
- Threads : Les modes d'acquisition fonctionnent dans des threads séparés pour
maintenir la réactivité de l'IHM.
- Visualisation : « matplotlib » permet une visualisation claire et segmentée des données
collectées.
- « Gestion des fichiers » : Utilisation de « [Link] » et « [Link] » pour
manipuler les profils et sauvegarder les données.
2.6. Points d'amélioration possibles
1. Décorréler les fichiers .csv chargés (les profils) des données acquises. (Fonction
« run_manual_mode(self) »)
2. Permettre la sélection des graphiques par l’utilisateur en fin d’acquisition
(Fonction « show_graphs(self) »)
3. Permettre à l’IHM de communiquer avec les différents autres appareils du
système (L’alimentation bidirectionnelle et la carte d’acquisition)
4. Ajouter des contrôles avancés pour définir les durées et les paramètres de cyclage
directement depuis l'IHM.
5. Implémenter un bouton pour pauser/reprendre le cycle.
6. Optimiser la gestion des threads pour améliorer la stabilité lors de longues
sessions.
Conclusion :
Il vous a donc été présenté, dans le détail, la technique qui a permis le développement
de ces parties du projet plus large de conception d’un banc de test de batteries. La
communication avec l’alimentation est donc totalement opérationnelle. L’exécution des
cycles pourra être développé à la suite en se basant sur les fonctions qui ont été codées.
Du côté de l’IHM, plus de travail devra être fourni. En effet, son développement finale (la
suite de ce qui a déjà été fait) nécessite la validation de plusieurs autres blocs cruciaux
du banc de test.
Tous les documents qui ont permis ce développement sont répertoriés dans la
bibliographie du rapport de stage.
Annexe
Annexe 1 : Code de communication ETH avec l’alimentation bidirectionnelle :
𝑖𝑚𝑝𝑜𝑟𝑡 𝑠𝑜𝑐𝑘𝑒𝑡
𝑖𝑚𝑝𝑜𝑟𝑡 𝑡𝑖𝑚𝑒
# 𝑃𝑎𝑟𝑎𝑚è𝑡𝑟𝑒𝑠 𝑑𝑒 𝑐𝑜𝑛𝑛𝑒𝑥𝑖𝑜𝑛
𝐼𝑃_𝐴𝐷𝐷𝑅𝐸𝑆𝑆 = "[Link]"
𝑃𝑂𝑅𝑇 = 5025
𝑁𝑈𝑀_𝐶𝑌𝐶𝐿𝐸𝑆 = 5 # 𝑁𝑜𝑚𝑏𝑟𝑒 𝑑𝑒 𝑐𝑦𝑐𝑙𝑒𝑠 𝑐ℎ𝑎𝑟𝑔𝑒/𝑑é𝑐ℎ𝑎𝑟𝑔𝑒
# 𝐹𝑜𝑛𝑐𝑡𝑖𝑜𝑛𝑠 𝑢𝑡𝑖𝑙𝑖𝑡𝑎𝑖𝑟𝑒𝑠
𝑑𝑒𝑓 𝑠𝑒𝑛𝑑_𝑐𝑜𝑚𝑚𝑎𝑛𝑑(𝑠𝑜𝑐𝑘, 𝑐𝑜𝑚𝑚𝑎𝑛𝑑):
"""𝐸𝑛𝑣𝑜𝑖𝑒 𝑢𝑛𝑒 𝑐𝑜𝑚𝑚𝑎𝑛𝑑𝑒 𝑆𝐶𝑃𝐼 𝑣𝑖𝑎 𝑢𝑛𝑒 𝑐𝑜𝑛𝑛𝑒𝑥𝑖𝑜𝑛 𝑒𝑥𝑖𝑠𝑡𝑎𝑛𝑡𝑒. """
𝑡𝑟𝑦:
𝑖𝑓 𝑠𝑜𝑐𝑘. 𝑓𝑖𝑙𝑒𝑛𝑜() == −1: # 𝑉é𝑟𝑖𝑓𝑖𝑒 𝑠𝑖 𝑙𝑒 𝑠𝑜𝑐𝑘𝑒𝑡 𝑒𝑠𝑡 𝑎𝑐𝑡𝑖𝑓
𝑝𝑟𝑖𝑛𝑡("𝐿𝑒 𝑠𝑜𝑐𝑘𝑒𝑡 𝑒𝑠𝑡 𝑓𝑒𝑟𝑚é. 𝑅𝑒𝑐𝑜𝑛𝑛𝑒𝑥𝑖𝑜𝑛. . . ")
𝑠𝑜𝑐𝑘 = 𝑟𝑒𝑐𝑜𝑛𝑛𝑒𝑐𝑡(𝑠𝑜𝑐𝑘)
𝑐𝑜𝑚𝑚𝑎𝑛𝑑 = 𝑐𝑜𝑚𝑚𝑎𝑛𝑑. 𝑠𝑡𝑟𝑖𝑝() + "\𝑛"
𝑠𝑜𝑐𝑘. 𝑠𝑒𝑛𝑑𝑎𝑙𝑙(𝑐𝑜𝑚𝑚𝑎𝑛𝑑. 𝑒𝑛𝑐𝑜𝑑𝑒())
𝑡𝑖𝑚𝑒. 𝑠𝑙𝑒𝑒𝑝(1) # 𝐴𝑢𝑔𝑚𝑒𝑛𝑡𝑒𝑧 𝑙𝑒 𝑑é𝑙𝑎𝑖 à 1 𝑠𝑒𝑐𝑜𝑛𝑑𝑒
𝑟𝑒𝑠𝑝𝑜𝑛𝑠𝑒 = 𝑠𝑜𝑐𝑘. 𝑟𝑒𝑐𝑣(1024). 𝑑𝑒𝑐𝑜𝑑𝑒(). 𝑠𝑡𝑟𝑖𝑝()
𝑝𝑟𝑖𝑛𝑡(𝑓"𝐶𝑜𝑚𝑚𝑎𝑛𝑑𝑒: {𝑐𝑜𝑚𝑚𝑎𝑛𝑑. 𝑠𝑡𝑟𝑖𝑝()}\𝑛𝑅é𝑝𝑜𝑛𝑠𝑒: {𝑟𝑒𝑠𝑝𝑜𝑛𝑠𝑒}")
𝑟𝑒𝑡𝑢𝑟𝑛 𝑟𝑒𝑠𝑝𝑜𝑛𝑠𝑒
𝑒𝑥𝑐𝑒𝑝𝑡 𝐵𝑟𝑜𝑘𝑒𝑛𝑃𝑖𝑝𝑒𝐸𝑟𝑟𝑜𝑟:
𝑝𝑟𝑖𝑛𝑡("𝐶𝑜𝑛𝑛𝑒𝑥𝑖𝑜𝑛 𝑟𝑜𝑚𝑝𝑢𝑒. 𝑇𝑒𝑛𝑡𝑎𝑡𝑖𝑣𝑒 𝑑𝑒 𝑟𝑒𝑐𝑜𝑛𝑛𝑒𝑥𝑖𝑜𝑛. . . ")
𝑟𝑎𝑖𝑠𝑒
𝑒𝑥𝑐𝑒𝑝𝑡 𝐸𝑥𝑐𝑒𝑝𝑡𝑖𝑜𝑛 𝑎𝑠 𝑒:
𝑝𝑟𝑖𝑛𝑡(𝑓"𝐸𝑟𝑟𝑒𝑢𝑟 𝑙𝑜𝑟𝑠 𝑑𝑒 𝑙′𝑒𝑛𝑣𝑜𝑖 𝑑𝑒 𝑙𝑎 𝑐𝑜𝑚𝑚𝑎𝑛𝑑𝑒 {𝑐𝑜𝑚𝑚𝑎𝑛𝑑. 𝑠𝑡𝑟𝑖𝑝()}: {𝑒}")
𝑟𝑎𝑖𝑠𝑒
𝑑𝑒𝑓 𝑐𝑜𝑛𝑛𝑒𝑐𝑡_𝑡𝑜_𝑑𝑒𝑣𝑖𝑐𝑒():
"""É𝑡𝑎𝑏𝑙𝑖𝑡 𝑢𝑛𝑒 𝑐𝑜𝑛𝑛𝑒𝑥𝑖𝑜𝑛 𝑎𝑢 𝑑𝑖𝑠𝑝𝑜𝑠𝑖𝑡𝑖𝑓. """
𝑡𝑟𝑦:
𝑠𝑜𝑐𝑘 = 𝑠𝑜𝑐𝑘𝑒𝑡. 𝑠𝑜𝑐𝑘𝑒𝑡(𝑠𝑜𝑐𝑘𝑒𝑡. 𝐴𝐹_𝐼𝑁𝐸𝑇, 𝑠𝑜𝑐𝑘𝑒𝑡. 𝑆𝑂𝐶𝐾_𝑆𝑇𝑅𝐸𝐴𝑀)
𝑠𝑜𝑐𝑘. 𝑠𝑒𝑡𝑡𝑖𝑚𝑒𝑜𝑢𝑡(10)
𝑠𝑜𝑐𝑘. 𝑠𝑒𝑡𝑠𝑜𝑐𝑘𝑜𝑝𝑡(𝑠𝑜𝑐𝑘𝑒𝑡. 𝑆𝑂𝐿_𝑆𝑂𝐶𝐾𝐸𝑇, 𝑠𝑜𝑐𝑘𝑒𝑡. 𝑆𝑂_𝐾𝐸𝐸𝑃𝐴𝐿𝐼𝑉𝐸, 1) # 𝐴𝑐𝑡𝑖𝑣𝑒 𝑇𝐶𝑃 𝐾𝑒𝑒𝑝
− 𝐴𝑙𝑖𝑣𝑒
𝑠𝑜𝑐𝑘. 𝑐𝑜𝑛𝑛𝑒𝑐𝑡((𝐼𝑃_𝐴𝐷𝐷𝑅𝐸𝑆𝑆, 𝑃𝑂𝑅𝑇))
𝑝𝑟𝑖𝑛𝑡("𝐶𝑜𝑛𝑛𝑒𝑐𝑡é à 𝑙′𝑎𝑙𝑖𝑚𝑒𝑛𝑡𝑎𝑡𝑖𝑜𝑛. ")
𝑟𝑒𝑡𝑢𝑟𝑛 𝑠𝑜𝑐𝑘
𝑒𝑥𝑐𝑒𝑝𝑡 𝐸𝑥𝑐𝑒𝑝𝑡𝑖𝑜𝑛 𝑎𝑠 𝑒:
𝑝𝑟𝑖𝑛𝑡(𝑓"𝐸𝑟𝑟𝑒𝑢𝑟 𝑙𝑜𝑟𝑠 𝑑𝑒 𝑙𝑎 𝑐𝑜𝑛𝑛𝑒𝑥𝑖𝑜𝑛 ∶ {𝑒}")
𝑟𝑎𝑖𝑠𝑒
𝑑𝑒𝑓 𝑟𝑒𝑐𝑜𝑛𝑛𝑒𝑐𝑡(𝑠𝑜𝑐𝑘):
"""𝑅é𝑡𝑎𝑏𝑙𝑖𝑡 𝑢𝑛𝑒 𝑐𝑜𝑛𝑛𝑒𝑥𝑖𝑜𝑛 𝑒𝑛 𝑐𝑎𝑠 𝑑𝑒 𝑝𝑒𝑟𝑡𝑒. """
𝑡𝑟𝑦:
𝑠𝑜𝑐𝑘. 𝑐𝑙𝑜𝑠𝑒()
𝑒𝑥𝑐𝑒𝑝𝑡:
𝑝𝑎𝑠𝑠
𝑟𝑒𝑡𝑢𝑟𝑛 𝑐𝑜𝑛𝑛𝑒𝑐𝑡_𝑡𝑜_𝑑𝑒𝑣𝑖𝑐𝑒()
𝑑𝑒𝑓 𝑙𝑜𝑔_𝑑𝑒𝑣𝑖𝑐𝑒_𝑒𝑟𝑟𝑜𝑟(𝑠𝑜𝑐𝑘):
"""𝑅é𝑐𝑢𝑝è𝑟𝑒 𝑙𝑒𝑠 𝑒𝑟𝑟𝑒𝑢𝑟𝑠 𝑑𝑒 𝑙′𝑎𝑝𝑝𝑎𝑟𝑒𝑖𝑙. """
𝑡𝑟𝑦:
𝑒𝑟𝑟𝑜𝑟_𝑙𝑜𝑔 = 𝑠𝑒𝑛𝑑_𝑐𝑜𝑚𝑚𝑎𝑛𝑑(𝑠𝑜𝑐𝑘, "𝑆𝑌𝑆𝑇: 𝐸𝑅𝑅? ")
𝑝𝑟𝑖𝑛𝑡(𝑓"𝐿𝑜𝑔 𝑑𝑒 𝑙′𝑎𝑝𝑝𝑎𝑟𝑒𝑖𝑙 ∶ {𝑒𝑟𝑟𝑜𝑟_𝑙𝑜𝑔}")
𝑒𝑥𝑐𝑒𝑝𝑡 𝐸𝑥𝑐𝑒𝑝𝑡𝑖𝑜𝑛 𝑎𝑠 𝑒:
𝑝𝑟𝑖𝑛𝑡(𝑓"𝐼𝑚𝑝𝑜𝑠𝑠𝑖𝑏𝑙𝑒 𝑑𝑒 𝑙𝑖𝑟𝑒 𝑙𝑒 𝑙𝑜𝑔 𝑑𝑒 𝑙′𝑎𝑝𝑝𝑎𝑟𝑒𝑖𝑙 ∶ {𝑒}")
𝑑𝑒𝑓 𝑖𝑛𝑖𝑡𝑖𝑎𝑙𝑖𝑧𝑒_𝑑𝑒𝑣𝑖𝑐𝑒(𝑠𝑜𝑐𝑘):
"""𝐼𝑛𝑖𝑡𝑖𝑎𝑙𝑖𝑠𝑒 𝑙𝑒 𝑑𝑖𝑠𝑝𝑜𝑠𝑖𝑡𝑖𝑓 𝑒𝑡 𝑎𝑐𝑡𝑖𝑣𝑒 𝑙𝑒 𝑐𝑜𝑛𝑡𝑟ô𝑙𝑒 à 𝑑𝑖𝑠𝑡𝑎𝑛𝑐𝑒. """
# 𝐼𝑑𝑒𝑛𝑡𝑖𝑓𝑖𝑐𝑎𝑡𝑖𝑜𝑛 𝑑𝑒 𝑙′𝑎𝑝𝑝𝑎𝑟𝑒𝑖𝑙
𝑟𝑒𝑠𝑝𝑜𝑛𝑠𝑒 = 𝑠𝑒𝑛𝑑_𝑐𝑜𝑚𝑚𝑎𝑛𝑑(𝑠𝑜𝑐𝑘, " ∗ 𝐼𝐷𝑁? ")
𝑖𝑓 𝑛𝑜𝑡 𝑟𝑒𝑠𝑝𝑜𝑛𝑠𝑒:
𝑟𝑎𝑖𝑠𝑒 𝐸𝑥𝑐𝑒𝑝𝑡𝑖𝑜𝑛("𝐴𝑢𝑐𝑢𝑛𝑒 𝑟é𝑝𝑜𝑛𝑠𝑒 𝑑𝑒 𝑙′𝑎𝑝𝑝𝑎𝑟𝑒𝑖𝑙. 𝑉é𝑟𝑖𝑓𝑖𝑒𝑧 𝑙𝑎 𝑐𝑜𝑛𝑛𝑒𝑥𝑖𝑜𝑛. ")
# 𝐴𝑐𝑡𝑖𝑣𝑎𝑡𝑖𝑜𝑛 𝑑𝑢 𝑐𝑜𝑛𝑡𝑟ô𝑙𝑒 à 𝑑𝑖𝑠𝑡𝑎𝑛𝑐𝑒 𝑒𝑡 𝑐𝑜𝑛𝑓𝑖𝑔𝑢𝑟𝑎𝑡𝑖𝑜𝑛
𝑠𝑒𝑛𝑑_𝑐𝑜𝑚𝑚𝑎𝑛𝑑(𝑠𝑜𝑐𝑘, "𝑆𝑌𝑆𝑇: 𝐿𝑂𝐶𝐾 𝑂𝑁")
𝑠𝑒𝑛𝑑_𝑐𝑜𝑚𝑚𝑎𝑛𝑑(𝑠𝑜𝑐𝑘, "𝑆𝑌𝑆𝑇: 𝑇𝐼𝑀𝐸𝑂𝑈𝑇 0") # 𝐷é𝑠𝑎𝑐𝑡𝑖𝑣𝑎𝑡𝑖𝑜𝑛 𝑑𝑢 𝑡𝑖𝑚𝑒𝑜𝑢𝑡 𝑆𝐶𝑃𝐼
𝑠𝑒𝑛𝑑_𝑐𝑜𝑚𝑚𝑎𝑛𝑑(𝑠𝑜𝑐𝑘, " ∗ 𝐶𝐿𝑆") # 𝐸𝑓𝑓𝑎𝑐𝑒 𝑙𝑒𝑠 𝑒𝑟𝑟𝑒𝑢𝑟𝑠 𝑝𝑟é𝑐é𝑑𝑒𝑛𝑡𝑒𝑠
𝑠𝑒𝑛𝑑_𝑐𝑜𝑚𝑚𝑎𝑛𝑑(𝑠𝑜𝑐𝑘, " ∗ 𝑅𝑆𝑇") # 𝑅é𝑖𝑛𝑖𝑡𝑖𝑎𝑙𝑖𝑠𝑎𝑡𝑖𝑜𝑛 𝑑𝑒 𝑙′𝑎𝑝𝑝𝑎𝑟𝑒𝑖𝑙
𝑑𝑒𝑓 𝑐𝑦𝑐𝑙𝑒_𝑐ℎ𝑎𝑟𝑔𝑒_𝑑𝑖𝑠𝑐ℎ𝑎𝑟𝑔𝑒(𝑠𝑜𝑐𝑘):
"""𝐸𝑓𝑓𝑒𝑐𝑡𝑢𝑒 𝑙𝑒𝑠 𝑐𝑦𝑐𝑙𝑒𝑠 𝑑𝑒 𝑐ℎ𝑎𝑟𝑔𝑒 𝑒𝑡 𝑑é𝑐ℎ𝑎𝑟𝑔𝑒. """
𝑓𝑜𝑟 𝑐𝑦𝑐𝑙𝑒 𝑖𝑛 𝑟𝑎𝑛𝑔𝑒(1, 𝑁𝑈𝑀_𝐶𝑌𝐶𝐿𝐸𝑆 + 1):
𝑝𝑟𝑖𝑛𝑡(𝑓"\𝑛𝐷é𝑏𝑢𝑡 𝑑𝑢 𝑐𝑦𝑐𝑙𝑒 {𝑐𝑦𝑐𝑙𝑒}/{𝑁𝑈𝑀_𝐶𝑌𝐶𝐿𝐸𝑆}")
# É𝑡𝑎𝑝𝑒 1 ∶ 𝐶ℎ𝑎𝑟𝑔𝑒
𝑝𝑟𝑖𝑛𝑡("𝐷é𝑚𝑎𝑟𝑟𝑎𝑔𝑒 𝑑𝑒 𝑙𝑎 𝑐ℎ𝑎𝑟𝑔𝑒. . . ")
𝑡𝑟𝑦:
𝑡𝑖𝑚𝑒. 𝑠𝑙𝑒𝑒𝑝(0.5) # 𝐷é𝑙𝑎𝑖 𝑎𝑣𝑎𝑛𝑡 𝑑𝑒 𝑐𝑜𝑛𝑓𝑖𝑔𝑢𝑟𝑒𝑟 𝑙𝑎 𝑡𝑒𝑛𝑠𝑖𝑜𝑛
𝑠𝑒𝑛𝑑_𝑐𝑜𝑚𝑚𝑎𝑛𝑑(𝑠𝑜𝑐𝑘, "𝑉𝑂𝐿𝑇 10")
𝑡𝑖𝑚𝑒. 𝑠𝑙𝑒𝑒𝑝(10) # 𝑇𝑒𝑚𝑝𝑠 𝑑𝑒 𝑐ℎ𝑎𝑟𝑔𝑒 (𝑒𝑥𝑒𝑚𝑝𝑙𝑒 ∶ 60 𝑠𝑒𝑐𝑜𝑛𝑑𝑒𝑠)
𝑒𝑥𝑐𝑒𝑝𝑡 𝐵𝑟𝑜𝑘𝑒𝑛𝑃𝑖𝑝𝑒𝐸𝑟𝑟𝑜𝑟:
𝑝𝑟𝑖𝑛𝑡("𝑅𝑒𝑐𝑜𝑛𝑛𝑒𝑥𝑖𝑜𝑛 𝑛é𝑐𝑒𝑠𝑠𝑎𝑖𝑟𝑒 𝑝𝑒𝑛𝑑𝑎𝑛𝑡 𝑙𝑎 𝑐ℎ𝑎𝑟𝑔𝑒. ")
𝑠𝑜𝑐𝑘 = 𝑟𝑒𝑐𝑜𝑛𝑛𝑒𝑐𝑡(𝑠𝑜𝑐𝑘)
𝑙𝑜𝑔_𝑑𝑒𝑣𝑖𝑐𝑒_𝑒𝑟𝑟𝑜𝑟(𝑠𝑜𝑐𝑘)
𝑐𝑜𝑛𝑡𝑖𝑛𝑢𝑒
# É𝑡𝑎𝑝𝑒 2 ∶ 𝐷é𝑐ℎ𝑎𝑟𝑔𝑒
#𝑝𝑟𝑖𝑛𝑡("𝐷é𝑚𝑎𝑟𝑟𝑎𝑔𝑒 𝑑𝑒 𝑙𝑎 𝑑é𝑐ℎ𝑎𝑟𝑔𝑒. . . ")
#𝑡𝑟𝑦:
#𝑠𝑒𝑛𝑑_𝑐𝑜𝑚𝑚𝑎𝑛𝑑(𝑠𝑜𝑐𝑘, "𝑆𝐼𝑁𝐾: 𝐶𝑈𝑅𝑅 2") # 𝐸𝑥𝑒𝑚𝑝𝑙𝑒 ∶ 𝐶𝑜𝑢𝑟𝑎𝑛𝑡 𝑑𝑒 𝑑é𝑐ℎ𝑎𝑟𝑔𝑒 𝑛é𝑔𝑎𝑡𝑖𝑓
#𝑡𝑖𝑚𝑒. 𝑠𝑙𝑒𝑒𝑝(10) # 𝑇𝑒𝑚𝑝𝑠 𝑑𝑒 𝑑é𝑐ℎ𝑎𝑟𝑔𝑒 (𝑒𝑥𝑒𝑚𝑝𝑙𝑒 ∶ 60 𝑠𝑒𝑐𝑜𝑛𝑑𝑒𝑠)
#𝑒𝑥𝑐𝑒𝑝𝑡 𝐵𝑟𝑜𝑘𝑒𝑛𝑃𝑖𝑝𝑒𝐸𝑟𝑟𝑜𝑟:
#𝑝𝑟𝑖𝑛𝑡("𝑅𝑒𝑐𝑜𝑛𝑛𝑒𝑥𝑖𝑜𝑛 𝑛é𝑐𝑒𝑠𝑠𝑎𝑖𝑟𝑒 𝑝𝑒𝑛𝑑𝑎𝑛𝑡 𝑙𝑎 𝑑é𝑐ℎ𝑎𝑟𝑔𝑒. ")
#𝑠𝑜𝑐𝑘 = 𝑟𝑒𝑐𝑜𝑛𝑛𝑒𝑐𝑡(𝑠𝑜𝑐𝑘)
#𝑙𝑜𝑔_𝑑𝑒𝑣𝑖𝑐𝑒_𝑒𝑟𝑟𝑜𝑟(𝑠𝑜𝑐𝑘)
#𝑐𝑜𝑛𝑡𝑖𝑛𝑢𝑒
𝑝𝑟𝑖𝑛𝑡("\𝑛𝑇𝑜𝑢𝑠 𝑙𝑒𝑠 𝑐𝑦𝑐𝑙𝑒𝑠 𝑠𝑜𝑛𝑡 𝑡𝑒𝑟𝑚𝑖𝑛é𝑠. ")
𝑠𝑒𝑛𝑑_𝑐𝑜𝑚𝑚𝑎𝑛𝑑(𝑠𝑜𝑐𝑘, "𝑂𝑈𝑇𝑃: 𝑆𝑇𝐴𝑇 𝑂𝐹𝐹") # 𝐷é𝑠𝑎𝑐𝑡𝑖𝑣𝑒 𝑙𝑎 𝑠𝑜𝑟𝑡𝑖𝑒
# 𝑆𝑐𝑟𝑖𝑝𝑡 𝑝𝑟𝑖𝑛𝑐𝑖𝑝𝑎𝑙
𝑡𝑟𝑦:
𝑠𝑜𝑐𝑘 = 𝑐𝑜𝑛𝑛𝑒𝑐𝑡_𝑡𝑜_𝑑𝑒𝑣𝑖𝑐𝑒()
𝑖𝑛𝑖𝑡𝑖𝑎𝑙𝑖𝑧𝑒_𝑑𝑒𝑣𝑖𝑐𝑒(𝑠𝑜𝑐𝑘)
𝑠𝑒𝑛𝑑_𝑐𝑜𝑚𝑚𝑎𝑛𝑑(𝑠𝑜𝑐𝑘, "𝑂𝑈𝑇𝑃 𝑂𝑁")
𝑐𝑦𝑐𝑙𝑒_𝑐ℎ𝑎𝑟𝑔𝑒_𝑑𝑖𝑠𝑐ℎ𝑎𝑟𝑔𝑒(𝑠𝑜𝑐𝑘)
𝑒𝑥𝑐𝑒𝑝𝑡 𝐵𝑟𝑜𝑘𝑒𝑛𝑃𝑖𝑝𝑒𝐸𝑟𝑟𝑜𝑟:
𝑝𝑟𝑖𝑛𝑡("𝐸𝑟𝑟𝑒𝑢𝑟 𝑐𝑟𝑖𝑡𝑖𝑞𝑢𝑒 ∶ 𝐶𝑜𝑛𝑛𝑒𝑥𝑖𝑜𝑛 𝑟𝑜𝑚𝑝𝑢𝑒. 𝑇𝑒𝑛𝑡𝑎𝑡𝑖𝑣𝑒 𝑑𝑒 𝑟é𝑖𝑛𝑖𝑡𝑖𝑎𝑙𝑖𝑠𝑎𝑡𝑖𝑜𝑛. . . ")
𝑠𝑜𝑐𝑘 = 𝑟𝑒𝑐𝑜𝑛𝑛𝑒𝑐𝑡(𝑠𝑜𝑐𝑘)
𝑒𝑥𝑐𝑒𝑝𝑡 𝐸𝑥𝑐𝑒𝑝𝑡𝑖𝑜𝑛 𝑎𝑠 𝑒:
𝑝𝑟𝑖𝑛𝑡(𝑓"𝐸𝑟𝑟𝑒𝑢𝑟 𝑐𝑟𝑖𝑡𝑖𝑞𝑢𝑒 ∶ {𝑒}")
𝑓𝑖𝑛𝑎𝑙𝑙𝑦:
𝑖𝑓 𝑠𝑜𝑐𝑘:
𝑡𝑟𝑦:
𝑠𝑒𝑛𝑑_𝑐𝑜𝑚𝑚𝑎𝑛𝑑(𝑠𝑜𝑐𝑘, "𝑂𝑈𝑇𝑃 𝑂𝐹𝐹")
𝑠𝑒𝑛𝑑_𝑐𝑜𝑚𝑚𝑎𝑛𝑑(𝑠𝑜𝑐𝑘, "𝑂𝑈𝑇𝑃: 𝑆𝑇𝐴𝑇 𝑂𝐹𝐹") # 𝐴𝑠𝑠𝑢𝑟𝑒𝑧
− 𝑣𝑜𝑢𝑠 𝑞𝑢𝑒 𝑙′𝑎𝑙𝑖𝑚𝑒𝑛𝑡𝑎𝑡𝑖𝑜𝑛 𝑒𝑠𝑡 𝑎𝑟𝑟ê𝑡é𝑒
𝑒𝑥𝑐𝑒𝑝𝑡:
𝑝𝑎𝑠𝑠
𝑠𝑜𝑐𝑘. 𝑐𝑙𝑜𝑠𝑒()
𝑝𝑟𝑖𝑛𝑡("𝐶𝑜𝑛𝑛𝑒𝑥𝑖𝑜𝑛 𝑓𝑒𝑟𝑚é𝑒. ")
Annexe 2 : Code de l’IHM :
𝑖𝑚𝑝𝑜𝑟𝑡 𝑡𝑘𝑖𝑛𝑡𝑒𝑟 𝑎𝑠 𝑡𝑘
𝑓𝑟𝑜𝑚 𝑡𝑘𝑖𝑛𝑡𝑒𝑟 𝑖𝑚𝑝𝑜𝑟𝑡 𝑓𝑖𝑙𝑒𝑑𝑖𝑎𝑙𝑜𝑔, 𝑚𝑒𝑠𝑠𝑎𝑔𝑒𝑏𝑜𝑥
𝑖𝑚𝑝𝑜𝑟𝑡 𝑐𝑠𝑣
𝑖𝑚𝑝𝑜𝑟𝑡 𝑡𝑖𝑚𝑒
𝑓𝑟𝑜𝑚 𝑡ℎ𝑟𝑒𝑎𝑑𝑖𝑛𝑔 𝑖𝑚𝑝𝑜𝑟𝑡 𝑇ℎ𝑟𝑒𝑎𝑑
𝑖𝑚𝑝𝑜𝑟𝑡 𝑚𝑎𝑡𝑝𝑙𝑜𝑡𝑙𝑖𝑏. 𝑝𝑦𝑝𝑙𝑜𝑡 𝑎𝑠 𝑝𝑙𝑡
𝑐𝑙𝑎𝑠𝑠 𝐵𝑎𝑡𝑡𝑒𝑟𝑦𝐶𝑦𝑐𝑙𝑖𝑛𝑔𝐴𝑝𝑝:
𝑑𝑒𝑓 __𝑖𝑛𝑖𝑡__(𝑠𝑒𝑙𝑓, 𝑟𝑜𝑜𝑡):
𝑠𝑒𝑙𝑓. 𝑟𝑜𝑜𝑡 = 𝑟𝑜𝑜𝑡
𝑠𝑒𝑙𝑓. 𝑟𝑜𝑜𝑡. 𝑡𝑖𝑡𝑙𝑒("𝐵𝑎𝑡𝑡𝑒𝑟𝑦 𝐶𝑦𝑐𝑙𝑖𝑛𝑔 𝐵𝑒𝑛𝑐ℎ")
# 𝑉𝑎𝑟𝑖𝑎𝑏𝑙𝑒𝑠
𝑠𝑒𝑙𝑓. 𝑑𝑎𝑡𝑎_𝑙𝑜𝑔 = []
𝑠𝑒𝑙𝑓. 𝑖𝑠_𝑟𝑢𝑛𝑛𝑖𝑛𝑔 = 𝐹𝑎𝑙𝑠𝑒
𝑠𝑒𝑙𝑓. 𝑝𝑟𝑜𝑓𝑖𝑙𝑒_𝑑𝑎𝑡𝑎 = []
𝑠𝑒𝑙𝑓. 𝑚𝑜𝑑𝑒 = 𝑡𝑘. 𝑆𝑡𝑟𝑖𝑛𝑔𝑉𝑎𝑟(𝑣𝑎𝑙𝑢𝑒 = "𝑎𝑢𝑡𝑜")
# 𝑈𝐼 𝐶𝑜𝑚𝑝𝑜𝑛𝑒𝑛𝑡𝑠
𝑠𝑒𝑙𝑓. 𝑐𝑟𝑒𝑎𝑡𝑒_𝑤𝑖𝑑𝑔𝑒𝑡𝑠()
𝑑𝑒𝑓 𝑐𝑟𝑒𝑎𝑡𝑒_𝑤𝑖𝑑𝑔𝑒𝑡𝑠(𝑠𝑒𝑙𝑓):
# 𝑀𝑜𝑑𝑒 𝑆𝑒𝑙𝑒𝑐𝑡𝑖𝑜𝑛
𝑡𝑘. 𝐿𝑎𝑏𝑒𝑙(𝑠𝑒𝑙𝑓. 𝑟𝑜𝑜𝑡, 𝑡𝑒𝑥𝑡 = "𝑆𝑒𝑙𝑒𝑐𝑡 𝑀𝑜𝑑𝑒: "). 𝑝𝑎𝑐𝑘()
𝑡𝑘. 𝑅𝑎𝑑𝑖𝑜𝑏𝑢𝑡𝑡𝑜𝑛(𝑠𝑒𝑙𝑓. 𝑟𝑜𝑜𝑡, 𝑡𝑒𝑥𝑡 = "𝐴𝑢𝑡𝑜", 𝑣𝑎𝑟𝑖𝑎𝑏𝑙𝑒 = 𝑠𝑒𝑙𝑓. 𝑚𝑜𝑑𝑒, 𝑣𝑎𝑙𝑢𝑒 = "𝑎𝑢𝑡𝑜"). 𝑝𝑎𝑐𝑘()
𝑡𝑘. 𝑅𝑎𝑑𝑖𝑜𝑏𝑢𝑡𝑡𝑜𝑛(𝑠𝑒𝑙𝑓. 𝑟𝑜𝑜𝑡, 𝑡𝑒𝑥𝑡 = "𝑀𝑎𝑛𝑢𝑎𝑙", 𝑣𝑎𝑟𝑖𝑎𝑏𝑙𝑒 = 𝑠𝑒𝑙𝑓. 𝑚𝑜𝑑𝑒, 𝑣𝑎𝑙𝑢𝑒
= "𝑚𝑎𝑛𝑢𝑎𝑙"). 𝑝𝑎𝑐𝑘()
# 𝐵𝑢𝑡𝑡𝑜𝑛𝑠
𝑡𝑘. 𝐵𝑢𝑡𝑡𝑜𝑛(𝑠𝑒𝑙𝑓. 𝑟𝑜𝑜𝑡, 𝑡𝑒𝑥𝑡 = "𝐿𝑜𝑎𝑑 𝑃𝑟𝑜𝑓𝑖𝑙𝑒", 𝑐𝑜𝑚𝑚𝑎𝑛𝑑 = 𝑠𝑒𝑙𝑓. 𝑙𝑜𝑎𝑑_𝑝𝑟𝑜𝑓𝑖𝑙𝑒). 𝑝𝑎𝑐𝑘()
𝑡𝑘. 𝐵𝑢𝑡𝑡𝑜𝑛(𝑠𝑒𝑙𝑓. 𝑟𝑜𝑜𝑡, 𝑡𝑒𝑥𝑡 = "𝑆𝑡𝑎𝑟𝑡", 𝑐𝑜𝑚𝑚𝑎𝑛𝑑 = 𝑠𝑒𝑙𝑓. 𝑠𝑡𝑎𝑟𝑡_𝑐𝑦𝑐𝑙𝑖𝑛𝑔). 𝑝𝑎𝑐𝑘()
𝑡𝑘. 𝐵𝑢𝑡𝑡𝑜𝑛(𝑠𝑒𝑙𝑓. 𝑟𝑜𝑜𝑡, 𝑡𝑒𝑥𝑡 = "𝑆𝑡𝑜𝑝", 𝑐𝑜𝑚𝑚𝑎𝑛𝑑 = 𝑠𝑒𝑙𝑓. 𝑠𝑡𝑜𝑝_𝑐𝑦𝑐𝑙𝑖𝑛𝑔). 𝑝𝑎𝑐𝑘()
𝑡𝑘. 𝐵𝑢𝑡𝑡𝑜𝑛(𝑠𝑒𝑙𝑓. 𝑟𝑜𝑜𝑡, 𝑡𝑒𝑥𝑡 = "𝑆𝑎𝑣𝑒 𝐷𝑎𝑡𝑎", 𝑐𝑜𝑚𝑚𝑎𝑛𝑑 = 𝑠𝑒𝑙𝑓. 𝑠𝑎𝑣𝑒_𝑑𝑎𝑡𝑎). 𝑝𝑎𝑐𝑘()
𝑡𝑘. 𝐵𝑢𝑡𝑡𝑜𝑛(𝑠𝑒𝑙𝑓. 𝑟𝑜𝑜𝑡, 𝑡𝑒𝑥𝑡 = "𝑆ℎ𝑜𝑤 𝐺𝑟𝑎𝑝ℎ𝑠", 𝑐𝑜𝑚𝑚𝑎𝑛𝑑 = 𝑠𝑒𝑙𝑓. 𝑠ℎ𝑜𝑤_𝑔𝑟𝑎𝑝ℎ𝑠). 𝑝𝑎𝑐𝑘()
𝑡𝑘. 𝐵𝑢𝑡𝑡𝑜𝑛(𝑠𝑒𝑙𝑓. 𝑟𝑜𝑜𝑡, 𝑡𝑒𝑥𝑡 = "𝐸𝑥𝑖𝑡", 𝑐𝑜𝑚𝑚𝑎𝑛𝑑 = 𝑠𝑒𝑙𝑓. 𝑟𝑜𝑜𝑡. 𝑞𝑢𝑖𝑡). 𝑝𝑎𝑐𝑘()
# 𝐿𝑜𝑔 𝐷𝑖𝑠𝑝𝑙𝑎𝑦
𝑠𝑒𝑙𝑓. 𝑙𝑜𝑔_𝑡𝑒𝑥𝑡 = 𝑡𝑘. 𝑇𝑒𝑥𝑡(𝑠𝑒𝑙𝑓. 𝑟𝑜𝑜𝑡, 𝑠𝑡𝑎𝑡𝑒 = ′𝑑𝑖𝑠𝑎𝑏𝑙𝑒𝑑′, ℎ𝑒𝑖𝑔ℎ𝑡 = 10)
𝑠𝑒𝑙𝑓. 𝑙𝑜𝑔_𝑡𝑒𝑥𝑡. 𝑝𝑎𝑐𝑘()
𝑑𝑒𝑓 𝑙𝑜𝑎𝑑_𝑝𝑟𝑜𝑓𝑖𝑙𝑒(𝑠𝑒𝑙𝑓):
𝑓𝑖𝑙𝑒_𝑝𝑎𝑡ℎ = 𝑓𝑖𝑙𝑒𝑑𝑖𝑎𝑙𝑜𝑔. 𝑎𝑠𝑘𝑜𝑝𝑒𝑛𝑓𝑖𝑙𝑒𝑛𝑎𝑚𝑒(𝑓𝑖𝑙𝑒𝑡𝑦𝑝𝑒𝑠 = [("𝐶𝑆𝑉 𝐹𝑖𝑙𝑒𝑠", " ∗. 𝑐𝑠𝑣")])
𝑖𝑓 𝑛𝑜𝑡 𝑓𝑖𝑙𝑒_𝑝𝑎𝑡ℎ:
𝑟𝑒𝑡𝑢𝑟𝑛
𝑤𝑖𝑡ℎ 𝑜𝑝𝑒𝑛(𝑓𝑖𝑙𝑒_𝑝𝑎𝑡ℎ, 𝑛𝑒𝑤𝑙𝑖𝑛𝑒 = ′′) 𝑎𝑠 𝑐𝑠𝑣𝑓𝑖𝑙𝑒:
𝑟𝑒𝑎𝑑𝑒𝑟 = 𝑐𝑠𝑣. 𝐷𝑖𝑐𝑡𝑅𝑒𝑎𝑑𝑒𝑟(𝑐𝑠𝑣𝑓𝑖𝑙𝑒)
𝑠𝑒𝑙𝑓. 𝑝𝑟𝑜𝑓𝑖𝑙𝑒_𝑑𝑎𝑡𝑎 = [𝑟𝑜𝑤 𝑓𝑜𝑟 𝑟𝑜𝑤 𝑖𝑛 𝑟𝑒𝑎𝑑𝑒𝑟]
𝑚𝑒𝑠𝑠𝑎𝑔𝑒𝑏𝑜𝑥. 𝑠ℎ𝑜𝑤𝑖𝑛𝑓𝑜("𝑃𝑟𝑜𝑓𝑖𝑙𝑒 𝐿𝑜𝑎𝑑𝑒𝑑", "𝑃𝑟𝑜𝑓𝑖𝑙𝑒 𝑠𝑢𝑐𝑐𝑒𝑠𝑠𝑓𝑢𝑙𝑙𝑦 𝑙𝑜𝑎𝑑𝑒𝑑. ")
𝑑𝑒𝑓 𝑠𝑡𝑎𝑟𝑡_𝑐𝑦𝑐𝑙𝑖𝑛𝑔(𝑠𝑒𝑙𝑓):
𝑖𝑓 𝑛𝑜𝑡 𝑠𝑒𝑙𝑓. 𝑝𝑟𝑜𝑓𝑖𝑙𝑒_𝑑𝑎𝑡𝑎:
𝑚𝑒𝑠𝑠𝑎𝑔𝑒𝑏𝑜𝑥. 𝑠ℎ𝑜𝑤𝑤𝑎𝑟𝑛𝑖𝑛𝑔("𝑊𝑎𝑟𝑛𝑖𝑛𝑔", "𝐿𝑜𝑎𝑑 𝑎 𝑝𝑟𝑜𝑓𝑖𝑙𝑒 𝑓𝑖𝑟𝑠𝑡. ")
𝑟𝑒𝑡𝑢𝑟𝑛
𝑠𝑒𝑙𝑓. 𝑖𝑠_𝑟𝑢𝑛𝑛𝑖𝑛𝑔 = 𝑇𝑟𝑢𝑒
𝑖𝑓 𝑠𝑒𝑙𝑓. 𝑚𝑜𝑑𝑒. 𝑔𝑒𝑡() == "𝑎𝑢𝑡𝑜":
𝑠𝑒𝑙𝑓. 𝑙𝑜𝑔_𝑚𝑒𝑠𝑠𝑎𝑔𝑒("𝑆𝑡𝑎𝑟𝑡𝑖𝑛𝑔 𝑎𝑢𝑡𝑜 𝑚𝑜𝑑𝑒. . . ")
𝑇ℎ𝑟𝑒𝑎𝑑(𝑡𝑎𝑟𝑔𝑒𝑡 = 𝑠𝑒𝑙𝑓. 𝑟𝑢𝑛_𝑎𝑢𝑡𝑜_𝑚𝑜𝑑𝑒). 𝑠𝑡𝑎𝑟𝑡()
𝑒𝑙𝑖𝑓 𝑠𝑒𝑙𝑓. 𝑚𝑜𝑑𝑒. 𝑔𝑒𝑡() == "𝑚𝑎𝑛𝑢𝑎𝑙":
𝑠𝑒𝑙𝑓. 𝑙𝑜𝑔_𝑚𝑒𝑠𝑠𝑎𝑔𝑒("𝑆𝑡𝑎𝑟𝑡𝑖𝑛𝑔 𝑚𝑎𝑛𝑢𝑎𝑙 𝑚𝑜𝑑𝑒. . . ")
𝑇ℎ𝑟𝑒𝑎𝑑(𝑡𝑎𝑟𝑔𝑒𝑡 = 𝑠𝑒𝑙𝑓. 𝑟𝑢𝑛_𝑚𝑎𝑛𝑢𝑎𝑙_𝑚𝑜𝑑𝑒). 𝑠𝑡𝑎𝑟𝑡()
𝑑𝑒𝑓 𝑠𝑡𝑜𝑝_𝑐𝑦𝑐𝑙𝑖𝑛𝑔(𝑠𝑒𝑙𝑓):
𝑠𝑒𝑙𝑓. 𝑖𝑠_𝑟𝑢𝑛𝑛𝑖𝑛𝑔 = 𝐹𝑎𝑙𝑠𝑒
𝑠𝑒𝑙𝑓. 𝑙𝑜𝑔_𝑚𝑒𝑠𝑠𝑎𝑔𝑒("𝐶𝑦𝑐𝑙𝑖𝑛𝑔 𝑠𝑡𝑜𝑝𝑝𝑒𝑑. ")
𝑑𝑒𝑓 𝑟𝑢𝑛_𝑎𝑢𝑡𝑜_𝑚𝑜𝑑𝑒(𝑠𝑒𝑙𝑓):
𝑠𝑡𝑎𝑟𝑡_𝑡𝑖𝑚𝑒 = 𝑡𝑖𝑚𝑒. 𝑡𝑖𝑚𝑒()
𝑤ℎ𝑖𝑙𝑒 𝑠𝑒𝑙𝑓. 𝑖𝑠_𝑟𝑢𝑛𝑛𝑖𝑛𝑔 𝑎𝑛𝑑 (𝑡𝑖𝑚𝑒. 𝑡𝑖𝑚𝑒() − 𝑠𝑡𝑎𝑟𝑡_𝑡𝑖𝑚𝑒) < 10:
𝑠𝑒𝑙𝑓. 𝑎𝑐𝑞𝑢𝑖𝑟𝑒_𝑑𝑎𝑡𝑎()
𝑡𝑖𝑚𝑒. 𝑠𝑙𝑒𝑒𝑝(0.25)
𝑠𝑒𝑙𝑓. 𝑖𝑠_𝑟𝑢𝑛𝑛𝑖𝑛𝑔 = 𝐹𝑎𝑙𝑠𝑒
𝑠𝑒𝑙𝑓. 𝑙𝑜𝑔_𝑚𝑒𝑠𝑠𝑎𝑔𝑒("𝐴𝑢𝑡𝑜 𝑚𝑜𝑑𝑒 𝑐𝑜𝑚𝑝𝑙𝑒𝑡𝑒𝑑 𝑎𝑓𝑡𝑒𝑟 10 𝑠𝑒𝑐𝑜𝑛𝑑𝑠. ")
𝑠𝑒𝑙𝑓. 𝑠ℎ𝑜𝑤_𝑔𝑟𝑎𝑝ℎ𝑠()
𝑑𝑒𝑓 𝑟𝑢𝑛_𝑚𝑎𝑛𝑢𝑎𝑙_𝑚𝑜𝑑𝑒(𝑠𝑒𝑙𝑓):
𝑓𝑜𝑟 𝑝𝑟𝑜𝑓𝑖𝑙𝑒 𝑖𝑛 𝑠𝑒𝑙𝑓. 𝑝𝑟𝑜𝑓𝑖𝑙𝑒_𝑑𝑎𝑡𝑎:
𝑖𝑓 𝑛𝑜𝑡 𝑠𝑒𝑙𝑓. 𝑖𝑠_𝑟𝑢𝑛𝑛𝑖𝑛𝑔:
𝑏𝑟𝑒𝑎𝑘
𝑠𝑒𝑙𝑓. 𝑎𝑐𝑞𝑢𝑖𝑟𝑒_𝑑𝑎𝑡𝑎(𝑝𝑟𝑜𝑓𝑖𝑙𝑒)
𝑡𝑖𝑚𝑒. 𝑠𝑙𝑒𝑒𝑝(0.25)
𝑑𝑒𝑓 𝑎𝑐𝑞𝑢𝑖𝑟𝑒_𝑑𝑎𝑡𝑎(𝑠𝑒𝑙𝑓, 𝑝𝑟𝑜𝑓𝑖𝑙𝑒 = 𝑁𝑜𝑛𝑒):
# 𝑆𝑖𝑚𝑢𝑙𝑎𝑡𝑒𝑑 𝑑𝑎𝑡𝑎 𝑎𝑐𝑞𝑢𝑖𝑠𝑖𝑡𝑖𝑜𝑛
𝑖𝑓 𝑝𝑟𝑜𝑓𝑖𝑙𝑒:
𝑑𝑎𝑡𝑎 = {
"𝑇𝑖𝑚𝑒": 𝑡𝑖𝑚𝑒. 𝑡𝑖𝑚𝑒(),
"𝑉𝑜𝑙𝑡𝑎𝑔𝑒": 𝑓𝑙𝑜𝑎𝑡(𝑝𝑟𝑜𝑓𝑖𝑙𝑒["𝑉𝑜𝑙𝑡𝑎𝑔𝑒"]),
"𝐶𝑢𝑟𝑟𝑒𝑛𝑡": 𝑓𝑙𝑜𝑎𝑡(𝑝𝑟𝑜𝑓𝑖𝑙𝑒["𝐶𝑢𝑟𝑟𝑒𝑛𝑡"]),
"𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒": 𝑓𝑙𝑜𝑎𝑡(𝑝𝑟𝑜𝑓𝑖𝑙𝑒["𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒"]),
"𝐵𝑀𝑆 𝐹𝑎𝑢𝑙𝑡𝑠": 𝑝𝑟𝑜𝑓𝑖𝑙𝑒. 𝑔𝑒𝑡("𝐵𝑀𝑆 𝐹𝑎𝑢𝑙𝑡𝑠", "𝑁𝑜 𝑓𝑎𝑢𝑙𝑡𝑠"),
"𝐶𝑒𝑙𝑙 𝑉𝑜𝑙𝑡𝑎𝑔𝑒": 𝑝𝑟𝑜𝑓𝑖𝑙𝑒. 𝑔𝑒𝑡("𝐶𝑒𝑙𝑙 𝑉𝑜𝑙𝑡𝑎𝑔𝑒", "𝑁/𝐴"),
"𝐶𝑒𝑙𝑙 𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒": 𝑝𝑟𝑜𝑓𝑖𝑙𝑒. 𝑔𝑒𝑡("𝐶𝑒𝑙𝑙 𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒", "𝑁/𝐴"),
}
𝑒𝑙𝑠𝑒:
𝑑𝑎𝑡𝑎 = {
"𝑇𝑖𝑚𝑒": 𝑡𝑖𝑚𝑒. 𝑡𝑖𝑚𝑒(),
"𝑉𝑜𝑙𝑡𝑎𝑔𝑒": 0.0,
"𝐶𝑢𝑟𝑟𝑒𝑛𝑡": 0.0,
"𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒": 0.0,
"𝐵𝑀𝑆 𝐹𝑎𝑢𝑙𝑡𝑠": "𝑁𝑜 𝑓𝑎𝑢𝑙𝑡𝑠",
"𝐶𝑒𝑙𝑙 𝑉𝑜𝑙𝑡𝑎𝑔𝑒": "𝑁/𝐴",
"𝐶𝑒𝑙𝑙 𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒": "𝑁/𝐴",
}
𝑠𝑒𝑙𝑓. 𝑑𝑎𝑡𝑎_𝑙𝑜𝑔. 𝑎𝑝𝑝𝑒𝑛𝑑(𝑑𝑎𝑡𝑎)
𝑠𝑒𝑙𝑓. 𝑙𝑜𝑔_𝑚𝑒𝑠𝑠𝑎𝑔𝑒(𝑓"𝐷𝑎𝑡𝑎 𝑎𝑐𝑞𝑢𝑖𝑟𝑒𝑑: {𝑑𝑎𝑡𝑎}")
𝑑𝑒𝑓 𝑙𝑜𝑔_𝑚𝑒𝑠𝑠𝑎𝑔𝑒(𝑠𝑒𝑙𝑓, 𝑚𝑒𝑠𝑠𝑎𝑔𝑒):
𝑠𝑒𝑙𝑓. 𝑙𝑜𝑔_𝑡𝑒𝑥𝑡. 𝑐𝑜𝑛𝑓𝑖𝑔(𝑠𝑡𝑎𝑡𝑒 = ′𝑛𝑜𝑟𝑚𝑎𝑙′)
𝑠𝑒𝑙𝑓. 𝑙𝑜𝑔_𝑡𝑒𝑥𝑡. 𝑖𝑛𝑠𝑒𝑟𝑡(𝑡𝑘. 𝐸𝑁𝐷, 𝑓"{𝑚𝑒𝑠𝑠𝑎𝑔𝑒}\𝑛")
𝑠𝑒𝑙𝑓. 𝑙𝑜𝑔_𝑡𝑒𝑥𝑡. 𝑐𝑜𝑛𝑓𝑖𝑔(𝑠𝑡𝑎𝑡𝑒 = ′𝑑𝑖𝑠𝑎𝑏𝑙𝑒𝑑′)
𝑠𝑒𝑙𝑓. 𝑙𝑜𝑔_𝑡𝑒𝑥𝑡. 𝑠𝑒𝑒(𝑡𝑘. 𝐸𝑁𝐷)
𝑑𝑒𝑓 𝑠𝑎𝑣𝑒_𝑑𝑎𝑡𝑎(𝑠𝑒𝑙𝑓):
𝑖𝑓 𝑛𝑜𝑡 𝑠𝑒𝑙𝑓. 𝑑𝑎𝑡𝑎_𝑙𝑜𝑔:
𝑚𝑒𝑠𝑠𝑎𝑔𝑒𝑏𝑜𝑥. 𝑠ℎ𝑜𝑤𝑤𝑎𝑟𝑛𝑖𝑛𝑔("𝑊𝑎𝑟𝑛𝑖𝑛𝑔", "𝑁𝑜 𝑑𝑎𝑡𝑎 𝑡𝑜 𝑠𝑎𝑣𝑒. ")
𝑟𝑒𝑡𝑢𝑟𝑛
𝑓𝑖𝑙𝑒_𝑝𝑎𝑡ℎ = 𝑓𝑖𝑙𝑒𝑑𝑖𝑎𝑙𝑜𝑔. 𝑎𝑠𝑘𝑠𝑎𝑣𝑒𝑎𝑠𝑓𝑖𝑙𝑒𝑛𝑎𝑚𝑒(𝑑𝑒𝑓𝑎𝑢𝑙𝑡𝑒𝑥𝑡𝑒𝑛𝑠𝑖𝑜𝑛 = ". 𝑐𝑠𝑣", 𝑓𝑖𝑙𝑒𝑡𝑦𝑝𝑒𝑠
= [("𝐶𝑆𝑉 𝐹𝑖𝑙𝑒𝑠", " ∗. 𝑐𝑠𝑣")])
𝑖𝑓 𝑛𝑜𝑡 𝑓𝑖𝑙𝑒_𝑝𝑎𝑡ℎ:
𝑟𝑒𝑡𝑢𝑟𝑛
𝑤𝑖𝑡ℎ 𝑜𝑝𝑒𝑛(𝑓𝑖𝑙𝑒_𝑝𝑎𝑡ℎ, 𝑚𝑜𝑑𝑒 = ′𝑤′, 𝑛𝑒𝑤𝑙𝑖𝑛𝑒 = ′′) 𝑎𝑠 𝑐𝑠𝑣𝑓𝑖𝑙𝑒:
𝑓𝑖𝑒𝑙𝑑𝑛𝑎𝑚𝑒𝑠
= ["𝑇𝑖𝑚𝑒", "𝑉𝑜𝑙𝑡𝑎𝑔𝑒", "𝐶𝑢𝑟𝑟𝑒𝑛𝑡", "𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒", "𝐵𝑀𝑆 𝐹𝑎𝑢𝑙𝑡𝑠", "𝐶𝑒𝑙𝑙 𝑉𝑜𝑙𝑡𝑎𝑔𝑒", "𝐶𝑒𝑙𝑙 𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒"]
𝑤𝑟𝑖𝑡𝑒𝑟 = 𝑐𝑠𝑣. 𝐷𝑖𝑐𝑡𝑊𝑟𝑖𝑡𝑒𝑟(𝑐𝑠𝑣𝑓𝑖𝑙𝑒, 𝑓𝑖𝑒𝑙𝑑𝑛𝑎𝑚𝑒𝑠 = 𝑓𝑖𝑒𝑙𝑑𝑛𝑎𝑚𝑒𝑠)
𝑤𝑟𝑖𝑡𝑒𝑟. 𝑤𝑟𝑖𝑡𝑒ℎ𝑒𝑎𝑑𝑒𝑟()
𝑤𝑟𝑖𝑡𝑒𝑟. 𝑤𝑟𝑖𝑡𝑒𝑟𝑜𝑤𝑠(𝑠𝑒𝑙𝑓. 𝑑𝑎𝑡𝑎_𝑙𝑜𝑔)
𝑚𝑒𝑠𝑠𝑎𝑔𝑒𝑏𝑜𝑥. 𝑠ℎ𝑜𝑤𝑖𝑛𝑓𝑜("𝐷𝑎𝑡𝑎 𝑆𝑎𝑣𝑒𝑑", "𝐷𝑎𝑡𝑎 𝑠𝑢𝑐𝑐𝑒𝑠𝑠𝑓𝑢𝑙𝑙𝑦 𝑠𝑎𝑣𝑒𝑑. ")
𝑑𝑒𝑓 𝑠ℎ𝑜𝑤_𝑔𝑟𝑎𝑝ℎ𝑠(𝑠𝑒𝑙𝑓):
𝑖𝑓 𝑛𝑜𝑡 𝑠𝑒𝑙𝑓. 𝑑𝑎𝑡𝑎_𝑙𝑜𝑔:
𝑚𝑒𝑠𝑠𝑎𝑔𝑒𝑏𝑜𝑥. 𝑠ℎ𝑜𝑤𝑤𝑎𝑟𝑛𝑖𝑛𝑔("𝑊𝑎𝑟𝑛𝑖𝑛𝑔", "𝑁𝑜 𝑑𝑎𝑡𝑎 𝑡𝑜 𝑠ℎ𝑜𝑤. ")
𝑟𝑒𝑡𝑢𝑟𝑛
𝑡𝑖𝑚𝑒𝑠 = [𝑑𝑎𝑡𝑎["𝑇𝑖𝑚𝑒"] 𝑓𝑜𝑟 𝑑𝑎𝑡𝑎 𝑖𝑛 𝑠𝑒𝑙𝑓. 𝑑𝑎𝑡𝑎_𝑙𝑜𝑔]
𝑣𝑜𝑙𝑡𝑎𝑔𝑒𝑠 = [𝑑𝑎𝑡𝑎["𝑉𝑜𝑙𝑡𝑎𝑔𝑒"] 𝑓𝑜𝑟 𝑑𝑎𝑡𝑎 𝑖𝑛 𝑠𝑒𝑙𝑓. 𝑑𝑎𝑡𝑎_𝑙𝑜𝑔]
𝑐𝑢𝑟𝑟𝑒𝑛𝑡𝑠 = [𝑑𝑎𝑡𝑎["𝐶𝑢𝑟𝑟𝑒𝑛𝑡"] 𝑓𝑜𝑟 𝑑𝑎𝑡𝑎 𝑖𝑛 𝑠𝑒𝑙𝑓. 𝑑𝑎𝑡𝑎_𝑙𝑜𝑔]
𝑡𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒𝑠 = [𝑑𝑎𝑡𝑎["𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒"] 𝑓𝑜𝑟 𝑑𝑎𝑡𝑎 𝑖𝑛 𝑠𝑒𝑙𝑓. 𝑑𝑎𝑡𝑎_𝑙𝑜𝑔]
𝑐𝑒𝑙𝑙_𝑣𝑜𝑙𝑡𝑎𝑔𝑒𝑠 = [𝑑𝑎𝑡𝑎["𝐶𝑒𝑙𝑙 𝑉𝑜𝑙𝑡𝑎𝑔𝑒"] 𝑓𝑜𝑟 𝑑𝑎𝑡𝑎 𝑖𝑛 𝑠𝑒𝑙𝑓. 𝑑𝑎𝑡𝑎_𝑙𝑜𝑔]
𝑐𝑒𝑙𝑙_𝑡𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒𝑠 = [𝑑𝑎𝑡𝑎["𝐶𝑒𝑙𝑙 𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒"] 𝑓𝑜𝑟 𝑑𝑎𝑡𝑎 𝑖𝑛 𝑠𝑒𝑙𝑓. 𝑑𝑎𝑡𝑎_𝑙𝑜𝑔]
𝑝𝑙𝑡. 𝑓𝑖𝑔𝑢𝑟𝑒(𝑓𝑖𝑔𝑠𝑖𝑧𝑒 = (10, 8))
# 𝑉𝑜𝑙𝑡𝑎𝑔𝑒 𝑔𝑟𝑎𝑝ℎ
𝑝𝑙𝑡. 𝑠𝑢𝑏𝑝𝑙𝑜𝑡(3, 2, 1)
𝑝𝑙𝑡. 𝑝𝑙𝑜𝑡(𝑡𝑖𝑚𝑒𝑠, 𝑣𝑜𝑙𝑡𝑎𝑔𝑒𝑠, 𝑙𝑎𝑏𝑒𝑙 = "𝑉𝑜𝑙𝑡𝑎𝑔𝑒 (𝑉)", 𝑐𝑜𝑙𝑜𝑟 = ′𝑏𝑙𝑢𝑒′)
𝑝𝑙𝑡. 𝑥𝑙𝑎𝑏𝑒𝑙("𝑇𝑖𝑚𝑒 (𝑠)")
𝑝𝑙𝑡. 𝑦𝑙𝑎𝑏𝑒𝑙("𝑉𝑜𝑙𝑡𝑎𝑔𝑒 (𝑉)")
𝑝𝑙𝑡. 𝑙𝑒𝑔𝑒𝑛𝑑()
# 𝐶𝑢𝑟𝑟𝑒𝑛𝑡 𝑔𝑟𝑎𝑝ℎ
𝑝𝑙𝑡. 𝑠𝑢𝑏𝑝𝑙𝑜𝑡(3, 2, 2)
𝑝𝑙𝑡. 𝑝𝑙𝑜𝑡(𝑡𝑖𝑚𝑒𝑠, 𝑐𝑢𝑟𝑟𝑒𝑛𝑡𝑠, 𝑙𝑎𝑏𝑒𝑙 = "𝐶𝑢𝑟𝑟𝑒𝑛𝑡 (𝐴)", 𝑐𝑜𝑙𝑜𝑟 = ′𝑔𝑟𝑒𝑒𝑛′)
𝑝𝑙𝑡. 𝑥𝑙𝑎𝑏𝑒𝑙("𝑇𝑖𝑚𝑒 (𝑠)")
𝑝𝑙𝑡. 𝑦𝑙𝑎𝑏𝑒𝑙("𝐶𝑢𝑟𝑟𝑒𝑛𝑡 (𝐴)")
𝑝𝑙𝑡. 𝑙𝑒𝑔𝑒𝑛𝑑()
# 𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒 𝑔𝑟𝑎𝑝ℎ
𝑝𝑙𝑡. 𝑠𝑢𝑏𝑝𝑙𝑜𝑡(3, 2, 3)
𝑝𝑙𝑡. 𝑝𝑙𝑜𝑡(𝑡𝑖𝑚𝑒𝑠, 𝑡𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒𝑠, 𝑙𝑎𝑏𝑒𝑙 = "𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒 (𝐶)", 𝑐𝑜𝑙𝑜𝑟 = ′𝑟𝑒𝑑′)
𝑝𝑙𝑡. 𝑥𝑙𝑎𝑏𝑒𝑙("𝑇𝑖𝑚𝑒 (𝑠)")
𝑝𝑙𝑡. 𝑦𝑙𝑎𝑏𝑒𝑙("𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒 (𝐶)")
𝑝𝑙𝑡. 𝑙𝑒𝑔𝑒𝑛𝑑()
# 𝐶𝑒𝑙𝑙 𝑉𝑜𝑙𝑡𝑎𝑔𝑒 𝑔𝑟𝑎𝑝ℎ
𝑝𝑙𝑡. 𝑠𝑢𝑏𝑝𝑙𝑜𝑡(3, 2, 4)
𝑝𝑙𝑡. 𝑝𝑙𝑜𝑡(𝑡𝑖𝑚𝑒𝑠, 𝑐𝑒𝑙𝑙_𝑣𝑜𝑙𝑡𝑎𝑔𝑒𝑠, 𝑙𝑎𝑏𝑒𝑙 = "𝐶𝑒𝑙𝑙 𝑉𝑜𝑙𝑡𝑎𝑔𝑒 (𝑉)", 𝑐𝑜𝑙𝑜𝑟 = ′𝑝𝑢𝑟𝑝𝑙𝑒′)
𝑝𝑙𝑡. 𝑥𝑙𝑎𝑏𝑒𝑙("𝑇𝑖𝑚𝑒 (𝑠)")
𝑝𝑙𝑡. 𝑦𝑙𝑎𝑏𝑒𝑙("𝐶𝑒𝑙𝑙 𝑉𝑜𝑙𝑡𝑎𝑔𝑒 (𝑉)")
𝑝𝑙𝑡. 𝑙𝑒𝑔𝑒𝑛𝑑()
# 𝐶𝑒𝑙𝑙 𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒 𝑔𝑟𝑎𝑝ℎ
𝑝𝑙𝑡. 𝑠𝑢𝑏𝑝𝑙𝑜𝑡(3, 2, 5)
𝑝𝑙𝑡. 𝑝𝑙𝑜𝑡(𝑡𝑖𝑚𝑒𝑠, 𝑐𝑒𝑙𝑙_𝑡𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒𝑠, 𝑙𝑎𝑏𝑒𝑙 = "𝐶𝑒𝑙𝑙 𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒 (𝐶)", 𝑐𝑜𝑙𝑜𝑟 = ′𝑜𝑟𝑎𝑛𝑔𝑒′)
𝑝𝑙𝑡. 𝑥𝑙𝑎𝑏𝑒𝑙("𝑇𝑖𝑚𝑒 (𝑠)")
𝑝𝑙𝑡. 𝑦𝑙𝑎𝑏𝑒𝑙("𝐶𝑒𝑙𝑙 𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒 (𝐶)")
𝑝𝑙𝑡. 𝑙𝑒𝑔𝑒𝑛𝑑()
𝑝𝑙𝑡. 𝑡𝑖𝑔ℎ𝑡_𝑙𝑎𝑦𝑜𝑢𝑡()
𝑝𝑙𝑡. 𝑠ℎ𝑜𝑤()
𝑖𝑓 __𝑛𝑎𝑚𝑒__ == "__𝑚𝑎𝑖𝑛__":
𝑟𝑜𝑜𝑡 = 𝑡𝑘. 𝑇𝑘()
𝑎𝑝𝑝 = 𝐵𝑎𝑡𝑡𝑒𝑟𝑦𝐶𝑦𝑐𝑙𝑖𝑛𝑔𝐴𝑝𝑝(𝑟𝑜𝑜𝑡)
𝑟𝑜𝑜𝑡. 𝑚𝑎𝑖𝑛𝑙𝑜𝑜𝑝()