249 lines
7.0 KiB
Python
249 lines
7.0 KiB
Python
# regles.py
|
|
from engine.logger import log_info, log_debug
|
|
from decimal import Decimal
|
|
from datetime import datetime, timezone
|
|
from engine.utils import format_temps_decimal
|
|
# -------------------------------------------------
|
|
# Règle de priorité : remplace, rejette, ou force tour
|
|
# -------------------------------------------------
|
|
def resolve_priority(
|
|
cur,
|
|
cnx,
|
|
detail_table,
|
|
tp_id,
|
|
dossard,
|
|
new_timestamp,
|
|
new_priority,
|
|
detail_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
|
|
|
|
|
|
|
|
# -------------------------------------------------
|
|
# 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 detail_id <> %s
|
|
AND ABS(timestamp_read - %s) <= %s
|
|
LIMIT 1
|
|
""", (tp_id, dossard, detail_id, new_timestamp, backup_window))
|
|
|
|
|
|
|
|
row = cur.fetchone()
|
|
|
|
# -------------------------------------------------
|
|
# Aucun doublon → accepter
|
|
# -------------------------------------------------
|
|
if not row:
|
|
log_debug(f"[resolve_priority] pas de doublon backup_window={backup_window}")
|
|
return 1, None, None, None
|
|
|
|
old_id = row["detail_id"]
|
|
old_priority = row["controller_priority"]
|
|
old_tour = row["tour"]
|
|
|
|
# -------------------------------------------------
|
|
# Comparaison des priorités
|
|
# -------------------------------------------------
|
|
log_debug(f"[resolve_priority] detail_id={detail_id} old_id={old_id} backup_window={backup_window}")
|
|
# Nouvelle lecture moins prioritaire → rejet
|
|
if new_priority > old_priority:
|
|
return 0, "LOWER_PRIORITY", detail_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
|
|
""", (detail_id, old_id))
|
|
|
|
cnx.commit()
|
|
|
|
forced_tour = old_tour
|
|
return 1, None, None, forced_tour
|
|
|
|
|
|
# Même priorité → rejet
|
|
return 0, "EQUAL_PRIORITY", detail_id, None
|
|
|
|
# -------------------------------------------------
|
|
# Regle interval
|
|
# ------------------------------------------------
|
|
def check_interval_rule(cur, detail_table, tp_id, dossard, timestamp, interval_sec, det_id):
|
|
"""
|
|
Retourne :
|
|
status (1 ou 0)
|
|
reason_code
|
|
reason_ref_id
|
|
"""
|
|
|
|
log_debug(f"[INTERVAL] tp_id={tp_id} dossard={dossard} interval_sec={interval_sec}")
|
|
|
|
last_timestamp, last_tour = get_last_valid_lecture_time(cur, detail_table, tp_id, dossard, timestamp)
|
|
|
|
log_debug(f"[INTERVAL] last_valid={last_timestamp} current={timestamp}")
|
|
|
|
if last_timestamp and interval_sec > 0:
|
|
delta = (timestamp - last_timestamp)
|
|
log_debug(f"[INTERVAL] delta={delta:.3f}s")
|
|
|
|
if delta < interval_sec:
|
|
reason = f"SKIP_INTERVAL {delta:.1f}s < {interval_sec}s"
|
|
log_debug(
|
|
f"[INTERVAL] REJECT dossard={dossard} tp_id={tp_id} "
|
|
f"reason='{reason}' det_id={det_id}"
|
|
)
|
|
return (0, reason, det_id)
|
|
|
|
log_debug(f"[INTERVAL] OK dossard={dossard} tp_id={tp_id}")
|
|
return (1, None, None)
|
|
|
|
|
|
# -------------------------------------------------
|
|
# Dernière lecture valide (timestamp_read < T)
|
|
# -------------------------------------------------
|
|
def get_last_valid_lecture_time(cur, detail_table, tp_id, dossard, current_ts):
|
|
log_debug(f"[get_last_valid_lecture_time] tp={tp_id} dossard={dossard} current={current_ts}")
|
|
cur.execute(f"""
|
|
SELECT timestamp_read, tour
|
|
FROM `{detail_table}`
|
|
WHERE tp_id = %s
|
|
AND cla_info_dossard = %s
|
|
AND status = 1
|
|
AND timestamp_read < %s
|
|
ORDER BY timestamp_read DESC
|
|
LIMIT 1
|
|
""", (tp_id, dossard, current_ts))
|
|
|
|
row = cur.fetchone()
|
|
log_debug(f"[get_last_valid_lecture_time] result={row}")
|
|
|
|
if not row:
|
|
return None, None
|
|
|
|
return (
|
|
Decimal(str(row["timestamp_read"])),
|
|
row["tour"]
|
|
)
|
|
|
|
|
|
# -------------------------------------------------
|
|
# Conversion d'un timestamp Decimal en datetime UTC
|
|
# -------------------------------------------------
|
|
def ts_to_utc(ts):
|
|
ts = Decimal(str(ts))
|
|
seconds = int(ts)
|
|
micro = int((ts - Decimal(seconds)) * Decimal(1_000_000))
|
|
return datetime.fromtimestamp(seconds, tz=timezone.utc).replace(microsecond=micro)
|
|
|
|
# -------------------------------------------------
|
|
# Recherche du dernier tour pour un coureur à un TP
|
|
# -------------------------------------------------
|
|
#def compute_tour(cur, detail_table, tp_id, dossard):
|
|
def check_tour_rule(
|
|
cur,
|
|
detail_table,
|
|
tp_id,
|
|
dossard,
|
|
timestamp,
|
|
det_id
|
|
|
|
):
|
|
|
|
|
|
last_timestamp, last_tour = get_last_valid_lecture_time(
|
|
cur, detail_table, tp_id, dossard, timestamp
|
|
)
|
|
|
|
if last_tour is None:
|
|
last_tour=0
|
|
|
|
if last_timestamp is None:
|
|
last_timestamp = Decimal("0")
|
|
|
|
tour=last_tour+1;
|
|
tempstour = timestamp - last_timestamp
|
|
tempstour_str = format_temps_decimal(tempstour)
|
|
|
|
return tour, tempstour,tempstour_str
|
|
|
|
|
|
def update_detail(
|
|
cur,
|
|
cnx,
|
|
detail_table,
|
|
detail_id,
|
|
tour,
|
|
tempstour,
|
|
tempstour_str
|
|
):
|
|
cur.execute(f"""
|
|
UPDATE `{detail_table}`
|
|
SET tour = %s,
|
|
tempstour = %s,
|
|
tempstour_str = %s,
|
|
reason_code = NULL,
|
|
reason_ref_id = NULL
|
|
WHERE detail_id = %s
|
|
""", (
|
|
tour,
|
|
tempstour,
|
|
tempstour_str,
|
|
detail_id
|
|
))
|
|
|
|
cnx.commit()
|
|
|
|
|
|
def update_detail_status_zero(cur, cnx, detail_table, detail_id, reason_code, reason_ref_id):
|
|
"""
|
|
Met à jour un enregistrement Detail existant pour le passer en status=0,
|
|
avec reason_code et reason_ref_id.
|
|
"""
|
|
cur.execute(f"""
|
|
UPDATE `{detail_table}`
|
|
SET status = 0,
|
|
reason_code = %s,
|
|
reason_ref_id = %s
|
|
WHERE detail_id = %s
|
|
""", (reason_code, reason_ref_id, detail_id))
|
|
|
|
cnx.commit()
|
|
|