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
+