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

18 KiB
Raw Blame History

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