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.
- {fletchtime-0.2.0 → fletchtime-0.2.2}/.gitignore +4 -0
- {fletchtime-0.2.0/src/fletchtime.egg-info → fletchtime-0.2.2}/PKG-INFO +29 -1
- {fletchtime-0.2.0 → fletchtime-0.2.2}/README.md +28 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/docs/architecture.md +55 -6
- {fletchtime-0.2.0 → fletchtime-0.2.2}/docs/index.md +11 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/docs/roadmap.md +54 -35
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/__main__.py +28 -8
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/_version.py +3 -3
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/engine/engine.py +22 -1
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/gui.py +216 -11
- fletchtime-0.2.2/src/fletchtime/logging_setup.py +56 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/server/config_store.py +37 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/server/match_server.py +37 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/server/ws_server.py +8 -2
- {fletchtime-0.2.0 → fletchtime-0.2.2/src/fletchtime.egg-info}/PKG-INFO +29 -1
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime.egg-info/SOURCES.txt +1 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime.egg-info/scm_file_list.json +1 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime.egg-info/scm_version.json +2 -2
- {fletchtime-0.2.0 → fletchtime-0.2.2}/tests/test_engine.py +15 -2
- {fletchtime-0.2.0 → fletchtime-0.2.2}/tests/test_match_server.py +116 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/.github/workflows/build.yml +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/.github/workflows/docs.yml +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/.github/workflows/test.yml +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/CONTRIBUTING.md +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/LICENSE +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/config/app.toml +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/config/flint.toml +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/config/indoor.toml +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/demo.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/docs/_static/logo.svg +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/docs/api-reference.md +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/docs/conf.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/docs/dev-guide/index.md +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/docs/requirements.txt +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/docs/specifications.md +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/docs/user-guide/index.md +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/fletchtime.spec +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/pyproject.toml +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/run_server.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/run_tests.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/scripts/generate_classic_sounds.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/setup.cfg +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/__init__.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/engine/__init__.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/engine/models.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/engine/modes/__init__.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/engine/modes/base.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/engine/modes/flint.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/engine/modes/indoor.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/engine/sequence.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/engine/turn_modes.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/runtime.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/server/__init__.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/server/http_static.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/__init__.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/banners/README.md +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/club/README.md +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/gui/theme.json +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/README.md +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/countdown_tick.wav +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/emergency_end.wav +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/emergency_start.wav +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/end_of_match.wav +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/end_of_volee.wav +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/pause_end.wav +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/pause_start.wav +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/prep_start.wav +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/shoot_start.wav +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/warning_orange.wav +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/targets/flint_20cm_4spot.jpg +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/targets/flint_35cm_1spot.jpg +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/targets/indoor_compound.jpg +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/targets/indoor_recurve.jpg +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/config.html +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/control.html +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/display.html +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/i18n.js +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/index.html +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/logo.svg +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/manual.html +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/screenshots/config.png +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/screenshots/control.png +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/screenshots/display.png +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/screenshots/index.png +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime/web/theme.js +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime.egg-info/dependency_links.txt +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime.egg-info/entry_points.txt +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime.egg-info/requires.txt +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/src/fletchtime.egg-info/top_level.txt +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/tests/test_config_store.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/tests/test_flint_mode.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/tests/test_indoor_mode.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/tests/test_runtime.py +0 -0
- {fletchtime-0.2.0 → fletchtime-0.2.2}/web/assets/banners/README.md +0 -0
- {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.
|
|
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
|
+
[](pyproject.toml)
|
|
25
|
+
[](LICENSE)
|
|
26
|
+
[](https://github.com/MrFanghoDev/fletchtime/actions/workflows/test.yml)
|
|
27
|
+
[](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
|
+
[](pyproject.toml)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://github.com/MrFanghoDev/fletchtime/actions/workflows/test.yml)
|
|
6
|
+
[](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
|
|
405
|
-
|
|
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
|
+
[](https://github.com/MrFanghoDev/fletchtime/blob/master/pyproject.toml)
|
|
8
|
+
[](https://github.com/MrFanghoDev/fletchtime/blob/master/LICENSE)
|
|
9
|
+
[](https://github.com/MrFanghoDev/fletchtime/actions/workflows/test.yml)
|
|
10
|
+
[](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
|
-
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
(
|
|
96
|
-
|
|
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
|
-
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
d'
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
|
34
|
-
|
|
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}:{
|
|
121
|
-
print(f" Contrôle / Control : http://{ip}:{
|
|
122
|
-
print(f" Affichage / Display : http://{ip}:{
|
|
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
|
-
|
|
162
|
+
log_file = configure_logging(data_root / "logs")
|
|
163
|
+
print(f"Journal détaillé / Detailed log: {log_file}")
|
|
156
164
|
|
|
157
|
-
|
|
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.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 2,
|
|
21
|
+
__version__ = version = '0.2.2'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 2)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
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
|
-
|
|
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
|