--- title: "Séquençage des tâches de picking TK → PS" tags: [picking, séquençage, transstockeur, événement, workflow, algorithme] status: draft standard_ref: concepts/picking.md jira_refs: [LIM-84, LIM-61] confluence_refs: [] sources: ["LIM-84 LOT2.2 [PICKING] Séquençage des tâches de picking TK PS.md", "Logique combinatoire picking - TK vers PS - V1.1.md", "REU PICKING SEQUENCAGE 11-05-2026 - Analyse comparative notes + AF + devops.md"] last_updated: 2026-05-12 author: Arthur --- # Séquençage des tâches de picking TK → PS > **Résumé** : algorithme événementiel qui ordonne les tâches de picking > depuis les transstockeurs (TK) vers les postes de sortie (PS). Il > écrit les numéros de séquence `Line.CstAtt` sur chaque tâche et > contrôle le flag `OS.CstAtt` pour autoriser le stacker_crane. > **Standard EasyWMS** : → voir [Picking](../../concepts/picking.md) > Ce qui suit documente les **spécificités Limagrain** par rapport au > standard. ## Contexte projet Le séquençage TK → PS est critique pour garantir que les palettes arrivent aux postes de picking dans le bon ordre. L'algorithme s'exécute **en amont** de l'algorithme de [placement PS → PK](placement-ps-pk.md) : les palettes arrivent au PS **déjà triées**. Quatre solutions ont été envisagées avant de retenir une approche événementielle (solution 4). ## Historique des solutions envisagées ### Solution 1 — Process isolé sur finalisation des tâches d'OS Déclenchement à la finalisation de la création des tâches d'un OS. Séquençage via `OS.Line.CstAtt`, marquage OS traité via `OS.CstAtt = true`. **Problème** : pas assez dynamique en cas de recréation de tâches suite à des imprévus dans le WMS. ### Solution 2 — Calcul dans le workflow stacker_crane Intégration du tri directement dans les WF `Galileo_StackerCraneSearch_PR` / `StackerCrane_SortTasks_PR`, avec recalcul complet du pool de tâches à chaque exécution. Supprimerait le besoin de `OS.Line.CstAtt` et `OS.CstAtt`. **Problème** : complexité élevée (requêtes LINQ imbriquées sur les tâches multi-OS). ### Solution 3 — Solution 1 transformée en job Reprise du process de la solution 1 sous forme de job planifié, en excluant les tâches de picking en cours. **Problème** : pas assez réactif par rapport à la cadence de recherche d'ordre des TK. ### Solution 4 — Process événementiel (retenue) Reprise de la solution 1 déclenchée sur deux événements, avec mécanisme de verrouillage `OS.CstAtt` pour bloquer le stacker_crane pendant le séquençage. **Avantages** : simple à développer, isolée et découplée du reste, flexible et réactive. ## Changelog V1.1 (11/05/2026) Modifications issues de la réunion Arthur + Justine (Mecalux) — Olivier (Limagrain), croisées avec l'AF §6.4.8 et le DevOps #64854 : - **Hiérarchie des règles** : nouvel ordre de priorité validé. La complétude palette et l'anti-split de lignes de stock sont des contraintes amont prioritaires sur les règles de tri - **Picking négatif** : ajout condition cumulative poids ≥ 7 kg - **Traitement commercial** : critère de regroupement par TC **supprimé** (`CONTROLE_TRAITEMENT_COMMERCIAL = false`). Pas de bio sur cet entrepôt - **Calcul de remplissage** : méthode pro rata Bag/pal validée, remplace la logique DevOps "Bag/pal max" - **Poids max palette** : 1 250 kg (l'AF fait foi, corrige les 1 200 kg du DevOps) - **Règles confirmées** : mélange d'espèces OK, pas de gerbage, hauteur max 1,90 m, séparateurs inter-lots hors WMS > Voir la section [Arbitrage des contradictions](#arbitrage-des-contradictions-reu-11052026) > pour le détail des décisions. ## Déclenchement L'algorithme est déclenché sur **deux événements** : ### TaskCreatedEvent Une nouvelle tâche de picking vient d'être créée (par le MINI JOB Picking LIM-75, ou suite à une réassignation de stock). ``` SI événement.type == TaskCreatedEvent : SI tâche.type == PICKING ET tâche.OS.statut == Released : traiter_séquençage(tâche.OS) ``` ### OutboundOrderReleasedEvent Un OS passe au statut Released (première mise en service ou relance après un arrêt). ``` SI événement.type == OutboundOrderReleasedEvent : traiter_séquençage(OS) ``` ## Process principal ``` FONCTION traiter_séquençage(OS) : // ─── Étape 1 : verrouiller l'OS ─── OS.CstAtt ← false // ─── Étape 2 : récupérer les tâches ─── toutes_tâches ← récupérer_tâches_picking(OS) // ─── Étape 3 : filtrer ─── tâches_à_séquencer ← [t POUR t DANS toutes_tâches SI t.statut == EN_ATTENTE] SI tâches_à_séquencer est vide : OS.CstAtt ← true RETOURNER // ─── Étape 4 : trier ─── tâches_triées ← trier_tâches(tâches_à_séquencer) // ─── Étape 5 : écrire les séquences ─── écrire_séquences(tâches_triées) // ─── Étape 6 : libérer l'OS ─── OS.CstAtt ← true ``` ```mermaid sequenceDiagram participant EVT as Événement (TaskCreated / OS Released) participant SEQ as Process séquençage participant SC as Stacker Crane EVT->>SEQ: Déclenche SEQ->>SEQ: OS.CstAtt = false Note over SC: Bloqué — ne prend pas
de tâche de cet OS SEQ->>SEQ: Récupère tâches en attente SEQ->>SEQ: Applique tri + écrit séquences SEQ->>SEQ: OS.CstAtt = true Note over SC: Débloqué — reprend
les tâches séquencées ``` ## Contraintes amont (constitution des palettes filles) Ces contraintes orientent le regroupement des lignes sur les palettes filles **avant** le tri. L'algorithme de séquençage doit les respecter : l'ordre de sortie doit être compatible avec la constitution de palettes conformes à ces règles. ### C1. Palettes les plus complètes possible Objectif premier — optimisation transport. Seuil de remplissage ~95%. **Calcul : pro rata Bag/pal.** Chaque sac consomme `1/Bag_pal` de son lot. Le calcul est additif et gère nativement des lots avec des Bag/pal différents sur une même palette. ``` Exemple : 16 sacs d'un lot Bag/pal 20 + 7 sacs d'un lot Bag/pal 50 = 16/20 + 7/50 = 0,80 + 0,14 = 94% ``` Regrouper les lots de même Bag/pal sur une même palette fille facilite la complétude. > Cette méthode remplace la logique DevOps #64854 de "prendre le > Bag/pal max entre lots". Le pro rata est plus précis et ne nécessite > pas de Bag/pal de référence unique. ### C2. Ne pas splitter les lignes de stock Éviter de répartir les sacs d'une même ligne de stock sur plusieurs palettes. **Prioritaire sur les règles de tri** : si respecter "Maïs first" implique de splitter une ligne, on regroupe la ligne complète quitte à décaler le maïs. ### Autres contraintes palette | Contrainte | Valeur | Source | |------------|--------|--------| | Poids max palette | **1 250 kg** | AF (corrige 1 200 kg du DevOps) | | Hauteur max palette | 1,90 m | AF — rejet au PIE si dépassement | | Gerbage | Interdit | — | | Mélange d'espèces | Autorisé sur une même palette fille | — | | Différenciation de marque | Aucune dans une même expédition | — | | Séparateurs (intercalaires) | Règle opérateur, hors WMS | — | ## Règles de tri (séquençage des sorties ASRS) Les tâches de picking d'un même OS sont triées selon les critères suivants, **par ordre de priorité décroissante** : ### Critère 1 — Picking négatif en premier Les tâches de type PICKING_NÉGATIF passent **avant** les tâches de type PICKING_DIRECT. Prioritaire sur toutes les règles suivantes, y compris Maïs first : si une tâche négatif concerne du tournesol, elle passe avant une tâche maïs classique. Condition cumulative pour la détermination NÉGATIF / DIRECT (cf. [détermination picking négatif](#détermination-picking-négatif)) : pourcentage quantité > seuil fiche article (défaut 55%) **ET** poids unitaire sac ≥ 7 kg. En dessous de 7 kg, pas de picking négatif. ``` tri_1(tâche) → 0 si PICKING_NÉGATIF, 1 si PICKING_DIRECT ``` ### Critère 2 — Espèce Maïs en premier Les tâches portant sur l'espèce **Maïs** passent avant les autres espèces. Le maïs est lourd/stable, il constitue la base de la palette fille. ``` tri_2(tâche) → 0 si espèce == MAÏS, 1 sinon ``` ### ~~Critère 3 (V1.0) — Traitement commercial~~ SUPPRIMÉ V1.1 `CONTROLE_TRAITEMENT_COMMERCIAL = false`. L'entrepôt ne fait pas de bio. Paramètre réactivable si besoin futur. ### Critère 3 — Espèce la plus volumineuse Les tâches sont regroupées par espèce, et l'espèce ayant la **plus grande quantité totale de sacs** dans l'OS passe en premier. Commencer par l'espèce la plus volumineuse permet de constituer rapidement la base de la palette fille. ``` tri_3(tâche) → -quantité_totale_espèce(tâche.espèce, OS) ``` ### Critère 4 — Article/lot le plus lourd en base À espèce égale, les tâches portant sur les articles/lots **les plus lourds** passent en premier. Corollaire : les semences essais, très légères, se retrouvent naturellement en haut de palette. ``` tri_4(tâche) → -tâche.article.poids ``` ### Critère 5 — Regroupement par palette source Toutes les tâches portant sur la **même palette source** sont consécutives. L'opérateur enchaîne toutes les tâches d'une palette source avant de la libérer. ``` tri_5(tâche) → tâche.PALETTE_SOURCE.identifiant ``` ### Récapitulatif du tri multi-critères (V1.1) ``` FONCTION trier_tâches(tâches) : RETOURNER tâches.trier_par( (1) type_picking ASC // NÉGATIF (0) avant DIRECT (1) (2) espèce_maïs ASC // MAÏS (0) avant autres (1) (3) quantité_espèce DESC // espèce la + volumineuse (4) poids_article DESC // article le + lourd (5) palette_source // regroupement par palette ) ``` ## Écriture des séquences (Line.CstAtt) ### Règle des ex-aequo Quand deux tâches consécutives sont **interchangeables** (l'ordre entre elles n'a aucun impact fonctionnel), elles reçoivent le **même numéro de séquence**. Cela laisse de la flexibilité au stacker_crane pour optimiser son débit. ### Critères d'interchangeabilité Deux tâches A et B sont interchangeables si : - Elles portent sur la **même palette source**, **OU** - Elles portent sur des **palettes sources différentes** mais tous les critères de tri sont identiques : même type de picking, même espèce, même quantité espèce, même poids article ### Algorithme d'écriture ``` FONCTION écrire_séquences(tâches_triées) : séquence_actuelle ← max(Line.CstAtt des tâches en cours) + 1 SI aucune tâche en cours : séquence_actuelle ← 1 POUR i DE 0 À len(tâches_triées) - 1 : tâche ← tâches_triées[i] tâche.Line.CstAtt ← séquence_actuelle SI i < len(tâches_triées) - 1 : tâche_suivante ← tâches_triées[i + 1] SI interchangeables(tâche, tâche_suivante) : CONTINUER SINON : séquence_actuelle ← séquence_actuelle + 1 ``` ### Exemple ``` Commande avec 5 tâches, après tri : Tâche 1 : Palette A, Maïs, 500kg → séq 1 Tâche 2 : Palette B, Maïs, 500kg → séq 1 (interchangeable) Tâche 3 : Palette C, Maïs, 500kg → séq 1 (interchangeable) Tâche 4 : Palette D, Blé, 300kg → séq 2 (espèce différente) Tâche 5 : Palette E, Blé, 300kg → séq 2 (interchangeable) Résultat Line.CstAtt : [1, 1, 1, 2, 2] Le stacker_crane peut sortir A, B, C dans n'importe quel ordre, puis D ou E dans n'importe quel ordre. ``` ## Comportement du stacker_crane après séquençage Le stacker_crane **ignore** les tâches dont `OS.CstAtt == false`, consomme celles dont `OS.CstAtt == true` par `Line.CstAtt` croissant, et entre tâches à séquence égale il optimise librement (proximité ASRS, charge TK). Il crée les tâches de mouvement TK → PS. Les palettes arrivent ensuite au PS, où l'algorithme de [placement PS → PK](placement-ps-pk.md) décide sur quelle table les poser. ## Détermination picking négatif Le type de picking (NÉGATIF ou DIRECT) est déterminé **par tâche** au moment de la création des tâches par le MINI JOB Picking (LIM-75) : ``` SI quantité_à_prélever > seuil_article × quantité_palette_source ET article unique dans la palette source ET pas d'attribut logistique à capturer ET poids_unitaire_sac ≥ 7 kg ALORS → PICKING_NÉGATIF SINON → PICKING_DIRECT ``` Le seuil % reste paramétrable par fiche article (champ "Complete quantity percent excess for negative picking", défaut : 55%). La détermination se fait **en amont** (MINI JOB Picking, LIM-75), avant l'algorithme de séquençage. L'algorithme de séquençage lit ce type mais ne le calcule pas. ## Cas particuliers ### Recalcul suite à une nouvelle tâche (réassignation de stock) Si une tâche est créée après que l'OS a déjà été séquencé, le `TaskCreatedEvent` déclenche un recalcul : verrouillage, re-tri des tâches en attente uniquement (en commençant après le dernier numéro des tâches en cours), puis libération. ### Relance d'un OS arrêté Si un OS est arrêté puis relancé (`OutboundOrderReleasedEvent`), le même process s'applique. Les tâches en cours conservent leur séquence, les tâches en attente sont re-séquencées. ## Arbitrage des contradictions (réu. 11/05/2026) Analyse comparative de 3 sources (DevOps #64854, AF §6.4.8, réunion 11/05/2026 Arthur + Justine + Olivier). **Tous les points sont résolus.** | Sujet | DevOps | AF | Réunion (fait foi) | |-------|--------|----|--------------------| | Traitement commercial | À creuser | Séparation stricte | **Supprimé** (`CONTROLE_TRAITEMENT_COMMERCIAL = false`) | | Seuil picking négatif | Pas de seuil explicite | > 50% | **Deux conditions cumulatives** : % seuil fiche article (défaut 55%) ET poids sac ≥ 7 kg | | Poids max palette | 1 200 kg | 1 250 kg | **1 250 kg** (AF fait foi) | | Picking négatif vs Maïs first | Pas de hiérarchie | Maïs en tête, négatif séparé | **Picking négatif prioritaire** (confirmé Olivier) | | Semences essais en haut | Oui (dédié) | Non mentionné | **Couvert par règle du plus lourd en base** (essais = légères → haut) | | Lots même Bag/pal | Prioriser regroupement | — | **Retenu** (WF palettes complètes) | | Différenciation marque | Aucune | — | **Confirmé** | | Calcul Bag/pal multi-lots | "Prendre le Bag/pal max" | Équivalent palette | **Pro rata** (chaque sac = 1/Bag_pal de son lot) | ## Paramètres WMS | Paramètre | Description | Défaut | Statut V1.1 | |-----------|-------------|--------|-------------| | `CONTROLE_TRAITEMENT_COMMERCIAL` | Regroupement par TC dans le tri | ~~true~~ | **false** — désactivé | | Seuil picking négatif (fiche article) | "Complete quantity percent excess for negative picking" | 55% | Inchangé | | Poids min picking négatif | Poids unitaire sac minimum pour autoriser le picking négatif | **7 kg** | **Nouveau V1.1** | | Seuil remplissage palette | Taux cible remplissage palette fille | **~95%** | **Nouveau V1.1** | | Poids max palette | Poids maximum palette fille | **1 250 kg** | Corrigé (était 1 200 kg) | | `OS.CstAtt` | Flag verrouillage OS pendant séquençage (true = stacker_crane autorisé) | true | Inchangé | | `MAX_NB_BUFFER_PK` | Nombre de buffers disponibles par PK | 3 | Inchangé | ## Points d'attention ⚠️ Le custom LIM-61 (vérification capacité buffer) doit être modifié pour prendre en compte les palettes multi-PK. ⚠️ Le verrouillage `OS.CstAtt` est temporaire (durée du séquençage) — en cas d'erreur du process, l'OS risque de rester bloqué. ⚠️ Les tâches **en cours** sont exclues du séquençage — seules les tâches en attente sont réordonnées. ⚠️ La condition poids ≥ 7 kg pour le picking négatif est ajoutée pour éviter l'instabilité palette (gros sacs ramenés sur petits sacs). ## Points non traités (hors scope) - Affichage opérateur au PK (combien de sacs ajouter, possibilité de dévier de la consigne) — à traiter une fois les fondations posées - Picking négatif < 7 kg en option (avantage opérationnel sans obligation) — nécessite discussion élargie - Messagerie carton (moins prioritaire, navette du lendemain, zone angle Est) — en attente de précisions - Verrou réception → recomptage avant picking (mentionné dans l'AF uniquement, non reconfirmé) ## Questions ouvertes - [ ] Comportement en cas d'erreur du process de séquençage — OS reste bloqué avec CstAtt = false ? (@Nicolas) - [ ] Impact sur les performances si beaucoup d'OS sont Released simultanément — risque de contention sur les événements ? (@Fabien) ## Historique des modifications | Date | Auteur | Modification | |------|--------|--------------| | 2026-05-12 | Arthur | Création initiale depuis LIM-84 | | 2026-05-12 | Arthur | Refonte complète : intégration spec V1.1 (algo complet, contraintes amont, 5 critères tri, écriture séquences, ex-aequo, cas particuliers) + arbitrage contradictions réunion 11/05/2026 | ## Références | Source | Type | Date | |--------|------|------| | [LIM-84](https://easywmsfrance.atlassian.net/browse/LIM-84) | Ticket Jira | 2026 | | [LIM-61](https://easywmsfrance.atlassian.net/browse/LIM-61) | Ticket Jira (custom capacité buffer) | 2026 | | Logique combinatoire picking - TK vers PS - V1.1 | Spécification technique | 11/05/2026 | | REU PICKING SEQUENCAGE 11-05-2026 | CR réunion + analyse comparative | 11/05/2026 | | DevOps #64854 | Note historique | — | | AF §6.4.8 | Analyse fonctionnelle V1.5 | 28/11/2025 |