18 KiB
Choix de la table du PK pour chaque palette arrivant au PS
Version 1.0 — 27 avril 2026
Ce document décrit l'algorithme exécuté par le WMS lorsqu'une palette source arrive au PS (poste de sortie) et que celui-ci demande au WMS sur quelle table du PK la poser.
Ce qui est hors périmètre : l'ordre de sortie des palettes du TK, le tri par espèce/TC/quantité/poids, l'écriture des
Line.CstAttetOS.CstAtt. Tout cela est géré par un autre process (déclenché surTaskCreatedEvent/OutboundOrderReleasedEvent). Ici, les palettes arrivent déjà triées au PS.
0. Rappel du contexte physique
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 directement posé 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. Seul l'AGV peut déplacer une palette (arrivée, évacuation, recentrage).
- TABLE_CENTRE est le pivot : c'est la seule table adjacente aux deux autres.
1. Déclenchement
L'algorithme est appelé à chaque fois qu'une palette arrive physiquement 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.
2. Entrées de l'algorithme
| Donnée | Source |
|---|---|
Tâche courante : palette source, article, quantité, type de picking (NÉGATIF / DIRECT), Line.CstAtt (numéro de séquence) |
Tâche associée à la palette |
| État des 3 tables du PK cible : 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 qui ne sont ni occupés ni ciblés par une tâche en cours | É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 |
Traitement commercial (TC) de l'article et flag CONTROLE_TRAITEMENT_COMMERCIAL |
Fiche article / paramètre WMS |
3. Décision de niveau 1 : table du PK ou buffer ?
FONCTION décider_destination(palette, PK) :
table_cible ← choisir_table(palette, PK) // voir §4
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 (pas de palette, pas de tâche en vol)
SI buffer existe :
RETOURNER buffer
// Ni table ni buffer disponible
RETOURNER ATTENTE
// La palette reste au PS, le WMS réessaiera
// dès qu'une place se libère (table ou buffer)
4. Décision de niveau 2 : choix de la table
Le choix dépend du type de picking de la tâche associée à la palette.
4.1. 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 source devient la palette fille.
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 ───
// La palette source va au centre (pivot), l'excédent ira sur un côté.
// Maximise la flexibilité : le centre est adjacent aux deux côtés.
SI TABLE_CENTRE == VIDE ET TABLE_GAUCHE == VIDE :
RETOURNER TABLE_CENTRE
SI TABLE_CENTRE == VIDE ET TABLE_DROITE == VIDE :
RETOURNER TABLE_CENTRE
// ─── Priorité 2 : côté + centre libre ───
// Moins optimal (la palette source n'est pas au pivot)
// mais fonctionnel si le centre est libre pour l'excédent.
SI TABLE_GAUCHE == VIDE ET TABLE_CENTRE == VIDE :
RETOURNER TABLE_GAUCHE
SI TABLE_DROITE == VIDE ET TABLE_CENTRE == VIDE :
RETOURNER TABLE_DROITE
// ─── Priorité 3 : centre libre + un côté libérable ───
SI TABLE_CENTRE == VIDE ET TABLE_GAUCHE est libérable :
évacuer(TABLE_GAUCHE)
RETOURNER TABLE_CENTRE
SI TABLE_CENTRE == VIDE ET TABLE_DROITE est libérable :
évacuer(TABLE_DROITE)
RETOURNER TABLE_CENTRE
// ─── Priorité 4 : un côté libre + centre libérable ───
SI TABLE_GAUCHE == VIDE ET TABLE_CENTRE est libérable :
évacuer(TABLE_CENTRE)
RETOURNER TABLE_GAUCHE
SI TABLE_DROITE == VIDE ET TABLE_CENTRE est libérable :
évacuer(TABLE_CENTRE)
RETOURNER TABLE_DROITE
// ─── Dernier recours : évacuation forcée ───
évacuer_table_prioritaire(PK) // cf. §6
RETOURNER choisir_table_picking_négatif(PK) // rappel récursif
4.2. Cas PICKING_DIRECT
En picking direct, la palette source doit être posée sur une table adjacente à la palette fille active. Le choix se fait donc 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) // cf. §4.3
SI table_fille == NULL :
// Pas encore de palette fille → il faudra en créer une.
// On anticipe son emplacement pour choisir la table source.
table_fille ← choisir_table_pour_nouvelle_palette_fille(PK) // cf. §4.4
// ─── Étape 2 : choisir une table adjacente pour la palette source ───
tables_adj ← tables_adjacentes(table_fille)
// TABLE_GAUCHE → [TABLE_CENTRE]
// TABLE_CENTRE → [TABLE_GAUCHE, TABLE_DROITE]
// TABLE_DROITE → [TABLE_CENTRE]
// Prio A : la palette source est déjà sur une adjacente
// (cas multi-tâches pour le même OS depuis la 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) → appliquer ping-pong (cf. §5)
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, bientôt libre)
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) // cf. §6
évacuer(t_à_libérer)
RETOURNER t_à_libérer
4.3. Localisation de la palette fille compatible
FONCTION localiser_palette_fille(tâche, PK) :
// Chercher d'abord parmi les PF actives
POUR chaque table DANS [TABLE_GAUCHE, TABLE_CENTRE, TABLE_DROITE] :
SI table.état == PALETTE_FILLE_ACTIVE :
SI CONTROLE_TRAITEMENT_COMMERCIAL == false :
RETOURNER table
SINON SI table.palette.TC == tâche.article.TC :
RETOURNER table
// Puis parmi les PF en attente (sera réactivée par l'exécution)
POUR chaque table DANS [TABLE_GAUCHE, TABLE_CENTRE, TABLE_DROITE] :
SI table.état == PALETTE_FILLE_EN_ATTENTE :
SI CONTROLE_TRAITEMENT_COMMERCIAL == false
OU table.palette.TC == tâche.article.TC :
RETOURNER table
RETOURNER NULL // aucune palette fille compatible
4.4. Choix de la table pour une nouvelle palette fille
FONCTION choisir_table_pour_nouvelle_palette_fille(PK) :
// TABLE_CENTRE = pivot, adjacente aux 2 côtés → 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) // cf. §6
RETOURNER choisir_table_pour_nouvelle_palette_fille(PK) // rappel récursif
5. 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) :
// Identifier le côté actuellement occupé par une palette source
SI TABLE_GAUCHE.état ∈ {PALETTE_SOURCE_EN_PICKING, PALETTE_SOURCE_EN_ATTENTE} :
RETOURNER TABLE_DROITE // envoyer du côté opposé
SI TABLE_DROITE.état ∈ {PALETTE_SOURCE_EN_PICKING, PALETTE_SOURCE_EN_ATTENTE} :
RETOURNER TABLE_GAUCHE // envoyer du côté opposé
// 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.
6. Évacuation des tables — Priorité
Lorsque aucune table n'est libre et qu'il faut en libérer une :
FONCTION évacuer_table_prioritaire(PK) :
// 1. Palette vide → retrait manuel instantané (pas d'AGV)
POUR chaque table :
SI table contient palette VIDE :
Opérateur retire manuellement → table = VIDE
RETOURNER table
// 2. Table déjà en attente d'évacuation → AGV en route, attendre
POUR chaque table :
SI table.état == EN_ATTENTE_EVACUATION :
Attendre fin du mouvement AGV
RETOURNER table
// 3. Palette source en attente, non réutilisée par la prochaine tâche
POUR chaque table :
SI table.état == PALETTE_SOURCE_EN_ATTENTE
ET table.palette ∉ prochaines_tâches_de_l_OS :
Commander AGV : table → buffer ES ou ASRS
RETOURNER table
// 4. Palette fille en attente → évacuer vers image de quai
POUR chaque table :
SI table.état == PALETTE_FILLE_EN_ATTENTE :
Commander AGV : table → image de quai
RETOURNER table
// 5. Palette source en attente (même si réutilisée) → buffer ES
POUR chaque table :
SI table.état == PALETTE_SOURCE_EN_ATTENTE :
Commander AGV : table → buffer ES
RETOURNER table
7. Gestion des sorties buffer → PK
Quand une table se libère au PK, le WMS choisit parmi les palettes en buffer affectées à ce PK.
FONCTION choisir_palette_buffer_vers_PK(PK, table_libérée) :
palettes_en_buffer ← toutes les palettes en ES affectées à ce PK
SI palettes_en_buffer est vide :
RETOURNER NULL // rien en buffer
// Trier par Line.CstAtt croissant (plus petit = plus prioritaire)
palettes_triées ← trier(palettes_en_buffer, par Line.CstAtt ASC)
// Prendre la palette avec la plus petite séquence
// qui est compatible avec la table libérée
POUR chaque palette DANS palettes_triées :
SI compatible(palette, table_libérée, PK) :
Commander AGV : buffer → table_libérée
RETOURNER palette
RETOURNER NULL // attendre qu'une table compatible se libère
Règle critique : l'ordre de sortie des buffers est dicté par Line.CstAtt, pas par l'ordre d'arrivée physique en buffer.
8. Palettes multi-commandes
Une palette source peut être assignée à plusieurs OS.
Après picking de la commande en cours :
SI palette a encore des tâches pour d'autres OS :
→ Marquer palette = MULTI_COMMANDE
→ Commander AGV : table → buffer ES libre
→ La palette reste en buffer jusqu'au lancement de l'OS suivant
Calcul de capacité buffer :
Une palette multi-commande au PK ou en mouvement compte
comme une place buffer occupée dans le calcul de MAX_PRELOAD_PAR_PK.
9. Priorité d'accès aux buffers entre PK
Lorsque les buffers ES sont saturés :
Priorité définie par la séquence du mode "Picking" dans MODES_PKxx (LIM-69).
Séquence 1 = priorité la plus haute.
Exemple :
PK01 : PICKING;1 → priorité haute
PK03 : PICKING;2 → priorité moyenne
PK05 : PICKING;3 → priorité basse
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.
10. Paramètres WMS utilisés
| 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 | true |
MODES_PKxx |
Modes autorisés + priorité par PK (LIM-69) | — |
PK_BIGBAG |
Autorise ou non les big-bags par PK (LIM-70) | — |
11. Diagramme de flux synthétique
Palette arrive au PS (poste de sortie)
Le PS demande au WMS : "sur quelle table ?"
│
▼
┌─────────────────────┐
│ Type de picking ? │
└───┬────────────┬────┘
│ │
NÉGATIF DIRECT
│ │
▼ ▼
┌──────────────┐ ┌──────────────────────────┐
│ 2 tables │ │ Localiser palette fille │
│ nécessaires: │ │ compatible (TC) │
│ palette + │ │ │
│ adjacente │ │ Si aucune → anticiper │
│ pour excéd. │ │ position nouvelle PF │
└──────┬───────┘ └─────────────┬────────────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────────────────┐
│ Prio 1: centre│ │ Choisir table adjacente │
│ + côté │ │ à la PF : │
│ │ │ - déjà occupée par PS ? │
│ Prio 2: │ │ - VIDE ? (ping-pong) │
│ côté + │ │ - en évacuation ? │
│ centre │ │ - forcer évacuation │
│ │ │ │
│ Prio 3: │ └─────────────┬────────────┘
│ évacuation │ │
└──────┬───────┘ │
│ │
└────────┬───────────────┘
│
▼
Table trouvée ?
┌────┴────┐
OUI NON
│ │
▼ ▼
Palette → Buffer dispo
table PK (< MAX_PRELOAD) ?
┌────┴────┐
OUI NON
│ │
▼ ▼
Palette → Palette reste
buffer ESx au PS (attente)
═══════════════════════════════════════════
Quand une table se libère au PK :
→ Chercher en buffer la palette avec
le plus petit Line.CstAtt pour ce PK
→ Commander AGV : buffer → table