From e9c7f51f91c3c0f818f667a96dbc402659f17707 Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 10 Dec 2025 10:31:59 -0500 Subject: [PATCH] pause idel --- engine/__pycache__/constants.cpython-313.pyc | Bin 201 -> 201 bytes engine/__pycache__/engine.cpython-313.pyc | Bin 1626 -> 2930 bytes engine/constants.py | 2 +- engine/engine.py | 45 ++++++- .../process_course.cpython-313.pyc | Bin 8101 -> 7154 bytes .../__pycache__/regles.cpython-313.pyc | Bin 0 -> 2029 bytes engine/engine_components/process_course.py | 119 +++++------------- engine/engine_components/regles.py | 93 ++++++++++++++ 8 files changed, 166 insertions(+), 93 deletions(-) create mode 100644 engine/engine_components/__pycache__/regles.cpython-313.pyc create mode 100644 engine/engine_components/regles.py diff --git a/engine/__pycache__/constants.cpython-313.pyc b/engine/__pycache__/constants.cpython-313.pyc index 4fb2aeaf760b8cb1339f566c6e72555789f14b9c..1385e17540414972ded404ecb6de4f0599a00a7f 100644 GIT binary patch delta 62 zcmX@fc#@I#GcPX}0}y1kSY`@LzT4=k#Y>Mmx?(8JCi&@uYfQ`Ge+qqCB z#I@~1(U>9~6HWU<(xkyY@!0gGt%}cUp}LGU#hNzh8%onOed?Ko&8q3y>^c8A=lj0@ z|IhsA=#Cej)@`S=1W^0w)8p|<8^AMTQfS1kvF*neTf>E|Y=9e-6Q3e2kTMCfkSOiNI z;}x9!#A$>^`Q!t16kSbO1E&C{tc@`4wm{SlfI>8qa27EQXnzv=@CKN~+FXAcPTDY? zT>R=IQxmSWVBfwUCJUZ+ZnYlOfp7kF1$UX+ay6&6Uxlth;EG}gqWgA!fY+a z`q_aHla^93L6#%35>-^0@dZZ%%-LDnS;jvU7z#0+y6aVh4!U#9%X4fb$e!X^E;MW9 z#|FJ27P0EjG93H5WH-~t?9Vgy&sK)>2ICzY^AE9uXP8(@jKnAUW~ne646=NX3B1Rh z@}FVa9NH*epV2MC1tF#?f^MA@l-P7i6m(Kerv*tj$5P2;T#?M^OeEasjLX+%)A5*i z0d=aRTXxiC-4%@~@fjf!OR179$OhMNt2+d7DlQ5UC3-F)=(v>nP&e}A+8wQCe{#N@-Y4+wYWFE0i)hn5G{sypUg z&pgfZzRkLprJ1Ga<>~C6WApwkGdSsm;VZ-0n(mdk74e6mY}LrRV>DOhzIkZzkk&pw z@JG34gRa{2RA*}rJn$TR*zC+2-ff%e>L_?c?W(d9Soi}qX7OLI$N z_DC>W%k5pKL&kE+V#y81FLYIQ&*7EsmDX(an-A&U=X;@Y-}V75Onj1=L>YYCJ=g=^ zF@v4(;~sA_vhU(v2O93t3|j~HI!%K|@q0ZbYz@9@a8}hTvP?-x63z}XgaN46eNZn39&&YjXoU{m884hD1y{Jg#=LBe0$q?E}g3Jisc1& z^}Kl?Ga)3S;>6wC%uGDGqtmX|zYMKJzIU+(WFQpu9~@|J^JoF`>x}V*ji|+FQiw!! zLWw5@-JFzXVv?ehazYT&I(h!wbR5m5jOx_!^TI{FbXd4}E)|s~hC~#qq^6bIu#KcZ zYRn7vCQ}n?LU>C$f~H3J@|URF!Z8ec0*z0g`FHR=f&O1J(7y)#FNim=(ya*$2*>>F z8eaXFpdJ&YPYFlXIlNBzw+IK>m2;JCI4Z84%sJ?VlUGk(m#>X(QtpMht8)vNu3XA_ zYHub!PiW$GJXh|ywNLBasHn}>vFjDSTt)TsQlQGWC?IP7*=hnu-8Nuk*M^nOmhSI- iXg!R=BD?;T(ene}d#V01xI>wYzRcCX7r)blq5eMsHnOt- delta 588 zcmXv~-D(p-82#pFvzwZ9ZPL_4p;@b7QY1EL0}>6mON&iy!GwU4>Jrjr2{aSkHPH4( zQ3Mr5WWWdT&KI!aT{n-A8(j!qsQ00H0cV@!TzqrR;e5lKIa%-{nNc)40|MzU`Bs?# z{0YPqctS=DK4t3X6o8%R60A;K4Q$0$vSGfD-r>K9Q~W(y!*RH)0x(iPGri@(*&5+7 zYTu4b9T^%)k)d^w9HUBWbQZ4TRW#Nbq@gMjcj)%|_~hTHizL1*<>H5GeXUYit5vkE zht>P{9%)&HM?;H6CnS~~Q39NxQ&#dxlkJPqb$q$mVPUUNDJVX`BCU* zZpUJFi&>K+_vFA=oGvpvtZ6w3krn;H?ThgOhIpPY$v2WQ8lL+ZQ9o|H-FUw_As;qJ zajq(_oV&1W5c6#i@n z?+#L|(Y8$UD~yB&Xa3Z^)$Z(e_pFkW7BcvFcSVdLK?n`t;s7rHgY^OEzd`SVenJyy ZZny$8^5(ElQom^Ab$LjDo_7|-(tlElh}QrB 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 b8adb2254dafd59be49259f1cdf9ad102f7c11e1..6a4c9304c09eade1188b0d18d9ba73c963281c94 100644 GIT binary patch delta 2654 zcmZuzO>7&-6`tk4NOAciiXtigtVoGJlB^%gvLnZen>vzgOR^J<9wKxT&E zNIH&ffX*d}kpNM;E|35@=u*H&fdYXpZV&0fr=n<~z%C*z8XzdpqL%_eqf<_Ov-Hw* z>;d%ncHZ~BH}l?`8U6P3|4xWMdcAIf<8L3#ZGN5oOpFKzpXI*z1tqk1IeI?EViI{R zlqS?WD{vSgY;cOOI2B`rC8!T&lKNju9kb9tjJ$1Q5)I-Ggfnr5V11{b@7ze&)g&v5A{>`rwRDBr~?hyOsOeG(t9f9T)yCV#px&%l#$}{My%!fpvc; zCsg7QEIAEaTPMKh+_2SGZ(XVj1!?LtND+WXFd76hQLvyyNe_<9+b6Axq{&2RR^T%sCo`K1_EE!YBtJgn4k7IIOzs(SEc)IUSiWxK0MK ztYNIdV+V1=LBep5)c)hlw8G#%dHX3`stYH9`^iA7zr)%LsUjt61plI#jI@!Am^@-G zb)y$zv)1;PItYWERAXIN|AMVHNHd(%QsZ*2Lplnf4k-bNCrJN7Qi2W}fs&|=7+MOo ztf5J$l`QH`ikbr`IW zL?7qff@{}1sE;& zVqZQwIxP%(%yguVU9>|#jPjMLV=|t^_JFYtVqC`Ew8w@37OY|rvyg%imU0NfAVLJ9 zP=GNQr?YbbiC;)V{6eZ=LoQiX@xq0H$61AzkwV^uU6;mjW4|XLhWmUD;wa}Jh4Lgw zcpBX1VkGYHI3XUqROJ(0%kn_L+`ojE%MibGvHmG?;lK0N6uB%Ep1+{G>x~j?XutMM zx30zM!pg##b*aXxWu;y(qN&lWON(n4SEWnWdM-&T=T^_HOVhgeBtj>;_vIIXE)>@m zUV#O=e$53hzx3kz0IMsobFVvD&=NF(Os97M)|F(s_6D+t)?*DQLb)pZ#I~RV;x+$IKjH5 zsEpsq4Br*}+%8>ZDJ?1H?F^3+(Kf`Fyhn6vxyewdS6NQGD_-xhvg*5Rk=rxAyl_-o z@kRW@+{p+ROI)1Q-t!G7p5y8q3f;z^Yu#P0sts1%+EN%hrTx*j96iU4&Kgs$6dQGR zp5vEM)Q@OM|72QXNp9~{r=1OU6(=V+>V{2I{YvWxk=R`yF2BFLADp;z65F?frE8*7(O0H_sf1f&F0m8!_{(SlDIXdZ)hePCf7*y}9^*eix^3oV|Pf`#!zc|jK&o7)QZ?sl!nlv2f*E!fx!8rdorym$;>B~oxQ zJd=dI!-bLkZLU>N=sjDddZXCDhGOsHcio9XY7ZmL>{Slsf(Y96@bw-EwF`x`p4L{9 z(Y>z{X9SC$;`&9Vyt$>+jTT`PQ4F}?Z?DmZ?T+GAGThxIOyasxOV_FGYNffQyud!f oIs7Mq)L+-;Qw8gHkcq-@7- zK>-B{J8fYZn*s{Z4<$eV2QAPb{wVs@kACzcKtV+TYAk}pA5Fj7f{2Bp$WMD`s9lLk z2jJa1Gxy%Pb025GuTH=Jeesps?LaWZC*NC5o_s1sgioIKy)%9-m_*n$Ca^7pXml9S zcI<9Pv;&JkJF(|l*Qfz|+tH6qG=_b!7RP>|(nrFO@O;YfwP22cW<;4Z{oNU5Hbh1u z%80pJ=u(!=o#q}`NGfld$Aq}@J9EPD7qjw-dApXg!a5(z&M`xilikE!03KUpcg}ve zjj)v%M1*a`Eus;V=B$KrPEBIjty>U}>DjrSBj?uSh^T3cwH8xE>_3z#LM(0};+8!` z450Id3nK{0-ke7_;l)lfOf*BXk9e_*t;qhI@0gUkMJkXB=zAXG8#C0JQ`wUX=^Fvv zXpjW6ZQAd=MuO2?F&JKSax-$w`RVv@5Q3B1BR_VjK4(v=T<> zm=p*9|&Xs%0hcG*Ru zW5(u8ZYS-Ub45u1{Kw#6JB~1&n%t3#>q_4teqE`Pbhapc2}+$f$`9%SuAOr+;5s-L z2df1>Djw(u%RUQcDc!D{?oKb`R;+9AAM9#L%(of_-KSCmGd}C->yKHP^B! zjXU}>M@J31bT8@E?Z1vtnDk%^PvICuC`5V(0SU5w%C@DW)<^nC4=)3#32-fpQ*s+g z$dO|u(alrcL;7&92Vszh>gpe{YR5qzrZra}ZaivZ5F|D>A(?W&oF=Ioz>$>PPtpY! z6x?B>Koj7!W=KAPn znEW{)m5g%PKa`-XnLI76 z6!~h}q*_b&ONGs9NwwW9RSUPva*0QfGSp35=_UBk+l=sg7J*ZZw0uvsudP%dL*Oly zVX?wqE2%m7jp!l*L;#A=mq?k4J{({uRnQ+Tn6SjLtiJkOKipfa9+t zJmk*9S5aJE=imbcjZN51tQd`&x)H3h*JnuimnGUEaz8xa$BzNW04msk6NRiBPHe9M zE6N`1#RwzfVd`MYrq~R=&ESx(jQioB3*KeItM4 z#vRHIp=W?RNaViRKtGgjtQ78SZshN+%Ej`%vny#+v&a?}9$?2|6-anyZf0spDwe7% zt84kyVn#aZ480dKQfpbl97;;JaW1-0b}9l5~pCRlDwl8u&j>P~M^9+~Sp)Z2nSqb}>7HOr?{Ds-`7mj(* z*QyNnm!v~&3aMHxg8s1^v+XOOKXVlFRso(PRh+p@Cg${OLXXd}RCDZvrw38W2y1^@ z0is$8<@NQ|YT8?Cg283B^9@9B%cXmHJt?Y-O*OQor

QnKBk`-csEywL`1?jPhC$ z?xVWcS5s7NY{n606@)5+7w|GrYog0VW|WctvaPGokXxuBA-jLi7TJmH*^*z_U5^8g zay!X=dsmas@7&n8CmL2{wl!QR6yBQu-0El;jP}@Po$+1c?!>#6-TQme@P6m1NAm|Z zWESh*Qy&g~7_W;HpPD8c4rB}M2+nfa9J@__uyKzRG%ULdt~?nNM6Z_Dfj2Dj#b-M;(u^{opH5!r(K z*5nJ1x9(5wo_jW3_YBuxtvB;}@=%;n*W57~JmOG40%`d(w4V8tl2;buqSUd1k6RGj&tuKaHOV zf<5-4v+J$-uPf|5{m3`fi~iJWn#vd#(g`)GJZz6Xe;J(;X6X-D(B~Op|7n`}Vggm~iIe0XbDj^umKIrrT2 zJ72cg<5$Z?t6s<`LoHxarn5|FvEF@$np zUGys`>+jgU*kxYvgpn*cAmWclJuk(^ zWJ3odgG#kk5PMjU$WfRAq(mqVV0Lvd2NBEz)PDeFo?KxEP=SeeXNXTT^g^jfgx0Rw zRT9$5xS-m&QdJih@0+x_#J7|y*YL>7;Jk?~qr8OGs#!A3lKt=lX)&kWuT|CMlAbq~ zufV9$#$_^@6*(ZaNls6t$LFLxwsWPjTFNIRfU`}bT*jvBDIwXV3byPV0Gl`mMz&Ei zT_4x7Gt<)DmGADlnp@Y?*|cO=!J%|Tdc$%LBq^1dkhF5HIq7QNu&kVUs12amH4AQ! z9JrjiDxcjSjx-Dqaq03tq^Zg2$+`ArBDndHor)F-Yv+8Viawa=DcV{K` z60#&IvlFSgH01g7i#%x&<@DS>JF0VoPbOYDg92KHu4+aeLw0Af>Dj5&czQy;dPluJ z`5qL`4dBfGUN&J2UM;O8wd(FByTA4({aBH3`h6ueTyr!PzxTCIY0+gg|{=WK9LnVeaeHH{Ck20A+B*u0=FT~L$BiwjsU zmUP_SAlyn7Dnjp?xLC&4x#~j_J^1>WXCM&xx*+QgymqU|k&w{6#UHI7-QtIS7lP|O zYxDJ?ZK1EltM$cgVbJj-FYg3Vtao+#nLproSfT&769b#D|giiAU;-Sw*AL;B10f}`Oj~DdecFkSY$Wy`kL+_U!T|U@?E?W zMPl^(ll98>@x30YAGvy&E5G^_*P2w$szw zcrCHjGrT$l?8eg9@2p;T!iYbz?H}5SL>t{ho7WysG$P3cpLBdJ{Vp#)64ylsF~?#% zvB8ZD#Bfq@Sbr=CS0k}TsPB2iFAO-{NC-X(tOvdd?)LVtiQsSJRGq8eZ$#n^J`V1J zp+>lW)AyKbh=~T5_{aH#VTAsj6MbJy|7+1-!jE0!r_nE`x$y*>iE|{>>eo5=j~20M z(%)Efwwo<^g7!d0=4n_6f4O4hYi0bd`4))u^Js-(aae|7{zSuj{4ItVHs1u*e-C%| Ad;kCd literal 0 HcmV?d00001 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