--- title: "Placement des palettes PS → PK (choix de table)" tags: [picking, placement, table, buffer, ping-pong, algorithme] status: draft standard_ref: concepts/picking.md jira_refs: [LIM-82, LIM-84, LIM-91, LIM-95, LIM-116] confluence_refs: [] sources: ["Logique combinatoire picking - PS vers PK - V1.0.md", "Jira LIM-82 (commentaires revue de code)", "Jira LIM-95 (Annulé, mergé dans LIM-82)", "Jira LIM-116 (capacité buffers PK → PK)"] last_updated: 2026-07-20 author: Arthur --- # Placement des palettes PS → PK (choix de table) > **Résumé** : algorithme exécuté par le WMS lorsqu'une palette source > arrive au poste de sortie (PS). Il décide sur quelle table du PK la > poser, ou la redirige vers un buffer ES. Ce process s'exécute **en > aval** du [séquençage TK → PS](sequencage-tk-ps.md) : les palettes > arrivent au PS déjà triées. > **Standard EasyWMS** : → voir [Picking](../../concepts/picking.md) > Ce qui suit documente les **spécificités Limagrain** par rapport au > standard. > **Statut (LIM-82)** : dev terminé, **en attente de déploiement pour > test**. Livré en préprod le 25/06/2026, revue de code validée le > **03/07/2026** (après une itération NOK le 02/07). Ce ticket est la > reprise de [LIM-75](https://easywmsfrance.atlassian.net/browse/LIM-75) > (V1 annulé). Positionnement dans la chaîne : > [LIM-80](https://easywmsfrance.atlassian.net/browse/LIM-80) assignation → > [LIM-84](https://easywmsfrance.atlassian.net/browse/LIM-84) séquençage > TK → PS → **LIM-82 placement PS → PK** → > [Process picking au PK (LIM-91)](process-picking-pk.md). > ⚠️ **LIM-95 (Annulé) mergé ici** : le ticket > [LIM-95](https://easywmsfrance.atlassian.net/browse/LIM-95) > « [JOB] génération des mouvements depuis les buffers » est > **Annulé(e)** ; ses modifications ont été intégrées à **LIM-82** > (commentaire Maxime Halgand, 26/06). Ses deux demandes sont couvertes > ici : (1) ne pas générer le mouvement d'une palette si une autre tâche > du même OS a une séquence prioritaire → voir > [Gate de séquence (critère B)](#gate-de-séquence-critère-b) ; (2) > contrôler la capacité de la **TP** (sous-emplacement) en plus de celle > du PK → voir la note ci-dessous. ## Contexte projet Chaque PK dispose de **3 tables** soumises à une contrainte d'adjacence stricte : ``` TABLE_GAUCHE ←→ TABLE_CENTRE ←→ TABLE_DROITE ✅ adjacentes ✅ adjacentes TABLE_GAUCHE ←----------------→ TABLE_DROITE ❌ INTERDIT ``` Règles de mouvement : - Le stock est **toujours sur une palette**, jamais posé directement sur la table - L'opérateur peut déplacer du **stock** (sacs, colis) d'une palette à une autre **uniquement entre deux tables adjacentes** - Il est **interdit de déplacer une palette d'une table à une autre** - ni par l'opérateur, ni par l'AGV. Une palette arrive sur une table et ne peut que repartir (évacuation) - **TABLE_CENTRE** est le pivot : seule table adjacente aux deux autres ## Déclenchement L'algorithme est appelé **à chaque arrivée physique d'une palette au PS**. Le PS interroge le WMS qui retourne une réponse parmi : - **TABLE_GAUCHE**, **TABLE_CENTRE** ou **TABLE_DROITE** → la palette est envoyée sur cette table - **Buffer ESx** → aucune table n'est disponible, la palette est redirigée vers un emplacement buffer - **Attente** → ni table ni buffer disponible, la palette reste au PS ## Entrées de l'algorithme | Donnée | Source | |--------|--------| | Tâche courante : palette source, article, quantité, type de picking (NÉGATIF / DIRECT), `Line.CstAtt` (séquence) | Tâche associée à la palette | | État des 3 tables du PK : VIDE, PALETTE_FILLE_ACTIVE, PALETTE_FILLE_EN_ATTENTE, PALETTE_SOURCE_EN_PICKING, PALETTE_SOURCE_EN_ATTENTE, EN_ATTENTE_EVACUATION | État temps réel du PK | | Tâches suivantes de l'OS (triées par `Line.CstAtt` croissant) | OS en base | | Buffers ES disponibles : parmi ES1–ES16, ceux non occupés ni ciblés | État temps réel des buffers | | Nombre de buffers déjà affectés à ce PK vs `MAX_PRELOAD_PAR_PK` (défaut : 3) | Compteur par PK | ## Décision niveau 1 : table du PK ou buffer ? ``` FONCTION décider_destination(palette, PK) : table_cible ← choisir_table(palette, PK) SI table_cible ≠ NULL : RETOURNER table_cible // Aucune table disponible → tenter un buffer SI nb_buffers_affectés(PK) < MAX_PRELOAD_PAR_PK : buffer ← premier ES libre SI buffer existe : RETOURNER buffer // Ni table ni buffer disponible RETOURNER ATTENTE ``` > [CUSTOM] **Capacité TP (LIM-95)** : dans tous les flux qui vérifient la > capacité du PK, la capacité de la **TP** (sous-emplacement de la table) > doit aussi être contrôlée. Attention : **aucun capteur de présence > n'est relié à Galileo** - la capacité est donc suivie logiquement par > le WMS, pas par un signal physique. ## Décision niveau 2 : choix de la table Le choix dépend du **type de picking** de la tâche associée. ### Cas PICKING_NÉGATIF En picking négatif, la palette source arrive sur une table, l'opérateur retire l'excédent sur une palette posée sur une **table adjacente**, puis échange d'étiquettes. La palette a besoin de **2 tables** : une pour elle, une adjacente libre pour l'excédent. ``` FONCTION choisir_table_picking_négatif(PK) : // Priorité 1 : centre + un côté libre SI TABLE_CENTRE == VIDE ET (TABLE_GAUCHE == VIDE OU TABLE_DROITE == VIDE) : RETOURNER TABLE_CENTRE // Priorité 2 : côté + centre libre SI (TABLE_GAUCHE == VIDE OU TABLE_DROITE == VIDE) ET TABLE_CENTRE == VIDE : RETOURNER côté vide // Priorité 3 : centre libre + un côté libérable SI TABLE_CENTRE == VIDE ET un côté est libérable : évacuer(côté libérable) RETOURNER TABLE_CENTRE // Priorité 4 : un côté libre + centre libérable SI un côté == VIDE ET TABLE_CENTRE est libérable : évacuer(TABLE_CENTRE) RETOURNER côté vide // Dernier recours : évacuation forcée évacuer_table_prioritaire(PK) RETOURNER choisir_table_picking_négatif(PK) // rappel ``` ### Cas PICKING_DIRECT La palette source doit être posée sur une table **adjacente à la palette fille active**. Le choix se fait en fonction de l'emplacement de la palette fille. ``` FONCTION choisir_table_picking_direct(tâche, PK) : // Étape 1 : localiser la palette fille compatible table_fille ← localiser_palette_fille(tâche, PK) SI table_fille == NULL : table_fille ← choisir_table_nouvelle_palette_fille(PK) // Étape 2 : choisir une table adjacente tables_adj ← tables_adjacentes(table_fille) // Prio A : palette source déjà sur une adjacente // (multi-tâches même palette) POUR chaque t DANS tables_adj : SI t contient tâche.PALETTE_SOURCE : RETOURNER t // Prio B : adjacente VIDE // Si 2 adjacentes libres (PF au centre) → ping-pong adjacentes_vides ← [t POUR t DANS tables_adj SI t == VIDE] SI len(adjacentes_vides) == 2 : RETOURNER choisir_côté_ping_pong(PK) SI len(adjacentes_vides) == 1 : RETOURNER adjacentes_vides[0] // Prio C : adjacente en cours d'évacuation (AGV en route) POUR chaque t DANS tables_adj : SI t == EN_ATTENTE_EVACUATION : RETOURNER t // Prio D : forcer l'évacuation d'une adjacente t_à_libérer ← choisir_table_à_évacuer(tables_adj) évacuer(t_à_libérer) RETOURNER t_à_libérer ``` ### Localisation de la palette fille compatible ``` FONCTION localiser_palette_fille(tâche, PK) : // Parmi les PF actives POUR chaque table : SI table.état == PALETTE_FILLE_ACTIVE : RETOURNER table // Note : CONTROLE_TRAITEMENT_COMMERCIAL = false, // donc pas de filtre TC // Puis parmi les PF en attente POUR chaque table : SI table.état == PALETTE_FILLE_EN_ATTENTE : RETOURNER table RETOURNER NULL ``` ### Choix de table pour une nouvelle palette fille ``` FONCTION choisir_table_nouvelle_palette_fille(PK) : // TABLE_CENTRE = pivot → maximise la flexibilité SI TABLE_CENTRE == VIDE : RETOURNER TABLE_CENTRE SI TABLE_GAUCHE == VIDE : RETOURNER TABLE_GAUCHE SI TABLE_DROITE == VIDE : RETOURNER TABLE_DROITE // Aucune table vide → forcer une évacuation évacuer_table_prioritaire(PK) RETOURNER choisir_table_nouvelle_palette_fille(PK) ``` ## Optimisation ping-pong Le ping-pong est l'optimisation principale pour le débit. Il n'est possible que lorsque la **palette fille est au centre** : les palettes sources alternent alors entre TABLE_GAUCHE et TABLE_DROITE, de sorte que la palette suivante est déjà en place quand l'opérateur termine. ``` FONCTION choisir_côté_ping_pong(PK) : SI TABLE_GAUCHE.état ∈ {PALETTE_SOURCE_EN_PICKING, PALETTE_SOURCE_EN_ATTENTE} : RETOURNER TABLE_DROITE SI TABLE_DROITE.état ∈ {PALETTE_SOURCE_EN_PICKING, PALETTE_SOURCE_EN_ATTENTE} : RETOURNER TABLE_GAUCHE // Aucun côté occupé → choix arbitraire RETOURNER TABLE_GAUCHE ``` **Recentrage** : si la palette fille est sur un côté (issue d'un picking négatif), le ping-pong est impossible. Si le nombre de PICKING_DIRECT restants ≥ `SEUIL_RECENTRAGE_PF` (défaut : 3), le WMS peut déclencher un mouvement AGV pour recentrer la PF sur TABLE_CENTRE. ## Évacuation des tables - Priorité Lorsqu'aucune table n'est libre et qu'il faut en libérer une : ``` FONCTION évacuer_table_prioritaire(PK) : // 1. Palette vide → retrait manuel (pas d'AGV) // 2. Table déjà en attente d'évacuation → attendre AGV // 3. Palette source en attente, non réutilisée par // la prochaine tâche → AGV vers buffer/ASRS // 4. Palette fille en attente → AGV vers image de quai // 5. Palette source en attente (même si réutilisée) // → AGV vers buffer ES ``` ## Gestion des buffers ES → PK Quand une table se libère au PK, le WMS choisit parmi les palettes en buffer affectées à ce PK : - Tri par `Line.CstAtt` **croissant** (plus petit = plus prioritaire) - La première palette compatible avec la table libérée est envoyée **Règle critique** : l'ordre de sortie des buffers est dicté par `Line.CstAtt`, **pas** par l'ordre d'arrivée physique en buffer. > [CUSTOM] **Capacité buffers PK → PK (LIM-116)** : la génération de > mouvement depuis le picking (`Task_GenerateMovement_PR_V2`) contrôle la > capacité des buffers lorsqu'une palette va d'un poste de picking à un > autre. Les mouvements manuels du poste (appeler / renvoyer, filmage au > retour) sont détaillés dans > [Process picking au PK](process-picking-pk.md#mouvements-manuels-du-poste-appeler--renvoyer---lim-116). ## Palettes multi-commandes Une palette source peut être assignée à plusieurs OS. Après picking de la commande en cours : - Si la palette a encore des tâches pour d'autres OS → marquée `MULTI_COMMANDE`, envoyée vers un buffer ES libre - Elle reste en buffer jusqu'au lancement de l'OS suivant - Une palette multi-commande au PK ou en mouvement compte comme une place buffer occupée dans le calcul de `MAX_PRELOAD_PAR_PK` ## Priorité d'accès aux buffers entre PK Lorsque les buffers ES sont saturés, la priorité est définie par la séquence du mode "Picking" dans `MODES_PKxx` (voir [Modes de travail PK](stations-picking.md)) : - Séquence 1 = priorité la plus haute - Quand un buffer se libère → affecté en priorité au PK avec la séquence la plus basse parmi ceux qui ont des palettes en attente ## Paramètres WMS | Paramètre | Description | Défaut | |-----------|-------------|--------| | `MAX_PRELOAD_PAR_PK` | Nombre max de palettes pré-chargées en buffer ES par PK | 3 | | `SEUIL_RECENTRAGE_PF` | Nombre min de PICKING_DIRECT restants pour recentrer la PF au centre | 3 | | `CONTROLE_TRAITEMENT_COMMERCIAL` | Séparer ou non les articles par TC sur les palettes filles | `false` (V1.1) | | `MODES_PKxx` | Modes autorisés + priorité par PK (LIM-69) | - | | `PK_BIGBAG` | Autorise ou non les big-bags par PK (LIM-70) | - | ## Implémentation définitive (LIM-82) > Cette section reflète le **code livré et validé** (revue de code du > 03/07/2026). Elle prime sur l'algorithme V1.0 ci-dessus en cas d'écart - > ce dernier reste documenté comme référence de conception. ### Records et workflows | Élément AD | Type | Rôle | |------------|------|------| | `TaskWF` | Record | Ajout d'un CstAtt de tâche (dont `CstAtt3`, flag « palette passée au shuttle »). | | `CST_ChooseDestinationFromPS` | Workflow | Décision principale : table de picking (avec choix de la position), buffer ES si aucune place, ou attente au PS s'il n'y a plus de place (selon `MAX_PRELOAD_PAR_PK`). | | `Container_MovedEventHandler_PS_PR` | Workflow | À l'arrivée d'une palette de tâche picking au PS, appelle `CST_ChooseDestinationFromPS`. | | `Container_MovedEventHandler_ET_PR` | Workflow | Passe `CstAtt3` de la tâche à `True` **après** que la palette a quitté le shuttle, pour autoriser la sortie des palettes suivantes du TK. | | `Container_MovedEventHandler_MS_PR` | Workflow | Idem : passe `CstAtt3` à `True` quand une palette atteint `TS_12` (sortie du `TK_01`, qui ne passe pas par le shuttle). | | `CST_Job_CheckPSBufferContainers` | Job | Ré-évalue les palettes en attente au PS et en buffer : quand une place picking se libère, envoie la bonne palette au picking. | ### Queries | Query | Rôle | |-------|------| | `CST_Picking_GetSiblingTasks` | Récupère les tâches de picking du même **ordre de sortie** (OS). | | `CST_GetTasksWaitingOnPSAndBufffer` | Récupère toutes les tâches sans mouvement présentes sur un PS ou un buffer (en attente de picking ou de stage). | | `CST_GetStationRoutes_ToDisable` | Récupère les routes à désactiver pour le mouvement PS : route PS → PK, et routes PS → buffer plein. | ### Gate de séquence (critère B) Avant toute pose sur une table, le WMS vérifie via `CST_Picking_GetSiblingTasks` s'il existe une autre palette du **même OS** avec un `Line.CstAtt` (séquence, écrit par [LIM-84](https://easywmsfrance.atlassian.net/browse/LIM-84)) **inférieur** encore à traiter en amont (buffer / shuttle). Si oui, la palette qui arrive n'est **pas** posée sur une table : elle part en buffer ES (ou en attente si buffer indisponible). > Cette règle correspond à la 1re demande de > [LIM-95](https://easywmsfrance.atlassian.net/browse/LIM-95) (Annulé, > mergé ici) : « le job traite chaque tâche une par une ; si une autre > tâche de l'OS a une séquence prioritaire (`Task.CstAtt01` plus faible), > ne pas générer le mouvement ». `Task.CstAtt01` et `Line.CstAtt` > désignent la même **séquence de picking** (voir > [ad-customs](../07-admin/ad-customs.md), CstAtt Tâche 01). > ⚠️ **Correction revue de code (02→03/07)** : le contrôle portait > initialement sur les tâches de la **route** ; il a été ramené au périmètre > de l'**OS**. Vérifier sur la route provoquait un faux blocage quand deux OS > d'une même route étaient préparés simultanément sur deux PK différents. ### Gating shuttle (CstAtt3) Le shuttle est le goulot d'ordonnancement entre le TK et les PS. Le flag `CstAtt3` de la tâche est mis à `True` une fois la palette sortie du shuttle (handler `ET`) ou arrivée à `TS_12` pour le `TK_01` qui ne passe pas par le shuttle (handler `MS`). Ce flag conditionne la sortie des palettes suivantes du TK, garantissant l'ordre de passage (cf. [ad-customs](../07-admin/ad-customs.md) CstAtt Tâche 03). ### Écarts avec l'algorithme V1.0 - Les états `EN_ATTENTE_EVACUATION` et la « réactivation » d'une `PALETTE_FILLE_EN_ATTENTE` sont **abandonnés** dans LIM-82 (barrés dans la description du ticket). Les priorités V1.0 qui s'y réfèrent (ex. cas picking direct « adjacente en cours d'évacuation ») ne sont donc plus applicables. - Picking négatif (critère C) : `TABLE_CENTRE` libre **ET** au moins une table de côté adjacente libre → palette source sur `TABLE_CENTRE` ; sinon buffer ES. Une seule palette négative à la fois par PK. - Picking direct (critère D) : table de côté libre **adjacente** à la `PALETTE_FILLE_ACTIVE` ; en cas de double disponibilité, `TABLE_GAUCHE` prioritaire sur `TABLE_DROITE` ; sinon buffer ES. - Les mécanismes `SEUIL_RECENTRAGE_PF` (recentrage AGV) et `CONTROLE_TRAITEMENT_COMMERCIAL` proviennent de la spec V1.0 et ne figurent pas dans les critères validés de LIM-82. → ⚠️ À confirmer côté implémentation. ## Points d'attention ⚠️ L'implémentation livrée (LIM-82) prime sur l'algorithme V1.0 ci-dessus : voir [Implémentation définitive (LIM-82)](#implémentation-définitive-lim-82) (états `EN_ATTENTE_EVACUATION` / réactivation abandonnés, gate de séquence OS, gating shuttle `CstAtt3`). ⚠️ La contrainte d'adjacence est **physique** : l'opérateur ne peut pas déplacer de sacs entre TABLE_GAUCHE et TABLE_DROITE directement. ⚠️ Le picking négatif nécessite 2 tables (palette source + adjacente pour l'excédent), ce qui complique la gestion des cas saturés. ⚠️ Le recentrage de la palette fille (AGV) n'est déclenché que si suffisamment de tâches de picking direct restent (≥ SEUIL_RECENTRAGE_PF). ⚠️ L'ordre de sortie des buffers suit `Line.CstAtt`, pas l'ordre FIFO d'entrée en buffer. ## Questions ouvertes - Big-bag : comportement attendu si une palette big-bag arrive et qu'aucun PK `PK_BIGBAG` autorisé n'est disponible (attente dédiée, refus, autre file) ? (question ouverte LIM-82) - Confirmer si les mécanismes V1.0 `SEUIL_RECENTRAGE_PF` (recentrage AGV) et `CONTROLE_TRAITEMENT_COMMERCIAL` sont réellement implémentés (absents des critères validés LIM-82). - LIM-75 est cité comme pré-requis « Mini Job picking négatif » (évaluation du `TaskType` OrdinaryPicking/NegativePicking) alors qu'il est confirmé Annulé → LIM-82 : clarifier où le `TaskType` est réellement écrit. ## Historique des modifications | Date | Auteur | Modification | |------|--------|--------------| | 2026-05-12 | Arthur | Création depuis spec "Logique combinatoire picking - PS vers PK - V1.0" | | 2026-05-13 | Arthur | Correction règle déplacement palette (jamais déplacée, ni AGV ni opérateur), ajout jira_ref LIM-82 | | 2026-07-17 | Arthur | Relecture revue de code LIM-82 (préprod, validée 03/07) : ajout section « Implémentation définitive » (WF `CST_ChooseDestinationFromPS`, `Container_MovedEventHandler_PS/ET/MS_PR`, job `CST_Job_CheckPSBufferContainers`, queries `CST_Picking_GetSiblingTasks` / `CST_GetTasksWaitingOnPSAndBufffer` / `CST_GetStationRoutes_ToDisable`) ; gate de séquence OS (correction route → OS) ; gating shuttle `CstAtt3` ; abandon états `EN_ATTENTE_EVACUATION` + réactivation PF ; statut préprod ; questions big-bag / recentrage / TaskType LIM-75 | | 2026-07-17 | Arthur | LIM-95 confirmé Annulé(e) → mergé dans LIM-82 (pattern V1/V2, pas de duplication) : note de supersession (chaîne), rattachement des 2 demandes (gate de séquence `Task.CstAtt01`=`Line.CstAtt` ; note capacité TP + absence capteurs Galileo), jira_refs/sources/Références | | 2026-07-20 | Arthur | Note capacité buffers PK → PK (LIM-116, `Task_GenerateMovement_PR_V2`) + renvoi vers les mouvements manuels du poste (process-picking-pk.md) ; jira_refs +LIM-116, sources, last_updated, Références | ## Références | Source | Type | Date | |--------|------|------| | Logique combinatoire picking - PS vers PK - V1.0 | Spécification technique | 27/04/2026 | | [LIM-82](https://easywmsfrance.atlassian.net/browse/LIM-82) | Ticket Jira (placement PS → PK, 5 commentaires, préprod) | 2026-06 → 07 | | [LIM-84](https://easywmsfrance.atlassian.net/browse/LIM-84) | Ticket Jira (séquençage TK → PS, écrit `Line.CstAtt`) | 2026 | | [LIM-75](https://easywmsfrance.atlassian.net/browse/LIM-75) | Ticket Jira (picking négatif TaskType, **Annulé** → LIM-82) | 2026 | | [LIM-95](https://easywmsfrance.atlassian.net/browse/LIM-95) | Ticket Jira ([JOB] mouvements depuis buffers, **Annulé** → mergé LIM-82) | 2026 | | [LIM-91](https://easywmsfrance.atlassian.net/browse/LIM-91) | Ticket Jira (workflow opérateur au PK) | 2026 | | [LIM-116](https://easywmsfrance.atlassian.net/browse/LIM-116) | Ticket Jira (LOT 3, mouvements manuels station picking - capacité buffers PK → PK `Task_GenerateMovement_PR_V2`) | 2026-07 | | [LIM-69](https://easywmsfrance.atlassian.net/browse/LIM-69) | Ticket Jira (modes PK) | 2026 | | [LIM-70](https://easywmsfrance.atlassian.net/browse/LIM-70) | Ticket Jira (Mega Job) | 2026 |