SAE Concevoir – Programme Python

                                                                                                                                            #Importation des bibliothèques
from random import randrange, choices, randint
import tkinter as tk
from tkinter import ttk
import serial
import serial.tools.list_ports as prtlst
import time

#On définit les variables pour le portUSB, le type de traitement et pour l'actualisation
portUSB = 'Simulateur'
strTypeTraitement = 'Maximum'
boolConnecte = False
intTempsActualisation = 400
intTempsActualisation_temp = 400
boolPlay = False

#Création de la fenêtre du programme
fenetre=tk.Tk()

#-------------------------#
#Les générateurs de trame
#-------------------------#

#Version 1 - Lucas, Elouen, Antoine V., Tom
def generateur_trames()->str:
    """
    Génerateur de trames aléatoires, le problème avec ce dernier est qu'il génère quasiment à chaque fois
    un octet avec un 7 donc les trames sont presques à chaque fois à l'état maximum.
    """
    strTrame = ""
    for i in range(50):
        strBitAB = hex(randrange(256))[2:] #On choisit un nombre aléatoire entre 0 et 256 et on le convertit en héxadecimal
        strBitCD = "0" + hex(choices((0,1,3,7))[0])[2:] #On choisit un état aléatoire pour les LEDs et on le convertit en hexadécimal
        if len(strBitAB) < 2:
            strBitAB = "0" + strBitAB

        strTrame += 'ab' + strBitAB + 'cd' + strBitCD #On associe les bits et on ajoute les identifiants pour créer une trame valide
    
    return strTrame

#Version du prof.
def generateur_niveau(intNiveau:int)->str:
    # Fonction permettant de RETOURNER une série de 400 octets de manière aléatoire
    # Sous le même format que les données envoyées par la carte STM32
        # Un paramètre : intNIveau qui est un entier symbolisant le niveau accoustique simulé :
            # 0 = Aucune led allumé.
            # 1 = Led verte allumée.
            # 3 = leds verte et orange allumées.
            # 7 : leds verte, orange et rouge allumées.
    byteDonneeEnvoyee = b''
    for i in range(100):
        intAlea=randint(0,intNiveau)
        if intAlea == 3:
            intAlea = 7
        elif intAlea == 2:
            intAlea = 3
        byteDonneeEnvoyee += (10*16**7+11*16**6+16**5+7*16**4+12*16**3+13*16**2+intAlea).to_bytes(4, byteorder='big')
    
    return byteDonneeEnvoyee

def generateur_donnees()->str:
    # Fonction permettant de RETOURNER une série de 400 octets de manière aléatoire
    # sous le même format que les données envoyées par la carte STM32
    # après avoir simulé un nievau donné par le choix d'une nombre aléatoire. 
        # Aucun paramètre.
    intNiveau = randint(0,4)
    return generateur_niveau(intNiveau).hex()

#------------------------------------------------#
#Connexion à la carte et récupération des données
#------------------------------------------------#

def recherche_port_STM32()->str:
    """
    Cette fonction recherche dans les périphériques connectés ceux correspondant
    à des cartes STM32, et renvoie la chaîne de caractère correspondant au port
    où est connectée cette dernière.
    """
    listeDesPorts = prtlst.comports() #On liste tout les ports
    Recherche = ['STM32', 'STLink' , 'USB']
    for i in range(len(Recherche)):
        for pt in listeDesPorts:
            if Recherche[i] in pt[1]: # On recherche 'STM32' 'STLink' ou 'USB' dans la description du port
                return pt[0] #Si il y est, on retourne le port
                break

def connexion_stm32()->None:
    """
    Cette procédure se connecte au port de la carte STM32 avec un baudrate de 9600bps
    et une taille de bits de 8 bits.
    """
    global portSerie
    if recherche_port_STM32() != None:
        portUSB = recherche_port_STM32()
    portSerie = serial.Serial(port=portUSB, #On se connecte au port trouvé précedemment
                                baudrate=921600,
                                bytesize=serial.EIGHTBITS)
    if portUSB != 'Simulateur':
        boolConnecte = True
        labelPort.config(text="Carte STM 32 sur le port : " + portUSB) #On configure le texte pour afficher le port où la carte est connecté

def recuperation_trames()->str:
    """
    Cette fonction renvoie une trame de 400 caractères prélevée
    sur le port où la carte est connectée.
    """
    global portSerie
    donneeRecue = portSerie.read(size=400) #On récupère une trame de 400 caractères
    return donneeRecue.hex()

#Fonction réalisée à l'aide du prof
def releve_niveau():
    """
    Cette fonction retourne la valeur maximale de l'état des LEDs sur 100 mesures reçues
    """
    valMax = 0
    try:
        #Je lis 400 octets , ce qui me donne 100 séries de 4 octets en hexa pour y prendre la plus grande valeur :
        if boolConnecte and boolPlay:
            donneeRecue = portSerie.read(size=400).hex()
        elif boolConnecte == False and boolPlay:
            donneeRecue = generateur_donnees()
        else:
            donneeRecue = 'ab00cd00ab00cd00ab00cd00ab00cd00'

        if strTypeTraitement == 'Maximum':
            #Je récupère la valeur après 'CD0' :
            indexCD = donneeRecue.find('cd')
            while indexCD >0:
                valMax = max(valMax,int(donneeRecue[indexCD+3:indexCD+4]))
                donneeRecue=donneeRecue[indexCD+2:len(donneeRecue)]
                indexCD = donneeRecue.find('cd')
            if valMax ==3:
                valMax = 2
            elif valMax == 7:
                valMax = 3
        elif strTypeTraitement == 'Moyenne':
            return traitement_trames(donneeRecue)

    except:
        portSerie.close()
        time.sleep(0.1)
        portSerie.open()
        time.sleep(float(10/100))
    return valMax

#-------------------------#
#Traitement des données
#-------------------------#

def traitement_trames(trame:str)->int:
    """
    Cette fonction traite les trames reçues à l'aide des fonctions précedentes
    """
    strTypeTraitement = comboboxChoixTraitement.get() #Une combobox est une liste de choix déroulant
    try:
        if strTypeTraitement  == 'Maximum': #Pour le type de traitement maximum on utilise la valeurs maximale sur une trame

            listBitAB = []
            listBitCD = []
            #Pour avoir une trame fonctionelle même si elle commence par autre chose que 'ab'
            while trame[0:2] != 'ab':
                trame = trame[0:1]

            trame = trame[0:392]

            for i in range(len(trame)-8):
                #On analyse la trame, si elle commence par 'ab' on regarde les élements d'après et si il y'a bien le cd et les bits ensuite
                if trame[i:i+2] == 'ab':
                    print(trame[i:i+2], trame[i+2:i+4], trame[i+4:i+6], trame[i+6:i+8] )
                    if trame[i+4:i+6] == 'cd':
                        if trame[i+6:i+8] in ['00', '01', '03', '07']: #Si le bit correspond à un état de LEDs, on le mets dans la liste des bits CD
                            listBitCD.append(trame[i+6:i+8])
            
            global intMaxEtatLed

            intMaxEtatLed = int(max(listBitCD)) #On prends le max de cette liste

            return intMaxEtatLed
        
        elif strTypeTraitement  == 'Moyenne':

            listQuantiteEtatLed = [0, 0, 0, 0, 0, 0, 0, 0]

            global intMoyVumetre

            intMoyVumetre = round(sum(listBitAB)/len(listBitAB), 0)

            listBitCD = [trame[i:i+2] for i in range(6, len(trame), 8)]

            for intBitsCD in listBitCD:
                #Pour tout les bits CD, on compte chacun d'entre eux et on prends celui le plus présent, et on le renvoie
                if intBitsCD == '00':
                        listQuantiteEtatLed[0] += 1        
                elif intBitsCD == '01':
                        listQuantiteEtatLed[1] += 1
                elif intBitsCD == '03':
                        listQuantiteEtatLed[3] += 1     
                elif intBitsCD == '07':
                        listQuantiteEtatLed[7] += 1

                intMoyEtatLed = listQuantiteEtatLed.index(max(listQuantiteEtatLed))
            return intMoyEtatLed
    except:
         portSerie.close()
         time.sleep(0.1)
         portSerie.open()
         time.sleep(0.1)

#----------------#
#Partie graphique
#----------------#

def dessin_Led()->None:
    """
    Procédure pour dessiner la forme principale des LEDs dans le canva
    """
    global LEDverte
    global LEDorange
    global LEDrouge
    #On dessine les LEDs dans un état vide
    LEDverte = canvasDessinLeds.create_oval(50,100,100,150,
                           fill= 'white'
                           ,width=4
                           ,outline = 'green')
    
    LEDorange = canvasDessinLeds.create_oval(200,100,250,150,
                           fill= 'white'
                           ,width=4
                           ,outline = 'Orange')
    LEDrouge = canvasDessinLeds.create_oval(350,100,400,150,
                           fill= 'white'
                           ,width=4
                           ,outline = 'red')
    contour = canvasDessinLeds.create_rectangle(25,75,425,175,
                                 fill=''
                                 ,width=4
                                 ,outline='Black'
                                 ,activefill='')
    
    #On appelle la fonction pour définir le nouvel état des LEDs
    changement_etat_led(releve_niveau())
    intTempsActualisation = entryTempsTrame.get()
    #On allume le timer pour actualiser la fenêtre
    fenetre.after(intTempsActualisation, dessin_Led)
    

def changement_etat_led(intEtatVuMetre:int)->None:
    """
    Cette procédure définit le nouvel état des LEDs selon l'état des LEDs
    entré en paramètre.
    """

    #Selon l'état des LEDs, on allume ou pas les LEDs virtuelles
    if intEtatVuMetre == 0:
        canvasDessinLeds.itemconfig(LEDverte, fill='white')
        canvasDessinLeds.itemconfig(LEDorange, fill='white')
        canvasDessinLeds.itemconfig(LEDrouge, fill='white')


    elif intEtatVuMetre == 1:
        canvasDessinLeds.itemconfig(LEDverte, fill='green')
        canvasDessinLeds.itemconfig(LEDorange, fill='white')
        canvasDessinLeds.itemconfig(LEDrouge, fill='white')

    elif intEtatVuMetre == 3:
        canvasDessinLeds.itemconfig(LEDverte, fill='green')
        canvasDessinLeds.itemconfig(LEDorange, fill='orange')
        canvasDessinLeds.itemconfig(LEDrouge, fill='white')

    elif intEtatVuMetre == 7:
        canvasDessinLeds.itemconfig(LEDverte, fill='green')
        canvasDessinLeds.itemconfig(LEDorange, fill='orange')
        canvasDessinLeds.itemconfig(LEDrouge, fill='red')
  
def reini()->None:
    """
    Cette procédure sert à réinitialiser l'état des LEDs.
    """
    global LEDverte
    global LEDorange
    global LEDrouge
    #On supprime les LEDs virtuelles et on les redesinnes
    canvasDessinLeds.delete(LEDverte)
    canvasDessinLeds.delete(LEDorange)
    canvasDessinLeds.delete(LEDrouge)
    dessin_Led()

def play_pause()->None:
    global boolPlay
    """
    Cette procédure sert à déclencher ou non le traitement des données.
    """
    if boolPlay == True:
        boolPlay = False
    else:
        boolPlay = True

#Création du texte du titre et du canvas contenant LEDs
labelTitre = tk.Label(fenetre, text="Sonomètre", fg='black', bg='white')
labelTitre.pack(padx=5, pady=5)

labelPort = tk.Label(fenetre, text="Carte STM 32 sur le port : " + portUSB, fg='black', bg='white')
labelPort.pack(padx=5, pady=5)

canvasDessinLeds = tk.Canvas(fenetre,
                        width=450,#Dimension largeur
                        height=200,#Dimension longeur
                        background='ivory') #Zone de dessin avec fond ivoire

#On range tout ça
canvasDessinLeds.pack(padx=5,pady=5)

#On crée les labels et bouton et widgets nécessaires à la fenêtre et on les ranges
buttonQuitter = tk.Button(fenetre, text="Marche/Arrêt", width=15, fg='black', bg='white',command = lambda :play_pause() )#tk.Button crée un bouton 
buttonQuitter.pack(padx=5,pady=5)

buttonReset = tk.Button(fenetre, text="Réinitialiser état LEDs", width=15, fg='black', bg='white',command = lambda :reini() )#tk.Button crée un bouton 
buttonReset.pack(padx=5,pady=5)

buttonConnexion = tk.Button(fenetre, text="Connexion au STM32", width=15, fg='black', bg='white',command = lambda :connexion_stm32() )#tk.Button crée un bouton 
buttonConnexion.pack(padx=5,pady=5)

labelTraitementDonnee = tk.Label(fenetre, text="Type de traitement des donées :", fg='black', bg='white')
labelTraitementDonnee.pack(padx=5, pady=5)

listChoixTraitement = ['Maximum', 'Moyenne']
comboboxChoixTraitement = ttk.Combobox(fenetre, values=listChoixTraitement)
comboboxChoixTraitement.current(0)
comboboxChoixTraitement.pack(padx=5, pady=5)

labelTempsActualisation = tk.Label(fenetre, text="Temps d'actualisation des trames (en mS) :", fg='black', bg='white')
labelTempsActualisation.pack(padx=5, pady=5)
entryTempsTrame = tk.Entry(fenetre)
entryTempsTrame.insert(0, '6')
entryTempsTrame.pack(padx=5, pady=5)

dessin_Led()
fenetre.mainloop()