diff --git a/Python/XML/aircraft.xml b/Python/XML/aircraft.xml index e434a4f..100b686 100644 --- a/Python/XML/aircraft.xml +++ b/Python/XML/aircraft.xml @@ -1,84 +1,294 @@ - - - - 330 - 410 - 2000 - 3000 - - - 220 - 450 - 1500 - 2000 - - - 200 - 430 - 1000 - 1500 - - - 330 - 410 - 2000 - 2500 - - - 200 - 450 - 1000 - 1500 - - - 330 - 450 - 500 - 1000 - - - 160 - 370 - 1000 - 1500 - - - 240 - 450 - 1300 - 1700 - - - 250 - 450 - 2000 - 3000 - - - 400 - 550 - 2000 - 2500 - - - 210 - 550 - 2000 - 2500 - - - 330 - 550 - 1500 - 2000 - - - 330 - 550 - 2500 - 3000 - - - \ No newline at end of file + + + + + 0.74 + 0.70 + 0.74 + + 270 + 270 + + 280 + + 1000 + 1500 + + 2500 + 2500 + + 3500 + + + + 0.79 + 0.78 + 0.78 + + 290 + 290 + + 280 + + 1500 + 800 + + 2000 + 2000 + + 3500 + + + + 0.79 + 0.76 + 0.76 + + 290 + 290 + + 290 + + 1000 + 800 + + 2000 + 2000 + + 3500 + + + + 0.79 + 0.78 + 0.78 + + 290 + 290 + + 290 + + 1000 + 800 + + 1500 + 2200 + + 3500 + + + + 0.79 + 0.78 + 0.78 + + 290 + 290 + + 290 + + 1000 + 800 + + 1400 + 2000 + + 3500 + + + + 0.79 + 0.78 + 0.78 + + 290 + 290 + + 290 + + 1000 + 1000 + + 1800 + 2000 + + 2500 + + + + 0.78 + 0.78 + 0.78 + + 290 + 290 + + 290 + + 1000 + 1000 + + 1500 + 2300 + + 2500 + + + + 0.78 + 0.78 + 0.78 + + 290 + 290 + + 290 + + 1000 + 1000 + + 1500 + 2100 + + 2500 + + + + 0.78 + 0.78 + 0.78 + + 290 + 290 + + 290 + + 1000 + 1500 + + 1200 + 1800 + + 2500 + + + + 0.81 + 0.80 + 0.81 + + 290 + 290 + + 290 + + 1200 + 1500 + + 1200 + 1500 + + 2500 + + + + 0.84 + 0.82 + 0.84 + + 300 + 300 + + 300 + + 1500 + 1000 + + 2000 + 2500 + + 2500 + + + + 0.46 + 0.46 + 0.46 + + 200 + 200 + + 240 + + 1000 + 1500 + + 1500 + 1000 + + 1500 + + + + + + AIGLE AZUR + AMERICAN + AIR Canada + AIR FRANCE + AEROFLOT + AIR FRANCE + AIRBUS INDUSTRIE + AIR MALTA + AEROMEXICO + ARGENTINA + AUSTRIAN + UKRAINE INTERNATIONAL + ALITALIA + SPEEDBIRD + BEE-LINE + AIR BERLIN + CONDOR + CONTINENTAL + C.S.A. + AIR ALGERIE + LUFTHANSA + SHAMROCK + EUROWINGS + EASY + ALPINE + FINNAIR + FRENCH POST + HOP AIR + IBERIA + SPANAIR + KLM + KENIYA + LIBAIR + LOT + MALEV + CEDAR JET + EGYPTAIR + OLYMPIC + ROYAL AIR MAROC + ROYAL AIR MAROC + JORDANIAN + TAROM + RYANAIR + SPRINGBOCK + SCANDINAVIAN + SAUDIA + SWISS + SYRIANAIR + AIR Portugal + TUNAIR + THOMAS COOK + TURKAIR + TRANSAVIA + FRANCE SOLEIL + EMIRATES + VIRGIN + + \ No newline at end of file diff --git a/Python/XML/carteSecteur.xml b/Python/XML/carteSecteur.xml index c2944ed..931d434 100644 --- a/Python/XML/carteSecteur.xml +++ b/Python/XML/carteSecteur.xml @@ -86,7 +86,7 @@ 709 - 995 + 1008 1146 @@ -109,6 +109,18 @@ 970 328 + + 838 + 412 + + + 739 + 225 + + + 806 + 354 + 947 620 @@ -141,10 +153,6 @@ 2028 997 - - 2000 - 980 - 1645 1097 @@ -174,12 +182,12 @@ 1644 - 1107 - 1237 + 1108 + 1236 - 991 - 1265 + 993 + 1266 1108 @@ -190,12 +198,40 @@ 1303 - 844 - 1249 + 840 + 1253 - 720 - 1339 + 699 + 1341 + + + 572 + 1421 + + + 2094 + 1968 + + + 2229 + 944 + + + 1995 + 533 + + + 1901 + 463 + + + 1273 + 446 + + + 1947 + 1538 @@ -301,6 +337,9 @@ TRANSIT SO NE + + FRI + MELKA @@ -338,6 +377,9 @@ TRANSIT E-SE N + + ATN + BURGO @@ -368,6 +410,9 @@ TRANSIT SE N + + ATN + BURGO @@ -391,6 +436,9 @@ JAMBI + + VAREK + I2 @@ -398,6 +446,9 @@ TRANSIT E SO + + BIELA + JUVEN @@ -428,12 +479,12 @@ OS - + DEPART SO LIML - LIML + BIELA 20 @@ -528,6 +579,9 @@ SEVET + + RAPID + 250 G2 @@ -556,6 +610,9 @@ SEVET + + RAPID + 250 G2 @@ -621,6 +678,12 @@ BERNI + + ETAMO + + + DIRMO + N2 N3 @@ -629,6 +692,9 @@ TRANSIT SE NO + + MIROS + JAMBI @@ -652,6 +718,12 @@ BERNI + + ETAMO + + + DIRMO + N2 N3 @@ -660,6 +732,9 @@ TRANSIT SE NO + + MIROS + JAMBI @@ -696,7 +771,7 @@ S BST - 20 + 50 LSE @@ -727,7 +802,7 @@ SE BST - 20 + 50 LSE @@ -749,6 +824,40 @@ JAMBI + + VAREK + + I2 + + + + DEPART + LFVB + SE + + BST + 50 + + + LSE + + + AVRIL + + + WS + 240 + + + RETNO + + + SANTO + True + + + SAMOS + I2 @@ -758,7 +867,7 @@ SO BST - 20 + 50 LSE diff --git a/Python/XML/simu.xml b/Python/XML/simu.xml deleted file mode 100644 index fb9033f..0000000 --- a/Python/XML/simu.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 1002 - - - FCACA - DH8D - ABISA-BERNI - 30000 - False - 1400 - 1300 - 300 - - - diff --git a/Python/capture.py b/Python/capture.py new file mode 100644 index 0000000..307c3d5 --- /dev/null +++ b/Python/capture.py @@ -0,0 +1,10 @@ + + +# Module Import + +import pygame + + +def saveScreenshot(win, path): + screencopy = win.copy() + pygame.image.save(screencopy, path) diff --git a/Python/client.py b/Python/client.py index 2c61f36..c2f3859 100644 --- a/Python/client.py +++ b/Python/client.py @@ -13,17 +13,25 @@ import Python.interface as interface from Python.paquets_avion import * import Python.outils_radar as outils_radar +import Python.capture as capture # recherche de tous les serveurs sur le réseau address = server_browser.serverBrowser() -print(address) +if address: + print(address) +else: + time.sleep(20) + exit() # fenêtre Pygame, mettre en 1920, 1080 pour plein écran pygame.init() width = 1000 height = 1000 - win = pygame.display.set_mode((width, height)) + +# win = pygame.display.set_mode() +# width, height = pygame.display.get_surface().get_size() + path = Path(os.getcwd()) manager = pygame_gui.UIManager((width, height), path / 'ressources' / 'theme.json') @@ -32,6 +40,11 @@ clock = pygame.time.Clock() font = pygame.font.SysFont('arial', 18) +if replayMode: + dossierScreen = Path('replay') / (str(time.localtime()[1]) + '_' + str(time.localtime()[2]) + '_' + + str(time.localtime()[3]) + 'h' + str(time.localtime()[4])) + dossierScreen.mkdir() + def main(server_ip: str): global temps @@ -43,12 +56,14 @@ def main(server_ip: str): # menus conflitBool = False conflitGen = None - menuAvion = None menuATC = None menuValeurs = None flightDataWindow = None menuRadar = interface.menuRadar() + # screenshots replays + dernierScreen = pygame.time.get_ticks() + # on se connecte au serveur n = Network(server_ip) packet = n.getP() @@ -206,21 +221,6 @@ def main(server_ip: str): curseur_cercles = True pygame.mouse.set_cursor(pygame.cursors.broken_x) - # on regarde si notre menu pour le pilote est actif - if menuAvion is not None: - - # si on valide les modifs, alors la fonction checkEvent retourne les modifs - modifications = menuAvion.checkEvent(event) - if modifications: - - # on applique alors les modifs - avionId = modifications[0] - modifications = modifications[1] - - # pour chaque modif, on prépare une requête au serveur - for changement, valeur in modifications.items(): - localRequests.append((avionId, changement, valeur)) - if menuATC is not None: # si on valide les modifs, alors la fonction checkEvent retourne les modifs @@ -249,8 +249,8 @@ def main(server_ip: str): if action: if type(action) in [list, tuple]: # si c'est un tuple alors cela correspond à une requête localRequests.append(action) - elif action in ['HDG', 'DCT']: - menuValeurs = interface.menuValeurs(menuValeurs.avion, pygame.mouse.get_pos(), action) + elif type(action) is str: + menuValeurs = interface.menuValeurs(menuValeurs.avion, pygame.mouse.get_pos(), action, pilote) if curseur_aliSep: for sep in sepDict: @@ -271,15 +271,12 @@ def main(server_ip: str): if type(action) in [list, tuple]: # si c'est un tuple alors cela correspond à une requête localRequests.append(action) - elif action == 'menuPIL' and menuAvion is None: # si c'est menu alors, on vérifie qu'on peut menu - menuAvion = interface.menuAvion(avion) - elif action == 'menuATC' and menuATC is None: menuATC = interface.menuATC(avion, pygame.mouse.get_pos()) elif menuValeurs is None and action is not None: # si on a renvoyé autre chose alors c'est une valeur pour ouvrir un menu - menuValeurs = interface.menuValeurs(avion, pygame.mouse.get_pos(), action) + menuValeurs = interface.menuValeurs(avion, pygame.mouse.get_pos(), action, pilote) # Menu de selection nouvel avion # si notre menu est ouvert @@ -318,7 +315,7 @@ def main(server_ip: str): elif event.type == pygame_gui.UI_TEXT_ENTRY_CHANGED: nouvelAvionWin.checkFields(event) - if menuAvion or nouvelAvionWin: + if nouvelAvionWin: pass # zoom géré ici @@ -369,9 +366,6 @@ def main(server_ip: str): manager.process_events(event) - if menuAvion is not None: - menuAvion.checkSliders() - if conflitGen is not None: conflitGen.checkScrollBar(carte) @@ -436,10 +430,6 @@ def main(server_ip: str): pressing = False # on se débarrasse des menus inutils - if menuAvion is not None: - if not menuAvion.checkAlive(): - menuAvion = None - if menuATC is not None: if not menuATC.checkAlive(): menuATC = None @@ -511,7 +501,7 @@ def main(server_ip: str): avion.drawEstimatedRoute(carte['points'], conflitGen.temps, color, win, zoom, scroll) for avion in dictAvionsAff.values(): - avion.draw(win, zoom, scroll, vecteurs, vecteurSetting, carte['points']) + avion.draw(win, zoom, scroll, vecteurs, vecteurSetting, carte['points'], game.heure) # on affiche les boutons manager.update(time_delta) @@ -535,6 +525,11 @@ def main(server_ip: str): img = font.render(str(distance), True, (255, 105, 180)) win.blit(img, (pygame.mouse.get_pos()[0] + 20, pygame.mouse.get_pos()[1])) + # prise des screenshots + if pygame.time.get_ticks() >= dernierScreen + delaiScreen and replayMode and not pilote: + capture.saveScreenshot(win, dossierScreen / (horloge.heureXML(game.heure) + '.png')) + dernierScreen = pygame.time.get_ticks() + # envoi des packets # on fait avec un try and except au cas où un paquet se perde diff --git a/Python/horloge.py b/Python/horloge.py index 0041236..6892dac 100644 --- a/Python/horloge.py +++ b/Python/horloge.py @@ -9,3 +9,26 @@ def affichageHeure(heure: float) -> str: minutes = '0' + minutes return heures + ':' + minutes + + +def heureXML(heure: float) -> str: + + """ + Renvoies un str avec l'heure en format hhmmss + :param heure: + :return: + """ + + heures = str(round(heure // 3600 % 24)) + if len(heures) == 1: + heures = '0' + heures + + minutes = str(round(heure % 3600 // 60)) + if len(minutes) == 1: + minutes = '0' + minutes + + secondes = str(round(heure % 3600 % 60)) + if len(secondes) == 1: + secondes = '0' + secondes + + return heures + minutes + secondes diff --git a/Python/interface.py b/Python/interface.py index 7a8a304..781bf25 100644 --- a/Python/interface.py +++ b/Python/interface.py @@ -199,7 +199,10 @@ def checkFields(self, event): self.returnValues.update({'PFL': int(event.text)}) except: # si l'utilisateur rentre n'importe quoi, on remet à la valeur du FL - self.returnValues.update({'PFL': self.returnValues['FL']}) + if 'FL' in self.returnValues: + self.returnValues.update({'PFL': self.returnValues['FL']}) + else: + self.returnValues.update({'PFL': 310}) # on vérifie l'indicatif elif event.ui_element == self.indicatifinput: @@ -212,200 +215,6 @@ def checkAlive(self): return self.window.alive() -class menuAvion: - - def __init__(self, avion): - self.avion = avion - self.window = self.window = pygame_gui.elements.UIWindow(pygame.Rect((400, 400), (600, 350))) - - # génération boutons heading - self.headingLabel = pygame_gui.elements.UILabel( - relative_rect=pygame.Rect((0, 17), (100, 17)), - container=self.window, - text=('Cap - ' + str(round(avion.papa.heading)))) - - self.headingContainer = pygame_gui.elements.UIScrollingContainer( - pygame.Rect((0, 34), (100, 200)), - container=self.window) - - tempo = scrollListGen(range(round(avion.papa.heading / 5) * 5 - 25, round(avion.papa.heading / 5) * 5 + 30, 5), - pygame.Rect((0, 0), (75, 17)), self.headingContainer) - - self.headingBoutonListe = tempo[1] - self.headingSlider = tempo[0] - self.headingSlider.set_scroll_from_start_percentage((round(avion.papa.heading / 5) * 5 - 15) / 300 * 0.8) - - # génération boutons Alti - self.altiLabel = pygame_gui.elements.UILabel( - relative_rect=pygame.Rect((0, 17), (100, 17)), - container=self.window, - text=('FL - ' + str(round(avion.papa.altitude / 100))), - anchors={'left': 'left', 'left_target': self.headingLabel}) - - self.altiContainer = pygame_gui.elements.UIScrollingContainer( - pygame.Rect((0, 34), (100, 200)), - container=self.window, - anchors={'left': 'left', 'left_target': self.headingContainer}) - - tempo = scrollListGen( - range(round(avion.papa.altitude / 1000) * 10 - 50, round(avion.papa.altitude / 1000) * 10 + 60, 10), - pygame.Rect((0, 0), (75, 17)), self.altiContainer) - - self.altiBoutonListe = tempo[1] - self.altiSlider = tempo[0] - - self.altiSlider.set_scroll_from_start_percentage((round(avion.papa.altitude / 1000) * 10 - 30) / 410 * 0.8) - - # génération boutons speed - self.speedLabel = pygame_gui.elements.UILabel( - relative_rect=pygame.Rect((0, 17), (100, 17)), - container=self.window, text=('IAS - ' + str(avion.papa.speedIAS)), - anchors={'left': 'left', 'left_target': self.altiLabel}) - - self.speedContainer = pygame_gui.elements.UIScrollingContainer( - pygame.Rect((0, 34), (100, 200)), - container=self.window, - anchors={'left': 'left', 'left_target': self.altiContainer}) - - tempo = scrollListGen( - range(round(avion.papa.speedIAS / 10) * 10 - 50, round(avion.papa.speedIAS / 10) * 10 + 60, 10), - pygame.Rect((0, 0), (75, 17)), self.speedContainer) - - self.speedBoutonListe = tempo[1] - self.speedSlider = tempo[0] - self.speedSlider.set_scroll_from_start_percentage((round(avion.papa.speedIAS / 10) * 10 - 100) / 330 * 0.8) - - # génération boutons points - - self.pointContainer = pygame_gui.elements.UIScrollingContainer( - pygame.Rect((0, 0), (100, 200)), - container=self.window, - anchors={'left': 'left', 'left_target': self.speedContainer}) - - tempo = scrollListGen([point['name'] for point in avion.papa.route['points']], - pygame.Rect((0, 0), (75, 17)), - self.pointContainer, - sliderBool=False) - - self.pointBoutonListe = tempo[1] - - # génération boutons next routes - - self.routeContainer = pygame_gui.elements.UIScrollingContainer( - pygame.Rect((0, 0), (100, 200)), - container=self.window, - anchors={'left': 'left', 'left_target': self.pointContainer}) - - # bouton validation - - self.validerBouton = pygame_gui.elements.UIButton( - pygame.Rect((200, 90), (75, 17)), - text='valider', - container=self.window, - anchors={'top': 'top', 'top_target': self.pointContainer}) - - # dict pour les valeurs que le menu renverra - self.returnValues = {} - - def checkAlive(self): - return self.window.alive() - - def checkSliders(self): - - """ - Change la valeur des boutons en fonction de la position du slider, pour tout le menu - """ - - selectedValue = None - if self.headingSlider.has_moved_recently: - if 'Heading' in self.returnValues: - selectedValue = self.returnValues['Heading'] - - value = round( - (360 - 5 * (len(self.headingBoutonListe) - 1)) * (self.headingSlider.start_percentage / 0.8) / 5) * 5 - for bouton in self.headingBoutonListe: - bouton.text = str(value) - bouton.rebuild() - if selectedValue == value: - bouton.disable() - else: - bouton.enable() - value += 5 - - elif self.altiSlider.has_moved_recently: - if 'Altitude' in self.returnValues: - selectedValue = self.returnValues['Altitude'] - - # la valeur de niveau oscille entre 0 et 410 - value = round( - (410 - 10 * (len(self.headingBoutonListe) - 1)) * (self.altiSlider.start_percentage / 0.8) / 10) * 10 - for bouton in self.altiBoutonListe: - bouton.text = str(value) - bouton.rebuild() - if selectedValue == value: - bouton.disable() - else: - bouton.enable() - value += 10 - - elif self.speedSlider.has_moved_recently: - if 'IAS' in self.returnValues: - selectedValue = self.returnValues['IAS'] - - value = round((330 - 10 * (len(self.headingBoutonListe) - 1)) * ( - self.speedSlider.start_percentage / 0.8) / 10) * 10 + 70 - for bouton in self.speedBoutonListe: - bouton.text = str(value) - bouton.rebuild() - if selectedValue == value: - bouton.disable() - else: - bouton.enable() - value += 10 - - def checkEvent(self, event): - - """ - Vérifie si un des boutons du menu a été pressé et modifie les données en conséquence - :arg event: l'évenement qu'il faut vérifier - :returns: le dictionaire de valeurs si on valide, None sinon - """ - - # heading - if event.ui_element in self.headingBoutonListe: - - self.returnValues.update({'Heading': int(event.ui_element.text)}) - selectButtonInList(self.headingBoutonListe, event.ui_element) - # on enlève le direct pour ne pas faire de confusion - if 'Direct' in self.returnValues: - self.returnValues.pop('Direct') - - # ALTI - elif event.ui_element in self.altiBoutonListe: - - self.returnValues.update({'Altitude': int(event.ui_element.text) * 100}) - selectButtonInList(self.altiBoutonListe, event.ui_element) - - # speed - elif event.ui_element in self.speedBoutonListe: - - selectButtonInList(self.speedBoutonListe, event.ui_element) - self.returnValues.update({'IAS': int(event.ui_element.text)}) - - # direct - elif event.ui_element in self.pointBoutonListe: - - selectButtonInList(self.pointBoutonListe, event.ui_element) - self.returnValues.update({'Direct': event.ui_element.text}) - if 'Heading' in self.returnValues: - self.returnValues.pop('Heading') - - elif event.ui_element is self.validerBouton: - - self.window.kill() - return self.avion.Id, self.returnValues - - class etiquette: def __init__(self, avion): @@ -562,7 +371,7 @@ def update(self, avion): if avion.papa.clearedIAS and self.extended: self.clearedSpeed.set_text("k" + avion.papa.clearedIAS) elif avion.papa.clearedMach and self.extended: - self.clearedSpeed.set_text("k" + avion.papa.clearedMach) + self.clearedSpeed.set_text("m" + avion.papa.clearedMach) else: self.clearedSpeed.set_text("S") @@ -753,13 +562,14 @@ def checkAlive(self): class menuValeurs: - def __init__(self, avion, pos: list[float, float], valeur: str): + def __init__(self, avion, pos: list[float, float] | tuple[float, float], valeur: str, pilote: bool): """ Menu utilisé par le controleur pour changer les valeurs des différents champs :param avion: :param pos: :param valeur: + :param pilote: si on est en pilote ou non """ self.avion = avion @@ -782,7 +592,7 @@ def __init__(self, avion, pos: list[float, float], valeur: str): self.listeDroite = None objectID = None - if valeur == 'DCT': # TODO boutons directs en blanc + if valeur == 'DCT': self.liste = [point['name'] for point in self.avion.papa.route['points']] self.listeAff = self.liste[self.liste.index(avion.papa.nextPoint['name']):] objectID = pygame_gui.core.ObjectID('@menuLabel', 'menuBlanc') @@ -801,21 +611,32 @@ def __init__(self, avion, pos: list[float, float], valeur: str): indexDeVitesse = self.liste.index(round(avion.papa.speedIAS / 10)) self.listeAff = self.liste[indexDeVitesse - 4: indexDeVitesse + 5] + elif valeur == 'C_Mach': + self.liste = [*range(30, 99)] + self.liste.reverse() + + for i in range(len(self.liste)): + self.liste[i] = self.liste[i] / 100 + + indexDeVitesse = self.liste.index(round(avion.papa.mach, 2)) + + self.listeAff = self.liste[indexDeVitesse - 4: indexDeVitesse + 5] + elif valeur == 'C_Rate': self.liste = [*range(500, 6000, 500)] self.liste.reverse() self.listeAff = self.liste[-7:] - elif valeur in ['XFL', 'PFL', 'CFL']: + elif valeur in ['XFL', 'PFL', 'CFL', 'FL']: self.liste = [*range(0, 600, 10)] self.liste.reverse() indexDuFL = self.liste.index(avion.papa.PFL) self.liste[indexDuFL] = "R" + str(avion.papa.PFL) self.listeAff = self.liste[indexDuFL - 4: indexDuFL + 5] - elif valeur == 'HDG': + elif valeur in ['HDG', 'C_HDG']: - cap = round(avion.papa.heading // 5) * 5 + 5 + cap = round(avion.papa.selectedHeading // 5) * 5 + 5 self.liste = [*range(5, 365, 5)] indexDuCap = self.liste.index(cap) @@ -839,7 +660,7 @@ def __init__(self, avion, pos: list[float, float], valeur: str): self.plusBouton = None self.moinsBouton = None - if valeur == 'C_IAS': + if valeur in ['C_IAS', 'C_Mach']: self.noeud = pygame_gui.elements.UIButton( pygame.Rect((0, 0), (width / 2, -1)), container=self.topContainer, @@ -907,7 +728,7 @@ def __init__(self, avion, pos: list[float, float], valeur: str): self.topContainer.set_dimensions((width, self.headingDCT.get_abs_rect()[3] * 2)) - if self.valeur == 'HDG': + if self.valeur in ['C_HDG', 'HDG']: listeDroite = [] listeGauche = [] @@ -1012,6 +833,23 @@ def __init__(self, avion, pos: list[float, float], valeur: str): (width, height)) + if self.valeur == 'C_IAS': + self.noeud.select() + elif self.valeur == 'C_Mach': + self.mach.select() + + if pilote: + if self.valeur == 'DCT': + self.valeur = 'Direct' + elif self.valeur == 'C_Rate': + self.valeur = 'Rate' + elif self.valeur == 'C_IAS': + self.valeur = 'IAS' + elif self.valeur == 'C_Mach': + self.valeur = 'Mach' + elif self.valeur == 'C_HDG': + self.valeur = 'HDG' + def checkEvent(self, event): """ Vérifies si un event est relié à ce menu et prend les actions en conséquence @@ -1063,29 +901,42 @@ def checkEvent(self, event): self.moinsBouton.select() self.plusBouton.unselect() + elif event.ui_element == self.noeud: + if self.mach.is_selected: + self.kill() + return 'C_IAS' + + elif event.ui_element == self.mach: + if self.noeud.is_selected: + self.kill() + return 'C_Mach' + elif event.ui_element == self.resume: self.kill() - if self.valeur in ['DCT', 'HDG']: # si c'est un cap, alors on veut continuer au cap - return self.avion.Id, 'HDG', ' ' + if self.valeur in ['DCT', 'C_HDG']: # si c'est un cap, alors on veut continuer au cap + return self.avion.Id, 'C_HDG', self.avion.papa.selectedHeading + + elif self.valeur in ['Direct', 'HDG']: + return self.avion.Id, 'HDG', self.avion.papa.selectedHeading else: # si c'est une rate ou IAS : on enlève return self.avion.Id, self.valeur, None elif event.ui_element == self.headingDCT: self.kill() - if self.valeur == 'HDG': + if self.valeur in ['C_HDG', 'HDG']: return 'DCT' else: - return 'HDG' + return 'C_HDG' - elif self.valeur == 'HDG': + elif self.valeur in ['C_HDG', 'HDG']: if event.ui_element in self.listeGauche: self.kill() if self.avion.papa.clearedHeading: heading = round(self.avion.papa.clearedHeading - int(event.ui_element.text[1:])) else: heading = round(self.avion.papa.selectedHeading - int(event.ui_element.text[1:])) - return self.avion.Id, 'HDG', heading + return self.avion.Id, self.valeur, heading elif event.ui_element in self.listeDroite: self.kill() @@ -1093,7 +944,8 @@ def checkEvent(self, event): heading = round(self.avion.papa.clearedHeading + int(event.ui_element.text[1:])) else: heading = round(self.avion.papa.selectedHeading + int(event.ui_element.text[1:])) - return self.avion.Id, 'HDG', heading + return self.avion.Id, self.valeur, heading + def checkScrolled(self, event): """ diff --git a/Python/paquets_avion.py b/Python/paquets_avion.py index 65c4259..73974f6 100644 --- a/Python/paquets_avion.py +++ b/Python/paquets_avion.py @@ -1,10 +1,11 @@ - +import math # Native imports import random # Imports fichiers from Python.geometry import * from Python.valeurs_config import * +import Python.vitesses as vitesses class Game: @@ -52,9 +53,8 @@ def __init__(self, gameMap, Id, indicatif, aircraft, perfos, route, arrival, FL= else: self.altitude = altiDefault - self.speedIAS = perfos['IAS'] - self.speedGS = self.speedIAS + self.altitude / 200 # la GS dépends de l'alti - self.speedPx = self.speedGS / gameMap['mapScale'] * heureEnRefresh + self.evolution = 0 # taux de variation/radar refresh + self.altitudeEvoTxt = '-' # RADAR display self.warning = False @@ -71,7 +71,12 @@ def __init__(self, gameMap, Id, indicatif, aircraft, perfos, route, arrival, FL= else: self.medevac = '' - self.callsignFreq = 'Austrian' # TODO ajouter les callsigns + if indicatif[:2] in gameMap['callsigns']: + self.callsignFreq = gameMap['callsigns'][indicatif[:2]] + elif indicatif[:3] in gameMap['callsigns']: + self.callsignFreq = gameMap['callsigns'][indicatif[:3]] + else: + self.callsignFreq = 'caca' if route['type'] == 'DEPART': self.provenance = route['provenance'] @@ -83,11 +88,6 @@ def __init__(self, gameMap, Id, indicatif, aircraft, perfos, route, arrival, FL= else: self.destination = random.choice(gameMap['aeroports'][route['destination']]) - # perfo - self.turnRate = turnRateDefault - self.maxROC = perfos['ROC'] - self.maxROD = perfos['ROD'] - # format route {nomRoute, routeType, listeRoutePoints, sortie} points : {caractéristiques eg : nom alti IAS} self.route = route @@ -96,8 +96,6 @@ def __init__(self, gameMap, Id, indicatif, aircraft, perfos, route, arrival, FL= self.nextPoint = None self.findNextPoint(gameMap) - self.evolution = 0 # taux de variation/radar refresh - self.altitudeEvoTxt = '-' if PFL is not None: self.PFL = PFL elif gameMap['floor'] < self.altitude/100 < gameMap['ceiling']: @@ -151,19 +149,43 @@ def __init__(self, gameMap, Id, indicatif, aircraft, perfos, route, arrival, FL= gameMap['points'][self.nextPoint['name']][1]) self.headingRad = (self.heading - 90) / 180 * math.pi - # selected + # perfo + self.turnRate = turnRateDefault + self.perfos = perfos + self.forcedSpeed = False + self.forcedEvo = False + self.machMode = False + self.changeSpeed() + + # selected + vitesses + if self.machMode: + self.selectedIAS = self.perfos['descentIAS'] + self.mach = self.selectedMach + self.speedTAS = vitesses.mach_to_TAS(self.mach, self.altitude) + self.speedIAS = vitesses.TAS_to_IAS(self.speedTAS, self.altitude) + else: + self.speedIAS = self.selectedIAS + self.mach = vitesses.IAS_to_Mach(self.speedIAS, self.altitude) + self.speedTAS = vitesses.mach_to_TAS(self.mach, self.altitude) + + self.speedGS = self.speedTAS + self.selectedAlti = self.CFL * 100 self.selectedHeading = self.heading - self.selectedIAS = self.speedIAS - self.mach = self.speedIAS + self.selectedMach = self.mach self.clearedIAS = None self.clearedMach = None self.clearedHeading = None self.clearedRate = None + self.clearedMachMode = False + self.clearedIASMode = False + + self.speedPx = self.speedGS / gameMap['mapScale'] * heureEnRefresh def findNextPoint(self, carte): self.nextPoint = findClosestSegment(self.route['points'], (self.x, self.y), carte['points'])[1] + self.DCT = self.nextPoint['name'] def changeXFL(self, carte) -> None: """ @@ -211,7 +233,6 @@ def updateAlti(self): if self.altitude != self.selectedAlti: # on regarde s'il faut évoluer if self.altitude - self.selectedAlti > 0: - self.evolution = - self.maxROD # on arrive dans moins d'un refresh ? if abs(self.altitude - self.selectedAlti) <= abs(self.evolution / 60 * radarRefresh): @@ -221,8 +242,6 @@ def updateAlti(self): self.altitudeEvoTxt = '↓' else: - self.evolution = self.maxROC - # on arrive dans moins d'un refresh ? if abs(self.altitude - self.selectedAlti) <= abs(self.evolution / 60 * radarRefresh): self.altitude = self.selectedAlti # alors, on met le niveau cible @@ -234,6 +253,42 @@ def updateAlti(self): self.altitudeEvoTxt = '-' self.evolution = 0 + def changeEvolution(self) -> None: + """ + Sélectionne la vitesse en fonction de la phase de vol + :return: + """ + + if self.forcedEvo: + return None + + if self.altitude >= altitude_conversion: + # ici, on prend les perfos en croisière + + if self.selectedAlti < self.altitude: + self.evolution = - self.perfos['cruiseROD'] + + else: + self.evolution = self.perfos['cruiseROC'] + + elif self.altitude >= altitude_cruise: + # ici, on regarde si on est en montée/descente ou alors, si on approche du niveau de croisière + + if self.selectedAlti < self.altitude: + self.evolution = - self.perfos['cruiseROD'] + + else: + self.evolution = self.perfos['climbROC'] + + else: + # ici, on est dans les basses couches donc on prend les perfos en basse alti + + if self.selectedAlti < self.altitude: + self.evolution = - self.perfos['descentROD'] + + else: + self.evolution = self.perfos['initialClimbROC'] + def move(self, gameMap): # frequence update @@ -277,19 +332,89 @@ def move(self, gameMap): self.comete = self.comete[1:9] self.comete.append((self.x, self.y)) + self.changeEvolution() self.updateAlti() # on change le niveau de l'avion si on est en evolution - if self.speedIAS != self.selectedIAS: # s'il faut accélérer ou ralentir - # on change la vitesse par un pas d'accél/deccel défaut - self.speedIAS += (self.selectedIAS - self.speedIAS)/abs(self.selectedIAS - self.speedIAS) * acceldefault + self.changeSpeed() + self.computeSpeed() - self.speedGS = self.speedIAS + self.altitude / 200 self.speedPx = self.speedGS / gameMap['mapScale'] * heureEnRefresh # on convertit les kt en px/refresh # mouvement self.x += self.speedPx * math.cos(self.headingRad) self.y += self.speedPx * math.sin(self.headingRad) + def computeSpeed(self): + + """ + Fait évoluer la vitesse si on a besoin + :return: + """ + + if not self.machMode: + if self.speedIAS != self.selectedIAS: # s'il faut accélérer ou ralentir + # on change la vitesse par un pas d'accél/deccel défaut + if abs(self.selectedIAS - self.speedIAS) <= acceldefault: + self.speedIAS = self.selectedIAS + else: + self.speedIAS += (self.selectedIAS - self.speedIAS)/abs(self.selectedIAS - self.speedIAS) * acceldefault + + self.mach = vitesses.IAS_to_Mach(self.speedIAS, self.altitude) + else: + if self.mach != self.selectedMach: + # on change la vitesse par un pas d'accél/deccel défaut + if abs(self.selectedMach - self.mach) <= acceldefaultMach: + self.mach = self.selectedMach + else: + self.mach += (self.selectedMach - self.mach) / abs(self.selectedMach - self.mach) * acceldefaultMach + + self.speedIAS = vitesses.TAS_to_IAS(self.speedTAS, self.altitude) + + self.speedTAS = vitesses.mach_to_TAS(self.mach, self.altitude) + self.speedGS = self.speedTAS + + def changeSpeed(self) -> None: + """ + Sélectionne la vitesse en fonction de la phase de vol + :return: + """ + + if self.forcedSpeed: + return None + + self.machMode = False + if self.altitude >= altitude_conversion: # au-dessus de l'alti de conversion tout se fera en mach + + self.machMode = True + + if self.evolution == 0: + self.selectedMach = self.perfos['cruiseMach'] + + elif self.evolution < 0: + self.selectedMach = self.perfos['descentMach'] + + else: + self.selectedMach = self.perfos['climbMach'] + + elif self.altitude >= altitude_cruise: # en-dessous de l'alti de conversion tout se fera en IAS + # ici, on regarde si on est en montée/descente initiale ou alors, si on approche du niveau de croisière + + if self.evolution <= 0: # on prend les perfs de descente pour la croisière (pas trouvé de perfo IAS cruise) + self.selectedIAS = self.perfos['descentIAS'] + + else: + self.selectedIAS = self.perfos['climbIAS'] + + else: + # ici, on est dans les basses couches donc on prend les perfos en basse alti + + if self.evolution <= 0: + self.selectedIAS = self.perfos['descentIAS'] + # les perfos sont les mêmes pendant la majorité de la descente d'où le manque de 240 + + else: + self.selectedIAS = self.perfos['initialClimbIAS'] + def calculeEstimate(self, points: dict, pointVoulu: str) -> float: """ Calcule le temps à un point sur notre route. Pour avoir l'estimée, il faudra rajouter l'heure courante diff --git a/Python/player.py b/Python/player.py index 5cf8416..8fc7220 100644 --- a/Python/player.py +++ b/Python/player.py @@ -10,6 +10,7 @@ from Python.valeurs_config import * import Python.geometry as geometry import Python.interface as interface +import Python.horloge as horloge def positionAffichage(x: int, y: int, zoom: float, scrollX: float, scrollY: float): @@ -154,7 +155,7 @@ def drawSep(self, window, zoom): img = font.render(lettre, True, (170, 170, 255)) window.blit(img, coords) - def draw(self, win, zoom, scroll, vecteurs, vecteurSetting, points): + def draw(self, win, zoom, scroll, vecteurs, vecteurSetting, points, temps): # updates @@ -165,7 +166,7 @@ def draw(self, win, zoom, scroll, vecteurs, vecteurSetting, points): self.bouton.set_position((self.affX, self.affY)) if self.drawRouteBool: - self.drawRoute(points, win, zoom, scroll) + self.drawRoute(points, win, zoom, scroll, temps) if self.pointDessinDirect: self.drawDirect(points, self.pointDessinDirect, win, zoom, scroll) @@ -323,13 +324,14 @@ def drawEstimatedRoute(self, points, temps, color, win, zoom, scroll): distance -= legDistance # on enlève la distance de la branche parcourue à la distance à parcourir pointUn = pointDeux # on passe au prochain point - def drawRoute(self, points, win, zoom, scroll): + def drawRoute(self, points: dict, win, zoom: float, scroll: tuple[float, float] | list[float, float], temps: float): """ Dessine la route future de l'avion avec les estimées en temps :param points: la liste des points récupérer les coords :param win: l'écran pygame :param zoom: le niveau de zoom :param scroll: le scroll format [x, y] + :param temps: l'heure de la partie :return: """ @@ -339,16 +341,23 @@ def drawRoute(self, points, win, zoom, scroll): point1 = points[route[route.index(nextPoint) - 1]['name']][:2] point2 = points[nextPoint['name']][:2] pointUn = geometry.calculateShortestPoint(point1, point2, [self.papa.x, self.papa.y]) - # TODO heure de passage pour chaque point route = route[route.index(nextPoint):] # on ne considère que la route devant l'avion + font = pygame.font.SysFont('arial', 15) + for point in route: + pointDeux = [points[point['name']][0], points[point['name']][1]] + temps += geometry.calculateDistance(pointUn[0], pointUn[1], pointDeux[0], pointDeux[1]) / self.papa.speedPx * radarRefresh pygame.draw.line(win, (46, 80, 174), (pointUn[0] * zoom + scroll[0], pointUn[1] * zoom + scroll[1]), (pointDeux[0] * zoom + scroll[0], pointDeux[1] * zoom + scroll[1]), 3) + img = font.render(horloge.affichageHeure(temps), True, (170, 170, 255)) + coords = (pointDeux[0] * zoom - 5 + scroll[0], pointDeux[1] * zoom - 20 + scroll[1]) + win.blit(img, coords) + pointUn = pointDeux # on passe au prochain point def checkClicked(self, event) -> bool: @@ -420,15 +429,11 @@ def checkEvent(self, event, pilote, conflitBool): if event.mouse_button == 3 and pilote: return self.Id, 'EtatFreq', None - if event.mouse_button == 1 and pilote: - return 'menuPIL' - - elif event.mouse_button == 1: + elif event.mouse_button == 1 and not pilote: return 'menuATC' elif event.mouse_button == 3 and self.papa.integreOrganique and ( - self.etiquette.indicatif.get_object_ids()[1] in ['@etiquetteBold', '@etiquetteBoldBlue'] - or pilote): # clic droit + self.etiquette.indicatif.get_object_ids()[1] in ['@etiquetteBold', '@etiquetteBoldBlue']): self.unBold() @@ -439,28 +444,36 @@ def checkEvent(self, event, pilote, conflitBool): elif event.ui_element == self.etiquette.XPT and event.mouse_button == 3 and not pilote: self.drawRouteBool = not self.drawRouteBool - elif event.ui_element == self.etiquette.XPT and event.mouse_button == 1 and not pilote: + elif event.ui_element == self.etiquette.XPT and event.mouse_button == 1: return 'XPT' - elif event.ui_element == self.etiquette.DCT and event.mouse_button == 1 and not pilote: + elif event.ui_element == self.etiquette.DCT and event.mouse_button == 1: return 'DCT' - elif event.ui_element == self.etiquette.DCT and event.mouse_button == 2 and not pilote: - return 'HDG' + elif event.ui_element == self.etiquette.DCT and event.mouse_button == 2: + return 'C_HDG' - elif event.ui_element == self.etiquette.XFL and event.mouse_button == 1 and not pilote: + elif event.ui_element == self.etiquette.XFL and event.mouse_button == 1: return 'XFL' - elif event.ui_element == self.etiquette.PFL and event.mouse_button == 1 and not pilote: + elif event.ui_element == self.etiquette.PFL and event.mouse_button == 1: return 'PFL' - elif event.ui_element == self.etiquette.CFL and event.mouse_button == 1 and not pilote: + elif event.ui_element == self.etiquette.CFL and event.mouse_button == 1: return 'CFL' - elif event.ui_element == self.etiquette.clearedSpeed and event.mouse_button == 1 and not pilote: + elif event.ui_element == self.etiquette.AFL and event.mouse_button == 1 and pilote: + return 'FL' + + elif (event.ui_element == self.etiquette.clearedSpeed and event.mouse_button == 1 and + (self.papa.machMode or self.papa.clearedMach) and not self.papa.clearedIAS): + + return 'C_Mach' + + elif event.ui_element == self.etiquette.clearedSpeed and event.mouse_button == 1: return 'C_IAS' - elif event.ui_element == self.etiquette.rate and event.mouse_button == 1 and not pilote: + elif event.ui_element == self.etiquette.rate and event.mouse_button == 1: return 'C_Rate' def checkEtiquetteOnHover(self): diff --git a/Python/server.py b/Python/server.py index 91cb6f8..6ff2ad5 100644 --- a/Python/server.py +++ b/Python/server.py @@ -14,8 +14,10 @@ from pathlib import Path # Import fichiers +import Python.horloge as horloge from Python.network import MCAST_GRP, MCAST_PORT, port from Python.paquets_avion import * +import Python.server_def as server_def dossierXML = Path("").absolute() / 'XML' @@ -214,35 +216,51 @@ tree = ET.parse(dossierXML / aircraftFile) root = tree.getroot() -for aircraft in root: +for aircraft in root.find('aircrafts'): + + aircraftPerf = {} + for XMLpoint in aircraft: + try: + XMLpointValue = float(XMLpoint.text) + except: + XMLpointValue = XMLpoint.text + aircraftPerf.update({XMLpoint.tag: XMLpointValue}) + aircraftType.update( - {aircraft.attrib['name']: {'IAS': int(aircraft.find('speed').text), - 'plafond': int(aircraft.find('ceiling').text), - 'ROC': int(aircraft.find('ROC').text), - 'ROD': int(aircraft.find('ROD').text)}}) + {aircraft.attrib['name']: aircraftPerf}) + +callsignList = {} +for callsign in root.find('callsigns'): + callsignList.update({callsign.attrib['indicatif']: callsign.text}) +gameMap.update({'callsigns': callsignList}) planeId = 0 -try: # on essaye de charger une simu, si elle existe - tree = ET.parse(dossierXML / simu) +simuTree = None - heure = tree.find('heure').text - heure = int(heure[0:2]) * 3600 + int(heure[2:]) * 60 +try: + simuTree = ET.parse(dossierXML / simu).getroot() + + heure = simuTree.find('heure').text + heure = int(heure[0:2]) * 3600 + int(heure[2:4]) * 60 + int(heure[4:]) + + game = Game(heure) avionSpawnListe = [] - for avion in tree.find('avions'): + avionsXML = simuTree.find('avions') + for avion in avionsXML: avionDict = {} for XMLpoint in avion: try: - XMLpointValue = int(XMLpoint.text) + XMLpointValue = float(XMLpoint.text) except: XMLpointValue = XMLpoint.text avionDict.update({XMLpoint.tag: XMLpointValue}) heureSpawn = avion.attrib['heure'] - heureSpawn = int(heureSpawn[0:2]) * 3600 + int(heureSpawn[2:]) * 60 + heureSpawn = int(heureSpawn[0:2]) * 3600 + int(heureSpawn[2:4]) * 60 + int(heureSpawn[4:]) for route in gameMap['routes']: if route == avionDict['route']: @@ -266,51 +284,16 @@ planeId += 1 avionSpawnListe.append((heureSpawn, avionPack)) -except: # sinon, on demande juste l'heure de début +except: heure = input('Heure de début de simu, format: hhmm') heure = int(heure[0:2]) * 3600 + int(heure[2:]) * 60 avionSpawnListe = [] - -game = Game(heure) - -# XML écriture - -SimuTree = ET.Element('simu') -heureXML = ET.SubElement(SimuTree, 'heure') -heureXML.text = '1002' -avionsXML = ET.SubElement(SimuTree, 'avions') - - -def generateAvionXML(parent, heureEcriture, indicatifEcriture, aircraftEcriture, routeEcriture, altitudeEcriture, xEcriture=None, yEcriture=None, headingEcriture=None, PFLEcriture=None): - - avionXML = ET.SubElement(parent, 'avion') - avionXML.set('heure', str(heureEcriture)) - - node = ET.SubElement(avionXML, 'indicatif') - node.text = str(indicatifEcriture) - node = ET.SubElement(avionXML, 'aircraft') - node.text = str(aircraftEcriture) - node = ET.SubElement(avionXML, 'route') - node.text = str(routeEcriture) - node = ET.SubElement(avionXML, 'altitude') - node.text = str(altitudeEcriture) - node = ET.SubElement(avionXML, 'arrival') - node.text = str(arrival) - - if xEcriture is not None: - node = ET.SubElement(avionXML, 'x') - node.text = str(xEcriture) - node = ET.SubElement(avionXML, 'y') - node.text = str(yEcriture) - - if headingEcriture is not None: - node = ET.SubElement(avionXML, 'heading') - node.text = str(headingEcriture) - - if PFLEcriture is not None: - node = ET.SubElement(avionXML, 'PFL') - node.text = str(PFLEcriture) + simuTree = ET.Element('simu') + game = Game(heure) + heureXML = ET.SubElement(simuTree, 'heure') + heureXML.text = horloge.heureXML(game.heure) + avionsXML = ET.SubElement(simuTree, 'avions') def threaded_client(conn, caca): @@ -379,118 +362,161 @@ def threaded_ping_responder(): requests.append(inReq) for reqSublist in requests: for req in reqSublist: # format requêtes : [Id avion, type requête, data] - if req[1] == 'Add': - req[2].Id = planeId - dictAvion.update({planeId: req[2]}) + reqType = req[1] + reqId = req[0] + reqContent = None + + if len(req) == 3: + reqContent = req[2] + print(req) + + if reqType == 'Add': + + reqContent.Id = planeId + dictAvion.update({planeId: reqContent}) planeId += 1 + if mode_ecriture: + heure = horloge.heureXML(game.heure) + server_def.generateAvionXML(avionsXML, reqContent, heure) + + elif reqType == 'DelayedAdd': - heures = str(round(game.heure // 3600)) - if len(heures) == 1: - heures = '0' + heures - minutes = str(round(game.heure % 3600 // 60)) - if len(minutes) == 1: - minutes = '0' + minutes - - generateAvionXML(avionsXML, - heures + minutes, - req[2].indicatif, - req[2].aircraft, - req[2].route['name'], - req[2].altitude, - xEcriture=req[2].x, - yEcriture=req[2].y, - PFLEcriture=req[2].PFL) - elif req[1] == 'DelayedAdd': - - avionSpawnListe.append((game.heure + req[2][0], req[2][1])) + reqContent[1].Id = planeId + avionSpawnListe.append((game.heure + reqContent[0], reqContent[1])) + planeId += 1 if mode_ecriture: + heure = horloge.heureXML(game.heure) + server_def.generateAvionXML(avionsXML, reqContent, heure) - heures = str(round(game.heure + req[2][0] // 3600)) - if len(heures) == 1: - heures = '0' + heures - minutes = str(round(game.heure + req[2][0] % 3600 // 60)) - if len(minutes) == 1: - minutes = '0' + minutes - - generateAvionXML(avionsXML, - heures + minutes, - req[2][1].indicatif, - req[2][1].aircraft, - req[2][1].route['name'], - req[2][1].altitude, - xEcriture=req[2][1].x, - yEcriture=req[2][1].y, - PFLEcriture=req[2][1].PFL) - - elif req[1] == 'Remove': - dictAvion.pop(req[0]) - elif req[1] == 'Altitude': - dictAvion[req[0]].selectedAlti = req[2] - elif req[1] == 'PFL': - dictAvion[req[0]].PFL = req[2] - dictAvion[req[0]].changeXFL(gameMap) - elif req[1] == 'CFL': - dictAvion[req[0]].CFL = req[2] - elif req[1] == 'C_IAS': - if len(req) == 3: - dictAvion[req[0]].clearedIAS = req[2] + elif reqType == 'Remove': + dictAvion.pop(reqId) + + elif reqType == 'FL': + dictAvion[reqId].selectedAlti = reqContent * 100 + + elif reqType == 'PFL': + dictAvion[reqId].PFL = reqContent + dictAvion[reqId].changeXFL(gameMap) + + elif reqType == 'CFL': + dictAvion[reqId].CFL = reqContent + + elif reqType == 'C_IAS': + if reqContent: + dictAvion[reqId].clearedIAS = reqContent + dictAvion[reqId].clearedMach = None else: - dictAvion[req[0]].clearedIAS = None - elif req[1] == 'C_Rate': - if len(req) == 3: - dictAvion[req[0]].clearedRate = req[2] + dictAvion[reqId].clearedMach = None + dictAvion[reqId].clearedIAS = None + + elif reqType == 'C_Mach': + if reqContent: + dictAvion[reqId].clearedMach = reqContent + dictAvion[reqId].clearedIAS = None + else: + dictAvion[reqId].clearedMach = None + dictAvion[reqId].clearedIAS = None + + elif reqType == 'C_Rate': + if reqContent: + dictAvion[reqId].clearedRate = reqContent else: - dictAvion[req[0]].clearedRate = None - elif req[1] == 'XFL': - dictAvion[req[0]].XFL = req[2] - dictAvion[req[0]].changeSortieSecteur(gameMap) - elif req[1] == 'XPT': - dictAvion[req[0]].XPT = req[2] - elif req[1] == 'HDG': - dictAvion[req[0]].clearedHeading = req[2] - elif req[1] == 'Heading': - dictAvion[req[0]].headingMode = True - dictAvion[req[0]].selectedHeading = req[2] - elif req[1] == 'IAS': - dictAvion[req[0]].selectedIAS = req[2] - elif req[1] == 'DCT': - dictAvion[req[0]].clearedHeading = None - dictAvion[req[0]].DCT = req[2] - elif req[1] == 'Warning': - dictAvion[req[0]].warning = not dictAvion[req[0]].warning - elif req[1] == 'Integre': - dictAvion[req[0]].integreOrganique = True - elif req[1] == 'Direct': - dictAvion[req[0]].headingMode = False - for point in dictAvion[req[0]].route['points']: - if point['name'] == req[2]: - dictAvion[req[0]].nextPoint = point + dictAvion[reqId].clearedRate = None + + elif reqType == 'Rate': + if reqContent: + dictAvion[reqId].forcedEvo = True + signe = (dictAvion[reqId].evolution * reqContent) / abs(dictAvion[reqId].evolution * reqContent) + dictAvion[reqId].evolution = signe * reqContent + else: + dictAvion[reqId].forcedEvo = False + + elif reqType == 'XFL': + dictAvion[reqId].XFL = reqContent + dictAvion[reqId].changeSortieSecteur(gameMap) + + elif reqType == 'XPT': + dictAvion[reqId].XPT = reqContent + + elif reqType == 'C_HDG': + dictAvion[reqId].clearedHeading = reqContent + + elif reqType == 'HDG': + if type(reqContent) in [float, int]: + newHeading = reqContent + elif reqContent[0] == '-': + newHeading = dictAvion[reqId].selectedHeading - int(reqContent[1:]) + elif reqContent[0] == '+': + newHeading = dictAvion[reqId].selectedHeading + int(reqContent[1:]) + dictAvion[reqId].headingMode = True + dictAvion[reqId].selectedHeading = reqContent + + elif reqType == 'IAS': + if reqContent: + dictAvion[reqId].forcedSpeed = True + dictAvion[reqId].machMode = False + dictAvion[reqId].selectedIAS = reqContent * 10 + else: + dictAvion[reqId].forcedSpeed = False + + elif reqType == 'Mach': + if reqContent: + dictAvion[reqId].forcedSpeed = True + dictAvion[reqId].machMode = True + dictAvion[reqId].selectedMach = float(reqContent) + else: + dictAvion[reqId].forcedSpeed = False + + elif reqType == 'DCT': + dictAvion[reqId].clearedHeading = None + dictAvion[reqId].DCT = reqContent + + elif reqType == 'Warning': + dictAvion[reqId].warning = not dictAvion[reqId].warning + + elif reqType == 'Integre': + dictAvion[reqId].integreOrganique = True + + elif reqType == 'Direct': + dictAvion[reqId].headingMode = False + for point in dictAvion[reqId].route['points']: + if point['name'] == reqContent: + dictAvion[reqId].nextPoint = point break - elif req[1] == 'Route': - dictAvion[req[0]].nextRoute = req[2] - dictAvion[req[0]].changeRoute(gameMap) - elif req[1] == 'HighlightBouton': - if req[2] in dictAvion[req[0]].boutonsHighlight: # si le bouton est déjà highlight alors: - dictAvion[req[0]].boutonsHighlight.remove(req[2]) + + elif reqType == 'Route': + dictAvion[reqId].nextRoute = reqContent + dictAvion[reqId].changeRoute(gameMap) + + elif reqType == 'HighlightBouton': + if reqContent in dictAvion[reqId].boutonsHighlight: # si le bouton est déjà highlight alors: + dictAvion[reqId].boutonsHighlight.remove(reqContent) else: - dictAvion[req[0]].boutonsHighlight.append(req[2]) - elif req[1] == 'Montrer': - dictAvion[req[0]].montrer = not dictAvion[req[0]].montrer - elif req[1] == 'EtatFreq': - dictAvion[req[0]].updateEtatFreq(req[2]) - elif req[1] == 'FL?': - dictAvion[req[0]].FLInterro = not dictAvion[req[0]].FLInterro - elif req[1] == 'Pause': + dictAvion[reqId].boutonsHighlight.append(reqContent) + + elif reqType == 'Montrer': + dictAvion[reqId].montrer = not dictAvion[reqId].montrer + + elif reqType == 'EtatFreq': + dictAvion[reqId].updateEtatFreq(reqContent) + + elif reqType == 'FL?': + dictAvion[reqId].FLInterro = not dictAvion[reqId].FLInterro + + elif reqType == 'Pause': game.paused = not game.paused - elif req[1] == 'Faster': + + elif reqType == 'Faster': accelerationTemporelle += 0.5 - elif req[1] == 'Slower': + + elif reqType == 'Slower': if accelerationTemporelle > 0.5: accelerationTemporelle -= 0.5 - elif req[1] == 'Save' and mode_ecriture: - xmlstr = minidom.parseString(ET.tostring(SimuTree)).toprettyxml(indent=" ") + + elif reqType == 'Save' and mode_ecriture: + xmlstr = server_def.prettyPrint(minidom.parseString(ET.tostring(simuTree))) with open("XML/simu.xml", "w") as f: f.write(xmlstr) diff --git a/Python/server_browser.py b/Python/server_browser.py index 6dd4201..ea06345 100644 --- a/Python/server_browser.py +++ b/Python/server_browser.py @@ -5,6 +5,7 @@ # Imports fichiers from Python.network import MCAST_GRP, MCAST_PORT + def serverBrowser(): """Lan scan et server browser en une fonction renvoie une adresse IP en str""" @@ -26,7 +27,11 @@ def serverBrowser(): serverList.update({data: address}) sock.close() if len(serverList) <= 1: - return list(serverList.values())[0][0] + try: + return list(serverList.values())[0][0] + except IndexError: + print('Aucun serveur trouvé') + return None print('Liste des serveurs actifs:') for i in range(len(list(serverList.keys()))): print(i, ' - ', list(serverList.keys())[i], '\n') diff --git a/Python/server_def.py b/Python/server_def.py index 88c01fb..204949b 100644 --- a/Python/server_def.py +++ b/Python/server_def.py @@ -2,6 +2,9 @@ # Native imports import math +# Module imports +import xml.etree.ElementTree as ET + # Imports fichiers import Python.geometry as geometry from Python.valeurs_config import * @@ -39,3 +42,50 @@ def STCA(avion1, avion2, carte) -> bool: return False +def generateAvionXML(parentNode, avion, heureXML): + + """ + Transforme un avion packet en un element XML + :param parentNode: La node XML dans laquelle on va inscrire notre nouvel avion + :param avion: L'avion Packet qu'il faut transformer en XML + :param heureXML: + :return: + """ + + avionXML = ET.SubElement(parentNode, 'avion') + avionXML.set('heure', str(heureXML)) + + node = ET.SubElement(avionXML, 'indicatif') + node.text = str(avion.indicatif) + node = ET.SubElement(avionXML, 'aircraft') + node.text = str(avion.aircraft) + node = ET.SubElement(avionXML, 'route') + node.text = str(avion.route['name']) + node = ET.SubElement(avionXML, 'altitude') + node.text = str(avion.altitude) + node = ET.SubElement(avionXML, 'arrival') + node.text = str(avion.arrival) + + node = ET.SubElement(avionXML, 'x') + node.text = str(avion.x) + node = ET.SubElement(avionXML, 'y') + node.text = str(avion.y) + + node = ET.SubElement(avionXML, 'PFL') + node.text = str(avion.PFL) + + +def prettyPrint(docXML): + """ + Rend le XML joli à regarder + :param docXML: le doc XML à transformer + :return: + """ + + XMLstring = '' + + for line in docXML.toprettyxml().split('\n'): + if not line.strip() == '': + XMLstring += line + '\n' + + return XMLstring diff --git a/Python/valeurs_config.py b/Python/valeurs_config.py index b6a6cf1..fc219fb 100644 --- a/Python/valeurs_config.py +++ b/Python/valeurs_config.py @@ -3,7 +3,8 @@ nmToFeet = 6076 secteurDefault = 'RU' # secteur par défault, si le programme n'arrive pas à trouver un secteur de sortie altiDefault = 30000 # alti en pied par défault, si on ne rentre pas d'alti pour spawn un avion -acceldefault = 3 # accélération/decelération de kt par refresh +acceldefault = 3 # accélération/decelération en kt par refresh +acceldefaultMach = 0.003 # accélération/decelération en point de mach par refresh turnRateDefault = 10 # turnrate/refresh par défault liste_etat_freq = ['previousFreq', 'previousShoot', 'inFreq', 'nextCoord', 'nextShoot', 'nextFreq'] valeurCoord = 8 # combien de minute avant la sortie la coord passe @@ -11,3 +12,8 @@ dragDelay = 150 # on utilise cette valeur seuil pour déterminer si on doit cliquer sur un bouton ou drag l'etiquette offsettEtiquetteDefault = 30 # de combien les etiquettes sont décalées en px à quand on les dessine la 1ere fois temps_disparition_menus = 300 # en combien de milli sec les menus disparaissent après ne plus être survolé +altitude_conversion = 27900 +altitude_cruise = 25000 + +delaiScreen = 6900 +replayMode = False diff --git a/Python/vitesses.py b/Python/vitesses.py new file mode 100644 index 0000000..504194b --- /dev/null +++ b/Python/vitesses.py @@ -0,0 +1,103 @@ + +# Native imports +from math import sqrt + +ms_per_kt = 0.51444 +feet_per_metre = 3.28 + +# Some useful constants + +g = 9.81 # Acceleration due to gravity +R = 287 # Specific gas constant for air +L = 0.0065 # Lapse rate in K/m +T0 = 288.15 # ISA sea level temp in K +p0 = 101325 # ISA sea level pressure in Pa +k = 1.4 # k is a shorthand for Gamma, the ratio of specific heats for air +lss0 = sqrt(k*R*T0) # ISA sea level speed sound + + +def compressible_pitot(M): + """ + Renvoies un delta de pression en fonction d'un mach + :param M: + :return: + """ + + return (M*M*(k-1)/2 + 1) ** (k/(k-1)) - 1 + + +def pitot_to_Mach(d): + """ + Retourne un machNumber en fonction d'un delta de pression + :param d: + :return: + """ + return sqrt(((d+1)**((k-1)/k) - 1)*2/(k-1)) + + +def temperature(h): + """ + Température en fonction de l'alti (bloquée à -56C) + :param h: + :return: + """ + T = T0 - h*L + if T <= 217.15: + T = 217.15 + return T + + +def lss(h): + """ + Vitesse du son en fonction de l'alti + :param h: + :return: + """ + return sqrt(k*R*temperature(h)) + + +def pressure(h): + """ + Pression en fonction de l'alti + :param h: + :return: + """ + return p0 * (temperature(h) / T0) ** (g / L / R) + + +def IAS_to_Mach(IAS, alti): + + """ + Converti une IAS en Mach + :param IAS: + :param alti: + :return: + """ + + alti = alti / feet_per_metre + IAS = IAS * ms_per_kt + ps = pressure(alti) + pd = compressible_pitot(IAS/lss0) * p0 + + return pitot_to_Mach(pd / ps) + + +def mach_to_TAS(M, alti): + """ + Converti un point de mach en TAS + :param M: + :param alti: + :return: + """ + alti = alti / feet_per_metre + return lss(alti) * M / ms_per_kt + + +def TAS_to_IAS(TAS, alti): + """ + Convertit une TAS en IAS, de façon très grossière + :return: + """ + + return TAS - alti / 185 +