fletchtime 0.2.0__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. {fletchtime-0.2.0 → fletchtime-0.2.2}/.gitignore +4 -0
  2. {fletchtime-0.2.0/src/fletchtime.egg-info → fletchtime-0.2.2}/PKG-INFO +29 -1
  3. {fletchtime-0.2.0 → fletchtime-0.2.2}/README.md +28 -0
  4. {fletchtime-0.2.0 → fletchtime-0.2.2}/docs/architecture.md +55 -6
  5. {fletchtime-0.2.0 → fletchtime-0.2.2}/docs/index.md +11 -0
  6. {fletchtime-0.2.0 → fletchtime-0.2.2}/docs/roadmap.md +54 -35
  7. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/__main__.py +28 -8
  8. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/_version.py +3 -3
  9. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/engine/engine.py +22 -1
  10. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/gui.py +216 -11
  11. fletchtime-0.2.2/src/fletchtime/logging_setup.py +56 -0
  12. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/server/config_store.py +37 -0
  13. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/server/match_server.py +37 -0
  14. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/server/ws_server.py +8 -2
  15. {fletchtime-0.2.0 → fletchtime-0.2.2/src/fletchtime.egg-info}/PKG-INFO +29 -1
  16. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime.egg-info/SOURCES.txt +1 -0
  17. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime.egg-info/scm_file_list.json +1 -0
  18. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime.egg-info/scm_version.json +2 -2
  19. {fletchtime-0.2.0 → fletchtime-0.2.2}/tests/test_engine.py +15 -2
  20. {fletchtime-0.2.0 → fletchtime-0.2.2}/tests/test_match_server.py +116 -0
  21. {fletchtime-0.2.0 → fletchtime-0.2.2}/.github/workflows/build.yml +0 -0
  22. {fletchtime-0.2.0 → fletchtime-0.2.2}/.github/workflows/docs.yml +0 -0
  23. {fletchtime-0.2.0 → fletchtime-0.2.2}/.github/workflows/test.yml +0 -0
  24. {fletchtime-0.2.0 → fletchtime-0.2.2}/CONTRIBUTING.md +0 -0
  25. {fletchtime-0.2.0 → fletchtime-0.2.2}/LICENSE +0 -0
  26. {fletchtime-0.2.0 → fletchtime-0.2.2}/config/app.toml +0 -0
  27. {fletchtime-0.2.0 → fletchtime-0.2.2}/config/flint.toml +0 -0
  28. {fletchtime-0.2.0 → fletchtime-0.2.2}/config/indoor.toml +0 -0
  29. {fletchtime-0.2.0 → fletchtime-0.2.2}/demo.py +0 -0
  30. {fletchtime-0.2.0 → fletchtime-0.2.2}/docs/_static/logo.svg +0 -0
  31. {fletchtime-0.2.0 → fletchtime-0.2.2}/docs/api-reference.md +0 -0
  32. {fletchtime-0.2.0 → fletchtime-0.2.2}/docs/conf.py +0 -0
  33. {fletchtime-0.2.0 → fletchtime-0.2.2}/docs/dev-guide/index.md +0 -0
  34. {fletchtime-0.2.0 → fletchtime-0.2.2}/docs/requirements.txt +0 -0
  35. {fletchtime-0.2.0 → fletchtime-0.2.2}/docs/specifications.md +0 -0
  36. {fletchtime-0.2.0 → fletchtime-0.2.2}/docs/user-guide/index.md +0 -0
  37. {fletchtime-0.2.0 → fletchtime-0.2.2}/fletchtime.spec +0 -0
  38. {fletchtime-0.2.0 → fletchtime-0.2.2}/pyproject.toml +0 -0
  39. {fletchtime-0.2.0 → fletchtime-0.2.2}/run_server.py +0 -0
  40. {fletchtime-0.2.0 → fletchtime-0.2.2}/run_tests.py +0 -0
  41. {fletchtime-0.2.0 → fletchtime-0.2.2}/scripts/generate_classic_sounds.py +0 -0
  42. {fletchtime-0.2.0 → fletchtime-0.2.2}/setup.cfg +0 -0
  43. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/__init__.py +0 -0
  44. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/engine/__init__.py +0 -0
  45. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/engine/models.py +0 -0
  46. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/engine/modes/__init__.py +0 -0
  47. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/engine/modes/base.py +0 -0
  48. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/engine/modes/flint.py +0 -0
  49. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/engine/modes/indoor.py +0 -0
  50. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/engine/sequence.py +0 -0
  51. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/engine/turn_modes.py +0 -0
  52. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/runtime.py +0 -0
  53. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/server/__init__.py +0 -0
  54. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/server/http_static.py +0 -0
  55. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/__init__.py +0 -0
  56. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/banners/README.md +0 -0
  57. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/club/README.md +0 -0
  58. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/gui/theme.json +0 -0
  59. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/README.md +0 -0
  60. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/countdown_tick.wav +0 -0
  61. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/emergency_end.wav +0 -0
  62. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/emergency_start.wav +0 -0
  63. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/end_of_match.wav +0 -0
  64. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/end_of_volee.wav +0 -0
  65. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/pause_end.wav +0 -0
  66. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/pause_start.wav +0 -0
  67. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/prep_start.wav +0 -0
  68. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/shoot_start.wav +0 -0
  69. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/warning_orange.wav +0 -0
  70. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/targets/flint_20cm_4spot.jpg +0 -0
  71. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/targets/flint_35cm_1spot.jpg +0 -0
  72. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/targets/indoor_compound.jpg +0 -0
  73. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/targets/indoor_recurve.jpg +0 -0
  74. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/config.html +0 -0
  75. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/control.html +0 -0
  76. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/display.html +0 -0
  77. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/i18n.js +0 -0
  78. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/index.html +0 -0
  79. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/logo.svg +0 -0
  80. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/manual.html +0 -0
  81. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/screenshots/config.png +0 -0
  82. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/screenshots/control.png +0 -0
  83. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/screenshots/display.png +0 -0
  84. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/screenshots/index.png +0 -0
  85. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/theme.js +0 -0
  86. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime.egg-info/dependency_links.txt +0 -0
  87. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime.egg-info/entry_points.txt +0 -0
  88. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime.egg-info/requires.txt +0 -0
  89. {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime.egg-info/top_level.txt +0 -0
  90. {fletchtime-0.2.0 → fletchtime-0.2.2}/tests/test_config_store.py +0 -0
  91. {fletchtime-0.2.0 → fletchtime-0.2.2}/tests/test_flint_mode.py +0 -0
  92. {fletchtime-0.2.0 → fletchtime-0.2.2}/tests/test_indoor_mode.py +0 -0
  93. {fletchtime-0.2.0 → fletchtime-0.2.2}/tests/test_runtime.py +0 -0
  94. {fletchtime-0.2.0 → fletchtime-0.2.2}/web/assets/banners/README.md +0 -0
  95. {fletchtime-0.2.0 → fletchtime-0.2.2}/web/assets/sounds/packs/README.md +0 -0
@@ -27,6 +27,10 @@ docs/_build/
27
27
  # vrai secret et ne doit jamais être versionné.
28
28
  config/auth.toml
29
29
 
30
+ # Préférences de la fenêtre graphique (thème...) -- propres à la machine/
31
+ # personne qui lance la fenêtre, pas un réglage de club à partager.
32
+ config/gui.toml
33
+
30
34
  # Instantané du match en cours (récupération après crash) -- propre à une
31
35
  # session de match en cours, pas un réglage du club à partager/versionner.
32
36
  config/match_state.json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fletchtime
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Chronométrage de compétitions d'archerie FFTL (Indoor, Flint) -- serveur + interfaces web incluses
5
5
  License: GPL-3.0-or-later
6
6
  Requires-Python: >=3.11
@@ -21,6 +21,11 @@ Dynamic: license-file
21
21
 
22
22
  <img src="https://mrfanghodev.github.io/fletchtime/_static/logo.svg" width="80" height="80" alt="FletchTime logo">
23
23
 
24
+ [![Python](https://img.shields.io/badge/python-version-from-pep-621-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2FMrFanghoDev%2Ffletchtime%2Fmaster%2Fpyproject.toml)](pyproject.toml)
25
+ [![Licence](https://img.shields.io/github/license/MrFanghoDev/fletchtime)](LICENSE)
26
+ [![Tests](https://github.com/MrFanghoDev/fletchtime/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/MrFanghoDev/fletchtime/actions/workflows/test.yml)
27
+ [![PyPI](https://img.shields.io/pypi/v/fletchtime)](https://pypi.org/project/fletchtime/)
28
+
24
29
  # FletchTime
25
30
 
26
31
  Logiciel open source de gestion du temps pour compétitions d'archerie FFTL
@@ -122,6 +127,29 @@ from fletchtime.engine import IndoorMode, IndoorConfig, MatchEngine
122
127
  engine = MatchEngine(IndoorMode(IndoorConfig()))
123
128
  ```
124
129
 
130
+ ## Plusieurs salles de compétition sur un même PC
131
+
132
+ Chaque installation de FletchTime (dossier `pip install`, ou dossier de
133
+ l'exécutable autoporteur) fait tourner **une seule salle** -- son propre
134
+ match, sa propre urgence, sa propre config. Pour plusieurs salles en
135
+ parallèle sur le même PC :
136
+
137
+ 1. **Copie le dossier FletchTime une fois par salle** (ex. `salle-A/`,
138
+ `salle-B/`) -- ça isole automatiquement `config/` (temps de tir,
139
+ urgence...), les journaux, et l'instantané de récupération après
140
+ plantage : chaque copie a sa propre urgence, indépendante des autres.
141
+ 2. Dans la fenêtre de chaque copie, section **Ports (HTTP/WS)** : donne
142
+ des ports différents à chaque salle (ex. salle A garde `8000`/`8765`
143
+ par défaut, salle B passe à `8001`/`8766`) -- **Appliquer** redémarre
144
+ le serveur automatiquement si besoin.
145
+ 3. Lance chaque copie séparément. Les écrans de chaque salle se
146
+ connectent à l'adresse (IP + port) affichée dans la fenêtre de leur
147
+ salle respective.
148
+
149
+ **Windows** : le pare-feu autorise généralement par chemin d'exécutable --
150
+ avec des copies de dossier séparées, prévoir de valider l'alerte une fois
151
+ par copie au premier lancement.
152
+
125
153
  ## Contribuer
126
154
 
127
155
  Envie de proposer un correctif, une idée, ou juste signaler un bug ? Voir
@@ -1,5 +1,10 @@
1
1
  <img src="https://mrfanghodev.github.io/fletchtime/_static/logo.svg" width="80" height="80" alt="FletchTime logo">
2
2
 
3
+ [![Python](https://img.shields.io/badge/python-version-from-pep-621-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2FMrFanghoDev%2Ffletchtime%2Fmaster%2Fpyproject.toml)](pyproject.toml)
4
+ [![Licence](https://img.shields.io/github/license/MrFanghoDev/fletchtime)](LICENSE)
5
+ [![Tests](https://github.com/MrFanghoDev/fletchtime/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/MrFanghoDev/fletchtime/actions/workflows/test.yml)
6
+ [![PyPI](https://img.shields.io/pypi/v/fletchtime)](https://pypi.org/project/fletchtime/)
7
+
3
8
  # FletchTime
4
9
 
5
10
  Logiciel open source de gestion du temps pour compétitions d'archerie FFTL
@@ -101,6 +106,29 @@ from fletchtime.engine import IndoorMode, IndoorConfig, MatchEngine
101
106
  engine = MatchEngine(IndoorMode(IndoorConfig()))
102
107
  ```
103
108
 
109
+ ## Plusieurs salles de compétition sur un même PC
110
+
111
+ Chaque installation de FletchTime (dossier `pip install`, ou dossier de
112
+ l'exécutable autoporteur) fait tourner **une seule salle** -- son propre
113
+ match, sa propre urgence, sa propre config. Pour plusieurs salles en
114
+ parallèle sur le même PC :
115
+
116
+ 1. **Copie le dossier FletchTime une fois par salle** (ex. `salle-A/`,
117
+ `salle-B/`) -- ça isole automatiquement `config/` (temps de tir,
118
+ urgence...), les journaux, et l'instantané de récupération après
119
+ plantage : chaque copie a sa propre urgence, indépendante des autres.
120
+ 2. Dans la fenêtre de chaque copie, section **Ports (HTTP/WS)** : donne
121
+ des ports différents à chaque salle (ex. salle A garde `8000`/`8765`
122
+ par défaut, salle B passe à `8001`/`8766`) -- **Appliquer** redémarre
123
+ le serveur automatiquement si besoin.
124
+ 3. Lance chaque copie séparément. Les écrans de chaque salle se
125
+ connectent à l'adresse (IP + port) affichée dans la fenêtre de leur
126
+ salle respective.
127
+
128
+ **Windows** : le pare-feu autorise généralement par chemin d'exécutable --
129
+ avec des copies de dossier séparées, prévoir de valider l'alerte une fois
130
+ par copie au premier lancement.
131
+
104
132
  ## Contribuer
105
133
 
106
134
  Envie de proposer un correctif, une idée, ou juste signaler un bug ? Voir
@@ -343,6 +343,41 @@ confirmer le rendu et l'ergonomie tactile -- voir aussi le piège
343
343
  PyInstaller/`customtkinter` documenté dans {doc}`dev-guide/index`.
344
344
  ```
345
345
 
346
+ ## Journal applicatif persistant
347
+
348
+ En plus du journal affiché dans la fenêtre graphique (en mémoire,
349
+ perdu à la fermeture -- voir `fletchtime.gui._QueueWriter`),
350
+ `fletchtime.logging_setup.configure_logging` écrit un fichier de journal
351
+ persistant (`<données du club>/logs/fletchtime.log`, avec rotation --
352
+ 1 Mo par fichier, 5 fichiers conservés) : utile pour comprendre après
353
+ coup ce qui s'est passé pendant un concours, sans dépendre de la mémoire
354
+ volatile de la fenêtre.
355
+
356
+ `MatchServer` y journalise :
357
+ - chaque commande reçue (`next`, `stop`, `start_indoor`...), avec la lane
358
+ d'origine si connue ;
359
+ - chaque connexion/déconnexion, avec le nombre de clients restants ;
360
+ - les pertes de connexion réseau détectées (WiFi, mise en veille...),
361
+ distinguées d'une fermeture normale ;
362
+ - les messages malformés reçus (JSON invalide) ;
363
+ - les transitions d'état significatives (`prep_start`, `shoot_start`,
364
+ `warning_orange`, `end_of_volee`, `pause_start`/`pause_end`,
365
+ `emergency_start`/`emergency_end`, `end_of_match`), avec le contexte
366
+ (unité, volée, tour, phase) -- qu'elles soient déclenchées naturellement
367
+ par le décompte ou manuellement (`next`, `goto`...), puisque les deux
368
+ passent par le même mécanisme de collecte d'événements. `countdown_tick`
369
+ est volontairement exclu (se déclenche une fois par seconde dans les
370
+ dernières secondes de chaque volée -- noierait le journal sans valeur
371
+ diagnostique ajoutée).
372
+
373
+ ```{important}
374
+ Le mot de passe (action `authenticate`) n'est **jamais** journalisé -- le
375
+ code ne journalise jamais `data` tel quel, seulement le nom de l'action
376
+ et la lane, précisément pour éviter qu'un secret ne traîne en clair dans
377
+ un fichier qui persiste sur disque. Vérifié par un test dédié
378
+ (`test_password_never_appears_in_logs`).
379
+ ```
380
+
346
381
  ## Récupération après un plantage ou redémarrage du serveur
347
382
 
348
383
  Avant ce mécanisme, un plantage ou un redémarrage du serveur en plein
@@ -398,15 +433,29 @@ comparable à travers un redémarrage du processus.
398
433
  `wallclock_deadline` vaut `None` pendant une pause/urgence (le décompte
399
434
  est gelé, aucune échéance à calculer) : `time_left` reste alors la
400
435
  valeur figée telle quelle, sans recalcul.
436
+ ```
437
+
438
+ ```{important}
439
+ **Si l'échéance est déjà dépassée à la restauration** (le temps
440
+ réellement écoulé pendant l'indisponibilité dépasse ce qui restait sur
441
+ l'étape en cours), le match est marqué **terminé** plutôt que de figer
442
+ `time_left` à 0. Sans ça, le tick normal suivant (`MatchEngine.tick()`,
443
+ voir plus haut) aurait déclenché son rattrapage automatique -- pensé
444
+ pour un simple ralentissement passager du serveur, pas une coupure de
445
+ plusieurs minutes -- et avancé à l'étape suivante avec une durée
446
+ fraîche : le chrono semblait alors "redémarrer" du point de vue des
447
+ archers, alors qu'en réalité bien plus de temps s'était écoulé que ce
448
+ que l'étape en cours pouvait raisonnablement absorber. Une coupure trop
449
+ longue pour être absorbée dans l'étape en cours demande donc une
450
+ intervention explicite du responsable du chronométrage (`restart`/
451
+ `goto`) plutôt qu'une reprise automatique qui devinerait une position
452
+ dans une coupure d'une durée arbitraire.
453
+ ```
401
454
 
402
455
  La sauvegarde à chaque tick (plutôt que périodiquement, ex. toutes les
403
456
  5s) garde malgré tout sa valeur avec ce mécanisme : elle limite la
404
- fenêtre pendant laquelle un **changement d'étape complet** (fin de
405
- volée, passage à l'orange...) pourrait ne pas être capturé avant un
406
- plantage -- l'échéance recalculée donne un temps exact pour l'étape
407
- *enregistrée*, mais ne devine jamais qu'une étape différente aurait dû
408
- être active entre-temps.
409
- ```
457
+ fenêtre pendant laquelle un changement d'étape aurait pu se produire
458
+ juste avant un plantage sans jamais être capturé dans l'instantané.
410
459
 
411
460
  ## Fonctionnement dégradé en cas de coupure réseau
412
461
 
@@ -1,3 +1,14 @@
1
+ ```{image} _static/logo.svg
2
+ :alt: FletchTime
3
+ :width: 140px
4
+ :align: center
5
+ ```
6
+
7
+ [![Python](https://img.shields.io/badge/python-version-from-pep-621-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2FMrFanghoDev%2Ffletchtime%2Fmaster%2Fpyproject.toml)](https://github.com/MrFanghoDev/fletchtime/blob/master/pyproject.toml)
8
+ [![Licence](https://img.shields.io/github/license/MrFanghoDev/fletchtime)](https://github.com/MrFanghoDev/fletchtime/blob/master/LICENSE)
9
+ [![Tests](https://github.com/MrFanghoDev/fletchtime/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/MrFanghoDev/fletchtime/actions/workflows/test.yml)
10
+ [![PyPI](https://img.shields.io/pypi/v/fletchtime)](https://pypi.org/project/fletchtime/)
11
+
1
12
  # FletchTime
2
13
 
3
14
  Logiciel de gestion du temps pour compétitions d'archerie FFTL (Indoor et Flint),
@@ -73,27 +73,50 @@ dépendent d'extensions compilées (Rust/C) peu fiables sur Pydroid 3, voir
73
73
 
74
74
  ## Backlog — à discuter / non encore programmé dans une étape précise
75
75
 
76
- - **Fenêtre graphique -- améliorations restant à faire** :
77
- - Paramétrage du serveur (ports, etc.) et de l'adresse IP exposée sur le
78
- réseau local, directement depuis la fenêtre plutôt que fichiers de
79
- config seuls.
80
- - Affichage des adresses exposées (accueil/contrôle/affichage) dans la
81
- fenêtre elle-même, pas seulement via les boutons de raccourci
82
- actuels.
83
- - Choix du thème clair/sombre dans la fenêtre -- actuellement forcé en
84
- sombre (`ctk.set_appearance_mode("dark")`), cohérent avec
85
- `display.html` mais pas ajustable par l'utilisateur comme le sont les
86
- autres pages web.
87
- - Couleur du bouton Quitter à revoir (actuellement neutre/transparent).
88
- - ~~**Récupération après plantage/redémarrage du serveur**~~ -- fait :
89
- `MatchServer` persiste un instantané JSON de l'état du match
90
- (`config/match_state.json`, jamais versionné) à chaque commande qui
91
- change l'état et périodiquement pendant le décompte, et tente une
76
+ - ~~**Journal applicatif persistant**~~ -- fait : `MatchServer` journalise
77
+ désormais chaque commande reçue, (dé)connexion, perte de connexion
78
+ réseau et message malformé dans un fichier avec rotation
79
+ (`logs/fletchtime.log`), en plus du journal déjà affiché dans la
80
+ fenêtre (en mémoire, perdu à la fermeture). Voir {doc}`architecture`,
81
+ section dédiée -- vérifié que le mot de passe ne s'y retrouve jamais.
82
+ - ~~**Fenêtre graphique -- améliorations**~~ -- toutes faites (paramétrage
83
+ réseau, affichage des adresses, thème, couleur du bouton Quitter) :
84
+ - ~~Paramétrage du serveur et de l'adresse IP exposée~~ -- fait :
85
+ ports HTTP/WebSocket modifiables directement dans la fenêtre
86
+ (`config/gui.toml`), avec redémarrage automatique du serveur si les
87
+ ports changent pendant qu'il tourne. Objectif principal : plusieurs
88
+ salles de compétition sur un même PC -- copier le dossier FletchTime
89
+ une fois par salle (isole aussi `config/`, `logs/`, l'instantané de
90
+ récupération, donc des urgences bien séparées), donner des ports
91
+ différents à chaque copie, lancer chacune séparément. Testé
92
+ concrètement : deux instances simultanées sur le même processus,
93
+ ports différents, contenu totalement isolé entre les deux.
94
+ - ~~Affichage des adresses exposées~~ -- fait : champ dédié (adresse
95
+ d'accueil) dans la fenêtre elle-même, en plus des boutons de
96
+ raccourci -- utile pour retaper l'adresse à la main sur un appareil
97
+ sans bouton de raccourci. Non vérifié visuellement si le texte reste
98
+ bien sélectionnable/copiable une fois le champ désactivé (`CTkEntry`
99
+ n'a pas de vrai état "readonly" contrairement à `ttk.Entry`).
100
+ - ~~Choix du thème clair/sombre dans la fenêtre~~ -- fait : sélecteur
101
+ Système/Clair/Sombre dans l'en-tête, préférence persistée dans
102
+ `config/gui.toml` (jamais versionné, propre à la machine -- voir
103
+ `.gitignore`). Indépendant de `display.html`, qui reste lui, par
104
+ choix délibéré, toujours sombre.
105
+ - ~~Couleur du bouton Quitter à revoir~~ -- fait : couleur pleine
106
+ (`#3a4354`) au lieu de transparent+contour, visible sans être
107
+ alarmante (distincte du rouge d'Arrêter et du doré de Démarrer).
108
+ - ~~**Récupération après plantage/redémarrage du serveur**~~ -- fait et
109
+ **confirmé en conditions réelles avec la v0.2.0** (synchronisation du
110
+ temps testée, plus de retour en arrière du chrono ; reconnexion
111
+ vérifiée) : `MatchServer` persiste un instantané JSON de l'état du
112
+ match (`config/match_state.json`, jamais versionné) à chaque commande
113
+ qui change l'état et périodiquement pendant le décompte, et tente une
92
114
  restauration silencieuse à la construction -- avant ça, un plantage en
93
115
  plein match perdait toute la progression (série, volée, temps écoulé).
94
116
  Voir {doc}`architecture`, section dédiée, pour le détail complet
95
- (écriture atomique, repli silencieux sur un démarrage normal si la
96
- reprise échoue).
117
+ (échéance en horloge murale pour un temps exact quelle que soit la
118
+ durée de l'indisponibilité, écriture atomique, repli silencieux sur un
119
+ démarrage normal si la reprise échoue).
97
120
  - ~~**Revamping du logo**~~ -- fait : nouveau logo cadran/flèche noir-or-blanc,
98
121
  wordmark "FletchTime" bicolore Fletch/Time, cohérent avec le thème clair/sombre.
99
122
  - ~~**Sécurisation cyber**~~ -- en partie fait : mot de passe optionnel
@@ -118,20 +141,16 @@ dépendent d'extensions compilées (Rust/C) peu fiables sur Pydroid 3, voir
118
141
  confirmée) -- voir le détail de l'investigation dans
119
142
  {doc}`dev-guide/index`. Pas de besoin utilisateur macOS avéré à ce jour ;
120
143
  à reconsidérer si ça change.
121
- - **Web app côté client (PWA)** : transformer `control.html`/`display.html`
122
- (voire `index.html`) en Progressive Web App -- `manifest.json` + service
123
- worker + icônes. Permettrait d'"installer" l'app sur l'écran d'accueil
124
- d'une tablette/téléphone (lancement direct, sans retaper l'URL ni passer
125
- par le navigateur), et de mettre en cache les fichiers statiques (HTML/CSS/
126
- JS/logo) pour un chargement instantané même en cas de WiFi capricieux --
127
- le WebSocket, lui, resterait bien sûr en temps réel, seul l'habillage
128
- statique serait mis en cache. Chantier indépendant du reste, à ne traiter
129
- qu'une fois l'interface stabilisée.
130
- - **Documentation Sphinx versionnée** : `docs.yml` publie aujourd'hui une
131
- seule version de la doc sur GitHub Pages, écrasée à chaque nouveau tag
132
- (voir plus bas pourquoi ce n'est plus à chaque push depuis peu) -- pas
133
- de moyen de consulter la doc telle qu'elle était pour une release
134
- précédente (ex. v0.1.2) si le code a changé depuis. À terme : une doc
135
- par tag de version (ex. `/v0.1.2/`, `/v0.1.3/`) en plus d'une version
136
- `/latest/`, avec un sélecteur de version dans le thème Furo (voir
137
- `sphinx-multiversion` ou équivalent).
144
+ - ~~**Web app côté client (PWA)**~~ -- abandonné délibérément : pas jugé
145
+ utile pour l'usage réel du club. (Aurait transformé
146
+ `control.html`/`display.html` en Progressive Web App -- installation
147
+ sur écran d'accueil, mise en cache des fichiers statiques.)
148
+ - ~~**Documentation Sphinx versionnée**~~ -- abandonné délibérément : les
149
+ archives `.tar.gz` jointes à chaque Release GitHub suffisent pour
150
+ consulter la doc d'une version précédente, pas besoin d'un système de
151
+ versionnage complet côté GitHub Pages.
152
+ - ~~**Termux / lancer FletchTime sur Android sans Pydroid**~~ -- abandonné
153
+ délibérément pour l'instant : usage réel actuel = téléphone en simple
154
+ client (accès réseau local), PC pour héberger le serveur. Pas de besoin
155
+ d'exécuter le serveur lui-même sur Android dans ces conditions. À
156
+ reconsidérer si l'usage change.
@@ -28,10 +28,17 @@ from importlib import resources
28
28
  from pathlib import Path
29
29
 
30
30
  from fletchtime import __version__
31
+ from fletchtime.logging_setup import configure_logging
31
32
  from fletchtime.runtime import ServerRuntime
33
+ from fletchtime.server import config_store
32
34
 
33
- HTTP_PORT = 8000
34
- WS_PORT = 8765
35
+ # Anciennement des constantes HTTP_PORT/WS_PORT figées ici -- les ports
36
+ # par défaut vivent maintenant uniquement dans
37
+ # config_store.DEFAULT_GUI_CONFIG, seule source de vérité (voir
38
+ # _run_headless ci-dessous et fletchtime.gui, qui lisent tous deux
39
+ # config_store.load_gui_config() plutôt qu'une constante figée). Permet
40
+ # de faire tourner plusieurs salles de compétition sur un même PC : une
41
+ # copie de dossier par salle, chacune avec son propre config/gui.toml.
35
42
 
36
43
  # Dossiers pré-remplis avec un contenu par défaut fourni avec FletchTime
37
44
  # (copié une seule fois, au tout premier lancement -- jamais écrasé
@@ -112,14 +119,14 @@ def local_ip() -> str:
112
119
  s.close()
113
120
 
114
121
 
115
- def _print_banner(ip: str, data_root: Path) -> None:
122
+ def _print_banner(ip: str, data_root: Path, http_port: int) -> None:
116
123
  print("=" * 60)
117
124
  print(f" FletchTime {__version__} -- serveur de contrôle et d'affichage")
118
125
  print(f" FletchTime {__version__} -- control and display server")
119
126
  print("=" * 60)
120
- print(f" Accueil / Home : http://{ip}:{HTTP_PORT}/")
121
- print(f" Contrôle / Control : http://{ip}:{HTTP_PORT}/control.html")
122
- print(f" Affichage / Display : http://{ip}:{HTTP_PORT}/display.html?lane=1")
127
+ print(f" Accueil / Home : http://{ip}:{http_port}/")
128
+ print(f" Contrôle / Control : http://{ip}:{http_port}/control.html")
129
+ print(f" Affichage / Display : http://{ip}:{http_port}/display.html?lane=1")
123
130
  print()
124
131
  print(" Depuis CET appareil, remplace l'IP par 127.0.0.1 si besoin.")
125
132
  print(" From THIS device, replace the IP with 127.0.0.1 if needed.")
@@ -152,9 +159,22 @@ def _run_headless() -> None:
152
159
  ensure_directories(data_root, app_web_dir)
153
160
  assets_dir = data_root / "web" / "assets"
154
161
 
155
- _print_banner(local_ip(), data_root)
162
+ log_file = configure_logging(data_root / "logs")
163
+ print(f"Journal détaillé / Detailed log: {log_file}")
156
164
 
157
- runtime = ServerRuntime(str(app_web_dir), str(assets_dir), HTTP_PORT, WS_PORT)
165
+ # Ports lus depuis config/gui.toml (mêmes préférences que la fenêtre
166
+ # graphique, voir config_store.load_gui_config) -- permet de faire
167
+ # tourner plusieurs salles de compétition sur le même PC : une copie
168
+ # de dossier par salle, chacune avec son propre gui.toml donnant des
169
+ # ports différents. "gui.toml" malgré le nom concerne aussi ce mode
170
+ # terminal, pour que les deux se comportent de façon cohérente.
171
+ gui_config = config_store.load_gui_config()
172
+ http_port = gui_config["http_port"]
173
+ ws_port = gui_config["ws_port"]
174
+
175
+ _print_banner(local_ip(), data_root, http_port)
176
+
177
+ runtime = ServerRuntime(str(app_web_dir), str(assets_dir), http_port, ws_port)
158
178
  runtime.start()
159
179
 
160
180
  stop_event = threading.Event()
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.2.0'
22
- __version_tuple__ = version_tuple = (0, 2, 0)
21
+ __version__ = version = '0.2.2'
22
+ __version_tuple__ = version_tuple = (0, 2, 2)
23
23
 
24
- __commit_id__ = commit_id = 'g76ecfc7d6'
24
+ __commit_id__ = commit_id = 'gceb71f360'
@@ -71,7 +71,28 @@ class MatchEngine:
71
71
  # ce cas, le temps reste gelé tel quel.
72
72
  deadline = restore.get("wallclock_deadline")
73
73
  if deadline is not None:
74
- self._time_left = max(0.0, deadline - time.time())
74
+ remaining = deadline - time.time()
75
+ if remaining <= 0:
76
+ # Le temps réellement écoulé pendant l'indisponibilité
77
+ # dépasse ce qui restait sur cette étape -- ne PAS se
78
+ # contenter de figer time_left à 0 : le tick normal
79
+ # suivant aurait alors déclenché le rattrapage
80
+ # automatique de tick() (comportement volontaire et
81
+ # préexistant, pensé pour un simple ralentissement
82
+ # passager, pas une coupure de plusieurs secondes/
83
+ # minutes) et avancé à l'étape suivante avec une durée
84
+ # fraîche -- exactement ce qui ressemblait à "le
85
+ # chrono repart" du point de vue des archers. Une
86
+ # coupure trop longue pour être absorbée dans l'étape
87
+ # en cours marque le match terminé : au responsable du
88
+ # chronométrage de décider explicitement de la suite
89
+ # (restart/goto), plutôt qu'une reprise automatique
90
+ # qui devine une position dans une coupure qui a pu
91
+ # durer n'importe combien de temps.
92
+ self._finished = True
93
+ self._time_left = 0.0
94
+ else:
95
+ self._time_left = remaining
75
96
  # Pas d'appel à _emit_current_step_event() ici, volontairement :
76
97
  # une reprise après crash doit être silencieuse (pas de
77
98
  # prep_start/shoot_start rejoué comme si l'étape venait de