diff --git a/engine/__pycache__/constants.cpython-313.pyc b/engine/__pycache__/constants.cpython-313.pyc index 4fb2aea..1385e17 100644 Binary files a/engine/__pycache__/constants.cpython-313.pyc and b/engine/__pycache__/constants.cpython-313.pyc differ diff --git a/engine/__pycache__/engine.cpython-313.pyc b/engine/__pycache__/engine.cpython-313.pyc index 1cc0e0e..1ef34dc 100644 Binary files a/engine/__pycache__/engine.cpython-313.pyc and b/engine/__pycache__/engine.cpython-313.pyc differ diff --git a/engine/constants.py b/engine/constants.py index 6a0673f..01a3662 100644 --- a/engine/constants.py +++ b/engine/constants.py @@ -1 +1 @@ -BATCH_SIZE = 1000 \ No newline at end of file +BATCH_SIZE = 250 \ No newline at end of file diff --git a/engine/engine.py b/engine/engine.py index 11e483c..e58a469 100644 --- a/engine/engine.py +++ b/engine/engine.py @@ -15,6 +15,32 @@ def run_once(): log_info("[INFO] Aucune course active.") return + # ------------------------------------------------- + # Appliquer les PAUSE_REQUEST avant tout traitement + # ------------------------------------------------- + active_courses = get_active_con_ids(cur) + + for con_id in active_courses: + + engine_table = f"resultv2_{con_id}_lecture" + + # Lire le statut actuel + cur.execute(f"SELECT process_status FROM `{engine_table}` LIMIT 1") + row = cur.fetchone() + + if not row: + continue + + status = (row["process_status"] or "").upper() + + # Si CI4 a demandé une pause → Python applique la pause maintenant + if status == "PAUSE_REQUEST": + cur.execute(f"UPDATE `{engine_table}` SET process_status = 'PAUSE'") + cnx.commit() + log_info(f"[PAUSE APPLIED] con_id={con_id}") + + + for con_id in con_ids: process_course(cur, cnx, con_id) @@ -30,4 +56,21 @@ def run_once(): if __name__ == "__main__": - run_once() + import time + import msvcrt # Spécifique Windows + + log_info("[TEST MODE] Boucle continue — appuyer ENTER pour arrêter.") + + try: + while True: + run_once() + time.sleep(0.5) + + if msvcrt.kbhit(): # Une touche a été pressée + key = msvcrt.getch() + if key == b'\r': # ENTER = arrêt + log_info("[TEST MODE] Arrêt demandé via ENTER.") + break + + except KeyboardInterrupt: + log_info("[TEST MODE] Arrêt via CTRL+C.") diff --git a/engine/engine_components/__pycache__/process_course.cpython-313.pyc b/engine/engine_components/__pycache__/process_course.cpython-313.pyc index b8adb22..6a4c930 100644 Binary files a/engine/engine_components/__pycache__/process_course.cpython-313.pyc and b/engine/engine_components/__pycache__/process_course.cpython-313.pyc differ diff --git a/engine/engine_components/__pycache__/regles.cpython-313.pyc b/engine/engine_components/__pycache__/regles.cpython-313.pyc new file mode 100644 index 0000000..2fb38a8 Binary files /dev/null and b/engine/engine_components/__pycache__/regles.cpython-313.pyc differ diff --git a/engine/engine_components/process_course.py b/engine/engine_components/process_course.py index cc1591d..cd7b4f6 100644 --- a/engine/engine_components/process_course.py +++ b/engine/engine_components/process_course.py @@ -9,6 +9,7 @@ from datetime import datetime, timezone from zoneinfo import ZoneInfo from decimal import Decimal from engine.utils import format_temps_decimal +from engine.engine_components.regles import resolve_priority LOCAL_TZ = ZoneInfo("America/Toronto") @@ -59,6 +60,30 @@ def process_course(cur, cnx, con_id: int): engine_table = f"resultv2_{con_id}_lecture" classement_table = f"resultv2_{con_id}_classement" + # ------------------------------------------------- + # Pause + # ------------------------------------------------- + + # Lire le statut + cur.execute(f"SELECT process_status FROM `{engine_table}` LIMIT 1") + row = cur.fetchone() + status = (row["process_status"] or "").upper() + + # Si une pause est demandée + if status == "PAUSE_REQUEST": + # Le moteur se met officiellement en PAUSE + cur.execute(f"UPDATE `{engine_table}` SET process_status = 'PAUSE'") + cnx.commit() + log_info(f"[PAUSE] con_id={con_id} — pause appliquée avant traitement.") + return + + # Si déjà en pause → sortir immédiatement + if status == "PAUSE": + log_info(f"[PAUSE] con_id={con_id} — course déjà en pause.") + return + + + # ------------------------------------------------- # Lire dernier ID traité (mécanisme incremental) # ------------------------------------------------- @@ -84,6 +109,8 @@ def process_course(cur, cnx, con_id: int): # ------------------------------------------------- for lec in lectures: + + rcourse_id = int(lec["rcourse_id"]) chr_loc = lec["chrinfo_location"] timestamp = Decimal(lec["chrinfo_time"]) @@ -249,94 +276,4 @@ def compute_tour(cur, detail_table, tp_id, dossard): return last_tour + 1, prev_ts -# ------------------------------------------------- -# Règle de priorité : remplace, rejette, ou force tour -# ------------------------------------------------- -def resolve_priority( - cur, - cnx, - detail_table, - tp_id, - dossard, - new_timestamp, - new_priority, - det_id, - tp_config -): - - # ------------------------------------------------- - # Lecture de la fenêtre backup pour priorité - # ------------------------------------------------- - cfg = ( - tp_config[tp_id]["config"] - .get("config_tp", {}) - .get("clef_temp_bbk", []) - ) - - backup_window = 0 - if cfg: - try: - backup_window = int(cfg[0]["value1"]) - except: - backup_window = 0 - - log_debug(f"[resolve_priority] backup_window={backup_window}") - - # ------------------------------------------------- - # Si backup_window = 0 → toujours accepter - # ------------------------------------------------- - if backup_window <= 0: - return 1, None, None, None - - # ------------------------------------------------- - # Recherche d'une lecture existante dans la fenêtre - # ------------------------------------------------- - cur.execute(f""" - SELECT detail_id, controller_priority, timestamp_read, tour - FROM `{detail_table}` - WHERE tp_id = %s - AND cla_info_dossard = %s - AND status = 1 - AND ABS(timestamp_read - %s) <= %s - LIMIT 1 - """, (tp_id, dossard, str(new_timestamp), backup_window)) - - row = cur.fetchone() - - # ------------------------------------------------- - # Aucun doublon → accepter - # ------------------------------------------------- - if not row: - return 1, None, None, None - - old_id = row["detail_id"] - old_priority = row["controller_priority"] - old_tour = row["tour"] - - # ------------------------------------------------- - # Comparaison des priorités - # ------------------------------------------------- - - # Nouvelle lecture moins prioritaire → rejet - if new_priority > old_priority: - return 0, "LOWER_PRIORITY", det_id, None - - # Nouvelle lecture plus prioritaire → remplacer - if new_priority < old_priority: - - cur.execute(f""" - UPDATE `{detail_table}` - SET - status = 0, - reason_code = 'REPLACED_BY_HIGHER_PRIORITY', - reason_ref_id = %s - WHERE detail_id = %s - """, (det_id, old_id)) - - cnx.commit() - - forced_tour = old_tour - return 1, None, None, forced_tour - - # Même priorité → rejet - return 0, "EQUAL_PRIORITY", det_id, None + \ No newline at end of file diff --git a/engine/engine_components/regles.py b/engine/engine_components/regles.py new file mode 100644 index 0000000..677d2d1 --- /dev/null +++ b/engine/engine_components/regles.py @@ -0,0 +1,93 @@ +# regles.py +from engine.logger import log_info, log_debug +# ------------------------------------------------- +# Règle de priorité : remplace, rejette, ou force tour +# ------------------------------------------------- +def resolve_priority( + cur, + cnx, + detail_table, + tp_id, + dossard, + new_timestamp, + new_priority, + det_id, + tp_config +): + + # ------------------------------------------------- + # Lecture de la fenêtre backup pour priorité + # ------------------------------------------------- + cfg = ( + tp_config[tp_id]["config"] + .get("config_tp", {}) + .get("clef_temp_bbk", []) + ) + + backup_window = 0 + if cfg: + try: + backup_window = int(cfg[0]["value1"]) + except: + backup_window = 0 + + log_debug(f"[resolve_priority] backup_window={backup_window}") + + # ------------------------------------------------- + # Si backup_window = 0 → toujours accepter + # ------------------------------------------------- + if backup_window <= 0: + return 1, None, None, None + + # ------------------------------------------------- + # Recherche d'une lecture existante dans la fenêtre + # ------------------------------------------------- + cur.execute(f""" + SELECT detail_id, controller_priority, timestamp_read, tour + FROM `{detail_table}` + WHERE tp_id = %s + AND cla_info_dossard = %s + AND status = 1 + AND ABS(timestamp_read - %s) <= %s + LIMIT 1 + """, (tp_id, dossard, str(new_timestamp), backup_window)) + + row = cur.fetchone() + + # ------------------------------------------------- + # Aucun doublon → accepter + # ------------------------------------------------- + if not row: + return 1, None, None, None + + old_id = row["detail_id"] + old_priority = row["controller_priority"] + old_tour = row["tour"] + + # ------------------------------------------------- + # Comparaison des priorités + # ------------------------------------------------- + + # Nouvelle lecture moins prioritaire → rejet + if new_priority > old_priority: + return 0, "LOWER_PRIORITY", det_id, None + + # Nouvelle lecture plus prioritaire → remplacer + if new_priority < old_priority: + + cur.execute(f""" + UPDATE `{detail_table}` + SET + status = 0, + reason_code = 'REPLACED_BY_HIGHER_PRIORITY', + reason_ref_id = %s + WHERE detail_id = %s + """, (det_id, old_id)) + + cnx.commit() + + forced_tour = old_tour + return 1, None, None, forced_tour + + # Même priorité → rejet + return 0, "EQUAL_PRIORITY", det_id, None