7496aafe64
- 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
743 lines
33 KiB
Markdown
743 lines
33 KiB
Markdown
---
|
|
title: "Intégration Still iGo - API PACS et architecture"
|
|
tags: [agv, still, igo, pacs, api, integration, architecture]
|
|
status: draft
|
|
standard_ref: modules/agv.md
|
|
jira_refs: [LIM-103, LIM-104]
|
|
confluence_refs: []
|
|
sources: ["CR technique - iGO STILL - fonctionnement et flux API + correspondance avec le module AGV EasyWMS Opus 4.7 v1.md", "Jira LIM-103 (lecture directe 2026-07-20)", "Jira LIM-104 (lecture directe 2026-07-20)"]
|
|
last_updated: 2026-07-20
|
|
author: Arthur
|
|
---
|
|
|
|
# Intégration Still iGo - API PACS et architecture
|
|
|
|
> **Résumé** : documentation complète de l'intégration du fleet manager
|
|
> **iGO easy** (STILL / KION Group) avec EasyWMS chez Limagrain.
|
|
> Couvre l'API REST PACS 2.3, le cycle de vie des transports, le mapping
|
|
> avec le module AGV standard, l'architecture cible à 4 composants et
|
|
> les réponses FAQ STILL contractuelles.
|
|
|
|
> **Standard EasyWMS** : → voir [AGV - Automated Guided Vehicles](../../modules/agv.md)
|
|
> Le standard communique par **tables d'échange DB** (EAG/AGE/AGS).
|
|
> Chez Limagrain, iGO easy remplace le protocole historique par une
|
|
> **API REST HTTPS** - un middleware (pool IIS C#) assure la traduction.
|
|
|
|
## Contexte projet
|
|
|
|
Limagrain utilise des AGV **Still EXV CB iGo** (gerbeurs électriques
|
|
automatisés) pour les transports internes entre images de quai, PIE,
|
|
postes de picking et zones de stockage. Le fleet manager est
|
|
**iGO easy** (variante simplifiée de PACS - Productized Automated
|
|
Concept Solutions), motorisé par le moteur interne **E'tricc**.
|
|
|
|
La communication est **100 % REST HTTPS** (port 7002, TLS 1.2) - pas
|
|
de PLC ni de tables d'échange SQL directes entre WMS et iGO. Le modèle
|
|
est **pull + push** : le WMS pousse les ordres (`POST /transports`),
|
|
iGO pousse les changements d'état via webhook (callback POST vers une
|
|
URL exposée par le WMS).
|
|
|
|
## Vue d'ensemble iGO / PACS
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
subgraph WMS["Hôte (EasyWMS)"]
|
|
EasyWMS["EasyWMS + CstAGV"]
|
|
end
|
|
|
|
subgraph FleetMgr["iGO easy / PACS Fleet Manager"]
|
|
Etricc["Moteur E'tricc"]
|
|
MyMA["MyMA admin UI"]
|
|
Etricc <--> MyMA
|
|
end
|
|
|
|
subgraph Fleet["Flotte AGV"]
|
|
EXV["EXV CB iGo<br/>(gerbeur ~3.4m)"]
|
|
end
|
|
|
|
EasyWMS <-->|"HTTPS REST<br/>port 7002<br/>TLS 1.2 + X-API-Key"| Etricc
|
|
Etricc -.->|"callback POST<br/>vers WMS"| EasyWMS
|
|
Etricc <--> Fleet
|
|
```
|
|
|
|
Concepts clés :
|
|
|
|
- Pas de notion de routes/segments côté WMS : iGO ne demande qu'une
|
|
`sourceLocation` et une `destinationLocation` - la trajectoire
|
|
physique est gérée par iGO en interne.
|
|
- **Décision tardive** (Group / decision point) : si la destination
|
|
exacte est inconnue à la création, on donne un `destinationGroupId`.
|
|
iGO place le transport en `RequestDestination` et interroge le WMS
|
|
quand l'AGV arrive au point de décision.
|
|
- **Load** = container EasyWMS - passé directement dans le payload de
|
|
création du transport (pas de `POST /api/loads` préalable - FAQ #5).
|
|
|
|
## Stack technique iGO (MyMA)
|
|
|
|
| Composant | Détail |
|
|
|-----------|--------|
|
|
| Backend | .NET 8.0 (C#) |
|
|
| Frontend | Vue 3.2 |
|
|
| Base de données | Postgres 12+ (défaut), SQL Server 2019 ou MySQL 8.0 |
|
|
| OS serveur | Windows 11/Server 2016-2022, Ubuntu 18.04 |
|
|
| Hardware (low) | 4 cores @2.26 GHz, 8 Go RAM, 500 Go RAID5, dual PSU |
|
|
| Hardware (high) | 8 cores, 16 Go RAM, 1 To, dual PSU |
|
|
|
|
### Ports réseau
|
|
|
|
| Service | Port |
|
|
|---------|------|
|
|
| Front-end admin MyMA | 82 |
|
|
| Backend REST interne | 50005 |
|
|
| API Host (HTTPS REST) | **7002** |
|
|
| Postgres | 5432 |
|
|
|
|
## Véhicule - Still EXV CB iGo
|
|
|
|
Gerbeur électrique automatisé (EXV = Elektro-Vertikal) - se déplace
|
|
sur ses propres roues et lève la charge avec le mât. Pas de couloir
|
|
mécanique ni de canal compact.
|
|
|
|
| Caractéristique | Valeur |
|
|
|-----------------|--------|
|
|
| Longueur totale (l1) | 3 383 mm |
|
|
| Longueur jusqu'au dosseret (l2) | 2 083 mm |
|
|
| Largeur tablier fourche | 1 000 / 920 / 1 109 mm |
|
|
| Fourche (s / e / l) | 50 / 100 / 1 300 mm |
|
|
| Hauteur véhicule (arche / mât) | 2 447 / 2 015 mm |
|
|
|
|
## Modèle de ressources API
|
|
|
|
L'API PACS 2.3 expose **8 ressources** :
|
|
|
|
| Ressource iGO | Équivalent EasyWMS | Description |
|
|
|----------------|-------------------|-------------|
|
|
| **Transport** | `AgvTask` | L'ordre de transport (le cœur) |
|
|
| **Vehicle** | AGV station (type 65) | Le véhicule physique |
|
|
| **Load** | `Container` / LPN | La charge physique |
|
|
| **LoadType** | `ContainerType` | Catalogue de types de charge |
|
|
| **Location** | `Location` (`IRealLocation`) | Point physique du warehouse |
|
|
| **Group** | `WorkingZone` | Ensemble de locations (décision tardive) |
|
|
| **System** | - | État global + abonnements |
|
|
|
|
### Champs clés du Transport
|
|
|
|
| Champ iGO | Type | Équivalent EasyWMS |
|
|
|-----------|------|--------------------|
|
|
| `id` | string | (généré par iGO) |
|
|
| `transportHostId` | string | `OrderExtId` (= `Task.TaskNumber`) |
|
|
| `sourceLocationId` / `sourceGroupId` | string | `LoadLocation` |
|
|
| `destinationLocationId` / `destinationGroupId` | string | `UnloadLocation` |
|
|
| `load` | Load | Container (`PalletId`, type, dimensions) |
|
|
| `priority` | int 0-10 | Priority 0-4 (conversion inversée) |
|
|
| `suspended` | bool | (pas d'équivalent - `false` par défaut) |
|
|
| `customMetaData` | dict | `HasTopper`, `PalletType`, etc. |
|
|
| `status` | enum | `AgvStatus` (mapping § ci-dessous) |
|
|
|
|
### Champs clés du Vehicle
|
|
|
|
| Champ | Type | Description |
|
|
|-------|------|-------------|
|
|
| `id` | string | Identifiant véhicule |
|
|
| `mode` | enum | Manual / Automatic / SemiAutomatic / Removed / Disabled |
|
|
| `status` | enum | Idle / Executing / Charging / Faulted / Manual |
|
|
| `pose` | object (x, y, orientation) | Position physique temps réel |
|
|
| `batteryLevel` | int | % batterie |
|
|
| `errors` | array | Codes/labels d'erreur |
|
|
|
|
## Inventaire des endpoints API
|
|
|
|
Tous sur `https://[IP]:7002/api/...` avec header `X-API-Key`.
|
|
|
|
### Transports
|
|
|
|
| Verbe | URL | Effet |
|
|
|-------|-----|-------|
|
|
| GET | `/api/transports` | Liste des transports en mémoire |
|
|
| POST | `/api/transports` | **Créer un transport** |
|
|
| GET | `/api/transports/{id}` | Détail d'un transport |
|
|
| POST | `/api/transports/{id}/final-destination` | Fixer destination finale (group → location) |
|
|
| POST | `/api/transports/{id}/final-source` | Fixer source finale (group → location) |
|
|
| POST | `/api/transports/{id}/suspend` | Mettre en pause |
|
|
| POST | `/api/transports/{id}/release` | Relancer après suspend |
|
|
| POST | `/api/transports/{id}/cancel` | Annuler |
|
|
| POST | `/api/transports/{id}/priority?priority={0-10}` | Changer priorité |
|
|
| POST | `/api/transports/subscription?callbackUrl=…` | S'abonner aux events transport |
|
|
| DELETE | `/api/transports/subscription?callbackUrl=…` | Se désabonner |
|
|
|
|
### Vehicles
|
|
|
|
| Verbe | URL | Effet |
|
|
|-------|-----|-------|
|
|
| GET | `/api/vehicles` / `/{id}` | Lecture véhicules |
|
|
| POST | `/api/vehicles/suspend` | Suspendre toute la flotte |
|
|
| POST | `/api/vehicles/resume` | Relancer toute la flotte |
|
|
| POST | `/api/vehicles/{id}/suspend` / `.../resume` | Suspend/resume un AGV |
|
|
| POST | `/api/vehicles/restart` | Redémarrer la flotte |
|
|
| POST | `/api/vehicles/subscription?callbackUrl=…` | S'abonner aux events vehicle |
|
|
|
|
### Autres
|
|
|
|
| Verbe | URL | Effet |
|
|
|-------|-----|-------|
|
|
| GET | `/api/system` | Status global + subscriptions |
|
|
| GET | `/api/groups` / `/{id}` | Lecture des groupes |
|
|
| GET | `/api/loads` / `/{id}` | Lecture des loads |
|
|
| POST | `/api/loads` | Créer une load (non recommandé - FAQ #5) |
|
|
| GET | `/api/locations` / `/{id}` | Lecture des locations |
|
|
| GET | `/api/load-types` | Catalogue des types |
|
|
|
|
### Endpoints callback côté WMS (webhook receiver)
|
|
|
|
| URL côté WMS | Body reçu | Déclenchement |
|
|
|-------------|-----------|---------------|
|
|
| `POST /agv/transport-event` | Objet Transport complet | Changement d'état transport |
|
|
| `POST /agv/vehicle-event` | Objet Vehicle complet | Changement d'état véhicule |
|
|
|
|
## Cycle de vie d'un Transport
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
Req[Requested] --> Pen[Pending]
|
|
Pen --> Asg[Assigned]
|
|
Asg -->|source = Group| RS[RequestSource]
|
|
Asg -->|source = Location| Ret[Retrieving]
|
|
RS -->|final source set| Ret
|
|
RS -->|AGV arrivé sans réponse| WS[WaitSource]
|
|
WS -->|final source set| Ret
|
|
Ret --> Rtd[Retrieved]
|
|
Rtd -->|dest = Group| RD[RequestDestination]
|
|
Rtd -->|dest = Location| Sto[Storing]
|
|
RD -->|final dest set| Sto
|
|
RD -->|AGV arrivé sans réponse| WD[WaitDestination]
|
|
WD -->|final dest set| Sto
|
|
Sto --> Std[Stored]
|
|
Std --> Fin[Finished]
|
|
|
|
Asg -.cancel.-> Can[Cancelled]
|
|
Pen -.cancel.-> Can
|
|
Ret -.error.-> Abo[Aborted]
|
|
Sto -.error.-> Abo
|
|
```
|
|
|
|
États terminaux : **Finished**, **Cancelled**, **Aborted**.
|
|
|
|
> ⚠️ Le statut `New` est un état interne instantané d'iGO - il
|
|
> n'apparaît jamais dans les callbacks. Le premier état observable est
|
|
> `Requested` (FAQ #4).
|
|
|
|
### Mapping états iGO → phases EasyWMS
|
|
|
|
| iGO Transport.status | Phase AGV EasyWMS | AgvStatus | Notes |
|
|
|---------------------|-------------------|-----------|-------|
|
|
| Requested | - | (après POST) | Premier état observable |
|
|
| Pending | 100 (Order accepted) | `Sent` | En file d'attente iGO |
|
|
| Assigned | 103 (Vehicle assigned) | (Sent) | ⚠️ Pas de Vehicle.id dans le payload (FAQ #1) |
|
|
| RequestSource | 104 (Load permission) | `PendingToBeLoad` | Uniquement en mode Group |
|
|
| Retrieving | - | (Sent) | AGV en route / chargement |
|
|
| Retrieved | 106 (Load confirmed) | (Sent) | **Vehicle.id disponible ici** (FAQ #1) |
|
|
| RequestDestination | 108 (Unload permission) | `PendingToBeUnload` | Uniquement en mode Group |
|
|
| Storing | - | - | AGV en dépose |
|
|
| Stored / Finished | 110 (Unload confirmed) | (purge) | Transport terminé |
|
|
| Cancelled | 255 | (Cancelled) | Annulé |
|
|
| Aborted | 255 | (Cancelled) | Erreur irrécupérable - aucun code d'erreur dans le payload (FAQ #2) |
|
|
|
|
### Différence sémantique majeure : CanPick / CanDrop
|
|
|
|
Le standard EasyWMS attend une **demande explicite d'autorisation**
|
|
(phases 104 / 108) quand `CanPick` / `CanDrop` sont à `false`. iGO ne
|
|
demande l'autorisation **qu'au point de décision d'un Group**. Si la
|
|
source/destination est une Location connue dès la création, iGO
|
|
exécute directement sans demander d'autorisation.
|
|
|
|
Pour reproduire le comportement EasyWMS dans iGO, il faut **forcer
|
|
l'usage de Groups** (même mono-location) là où EasyWMS aurait
|
|
`CanPick = false`.
|
|
|
|
### Conversion de priorité
|
|
|
|
| EasyWMS | iGO | Suggestion |
|
|
|---------|-----|------------|
|
|
| 0 - Urgent | 10 - Highest | mapping direct |
|
|
| 1 - High | 8 | |
|
|
| 2 - Normal | 5 | |
|
|
| 3 - Low | 3 | |
|
|
| 4 - VeryLow | 1 | |
|
|
|
|
## Flux nominal - création et exécution
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
autonumber
|
|
participant WMS as EasyWMS
|
|
participant iGO as iGO easy
|
|
participant V as Vehicle
|
|
|
|
WMS->>iGO: POST /api/transports
|
|
iGO-->>WMS: 200 OK (status=Requested)
|
|
iGO->>WMS: callback (Pending)
|
|
iGO->>V: assigne véhicule
|
|
iGO->>WMS: callback (Assigned)
|
|
V->>iGO: arrivé source
|
|
iGO->>WMS: callback (Retrieving)
|
|
V->>iGO: chargé
|
|
iGO->>WMS: callback (Retrieved)
|
|
V->>iGO: arrivé destination
|
|
iGO->>WMS: callback (Storing)
|
|
V->>iGO: déchargé
|
|
iGO->>WMS: callback (Stored → Finished)
|
|
```
|
|
|
|
### Flux avec décision tardive (Group)
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
autonumber
|
|
participant WMS
|
|
participant iGO
|
|
participant V as Vehicle
|
|
|
|
WMS->>iGO: POST /api/transports {destinationGroupId}
|
|
iGO-->>WMS: 200 OK (Requested)
|
|
iGO->>WMS: callback (Assigned → Retrieving → Retrieved)
|
|
V->>iGO: arrivé au decision point
|
|
iGO->>WMS: callback (RequestDestination)
|
|
WMS->>iGO: POST /final-destination {destinationId}
|
|
iGO-->>WMS: 200 OK
|
|
iGO->>WMS: callback (Storing → Stored → Finished)
|
|
```
|
|
|
|
> ⚠️ Si le WMS ne répond pas assez vite, iGO bascule de
|
|
> `RequestDestination` vers `WaitDestination` (AGV arrivé et en
|
|
> attente). Symétrique côté source : `RequestSource` → `WaitSource`.
|
|
|
|
### Annulation
|
|
|
|
Un transport **ne peut pas être annulé après l'état `Retrieved`**
|
|
(FAQ #6). Si un cancel arrive côté WMS après `Retrieved`, deux
|
|
options : attendre `Finished` puis créer une tâche retour, ou
|
|
intervenir manuellement.
|
|
|
|
## Sécurité et abonnements
|
|
|
|
### Authentification
|
|
|
|
**`X-API-Key`** (confirmé par STILL - FAQ #8). Le header
|
|
`Authorization: Bearer` mentionné dans certaines parties de la doc
|
|
PACS est obsolète. La clé est fixe, fournie par le PM STILL, stockée
|
|
chiffrée dans la config du middleware.
|
|
|
|
### TLS
|
|
|
|
HTTPS avec TLS 1.2. Certificats **auto-signés** côté iGO - le WMS
|
|
doit les truster explicitement (import dans le keystore).
|
|
|
|
### Modèle d'abonnement
|
|
|
|
Au démarrage du WMS :
|
|
|
|
1. `POST /api/transports/subscription?callbackUrl=https://wms/agv/events/transport`
|
|
2. `POST /api/vehicles/subscription?callbackUrl=https://wms/agv/events/vehicle`
|
|
|
|
À l'arrêt : `DELETE` sur les mêmes URLs. Le endpoint callback doit
|
|
être en HTTPS, retourner **200 OK rapidement** (< 1s), traitement
|
|
asynchrone derrière. Le listener doit être **idempotent** : clé de
|
|
déduplication = `transport.id + status` (FAQ #9).
|
|
|
|
## Mapping erreurs iGO → EasyWMS
|
|
|
|
| Code EasyWMS | Famille | Équivalent iGO |
|
|
|-------------|---------|----------------|
|
|
| 1001-1014 | Configuration | `HTTP 400 BadRequest` à la création |
|
|
| 2003 | Extraction error | `Transport.status = Aborted` + `Vehicle.errors[]` |
|
|
| 2004 | Putaway error | idem |
|
|
| 2005 | Manual cancel | `Transport.status = Cancelled` en callback |
|
|
| 2500-2503 | Communication | Erreurs HTTP 5xx / timeouts |
|
|
| 2700 | Wrong container | `Vehicle.errors[]` + `status = Faulted` |
|
|
|
|
> ⚠️ Le payload `Aborted` ne contient **aucun code d'erreur** (FAQ #2).
|
|
> Pour enrichir le `Flags` AGE, il faut faire un `GET /api/vehicles/{id}`
|
|
> complémentaire pour récupérer `errors[].errorCode`.
|
|
|
|
## Mapping verbes / opérations
|
|
|
|
| Action métier | EasyWMS (Operation + EAG) | iGO (REST) |
|
|
|--------------|--------------------------|------------|
|
|
| Créer un ordre | `Operation = Create` + ligne EAG | `POST /api/transports` |
|
|
| Modifier un ordre | `Operation = Update` + ligne EAG | `POST /priority`, `/final-source`, `/final-destination` |
|
|
| Annuler un ordre | `Operation = Delete` + ligne EAG | `POST /api/transports/{id}/cancel` |
|
|
| Suspendre un ordre | (pas d'équivalent) | `POST /suspend` |
|
|
| Reprendre un ordre | (pas d'équivalent) | `POST /release` |
|
|
| Suspendre la flotte | (pas d'équivalent) | `POST /api/vehicles/suspend` |
|
|
| Auth load | EAG `Update` (CanPick=true) | `POST /final-source` |
|
|
| Auth unload | EAG `Update` (CanDrop=true) | `POST /final-destination` |
|
|
|
|
## Architecture cible - Pattern à 4 composants
|
|
|
|
### Principe directeur
|
|
|
|
EasyWMS communique avec les fleet managers externes via une **base
|
|
intermédiaire** (5 tables). Côté Mecalux, la **Gateway AGV** (existante)
|
|
mediate entre les workflows et les tables. Côté flotte, un **middleware
|
|
à développer** (pool IIS C# .NET 8) traduit les tables vers/depuis
|
|
l'API REST PACS.
|
|
|
|
**Le custom CstAGV n'est pas modifié.** L'intégration iGO consiste
|
|
uniquement à fournir le middleware.
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
subgraph EasyWMS["EasyWMS (existant)"]
|
|
Process[Process<br/>Putaway/Picking/Shipping]
|
|
AgvCore[Module AGV core<br/>+ CstAGV]
|
|
GwMec[Gateway AGV Mecalux<br/>workflows ↔ tables]
|
|
end
|
|
|
|
subgraph DB["Base intermédiaire"]
|
|
OQ[(AGV_OUTPUTQUEUE)]
|
|
EAG[(AGV_EAG)]
|
|
IQ[(AGV_INPUTQUEUE)]
|
|
AGE[(AGV_AGE)]
|
|
AGS[(AGV_AGS)]
|
|
end
|
|
|
|
subgraph Pool["Pool IIS C# .NET 8 - À DÉVELOPPER"]
|
|
Pump[Pompe sortante<br/>poll OUTPUTQUEUE → API iGO]
|
|
Hook[Webhook receiver<br/>callbacks iGO → tables]
|
|
end
|
|
|
|
subgraph KION["iGO / PACS (STILL)"]
|
|
iGO[API REST port 7002]
|
|
end
|
|
|
|
Process --> AgvCore --> GwMec
|
|
GwMec --> OQ & EAG
|
|
GwMec -.poll.-> IQ & AGE & AGS
|
|
Pump -.poll.-> OQ
|
|
Pump -.lit.-> EAG
|
|
Pump -->|HTTPS X-API-Key| iGO
|
|
iGO -.webhook.-> Hook
|
|
Hook --> IQ & AGE & AGS
|
|
```
|
|
|
|
### Composants existants - RIEN à modifier
|
|
|
|
| Composant | Rôle | Statut |
|
|
|-----------|------|--------|
|
|
| CstAGV (workflows) | Logique métier AGV | ✅ Existant |
|
|
| Gateway AGV Mecalux | Mediator workflows ↔ tables | ✅ Existant |
|
|
| Tables AGV_* (5) | Base intermédiaire | ✅ Existantes |
|
|
| Vues SmartUI AGV | Monitoring opérateur | ✅ Existantes |
|
|
|
|
### Middleware pool IIS - seul livrable nouveau
|
|
|
|
| Aspect | Description |
|
|
|--------|-------------|
|
|
| Forme | Pool IIS C# ASP.NET Core (.NET 8.0) |
|
|
| Hébergement | Serveur Mecalux (co-localisé ou VM séparée) |
|
|
| Rôle | Pompe sortante + webhook receiver dans un service unique |
|
|
| Accès BDD | Connection vers les 5 tables AGV_* |
|
|
| Sécurité | `X-API-Key` sortant + HTTPS entrant (TLS 1.2) |
|
|
|
|
#### Pompe sortante (OUTPUTQUEUE → API iGO)
|
|
|
|
Polling régulier (1-5 s) sur `AGV_OUTPUTQUEUE WHERE processedDate IS
|
|
NULL ORDER BY creationDate ASC`. Pour chaque ligne : récupère
|
|
`batchId` → lit `AGV_EAG` → interprète l'`Operation` (C/U/D) → appel
|
|
REST iGO. Après ack synchrone (200 OK) : marque `processedDate`.
|
|
|
|
#### Webhook receiver (callbacks → tables)
|
|
|
|
Controller ASP.NET Core exposant deux endpoints HTTPS. À réception :
|
|
insert dans `AGV_INPUTQUEUE` → insert dans `AGV_AGE` ou `AGV_AGS`
|
|
avec le mapping Status → EventType/Flags (cf. tableau ci-dessous).
|
|
Horodatage `DateTime.UtcNow` à la réception (iGO ne fournit pas de
|
|
timestamp - FAQ #3).
|
|
|
|
### Mapping Status iGO → (EventType, Flags) AGE
|
|
|
|
| iGO Transport.status | EventType | Flags | Notes |
|
|
|---------------------|-----------|-------|-------|
|
|
| Pending | 100 | 0 | Order accepted |
|
|
| Assigned | 103 | 0 | Vehicle assigned - `StationNumber=null` (FAQ #1) |
|
|
| Retrieved | 106 | 0 | Load confirmed - `Vehicle.id` disponible ici |
|
|
| Stored / Finished | 110 | 0 | Unload confirmed |
|
|
| Cancelled | 255 | 0 | Annulé |
|
|
| Aborted | - | 2003/2004 | À enrichir via `GET /api/vehicles/{id}` (FAQ #2) |
|
|
|
|
### Pattern de boot du middleware
|
|
|
|
À chaque démarrage :
|
|
|
|
1. Vérifier la base intermédiaire accessible
|
|
2. Vérifier l'API iGO : `GET /api/system`
|
|
3. Vérifier les subscriptions actives - (re)créer si absentes
|
|
4. Réconcilier les transports : `GET /api/transports` vs
|
|
`AGV_OUTPUTQUEUE` non acquittées → générer les lignes AGE manquantes
|
|
5. Démarrer le polling sortant
|
|
|
|
### Logique d'annulation côté middleware
|
|
|
|
À la lecture d'un EAG `Operation=Delete`, le middleware doit vérifier
|
|
le status iGO courant :
|
|
|
|
- **Requested / Pending / Assigned / Retrieving** → `POST /cancel` ✅
|
|
- **Retrieved / Storing / Stored** → ❌ Cancel impossible (FAQ #6) →
|
|
écrire un Flags d'erreur dans AGE → notification opérateur →
|
|
fallback (attendre Finished + tâche retour, ou RFT)
|
|
- **Cancelled / Aborted / Finished** → no-op (déjà terminal)
|
|
|
|
### Refus WMS de l'annulation quand le support est sur un AGV (LIM-104)
|
|
|
|
> **Statut** : préprod ; revue de code validée le 23/06/2026 (Vincent Charvet).
|
|
|
|
**Problème** (découvert aux tests STILL) : si on annule / supprime une Task
|
|
AGV alors que le support est **déjà pris par l'AGV** (transport iGO
|
|
`>= Retrieved`), iGO **refuse** l'annulation et termine physiquement la
|
|
mission - mais le WMS a déjà **supprimé la Task** au moment de la demande.
|
|
`ProcessEvents_PR` s'arrête alors sur sa garde `CST Get Task` (« CST Task
|
|
exists » = false) pour toutes les AGE suivantes :
|
|
|
|
- l'AGE `255/1009` ne déclenche jamais `ProcessError1009` → **notification
|
|
opérateur du refus perdue** ;
|
|
- l'AGE `110` (Unload confirmed) ne déclenche jamais `ContainerMove` →
|
|
**support figé sur l'AGV**.
|
|
|
|
**Solution** : souscription **preview** sur la commande de suppression /
|
|
annulation de Task. Avant exécution :
|
|
|
|
1. récupérer le support (container) associé à la Task ;
|
|
2. vérifier s'il est sur un AGV - critère retenu `Container.StationType ==
|
|
Agv` (équivalent métier de « palette physiquement sur l'AGV » = iGO
|
|
`>= Retrieved` ; repli possible : `LocationCode` commençant par `AGV_`) ;
|
|
3. si **sur AGV** → **refuser** la commande (exception) avec message
|
|
opérateur explicite. La Task reste vivante, l'AGV termine sa mission, le
|
|
support est livré à destination ;
|
|
4. sinon (support encore à la source ou déjà déposé) → laisser la commande
|
|
s'exécuter normalement.
|
|
|
|
| Élément AD | Type | Rôle |
|
|
|-----------|------|------|
|
|
| `CST_TaskCancel_CheckAgv` | Subscription | Appelle le WF à l'**annulation** d'une tâche |
|
|
| `CST_TaskDelete_CheckAgv` | Subscription | Appelle le WF à la **suppression** d'une tâche |
|
|
| `CST_CancelTask_CheckForAgv` | Workflow | Récupère la tâche annulée / supprimée ; si le conteneur est sur un emplacement AGV, lève une erreur (numéro de tâche + code conteneur) |
|
|
| `CST_CancelTask_NotPossible_2` | Ressource | FR « Impossible de supprimer la tâche {0} car le conteneur {1} est sur un AGV » / EN « Cannot delete task {0} because container {1} is on AGV » |
|
|
|
|
Cas de test : (1) annulation **après** pickup (`>= Retrieved`) → refusée,
|
|
support livré, Task `Finished` ; (2) annulation **avant** pickup
|
|
(`< Retrieved`) → acceptée, EAG `D`, middleware POST cancel iGO `204`.
|
|
|
|
### Comparaison avec les Gateways historiques
|
|
|
|
| Aspect | EasyWMSGateway2015 (Galileo) | GatewayRocla2015 | Pool IIS iGO |
|
|
|--------|------------------------------|------------------|--------------|
|
|
| Communication aval | TCP socket (port 3000) | TCP socket (port 50011) | HTTPS REST (port 7002) |
|
|
| Format messages | Frames GALILEO bas niveau | Frames Rocla propriétaires | JSON REST |
|
|
| Auth | `TokenUser` dans config | Intégrée au protocole | `X-API-Key` |
|
|
| Architecture | Monolithique | Monolithique (Gateway + middleware fusionnés) | **Découplée** via tables AGV_* |
|
|
| Liaison Mecalux | (autre architecture) | Abonnement direct WF (héritage EasyB) | Tables AGV_* (archi moderne) |
|
|
|
|
> La pool IIS iGO **ne doit pas** être un fork de GatewayRocla2015
|
|
> (ancienne architecture monolithique). Développement **from scratch**
|
|
> sur ASP.NET Core .NET 8 recommandé.
|
|
|
|
## Workflows EasyWMS - impact iGO
|
|
|
|
Avec le pattern Gateway iGO + tables AGV_*, **aucun workflow EasyWMS
|
|
ni la Gateway AGV Mecalux n'a besoin d'être modifié**. La spécificité
|
|
iGO est entièrement encapsulée dans le middleware.
|
|
|
|
> ⚠️ **Réserve (LIM-103, préprod)** : ce tableau reflète l'**hypothèse de
|
|
> conception**. À l'implémentation, plusieurs **bugs du module AGV standard
|
|
> Mecalux** ont dû être corrigés (dispatch d'events cassé par la
|
|
> transformation Gateway `phase + 100`, attributs `canPick`/`canDrop`
|
|
> jamais assignés, events 106/110 non gérés, refus de mission iGo). Détail :
|
|
> [Corrections des bugs du module AGV standard (LIM-103)](#corrections-des-bugs-du-module-agv-standard-lim-103).
|
|
|
|
| Workflow | Comportement avec Gateway iGO |
|
|
|----------|-------------------------------|
|
|
| `MovementCreatedEventHandler_PR` | ✅ Inchangé - déclencheur |
|
|
| `AgvTask_CreateTaskFromMovement_PR` | ✅ Inchangé |
|
|
| `SerializeAgvTasks_PR` | ✅ Inchangé - écrit EAG, le middleware lit et POST |
|
|
| `ProcessEvents_PR` + `ProcessEvent_*_PR` | ⚠️ **Modifié (LIM-103)** - patch dispatch `phase+100`, gestion events 106/110, refus mission, fix `canPick`/`canDrop` |
|
|
| `ProcessErrors_PR` | ✅ Inchangé - réagit aux Flags dans AGE |
|
|
| `Agvtask_CanPick/CanDrop_PendingToBeSent_PR` | ✅ Inchangé - écrit EAG, le middleware appelle `/final-source` ou `/final-destination` |
|
|
| `TaskCanceledEventHandler_PR` | ✅ Inchangé - écrit EAG Delete, le middleware gère |
|
|
| Workflows RFT | ✅ Inchangés - fallback préservé |
|
|
|
|
## Corrections des bugs du module AGV standard (LIM-103)
|
|
|
|
> **Statut** : préprod ; revue de code à faire (@Vincent, 30/06/2026),
|
|
> assignée Vincent Charvet. Ticket regroupant les corrections nécessaires
|
|
> pour faire fonctionner le module AGV standard sur ce projet.
|
|
|
|
### Queries standard corrigées
|
|
|
|
- `Equipment_AgvTask_GetTask_ByWorkingZone_UI` : correction d'une null
|
|
exception sur query standard.
|
|
- `Equipment_AgvTask_LoadEquipment_UI` : correction d'un paramètre erroné
|
|
sur query standard.
|
|
|
|
### Dispatch d'events cassé par la transformation Gateway `phase + 100`
|
|
|
|
Bug de cohérence Mecalux entre la Gateway et le module AGV. Le record
|
|
`agvEvent` reçu par `ProcessEvents_PR` est un `GalileoEventCreatedWF` dont
|
|
le champ `EventType` (Integer) porte la valeur **SIMO** de la Gateway,
|
|
alors que la DecisionActivity « Event type » de `ProcessEvents_PR` dispatche
|
|
sur les **valeurs natives** 100 / 103 / 104 / 108.
|
|
|
|
Chaîne du bug :
|
|
|
|
- le middleware écrit `phase = 104` dans `agv_age.phase` ;
|
|
- la Gateway lit la ligne et pose `EventType = phase + 100 = 204`
|
|
(avec `Extension[PHASE] = 104`) ;
|
|
- `ProcessEvents_PR` reçoit `EventType = 204` ≠ 100/103/104/108 → **default
|
|
silencieux** → workflow Completed sans side-effect (silent fail).
|
|
|
|
Fix : patch de l'activité « Check parameters » de `ProcessEvents_PR`
|
|
(soustraction de 100 pour retrouver la valeur native).
|
|
|
|
### `canPick` / `canDrop` jamais assignés (LoadPermission / UnloadPermission)
|
|
|
|
Bug majeur du standard : dans `ProcessEvent_LoadPermission_PR`, l'attribut
|
|
interne `canPick` (InitialValue vide) n'est **jamais assigné**. La
|
|
DecisionActivity « Can pick? » prend donc toujours la branche `Otherwise No`
|
|
→ End, et le sous-workflow `Agvtask_CanPick_PendingToBeSent_PR` (qui pose
|
|
`CanPick = true`) n'est **jamais appelé**. Même défaut dans
|
|
`ProcessEvent_UnloadPermission_PR` (attribut `canDrop`).
|
|
|
|
| Workflow | État | Logique |
|
|
|----------|------|---------|
|
|
| `ProcessEvents_PR` | OK (après patch -100) | Dispatch sur `EventType == 100/103/104/108` |
|
|
| `Agvtask_CanPick_PendingToBeSent_PR` | OK | `CanPick = true` hardcodé, fait le flip |
|
|
| `SetAgvStatus_PR` | OK | `if CanPick && CanDrop → Sent ; elif CanPick → PendingToBeUnload ; else → PendingToBeLoad` |
|
|
| `ProcessEvent_LoadPermission_PR` / `ProcessEvent_UnloadPermission_PR` | BUGGÉ | `canPick` / `canDrop` jamais assigné → branche No systématique |
|
|
|
|
Fix : remplacer l'expression de la condition `Yes` de la DecisionActivity
|
|
par `!agvTask.CanPick` (resp. `!agvTask.CanDrop`), qui teste directement la
|
|
propriété de l'`AgvTask` récupérée par « Get AGV task ». En l'état, le
|
|
standard Mecalux est **inutilisable** pour le flow LoadPermission /
|
|
UnloadPermission.
|
|
|
|
### Events 106 / 110 non gérés (fix temporaire)
|
|
|
|
Le standard ne gère pas les events **106** (déplacer le support sur l'AGV)
|
|
et **110** (déplacer le support de l'AGV vers son emplacement de
|
|
destination). Fix temporaire (Michael Chaudier, 29/05) : `ProcessEvents_PR`
|
|
gère désormais les events 106 (confirmation load) et 110 (confirmation
|
|
unload), avec ajout des commandes de déplacement du support - chargement
|
|
sur l'AGV à l'event 106, déchargement à la destination de la tâche à
|
|
l'event 110.
|
|
|
|
### Refus de mission iGo
|
|
|
|
Ajout (Arthur, 01/06) d'une condition dans `ProcessEvents_PR` pour gérer le
|
|
refus de mission par iGo → **annulation de la tâche AGV** ; la transition
|
|
« sequence 0 » est modifiée pour ne pas catcher cette erreur (non gérée par
|
|
le sous-workflow) tant qu'aucun flag d'erreur n'est levé.
|
|
|
|
### Génération du mouvement PS → position PK
|
|
|
|
`Container_MovedEventHandler_PS_PR` modifié (Vincent, 02/06, commit
|
|
[`9fa84a83e2`](https://msscode.mecalux.com/Proyectos_SW/EASYWMS_11351_LIMAGRAIN/commit/9fa84a83e2dfeaa8a4bde22b10a7e2318658bfbb))
|
|
pour générer le mouvement des PS vers la bonne position du PK (**première
|
|
position libre**). Logique conservée pour la recertification et
|
|
l'échantillonnage.
|
|
|
|
> Ce même handler est ensuite spécialisé : redirection selon le **type de
|
|
> tâche** au picking (voir [Placement PS → PK](../03-picking/placement-ps-pk.md),
|
|
> LIM-82) et placement à la **position X max** de l'image de quai à
|
|
> l'expédition (voir [Assignation automatique de l'image de quai](../04-outbound/assignation-image-quai.md),
|
|
> LIM-94). LIM-103 en pose la base (première position libre).
|
|
|
|
## FAQ STILL - réponses contractuelles
|
|
|
|
Réponses obtenues de STILL en avril 2026. Valeur contractuelle.
|
|
|
|
| # | Question | Réponse | Impact |
|
|
|---|----------|---------|--------|
|
|
| 1 | Vehicle.id dès Assigned ? | **Non**, seulement à partir de `Retrieved` | Insérer StationNumber dans AGE uniquement à partir de Retrieved |
|
|
| 2 | Code d'erreur dans payload Aborted ? | **Non**, juste le statut | Enrichir via `GET /api/vehicles/{id}` (errors[]) |
|
|
| 3 | Timestamp dans callbacks ? | **Non** | Horodater à la réception (`DateTime.UtcNow`) |
|
|
| 4 | Statut New vs Requested ? | New = interne instantané, traiter `Requested` comme premier état | Ignorer New |
|
|
| 5 | Créer la Load avant le Transport ? | **Non**, passer les infos dans le transport | Ne PAS appeler `POST /api/loads` |
|
|
| 6 | Annulation après Retrieved ? | **Non** | Vérifier status iGO avant cancel |
|
|
| 7 | Cas d'usage de suspended ? | Pré-création transport avant dispo palette | `suspended = false` par défaut |
|
|
| 8 | X-API-Key vs Bearer ? | **X-API-Key** | Jamais Bearer |
|
|
| 9 | Retry webhook en cas d'indispo ? | Oui, mais fréquence inconnue | Listener idempotent (transport.id + status) |
|
|
| 10 | Persistance après reboot iGO ? | **Oui** | Boot : vérifier subscriptions + réconcilier via GET /transports |
|
|
| 11 | Communication directe ou via iGo Flow ? | **Directe** avec l'API PACS | Pas de couche iGo Flow |
|
|
|
|
### Points encore ouverts avec STILL
|
|
|
|
| # | Sujet | Statut |
|
|
|---|-------|--------|
|
|
| 3b | Fréquence des vehicle/event (pose updates) | ❌ Ouvert |
|
|
| 5b | customMetaData (taille, caractères, ré-émis ?) | ❌ Ouvert |
|
|
| 6b | Création/modification de Location via API | ❌ Ouvert (API GET only) |
|
|
| 7b | Pallet Shuttle (LoadType = 1) | ❌ Ouvert |
|
|
| 8b | Multi-warehouse | ❌ Ouvert |
|
|
| 9b | HasTopper (attribut véhicule) | ❌ Ouvert |
|
|
|
|
## Points d'attention
|
|
|
|
⚠️ **CanPick/CanDrop** : pour reproduire le standard EasyWMS, forcer
|
|
l'usage de Groups même mono-location.
|
|
|
|
⚠️ **Priorité inversée** : EasyWMS 0=Urgent, iGO 10=Max - conversion
|
|
à coder dans le middleware.
|
|
|
|
⚠️ **Pas de routing exposé** : iGO gère ses routes en interne - pas
|
|
d'équivalent à "Routes between stations" en EasyS, pas d'erreur
|
|
"Disabled route" côté iGO.
|
|
|
|
⚠️ **Annulation après chargement impossible** : la logique
|
|
`AgvTask_SetCancelledTask_PR` qui déclenche la recherche de relocation
|
|
après chargement n'a plus de sens dans le mapping iGO. Le WMS **refuse
|
|
désormais l'annulation en amont** via la souscription preview LIM-104 (voir
|
|
[Refus WMS de l'annulation…](#refus-wms-de-lannulation-quand-le-support-est-sur-un-agv-lim-104)).
|
|
|
|
⚠️ **Tests de charge webhook à mener** : simuler Gateway iGO down
|
|
pendant 30s / 1min / 5min pour observer le comportement réel d'iGO
|
|
(retries, intervalles, abandon).
|
|
|
|
## Capacités nouvelles iGO (hors standard EasyWMS)
|
|
|
|
- **Suspend / Resume / Restart** d'une flotte ou d'un véhicule individuel
|
|
- **Pose temps réel** (x, y, orientation) → dashboard live possible
|
|
- **Battery level** → seuils de notification possibles
|
|
|
|
## Questions ouvertes
|
|
|
|
- ❓ Fréquence des callbacks `vehicle/event` pour les mises à jour de
|
|
position - risque de flood (@Nicolas)
|
|
- ❓ Taille max et caractères autorisés dans `customMetaData` (@STILL)
|
|
- ❓ Les `customMetaData` sont-elles ré-émises dans les callbacks
|
|
transport ? (@STILL)
|
|
- ❓ Création/modification de Location via API iGO - limité à GET
|
|
pour l'instant (@STILL)
|
|
- ❓ Gestion Pallet Shuttle via iGO (LoadType = 1) - hors scope
|
|
actuel ? (@Théo)
|
|
- ❓ Multi-warehouse : iGO suppose un seul site - impact si extension
|
|
future ? (@Michael)
|
|
|
|
## Historique des modifications
|
|
|
|
| Date | Auteur | Modification |
|
|
|------|--------|-------------|
|
|
| 2026-05-12 | Arthur | Création initiale depuis CR technique iGO STILL |
|
|
| 2026-07-20 | Arthur | Intégration LIM-103 (corrections bugs module AGV standard, préprod, revue de code à faire) : nouvelle section (2 queries standard corrigées, dispatch d'events cassé par `phase+100`, `canPick`/`canDrop` jamais assignés dans LoadPermission/UnloadPermission, events 106/110 non gérés, refus de mission iGo, `Container_MovedEventHandler_PS_PR` première position libre) ; réserve ajoutée sur le tableau « workflows inchangés » (ligne `ProcessEvents_PR` = Modifié) ; front matter jira_refs/sources/last_updated |
|
|
| 2026-07-20 | Arthur | Intégration LIM-104 (refus WMS annulation/suppression Task si support sur AGV, préprod, revue validée 23/06) : sous-section sous « Logique d'annulation » (désync task supprimée avant refus iGo, souscription preview, critère `Container.StationType == Agv`, table AD `CST_TaskCancel_CheckAgv`/`CST_TaskDelete_CheckAgv`/`CST_CancelTask_CheckForAgv`/`CST_CancelTask_NotPossible_2`, cas de test) ; cross-ref depuis le point d'attention « Annulation après chargement impossible » ; jira_refs +LIM-104 |
|
|
|
|
## Références
|
|
|
|
| Source | Type | Date |
|
|
|--------|------|------|
|
|
| CR technique iGO STILL - fonctionnement et flux API v1 | CR technique | 2026-04-28 |
|
|
| [LIM-103](https://easywmsfrance.atlassian.net/browse/LIM-103) | Ticket Jira (corrections bugs module AGV standard - préprod, revue à faire, commit `9fa84a83e2`) | 2026 |
|
|
| [LIM-104](https://easywmsfrance.atlassian.net/browse/LIM-104) | Ticket Jira (refus annulation Task si support sur AGV - préprod, revue validée 23/06) | 2026 |
|
|
| 2510_PACS-2.3-Host-Interface-Technical-Specifications | Spec API STILL | 2025-10 |
|
|
| iGo easy 2.3 - Host Interface Specifications | Spec API STILL | 2025 |
|
|
| IT requirements R1 20250929 | Spec infra STILL | 2025-09-29 |
|
|
| Technical specification EXV CB iGo | Datasheet véhicule | 2025 |
|