Files
mcp-wms-wiki/wiki/sources/archives/Logique combinatoire picking - PS vers PK - V1.0.md
T
2026-05-20 09:41:27 +02:00

431 lines
18 KiB
Markdown
Raw 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.
## 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.CstAtt` et `OS.CstAtt`. Tout cela est géré par un autre process (déclenché sur `TaskCreatedEvent` / `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 ES1ES16, 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
```