Files
arthur 7496aafe64 lint(limagrain): corrections completes Phase 1+2
- Bloquants: mermaid 03-picking/_index reconstitue (archive 11-05), 13 liens vers pages squelette retires, 2 liens recibles (anoxie, application-dictionary)
- Conventions: 910 em dashes -> tirets simples, 145 checklists -> puces question, 13 questions resolues barrees
- Liens: 9 ancres reparees (slugs GitHub)
- Delta: 12 standard_ref remappes, blocs Standard EasyWMS + sections References ajoutes, front matter complete
- Glossaire: 15 termes standard deplaces en section rappel avec renvoi
- Rapport: limagrain/_lint_report.md (Phase 1 + Phase 2 + re-scan final)
- Inclut les pages des sessions precedentes non commitees + CLAUDE.md et consume.log en l'etat
2026-07-20 12:56:42 +02:00

457 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 ES1ES16, 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 |