---
share_link: https://share.note.sx/ziuyd353#P//B0nvVix4HFcZp0NAOVtbR3EXhPIYEtuv1VDkKkV4
share_updated: 2026-04-30T09:33:12+02:00
cssclasses:
- full-width
---
# CR technique — iGO STILL : fonctionnement et flux API + correspondance avec le module AGV EasyWMS
> Compte-rendu technique simplifié du fleet manager **iGO easy** (alias **PACS Host API** côté KION/STILL) à partir des documents fournis dans le dossier STILL, suivi d'une table de correspondance complète avec le **module AGV EasyWMS** (cf. `CR_ModuleAGV.md`).
>
> Public : développeurs EasyWMS / intégration robotique. Date : 2026-04-28. Sources :
>
> - `2510_PACS-2.3-Host-Interface-Technical-Specifications_en-US--STILL-.pdf` (référence API la plus récente, 10/2025)
> - `iGo easy 2.3 - Host Interface Specifications.pdf` (variante iGO easy — API identique au PACS 2.3)
> - `IT requirements R1 20250929.pdf` (environnement MyMA — frontend Vue3 + backend .NET 8 + Postgres)
> - `Technical specification EXV CB iGo.pdf` (datasheet véhicule — gerbeur EXV CB iGo)
> - `Limagrain-LWM_Std_Interface_Compliance.xlsx` (matrice de compliance — _non lu automatiquement, format binaire ; sera abordé manuellement avec l'équipe Limagrain_)
---
## Sommaire
1. [Vue d'ensemble iGO / PACS](https://claude.ai/local_sessions/local_5e652410-ce93-46d4-bb6d-ec829a64d798#1-vue-densemble-igo--pacs)
2. [Stack technique et déploiement](https://claude.ai/local_sessions/local_5e652410-ce93-46d4-bb6d-ec829a64d798#2-stack-technique-et-d%C3%A9ploiement)
3. [Modèle de ressources](https://claude.ai/local_sessions/local_5e652410-ce93-46d4-bb6d-ec829a64d798#3-mod%C3%A8le-de-ressources)
4. [Flux API principaux](https://claude.ai/local_sessions/local_5e652410-ce93-46d4-bb6d-ec829a64d798#4-flux-api-principaux)
5. [Cycle de vie d'un Transport](https://claude.ai/local_sessions/local_5e652410-ce93-46d4-bb6d-ec829a64d798#5-cycle-de-vie-dun-transport)
6. [Sécurité et abonnements aux events](https://claude.ai/local_sessions/local_5e652410-ce93-46d4-bb6d-ec829a64d798#6-s%C3%A9curit%C3%A9-et-abonnements-aux-events)
7. [Table de correspondance EasyWMS AGV ↔ iGO](https://claude.ai/local_sessions/local_5e652410-ce93-46d4-bb6d-ec829a64d798#7-table-de-correspondance-easywms-agv--igo)
8. [Points d'attention pour l'intégration](https://claude.ai/local_sessions/local_5e652410-ce93-46d4-bb6d-ec829a64d798#8-points-dattention-pour-lint%C3%A9gration)
9. [FAQ STILL — réponses officielles](https://claude.ai/local_sessions/local_5e652410-ce93-46d4-bb6d-ec829a64d798#9-faq-still--r%C3%A9ponses-officielles)
---
## 1. Vue d'ensemble iGO / PACS
**iGO easy** (et son grand frère **PACS** — _Productized Automated Concept Solutions_) est le **fleet manager AGV** de KION Group / STILL. Le moteur sous-jacent s'appelle `E'tricc` (visible dans la phrase de la doc PACS « _all transports that are still in the memory of E'tricc_ »). Les deux versions exposent **strictement la même API REST** en version 2.3 — le PACS est l'offre commerciale large, iGO easy est la déclinaison « _easy_ » destinée à des installations plus simples.
```mermaid
flowchart LR
subgraph WMS["Hôte (WMS / WES / ERP)"]
direction TB
EasyWMS["EasyWMS"]
end
subgraph FleetMgr["iGO easy / PACS Fleet Manager"]
direction TB
Etricc["Moteur E'tricc"]
MyMA["MyMA - admin UI
Vue3 + .NET8 + Postgres"]
Etricc <--> MyMA
end
subgraph Fleet["Flotte AGV"]
EXV["EXV CB iGo
(gerbeur ~3.4m)"]
Other["...autres modèles STILL"]
end
EasyWMS <-->|"HTTPS REST
port 7002
TLS 1.2 + Bearer"| Etricc
Etricc -.->|"callback POST
vers WMS"| EasyWMS
Etricc <--> Fleet
```
**Concepts clés à retenir :**
- **Pas de PLC, pas de tables d'échange SQL** côté hôte. Tout passe par REST sur HTTPS.
- **Modèle pull + push** : le WMS _pousse_ les ordres (POST /transports) ; iGO _pousse_ les changements d'état via webhook (callback vers une URL exposée par le WMS).
- **Pas de notion de routes/segments** côté WMS : iGO ne demande qu'une `sourceLocation` et une `destinationLocation`. La trajectoire physique entre les deux est entièrement gérée par iGO.
- **Décision tardive (group / decision point)** : si on ne sait pas encore _où exactement_ charger ou décharger au moment de la création du transport, on peut donner un `sourceGroupId` / `destinationGroupId`. iGO place alors le transport en `RequestSource` / `RequestDestination` et **interroge** le WMS au moment où l'AGV arrive au point de décision.
- **Charge utile (Load)** = container EasyWMS. Existe comme ressource indépendante (catalogue) avec dimensions et type.
---
## 2. Stack technique et déploiement
### 2.1 Environnement (MyMA)
D'après `IT requirements R1` :
|Composant|Détail|
|---|---|
|Backend|.NET 8.0 (C#)|
|Frontend|Vue 3.2|
|Base de données|**Postgres 12+** par défaut, SQL Server 2019 ou MySQL 8.0 supportés|
|OS serveur|Windows 11 Pro/Ent, Windows Server 2016/2019/2022, Ubuntu 18.04|
|Navigateurs admin|Edge, Chrome, Firefox|
|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|
### 2.2 Ports réseau
|Service|Port|
|---|---|
|Front-end web (admin MyMA)|**82**|
|Backend (REST interne)|**50005**|
|API Host vers WMS (HTTPS REST)|**7002**|
|Postgres|**5432**|
### 2.3 Wifi (pour terminaux opérateur si présents)
Signal min `-70 dBm`, SNR min `20 dB`, débit min `24 Mbps`, ping moyen `≤ 50 ms`.
### 2.4 Véhicule type — STILL EXV CB iGo
|Caractéristique|Valeur|
|---|---|
|Longueur totale (l1)|3 383 mm|
|Longueur jusqu'au dosseret (l2)|2 083 mm|
|Largeur tablier fourche (b3 / b1 / b2)|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|
> Le véhicule est un **gerbeur électrique automatisé** (EXV = Elektro-Vertikal) — il se déplace sur ses propres roues et lève la charge avec le mât. Il n'a pas de notion de couloir mécanique ni de canal compact à l'instar d'un Pallet Shuttle.
---
## 3. Modèle de ressources
L'API expose **8 ressources** (chapitres 1-8 du PACS) :
```text
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ System │ │ Transport │ ─────▶│ Vehicle │
│ (état global + │ │ (l'ordre du │ │ (AGV physique) │
│ abonnements) │ │ WMS) │ └──────────────────┘
└──────────────────┘ └────────┬─────────┘ ▲
│ │ assignation
▼ │
┌──────────────────┐ ┌──────────────────┐
│ Load │◀──────│ Location │
│ (charge physique │ │ (point géo + │
│ = container) │ │ actions) │
└────────┬─────────┘ └────────┬─────────┘
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ LoadType │ │ Group │
│ (catalogue de │ │ (ensemble de │
│ types) │ │ locations) │
└──────────────────┘ └──────────────────┘
```
### 3.1 Transport (l'ordre)
C'est l'**équivalent direct de l'`AgvTask`** côté EasyWMS.
|Champ|Type|Rôle|
|---|---|---|
|`id`|string|Id généré par iGO|
|`transportHostId`|string|Id hôte (= `OrderExtId` côté EasyWMS)|
|`sourceLocationId` _ou_ `sourceGroupId`|string|Source (un des deux requis)|
|`destinationLocationId` _ou_ `destinationGroupId`|string|Destination (un des deux requis)|
|`load`|Load|Charge embarquée|
|`priority`|int 0-10|10 = max, 0 = min|
|`suspended`|bool|Crée le transport mais sans l'exécuter|
|`customMetaData`|dict (Key/Val)|Métadonnées libres|
|`status`|enum|Voir § 5|
### 3.2 Vehicle (l'AGV)
|Champ|Type|Rôle|
|---|---|---|
|`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 en temps réel|
|`batteryLevel`|int|% batterie|
|`loadId` / `loadHostId`|string|Charge embarquée|
|`locationStationId`|string|Station courante|
|`errors`|array|Codes/labels d'erreur|
### 3.3 Load et LoadType
`Load` = instance physique d'une charge (un container, une palette). Possède `loadHostId` (id côté WMS), `loadTypeId`, `locationId`, `dimensions` (W/D/H/Weight) et `customMetaData`.
`LoadType` = catalogue (id + dimensions par défaut). Endpoint en lecture seule (`GET /api/load-types`).
### 3.4 Location & Group
- **Location** : un point physique sur lequel un AGV peut faire des actions (`possibleActions`), avec types de véhicules et de charges autorisés et `actualLoads` courantes.
- **Group** : un ensemble de Locations. Sert quand on veut différer le choix de la location finale jusqu'au point de décision (cas typique : zone de déchargement avec plusieurs alvéoles). Le WMS appelle alors `POST /api/transports/{id}/final-source` ou `/final-destination` pour fixer le choix.
### 3.5 System
État global : projet courant, `isRunning`, abonnements actifs, timestamp de démarrage.
---
## 4. Flux API principaux
### 4.1 Inventaire des endpoints
Tous sur `https://[IP]:7002/api/...` avec `X-API-Key` (header) et `Authorization: Bearer ` (header).
|Verbe|URL|Effet|
|---|---|---|
|GET|`/api/system`|Status global + liste des subscriptions|
|GET|`/api/transports`|Liste de tous les transports en mémoire|
|POST|`/api/transports`|**Créer un nouveau transport**|
|GET|`/api/transports/{id}`|Détail d'un transport|
|POST|`/api/transports/{id}/final-destination`|Fixer la destination finale (group → location)|
|POST|`/api/transports/{id}/final-source`|Fixer la 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 la priorité|
|POST|`/api/transports/subscription?callbackUrl=…`|S'abonner aux events transport|
|DELETE|`/api/transports/subscription?callbackUrl=…`|Se désabonner|
|GET|`/api/groups` / `/api/groups/{id}`|Lecture des groupes|
|GET|`/api/loads` / `/api/loads/{id}`|Lecture des loads|
|POST|`/api/loads`|Créer une load à une location|
|POST|`/api/loads/{id}/location`|Déplacer une load (manuellement)|
|DELETE|`/api/loads/{id}`|Supprimer une load|
|GET|`/api/load-types`|Catalogue des types|
|GET|`/api/locations` / `/api/locations/{id}`|Lecture des locations|
|GET|`/api/vehicles` / `/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 donné|
|POST|`/api/vehicles/restart`|Redémarrer toute la flotte|
|POST|`/api/vehicles/subscription?callbackUrl=…`|S'abonner aux events vehicle|
|DELETE|`/api/vehicles/subscription?callbackUrl=…`|Se désabonner|
### 4.2 Endpoints à implémenter **côté WMS** (callbacks)
Spécifiés par le client (l'URL est passée à l'inscription).
|URL (côté WMS)|Body reçu|Quand iGO l'appelle|
|---|---|---|
|`POST /api/transport/event`|objet `Transport` complet|À chaque changement d'état d'un transport|
|`POST /api/vehicles/event`|objet `Vehicle` complet|À chaque changement d'état d'un véhicule|
> ℹ️ Le contrat ne définit **pas** la fréquence ni le rate-limiting. À tester en charge — un transport qui passe par tous les états émet ~7 callbacks ; un véhicule qui se déplace peut en émettre beaucoup plus (pose updates).
### 4.3 Flux nominal — création et exécution d'un transport
```mermaid
sequenceDiagram
autonumber
participant WMS as EasyWMS
participant iGO as iGO easy
participant V as Vehicle
WMS->>iGO: POST /api/transports {sourceLocationId, destinationLocationId, load, priority, transportHostId}
iGO-->>WMS: 200 OK Transport(id, status=Requested)
iGO->>WMS: callback transport/event (status=Pending)
iGO->>V: assigne le véhicule
iGO->>WMS: callback transport/event (status=Assigned)
Note over V: AGV se déplace
vers source
V->>iGO: arrivé source
iGO->>WMS: callback (status=Retrieving)
Note over V: AGV charge
V->>iGO: chargé
iGO->>WMS: callback (status=Retrieved)
Note over V: AGV se déplace
vers destination
V->>iGO: arrivé destination
iGO->>WMS: callback (status=Storing)
Note over V: AGV décharge
V->>iGO: déchargé
iGO->>WMS: callback (status=Stored)
iGO->>WMS: callback (status=Finished)
```
### 4.4 Flux avec décision tardive (Group)
C'est le cas où le WMS ne sait pas, à la création, _quelle alvéole exacte_ utiliser :
```mermaid
sequenceDiagram
autonumber
participant WMS
participant iGO
participant V as Vehicle
WMS->>iGO: POST /api/transports {destinationGroupId="DOCK_OUT", ...}
iGO-->>WMS: 200 OK (status=Requested)
iGO->>WMS: callback (status=Pending)
iGO->>WMS: callback (status=Assigned)
iGO->>WMS: callback (status=Retrieving → Retrieved)
Note over V: AGV se déplace
vers le decision point
du groupe DOCK_OUT
V->>iGO: arrivé au decision point
iGO->>WMS: callback (status=RequestDestination)
Note over WMS: WMS choisit une
alvéole précise dans
le groupe DOCK_OUT
WMS->>iGO: POST /api/transports/{id}/final-destination
{destinationId: "DOCK_OUT_03"}
iGO-->>WMS: 200 OK
Note over V: AGV poursuit
vers DOCK_OUT_03
iGO->>WMS: callback (status=Storing → Stored → Finished)
```
> ⚠️ Si le WMS **ne répond pas assez vite**, iGO bascule de `RequestDestination` vers `WaitDestination` (l'AGV est arrivé physiquement et attend). Symétrique côté source : `RequestSource` → `WaitSource`.
### 4.5 Annulation et suspension
```text
Action Endpoint Effet
─────────────────────────────────────────────────────────────────────────────────────
Annuler POST /api/transports/{id}/cancel status=Cancelled (terminal)
Suspendre POST /api/transports/{id}/suspend gel temporaire (peut être levé)
Relancer POST /api/transports/{id}/release inverse de suspend
Re-prioriser POST /api/transports/{id}/priority?p=N 0..10
Suspendre flotte POST /api/vehicles/suspend tous les AGV s'arrêtent
Reprendre flotte POST /api/vehicles/resume
Redémarrer flotte POST /api/vehicles/restart reset hard
```
> ⚠️ **Limite d'annulation confirmée par STILL** : un transport **ne peut pas être annulé après l'état `Retrieved`** (= AGV a déjà pris la palette) — cf. FAQ #6.
>
> **Conséquence côté EasyWMS** : la branche du `AgvTask_SetCancelledTask_PR` qui gérait l'annulation après chargement (et déclenchait la recherche de relocation) **n'a plus de sens dans le mapping iGO**. Si un cancel arrive côté WMS après `Retrieved`, il faut soit :
>
> 1. attendre la fin du transport (`Stored`) et faire alors une nouvelle tâche AGV de retour ;
> 2. ou intervenir physiquement (intervention manuelle / fallback RFT) avant de cancel.
> 💤 **Le champ `suspended`** (paramètre du POST de création) n'a pas d'usage métier identifié par STILL côté WMS classique (cf. FAQ #7). À utiliser uniquement si on veut pré-créer un transport avant que la palette soit physiquement disponible.
---
## 5. Cycle de vie d'un Transport
État final possible : **`Finished`**, **`Cancelled`**, **`Aborted`**.
```mermaid
flowchart TD
New[New] --> Req[Requested]
Req --> Pen[Pending]
Pen --> Asg[Assigned]
Asg -->|source = Group| RS[Request Source]
Asg -->|source = Location| Ret[Retrieving]
RS -->|final source set| Ret
RS -->|AGV arrivé sans réponse| WS[Wait Source]
WS -->|final source set| Ret
Ret --> Rtd[Retrieved]
Rtd -->|dest = Group| RD[Request Destination]
Rtd -->|dest = Location| Sto[Storing]
RD -->|final dest set| Sto
RD -->|AGV arrivé sans réponse| WD[Wait Destination]
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
```
|État|Sens fonctionnel|
|---|---|
|`New`|État **interne instantané** — n'apparaît jamais dans les callbacks. À ignorer côté WMS. (cf. FAQ #4)|
|`Requested`|**Premier état réellement observable** côté WMS, juste après le POST|
|`Pending`|En file d'attente, en attente d'un AGV libre|
|`Assigned`|Un AGV a été désigné|
|`RequestSource` / `WaitSource`|Source = group ; iGO attend la décision finale du WMS|
|`Retrieving`|AGV en route vers la source / en cours de chargement|
|`Retrieved`|Charge prise|
|`RequestDestination` / `WaitDestination`|Destination = group ; iGO attend la décision finale|
|`Storing`|AGV en route vers destination / en cours de dépose|
|`Stored`|Charge déposée|
|`Finished`|Transport terminé proprement (état terminal)|
|`Cancelled`|Annulé via `/cancel` (état terminal)|
|`Aborted`|Erreur d'exécution irrécupérable (état terminal). ⚠ **Aucun code ni message d'erreur** dans le payload — seul le statut est transmis (cf. FAQ #2)|
> 🔑 **Identifiant véhicule** : il n'apparaît dans le payload **qu'à partir de l'état `Retrieved`** (et plus, après donc). À l'état `Assigned`, le payload ne contient pas encore le `Vehicle.id` (cf. FAQ #1). Les workflows EasyWMS (typiquement `ProcessEvent_TaskAssigned_PR` qui faisait le lien Equipment ↔ Task) doivent donc **être recalés sur `Retrieved`** pour le mapping iGO.
> ⏱️ **Pas de timestamp dans le payload** des callbacks (cf. FAQ #3). Si le WMS a besoin d'horodater, il doit le faire à réception (`DateTime.UtcNow`).
---
## 6. Sécurité et abonnements aux events
### 6.1 Sécurité
- **Transport** : HTTPS avec **TLS 1.2** ; certificats **auto-signés** → le WMS doit les _truster_ explicitement (import dans le keystore).
- **Authentification** : ✅ **C'est `X-API-Key` qui est utilisé** (confirmé par STILL — cf. FAQ #8). Le header `Authorization: Bearer ` mentionné dans certaines parties de la doc est obsolète / à ignorer.
- La clé est **fixe** (pas de refresh automatique) → fournie par le PM STILL.
### 6.2 Modèle d'abonnement
```mermaid
sequenceDiagram
participant WMS
participant iGO
Note over WMS: au démarrage du WMS
WMS->>iGO: POST /api/transports/subscription?callbackUrl=https://wms/agv/events/transport
iGO-->>WMS: 204 No Content
WMS->>iGO: POST /api/vehicles/subscription?callbackUrl=https://wms/agv/events/vehicle
iGO-->>WMS: 204 No Content
Note over iGO: en exploitation
iGO->>WMS: POST https://wms/agv/events/transport (Transport JSON)
WMS-->>iGO: 200 OK
iGO->>WMS: POST https://wms/agv/events/vehicle (Vehicle JSON)
WMS-->>iGO: 200 OK
Note over WMS: à l'arrêt du WMS
WMS->>iGO: DELETE /api/transports/subscription?callbackUrl=…
WMS->>iGO: DELETE /api/vehicles/subscription?callbackUrl=…
```
### 6.3 Bonnes pratiques côté EasyWMS
- **Idempotence** : iGO peut retransmettre un event en cas de timeout côté WMS. Le handler côté EasyWMS doit gérer le doublon (par exemple via `transport.id` + `status`).
- **Re-souscription** au boot : si iGO redémarre, les abonnements en mémoire peuvent être perdus. Le WMS doit toujours appeler `GET /api/system` au démarrage et vérifier la présence de la subscription, et la recréer sinon.
- **Endpoint callback** : doit être en HTTPS (sinon iGO refuse), retourner **200 OK** rapidement (idéalement < 1s), traitement asynchrone derrière.
---
## 7. Table de correspondance EasyWMS AGV ↔ iGO
### 7.1 Concepts métier
|EasyWMS (module AGV)|iGO easy / PACS|Commentaire|
|---|---|---|
|`AgvTask` (entité)|`Transport` (resource)|Cœur fonctionnel — l'ordre de transport|
|`Container` / LPN|`Load`|La charge physique|
|`ContainerType`|`LoadType`|Catalogue de types|
|`Location` (`IRealLocation`)|`Location`|Point physique du warehouse|
|`WorkingZone`|`Group`|Ensemble de locations à décision tardive|
|AGV station (type 65)|`Vehicle`|Le véhicule lui-même|
|`AgvLockType` (`AGV_Lock`)|`Location.isEnabled = false`|iGO n'a pas de typage de lock — juste un flag|
|`ManualEquipment` (RFT fallback)|_(aucun équivalent)_|Concept WMS-uniquement|
|`EagMessages` (table SQL)|API REST `POST /api/transports/...`|Tables ↔ HTTP|
|`AgeMessages` (table SQL)|Webhook callback `POST /transport/event`|Tables ↔ HTTP|
|`AgsMessages` (table SQL)|Webhook callback `POST /vehicles/event`|Status AGV|
### 7.2 Champs de l'ordre
|EasyWMS `AgvTask`|iGO `Transport`|Conversion|
|---|---|---|
|`OrderExtId` (long, = `Task.TaskNumber`)|`transportHostId` (string)|`OrderExtId.ToString()`|
|`Id` (Guid)|`id` (string)|iGO génère son propre `id`|
|`LoadLocation`|`sourceLocationId`|mapping direct par code location|
|`UnloadLocation`|`destinationLocationId`|id direct|
|(groupe de couloirs / décision tardive)|`sourceGroupId` / `destinationGroupId`|Pour WorkingZones|
|`LoadType` (0=Container, 1=PalletShuttle)|`Load.loadTypeId`|iGO ne gère pas nativement le PS|
|`PalletId`|`Load.loadHostId`|id côté WMS|
|`PalletType`|`Load.loadTypeId`|référence au catalogue LoadType|
|`Priority` (0=Urgent ... 4=VeryLow)|`priority` (10=max, 0=min)|**Conversion inversée** — voir ci-dessous|
|`HasTopper`|`customMetaData["HASTOPPER"]`|passe en metadata|
|`CanPick`, `CanDrop`|_(pas d'équivalent natif)_|Voir § 8|
|`UnloadAisleNumber/Side/X/Y/Depth`|_(porté par la `Location`)_|iGO gère la géométrie en interne|
|`StationNumber` (AGV courant)|`Vehicle.id` (vu en callback)|id du véhicule assigné|
|`PreviousOrderId` (chaînage)|_(pas d'équivalent natif)_|À encoder en customMetaData|
|`Status` (enum AgvStatus)|`status` (enum Transport)|Voir § 7.3|
|`Operation` (Create/Update/Delete)|endpoints REST distincts|POST / final-* / cancel|
|`WarehouseNumber` / `WarehouseCode`|_(pas exposé)_|iGO suppose un seul site|
**Conversion de priorité :**
|EasyWMS|iGO|Suggestion|
|---|---|---|
|0 — Urgent|10 — Highest|mapping direct|
|1 — High|8||
|2 — Normal|5||
|3 — Low|3||
|4 — VeryLow|1||
### 7.3 États / phases
|Phase AGV (EasyWMS)|EasyWMS `AgvStatus`|iGO `Transport.status`|Notes|
|---|---|---|---|
|(création)|`PendingToBeSend`|(pas encore envoyé)|côté WMS uniquement|
|Envoi EAG|—|`New` → `Requested`|après `POST /api/transports`|
|Phase 100 (Order accepted)|`Sent`|`Pending`|en file d'attente iGO|
|Phase 103 (Vehicle assigned)|(Sent)|`Assigned`|véhicule affecté|
|Phase 104 (Load permission)|`PendingToBeLoad`|`RequestSource` _(group only)_|iGO n'a ce comportement qu'en mode group|
|(autorisation WMS load)|(Sent / re-export EAG)|`POST /final-source`|mais pas un _renvoi du même ordre_ — c'est un endpoint dédié|
|Phase 106 (Load confirmed)|(Sent)|`Retrieving` → `Retrieved`|charge prise|
|Phase 108 (Unload permission)|`PendingToBeUnload`|`RequestDestination` _(group only)_|idem auth load|
|(autorisation WMS unload)|(Sent / re-export EAG)|`POST /final-destination`|endpoint dédié|
|Phase 110 (Unload confirmed)|(Sent → purge)|`Storing` → `Stored` → `Finished`|terminé|
|Phase 255 (Cancel by AGV)|`(Cancelled)`|`Cancelled` ou `Aborted`|aborted = erreur, cancelled = ordre annulé|
> 🎯 **Différence sémantique majeure** : EasyWMS attend une **demande explicite d'autorisation** (phases 104 / 108) **à chaque ordre** quand `CanPick` / `CanDrop` sont à `false`. iGO ne demande l'autorisation qu'**au point de décision d'un Group**. Si la `sourceLocation` est connue dès la création, iGO ne demande **aucune autorisation** — elle exécute directement.
>
> Pour reproduire le comportement EasyWMS dans iGO, il faut **forcer** l'usage de Groups, même mono-location, là où EasyWMS aurait CanPick = false.
### 7.4 Erreurs
|Code EasyWMS|Famille|Équivalent iGO|Remarque|
|---|---|---|---|
|1001-1014|Configuration (mauvaise station, ordre dupliqué…)|`HTTP 400 BadRequest` à la création|iGO refuse à la requête|
|1003 (duplicate)||`HTTP 400` sur POST /transports si transportHostId déjà utilisé (à valider)||
|1009 (cancellation error)||`HTTP 400/404` sur POST /cancel||
|2003 (extraction error)|Exécution|`Transport.status = Aborted` + `Vehicle.errors[]`|erreur runtime|
|2004 (putaway error)|Exécution|idem||
|2005 (manual cancel from AGV UI)|Exécution|`Transport.status = Cancelled` reçu en callback||
|2500-2503|Communication|erreurs **HTTP 5xx / timeouts**|au niveau réseau|
|2700 (wrong/missing container)|Exécution|`Vehicle.errors[]` + `status = Faulted`|côté véhicule|
> 💡 Côté EasyWMS, les codes 1001-2700 sont stockés dans `agvEvent.Flags` et routés par `ProcessErrors_PR`. **La logique côté EasyWMS doit donc être étendue** pour transformer une réponse HTTP / un callback iGO en un `agvEvent.Flags` numérique, qui ensuite ré-utilise toute la machinerie standard de notification (`ProcessError1001_PR`, etc.).
### 7.5 Verbe / opération
|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|(selon le cas) `POST /priority`, `POST /final-source`, `POST /final-destination`|
|Annuler un ordre|`Operation = Delete` + ligne EAG|`POST /api/transports/{id}/cancel`|
|Modifier priorité|`AgvTaskEditCommand` + `Operation = Update`|`POST /api/transports/{id}/priority?priority=N`|
|Suspendre un ordre|(pas d'équivalent)|`POST /api/transports/{id}/suspend` ➕|
|Reprendre un ordre|(pas d'équivalent)|`POST /api/transports/{id}/release` ➕|
|Suspendre la flotte|(pas d'équivalent)|`POST /api/vehicles/suspend` ➕|
|Demande d'auth load|EAG `Operation = Update` (CanPick=true)|`POST /api/transports/{id}/final-source`|
|Demande d'auth unload|EAG `Operation = Update` (CanDrop=true)|`POST /api/transports/{id}/final-destination`|
➕ = capacité **nouvelle** offerte par iGO, sans contrepartie WMS — utile pour la maintenance.
### 7.6 Topologie / configuration
|EasyWMS|iGO|
|---|---|
|AGV créé en EasyS dans un _AGV equipment group_ (type 65)|`Vehicle` configuré dans MyMA|
|`Routes` configurées en EasyS entre stations|Layout iGO, géré dans MyMA / iGO designer|
|`LoadAisle` / `UnloadAisle` (couloirs manuels)|`Location.possibleActions`|
|`Allow loading / Allow unloading` per location|`Location.isEnabled` + `possibleActions`|
|`Manual loading aisle`|(pas d'équivalent — iGO calcule la trajectoire)|
|`LocationLockType` "For AGV"|`Location.isEnabled = false` (binaire)|
|Fault notification group AGV|callback `POST /vehicles/event` (errors[])|
### 7.7 Workflows EasyWMS impactés par l'intégration iGO
> 📖 **Vocabulaire** : dans la suite, l'expression **« Gateway iGO »** est utilisée comme raccourci pour désigner le **middleware côté flotte à développer pour iGO** (cf. § 8.4) — concrètement, **une pool IIS C# .NET 8** développée par Mecalux qui regroupe la pompe sortante (poll `AGV_OUTPUTQUEUE` → API iGO) et le webhook receiver (callbacks iGO → écrit `AGV_INPUTQUEUE`+`AGE`/`AGS`). Il **ne doit pas être confondu** avec la **Gateway AGV Mecalux**, qui elle existe déjà dans le module AGV core et joue un rôle différent (mediator côté Mecalux entre workflows et tables).
> ✅ **Avec le pattern Gateway iGO + Gateway AGV Mecalux + tables EAG/AGE/AGS (cf. § 8.4) : 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 Gateway iGO, qui se contente de traduire le contenu des tables vers/depuis l'API REST PACS.
|Workflow EasyWMS|Comportement avec Gateway iGO|
|---|---|
|`MovementCreatedEventHandler_PR`|✅ Inchangé — toujours le déclencheur|
|`AgvTask_CreateTaskFromMovement_PR`|✅ Inchangé — produit un `AgvTaskWF`|
|`AgvTask_SetCreatedTask_PR`|✅ Inchangé — `Status = PendingToBeSend`|
|`SerializeAgvTasks_PR` + `CreateMovTrackings_PR`|✅ Inchangé — écrit en table EAG. **Le Gateway iGO** lit cette ligne et exécute le `POST /api/transports`.|
|`ProcessEvents_PR` + `ProcessEvent_*_PR`|✅ Inchangé — poll AGE comme d'habitude. **Le Gateway iGO** alimente AGE en traduisant les callbacks REST.|
|`ProcessErrors_PR` + `ProcessError*_PR`|✅ Inchangé — réagit aux `Flags` dans AGE. **Le Gateway** est responsable d'inscrire un `Flags` cohérent (2003, 2004, etc.) à partir du `status=Aborted` + GET vehicles errors.|
|`Agvtask_CanPick/CanDrop_PendingToBeSent_PR`|✅ Inchangé — réécrit une ligne EAG avec `CanPick=true`/`CanDrop=true`. **Le Gateway** reconnaît ce pattern et appelle `POST /transports/{id}/final-source` ou `/final-destination`.|
|`TaskCanceledEventHandler_PR` + `AgvTask_SetCancelledTask_PR`|✅ Inchangé — écrit une ligne EAG `Operation=Delete`. **Le Gateway** appelle `POST /transports/{id}/cancel` (et signale impossibilité si statut iGO ≥ Retrieved).|
|`MovementDestinationChanged_PR`|✅ Inchangé — émet un `Operation=Delete` puis `Operation=Create`. **Le Gateway** chaîne `cancel` puis nouveau `POST /transports`.|
|Workflows RFT (`Equipment_AgvTask_*_UI`)|✅ Inchangés — fallback opérateur préservé si iGO indisponible|
|`AGV_LocationLockType_Create_PR`|✅ Inchangé — init du LockType `AGV_Lock` côté EasyWMS. La pose effective du verrou n'a pas d'écho direct vers iGO (les locations iGO ne sont gérées que par MyMA).|
|`Galileo_EventCreatedEventHandler_PR` (partial)|✅ Inchangé — non concerné par iGO (côté Galileo seulement)|
> 📌 **Conséquence projet** : la livraison du custom `CstAGV` n'a **rien à voir** avec l'intégration iGO. Le custom AGV reste tel qu'il est, et c'est le Gateway iGO (livrable séparé, service Windows à part) qui porte toute la couche d'adaptation. C'est la bonne séparation des responsabilités.
---
## 8. Points d'attention pour l'intégration
### 8.1 Différences structurelles
1. **Pas de tables d'échange** : il faut un service HTTP listener côté EasyWMS pour recevoir les callbacks. Exposé typiquement sur le serveur WMS, sécurisé en HTTPS, avec un certificat trusté par MyMA.
2. **Pas de phases distinctes 100/103/104/106/108/110** dans iGO — tout est **état du Transport**. Le mapping en phases EasyWMS est un travail de traduction côté custom.
3. **Pas de `CanPick`/`CanDrop` natifs** : pour reproduire la demande d'autorisation, il faut **passer par les Groups**. Conséquence : le layout iGO doit déclarer un Group pour chaque location qui doit demander auth.
4. **Priorité 0-10** vs 0-4 : conversion bijective à fixer (cf. § 7.2).
5. **Pas de routing exposé** : iGO gère ses routes en interne. Donc pas d'équivalent à `Routes between stations` en EasyS, et pas d'erreur `Disabled route` côté iGO.
### 8.2 Capacités nouvelles
iGO offre des capacités que l'AGV core EasyWMS n'expose pas :
- **Suspend / Resume / Restart** d'une flotte ou d'un véhicule individuel sur demande WMS.
- **Pose temps réel** de chaque véhicule (`x, y, orientation`) → utilisable pour un dashboard live côté EasyWMS.
- **Battery level** → permet de déclencher des événements ERP/notifs à seuils.
### 8.3 Sujets levés avec STILL — état au 28/04/2026
Le retour FAQ STILL a clos plusieurs questions (cf. § 9). Récapitulatif :
|#|Sujet|Statut|
|---|---|---|
|1|`X-API-Key` vs `Bearer token`|✅ **Résolu** : c'est `X-API-Key` (FAQ #8)|
|2|Re-livraison des callbacks|🟡 **Partiel** : iGO retente, mais fréquence inconnue → à tester en charge (FAQ #9)|
|3|Fréquence des `vehicle/event` (pose updates)|❌ Toujours ouvert|
|4|Comportement queue saturée (transport `Pending` sans véhicule)|🟡 **Partiel** : on sait au moins que les transports survivent à un redémarrage iGO (FAQ #10)|
|5|`customMetaData` (taille, caractères, ré-émis ?)|❌ Toujours ouvert|
|6|Création/modification de `Location` via API|❌ Toujours ouvert (l'API n'expose que `GET`)|
|7|Pallet Shuttle (LoadType = 1)|❌ Toujours ouvert|
|8|Multi-warehouse|❌ Toujours ouvert|
|9|HasTopper (attribut véhicule)|❌ Toujours ouvert|
|10|`New` vs `Requested`|✅ **Résolu** : `New` est interne et instantané, traiter `Requested` comme premier état (FAQ #4)|
|11|`Vehicle.id` disponible dès `Assigned` ?|✅ **Résolu** : non, seulement à partir de `Retrieved` (FAQ #1)|
|12|Code/message d'erreur dans payload `Aborted`|✅ **Résolu** : aucun — juste le statut (FAQ #2)|
|13|Timestamp dans les callbacks|✅ **Résolu** : aucun — le WMS doit horodater à réception (FAQ #3)|
|14|Workflow `Load` : créer la palette avant ou avec le transport ?|✅ **Résolu** : ne pas créer — passer les infos directement dans le transport (FAQ #5)|
|15|Annulation après chargement|✅ **Résolu** : non, impossible après `Retrieved` (FAQ #6)|
|16|Cas d'usage du `suspended`|✅ **Résolu** : utile uniquement si pré-création d'un transport avant disponibilité de la palette (FAQ #7)|
|17|Persistance après redémarrage iGO|✅ **Résolu** : oui, les données survivent (FAQ #10)|
|18|Communication directe vs via `iGo Flow`|✅ **Résolu** : directement avec l'API PACS (FAQ #11)|
### 8.4 Architecture cible — Pattern à 4 composants (Gateway Mecalux + middleware pool IIS C#)
> 🔑 **Principe directeur (vérifié dans la doc Mecalux `Interface_Communications_AGVs_EN.pdf` et le contrat technique `RT_AGVs_EN.pdf`)** : EasyWMS communique avec les fleet managers externes via une **base intermédiaire dédiée aux communications** (5 tables réelles). Côté Mecalux, un service **Gateway AGV (existant)** mediate entre les workflows EasyWMS et les tables. Côté flotte, un **middleware à fournir** (à développer **par Mecalux** pour iGO, sous forme de pool IIS C# .NET 8) mediate entre ces tables et l'API REST PACS.
>
> **Le `EasyWMSGateway2015` que l'on connaît pour Galileo ne joue PAS le rôle de la Gateway AGV Mecalux** — il joue le rôle du middleware côté flotte (médiateur entre tables et TCP frames). Pour iGO, il faut son équivalent fonctionnel mais en REST/HTTPS, sous forme de pool IIS C#.
>
> **Le custom `CstAGV` n'est pas modifié.** L'intégration iGO consiste uniquement à fournir le middleware pool IIS.
#### 8.4.1 Schéma cible — 4 composants
```mermaid
flowchart LR
subgraph EasyWMS["Côté Mecalux EasyWMS (existant)"]
Process[Process
Putaway/Picking/Shipping]
Task[Task & Movement]
AgvCore[Module AGV core
workflows CstAGV]
GwMec[Gateway AGV Mecalux
service mediator
workflows ↔ tables]
MonitorView[Vues SmartUI
AGV Tasks/Stations/
Messages]
end
subgraph DB["Intermediate database for communications"]
direction TB
OQ[(AGV_OUTPUTQUEUE
FIFO sortante)]
EAG[(AGV_EAG
data ordres)]
IQ[(AGV_INPUTQUEUE
FIFO entrante)]
AGE[(AGV_AGE
data phases)]
AGS[(AGV_AGS
data statuts AGV)]
end
subgraph FleetSide["Pool IIS C# .NET 8 — À DÉVELOPPER PAR MECALUX pour iGO"]
direction TB
Pump[Pompe sortante :
poll OUTPUTQUEUE
lit EAG via batchId
+ POST API iGO]
Hook[Webhook receiver :
HTTPS endpoint
écrit INPUTQUEUE+AGE/AGS]
end
subgraph KION["iGO / PACS (existant chez STILL)"]
iGO[API REST
port 7002]
end
Process --> Task --> AgvCore
AgvCore -->|events workflow| GwMec
GwMec -->|écrit OUTPUTQUEUE+EAG| OQ
GwMec -->|écrit OUTPUTQUEUE+EAG| EAG
GwMec -->|poll INPUTQUEUE+AGE/AGS| IQ
GwMec -->|poll INPUTQUEUE+AGE/AGS| AGE
GwMec -->|alimente| MonitorView
Pump -->|poll| OQ
Pump -->|lit data via batchId| EAG
Pump -->|HTTPS REST
X-API-Key| iGO
iGO -.webhook callback.-> Hook
Hook -->|écrit| IQ
Hook -->|écrit| AGE
Hook -->|écrit| AGS
```
#### 8.4.2 Composants existants côté Mecalux — RIEN à développer
|Composant|Rôle|Statut|
|---|---|---|
|`CstAGV` (workflows)|Logique métier AGV — création de tâches, gestion d'erreurs, fallback RFT, etc.|✅ Existant — non touché|
|**Gateway AGV Mecalux**|Service mediator côté Mecalux : écoute events workflow → écrit `AGV_OUTPUTQUEUE` + `AGV_EAG` ; poll `AGV_INPUTQUEUE` → lit `AGV_AGE`/`AGV_AGS` → alimente vues SmartUI ; gère le `processedDate`|✅ Existant dans le module AGV — non touché|
|Tables `AGV_INPUTQUEUE`, `AGV_OUTPUTQUEUE`, `AGV_EAG`, `AGV_AGE`, `AGV_AGS`|Intermediate database for communications|✅ Existantes|
|Vues SmartUI (AGV Tasks/Stations/Messages)|Monitoring opérateur|✅ Existantes|
#### 8.4.3 Middleware côté flotte — pool IIS C# à développer **par Mecalux**
C'est **le seul livrable nouveau** de l'intégration. **Mecalux** (et non STILL) prend en charge ce développement, car le contrat avec les tables `AGV_*` est propriétaire Mecalux. Architecture cible :
|Aspect|Description|
|---|---|
|**Forme**|Pool **IIS** dédiée hébergeant un service ASP.NET Core (C#, .NET 8.0)|
|**Hébergement**|Serveur Mecalux — peut être co-localisé avec EasyWMS ou sur une VM séparée selon la topologie projet|
|**Rôle**|Service unique multi-thread combinant pompe sortante ET webhook receiver|
|**Accès BDD**|Connection chaîne vers la base intermédiaire (les 5 tables `AGV_*`) — typiquement la même base que le WMS, ou une base dédiée si l'architecture projet le décide|
|**Sécurité**|Header `X-API-Key` sortant + endpoint HTTPS entrant (TLS 1.2, certificat trusté ou auto-signé selon politique)|
##### A. Pompe sortante (poll `AGV_OUTPUTQUEUE` → API iGO)
|Aspect|Description|
|---|---|
|Mode|Background worker / hosted service ASP.NET Core — polling régulier (par ex. toutes les 1-5s) sur `AGV_OUTPUTQUEUE WHERE processedDate IS NULL ORDER BY creationDate ASC`|
|Action|Pour chaque ligne queue : récupère le `id` (= `batchId`) → SELECT data correspondante dans `AGV_EAG WHERE batchId = ` → interprète l'`Operation` (`C`/`U`/`D`) → fait l'appel REST iGO adéquat|
|Endpoints iGO appelés|`POST /api/transports` (Create), `POST /transports/{id}/cancel` (Delete), `POST /transports/{id}/priority`, `POST /transports/{id}/final-source`, `POST /transports/{id}/final-destination` (Update selon le pattern `CanPick`/`CanDrop`)|
|Marquage|Après ack synchrone d'iGO (200 OK) : `UPDATE AGV_OUTPUTQUEUE SET processedDate = NOW() WHERE id = `|
##### B. Webhook receiver (callbacks iGO → écrit `AGV_INPUTQUEUE` + `AGV_AGE`/`AGS`)
|Aspect|Description|
|---|---|
|Mode|Controller ASP.NET Core exposant deux endpoints HTTPS : `POST /agv/transport-event` et `POST /agv/vehicle-event` (URL configurable). Souscriptions iGO créées au boot via `POST /api/transports/subscription?callbackUrl=...` et `POST /api/vehicles/subscription?callbackUrl=...`|
|Action|Pour chaque callback reçu : (1) `INSERT INTO AGV_INPUTQUEUE (message, creationDate) VALUES ('AGE'/'AGS', NOW())` → récupère le `id` généré → (2) `INSERT INTO AGV_AGE` (ou `AGV_AGS`) avec `batchId = `, `Phase` (selon mapping iGO `status` → 100/103/106/110/255), `ErrorType` (0 ou code 2003/2004/2700 selon contexte), et tous les autres champs du payload Transport/Vehicle iGO|
|Idempotence|Déduplication sur `transport.id + status` pour éviter les doubles inserts en cas de retry iGO (cf. FAQ #9). Implémentée via cache local distribué (par ex. table de dedupe interne avec TTL)|
|Horodatage|`creationDate = NOW()` UTC à la réception, car iGO ne fournit pas de timestamp (cf. FAQ #3)|
|Réponse|200 OK rapidement (< 1s) après insertion en base — le traitement métier est différé à la Gateway AGV Mecalux qui poll `AGV_INPUTQUEUE`|
##### Pattern de boot du middleware
À chaque démarrage de la pool IIS :
1. Vérifier la base intermédiaire accessible (connection string + ping `SELECT 1`)
2. Vérifier l'API iGO accessible : `GET /api/system`
3. Récupérer la liste des subscriptions actives via `GET /api/system` (champ `subscriptions`) → comparer avec ce qui est attendu → (re)créer les manquantes via `POST /api/transports/subscription` et `POST /api/vehicles/subscription`
4. Réconcilier les transports : `GET /api/transports` → comparer avec les `AGV_OUTPUTQUEUE` non encore acquittées → générer les lignes `AGV_INPUTQUEUE` + `AGV_AGE` manquantes pour les events perdus pendant la coupure
5. Démarrer le hosted service de polling sortant
> 💡 **Une seule pool IIS** héberge les deux fonctions (pompe + webhook). C'est plus simple à exploiter (un seul service à monitorer, un seul recycling, un seul fichier de logs) et c'est le pattern recommandé pour iGO.
#### 8.4.4 Mapping `Status` iGO → couple `(EventType, Flags)` AGE
C'est le cœur de la traduction entrante côté webhook receiver :
|iGO `Transport.status`|Inséré dans AGE comme|
|---|---|
|`Pending`|`EventType=100, Flags=0` (Order accepted)|
|`Assigned`|`EventType=103, Flags=0` (Vehicle assigned) — mais cf. FAQ #1 : pas de `Vehicle.id` à ce moment, donc `StationNumber=null`|
|`Retrieving`|(selon le mode group) `EventType=104, Flags=0` ou rien|
|`Retrieved`|(selon le mode group) `EventType=106, Flags=0` (Load confirmed) — c'est ici que le `Vehicle.id` arrive|
|`Storing`|`EventType=108, Flags=0` (Unload permission) si group, sinon avancement standard|
|`Stored` / `Finished`|`EventType=110, Flags=0` (Unload confirmed)|
|`Cancelled`|`EventType=255, Flags=0`|
|`Aborted`|`Flags=2003` ou `2004` selon contexte (cf. FAQ #2 : à enrichir via `GET /api/vehicles/{id}` pour récupérer `errors[]`)|
#### 8.4.5 Avantages du pattern
✅ **CstAGV livré tel quel, Gateway AGV Mecalux non modifiée**. L'intégration iGO ne touche à aucun code EasyWMS — toute la spécificité iGO est encapsulée dans le composant côté flotte.
✅ **RFT fallback préservé**, notifications préservées, recherche de relocation préservée — toute la machinerie AGV continue à fonctionner.
✅ **Découplage temporel** : si iGO est down, EAG s'accumule et sera pompé à la reprise. Si la pompe sortante crashe, EasyWMS n'est pas impacté. Si le webhook receiver est down, iGO retransmet (FAQ #9).
✅ **Pattern symétrique avec Galileo** : Galileo a son `EasyWMSGateway2015` côté flotte ; iGO aura son équivalent REST/HTTPS — même rôle, protocole différent.
#### 8.4.6 Points de vigilance
⚠️ **Pas d'équivalent natif EAG du `final-source` / `final-destination`** : ces endpoints iGO sont consommés en réponse à un statut `RequestSource` / `RequestDestination`. Côté EAG, ils correspondent aux ré-exports `Operation=Update` avec `CanPick=true` / `CanDrop=true` → la pompe sortante doit reconnaître ce pattern et router vers le bon endpoint REST.
⚠️ **Annulation après `Retrieved`** (FAQ #6) : la pompe sortante doit, à la lecture d'un EAG `Operation=Delete`, vérifier le `status` iGO actuel (`GET /api/transports/{id}`). Si `≥ Retrieved` → ne pas appeler `/cancel` et écrire un Flags d'erreur dans AGE pour notifier le WMS.
⚠️ **Persistance** : les tables EAG/AGE/AGS persistent dans la base EasyWMS — pas de risque de perte si le composant côté flotte crashe. Le `BatchId` permet de reprendre proprement.
#### 8.4.7 Comparaison avec les services Gateway côté flotte historiques
Le **middleware iGO à développer** joue le même rôle que les services Gateway côté flotte que Mecalux a déjà développés pour d'autres protocoles. Comparaison côte à côte :
|Aspect|`EasyWMSGateway2015` (Galileo TMS)|`EasyWMSGatewayRocla2015` (Rocla AGV)|Pool IIS iGO (à développer)|
|---|---|---|---|
|Côté logique|Flotte (TCP frames ↔ hardware)|Flotte (TCP frames ↔ AGV Rocla)|Flotte (REST ↔ iGO PACS)|
|Forme|Service Windows|Service Windows|**Pool IIS C# .NET 8**|
|Mode de communication aval|TCP socket binaire (port 3000)|TCP socket binaire (port 50011)|HTTPS REST (port 7002)|
|Pattern d'échange|Long-polling Search/Event/End|Messages q/m/n/s/b|POST + webhook callback|
|Format des messages|Frames GALILEO bas niveau|Frames Rocla propriétaires|JSON REST|
|Authentification|`TokenUser` dans `MainObject.config`|(intégrée au protocole Rocla)|`X-API-Key`|
|Tables/storage côté flotte|(Galileo a son propre stockage)|SQLite locale (`WCSROCLATASK`) pour mapping interne|(BDD partagée Mecalux uniquement)|
|Liaison côté Mecalux|(autre architecture)|**Abonnement direct aux workflows** (`Galileo_SearchCreatedEventHandler_PR`, etc.) — héritage EasyB|**Tables `AGV_*`** (architecture moderne)|
|Configuration|`MainObject.config`|`Communications.config` + JSON (Stations/Addresses/ContainerTypes/HeightTypes)|`appsettings.json` ASP.NET Core|
|Logs|`C:\ProgramData\Mecalux\EasyWMS Gateway 2015\Logs\AllLog.log`|`C:\ProgramData\Mecalux\EasyWMS GatewayRocla 2015\Logs\`|Logging ASP.NET (Serilog/log4net selon standard projet)|
|Architecture|Monolithique|Monolithique (Gateway Mecalux + middleware fusionnés)|**Découplée** via tables `AGV_*`|
|Statut|Existant|Existant (historique)|À développer par Mecalux|
> 💡 **Architecture moderne vs historique** : le pattern Rocla (`EasyWMSGatewayRocla2015`) **fusionnait** la Gateway Mecalux et le middleware côté flotte en un seul service Windows monolithique qui s'abonnait directement aux workflows EasyWMS et écrivait sa propre base SQLite locale. Le pattern iGO **respecte la séparation moderne** : la Gateway AGV Mecalux (existant côté Mecalux) et la pool IIS C# (côté flotte) communiquent uniquement via les tables `AGV_*`. C'est plus propre, plus testable, et permet de changer le fleet manager sans toucher à la Gateway Mecalux.
>
> 💡 **Recommandation projet** : ne PAS partir d'un fork de `EasyWMSGatewayRocla2015` (qui est dans l'ancienne architecture monolithique), mais bâtir la pool IIS iGO **from scratch** sur ASP.NET Core .NET 8, avec une dépendance unique vers la base intermédiaire (côté Mecalux) et une dépendance HTTP/REST vers iGO PACS (côté flotte). Cela donne un livrable plus simple, plus testable, et conforme aux standards Mecalux actuels.
---
## 9. FAQ STILL — réponses officielles
> Réponses obtenues de STILL en avril 2026 sur les points clés d'intégration. Ces éléments **ont valeur contractuelle** et sont à intégrer aux workflows EasyWMS adaptés iGO.
### FAQ #1 — Identifiant véhicule au passage à `Assigned`
**Q.** Lors du passage en état `Assigned`, le payload webhook contient-il l'identifiant du véhicule (sens iGO → EasyWMS) ?
**R. STILL.** _Non, l'identifiant du véhicule n'est pas disponible au passage à `Assigned`. L'identifiant du véhicule fait partie du payload à partir du passage à l'état `Retrieved`._
**Impact EasyWMS.** Côté Gateway iGO : ne pas insérer la ligne AGE `EventType=103` avec un `StationNumber` au passage à `Assigned`. Insérer uniquement `EventType=103, Flags=0, StationNumber=null` (vehicle assigned mais inconnu), puis attendre le passage à `Retrieved` pour insérer `EventType=106` avec le `Vehicle.id` cette fois — ce qui déclenchera correctement `ProcessEvent_TaskAssigned_PR` côté core EasyWMS.
### FAQ #2 — Erreurs en cas d'`Aborted`
**Q.** En cas d'`Aborted`, reçoit-on un code ou message d'erreur dans le payload ?
**R. STILL.** _Non, nous recevons uniquement un POST avec l'état._
**Impact EasyWMS.** Le mapping vers les codes EasyWMS 2003 (extraction error) / 2004 (putaway error) / 2700 (wrong container) n'est **pas dérivable** du callback iGO seul. Côté Gateway iGO, à réception d'un `Aborted` :
- soit faire un `GET /api/vehicles/{id}` complémentaire pour récupérer `errors[].errorCode` / `errorLabel` du véhicule, et **traduire** dans `Flags` de la ligne AGE ;
- soit insérer un `Flags` générique (à définir, par ex. `2003`) et laisser une notification AGV « Aborted — investigation nécessaire » se déclencher.
### FAQ #3 — Timestamp des événements
**Q.** Les événements webhook contiennent-ils un horodatage (timestamp) ?
**R. STILL.** _Non._
**Impact EasyWMS.** Le Gateway iGO **doit horodater à la réception** (`DateTime.UtcNow` au moment du POST entrant) avant l'insertion en table `AgeMessages` (champ `CreationDate`). Sinon perte d'information pour le LMS / les KPI / l'audit.
### FAQ #4 — Statut `New`
**Q.** Le statut `New` : quand apparaît-il et quelle est la différence avec `Requested` ?
**R. STILL.** _Le statut `New` est un statut interne qui n'existe qu'un instant. Vous pouvez considérer que le premier statut est `Requested`._
**Impact EasyWMS.** Pas de gestion à prévoir pour `New`. Le mapping commence directement à `Requested` (= phase EasyWMS 100 / `AgvStatus.Sent`).
### FAQ #5 — Création préalable des palettes (`Load`)
**Q.** Faut-il créer les palettes dans iGO (`POST /api/loads`) avant de créer un transport, ou peut-on inclure les infos palette directement dans la création du transport ?
**R. STILL.** _Ce n'est pas nécessaire. Ni même souhaitable. Cela augmenterait la complexité de votre côté. Le seul cas d'usage que nous connaissons pour l'utilisation des `Loads`, ce serait dans le cas où le système qui reçoit les palettes est différent de celui qui crée le transport._
**Impact EasyWMS.** Le Gateway iGO **ne doit PAS** appeler `POST /api/loads` en amont du transport. Toutes les informations utiles de la palette (`loadHostId` = `PalletId`, dimensions, customMetaData reprenant `HasTopper`, `PalletType`, `PalletSide` etc.) sont passées **directement dans l'objet `load`** du payload `POST /api/transports`. Cela simplifie le Gateway et évite un point de défaillance supplémentaire.
### FAQ #6 — Annulation après chargement
**Q.** Peut-on annuler un transport après que l'AGV a déjà pris la palette (état `Retrieved`) ?
**R. STILL.** _Non._
**Impact EasyWMS.** Pas de modification du workflow EasyWMS `AgvTask_SetCancelledTask_PR` lui-même — il continue à écrire une ligne EAG `Operation=Delete` comme d'habitude. **C'est le Gateway iGO** qui, à la lecture de cette ligne, doit :
```text
Status iGO courant du transport ?
├ Requested / Pending / Assigned / Retrieving
│ → POST /api/transports/{id}/cancel ✅
│ → Insère AGE EventType=255 quand iGO confirme l'annulation
│
├ Retrieved / Storing / Stored
│ → ❌ Cancel impossible côté iGO (limite STILL)
│ → Le Gateway répond NotifyError dans AGE (Flags spécifique)
│ → Le module AGV core notifie l'utilisateur via le notification group AGV
│ → Workflow alternatif côté process (attendre Finished + tâche retour, ou RFT)
│
└ Cancelled / Aborted / Finished → no-op (déjà terminal)
```
Cette logique est **interne au Gateway iGO** et n'impacte pas le custom EasyWMS.
### FAQ #7 — Cas d'usage du `suspended`
**Q.** Le champ `suspended` : quel est son cas d'usage typique ?
**R. STILL.** _Cela pourrait être utile si besoin de créer le transport avant que la palette ne soit disponible. Dans notre cas, je ne vois pas à quoi cela pourrait servir._
**Impact EasyWMS.** Champ à **mettre à `false`** par défaut dans tous les `POST /api/transports`. Pas de cas d'usage immédiat dans le mapping standard EasyWMS — pourrait éventuellement servir pour de la pré-réservation dans des scénarios spéciaux (e-commerce wave preparation, etc.) mais hors scope du custom AGV générique.
### FAQ #8 — Authentification
**Q.** Authentification : faut-il utiliser `X-API-Key` ou `Bearer token` ?
**R. STILL.** _`X-API-Key`._
**Impact EasyWMS.** Le Gateway iGO envoie systématiquement le header :
```http
X-API-Key:
```
Le `Authorization: Bearer ` mentionné dans certaines parties de la doc PACS est à **ignorer**. La clé est stockée chiffrée dans la config locale du Gateway (analogie : `PasswordEncrypt.exe` côté `EasyWMSGateway2015`).
### FAQ #9 — Politique de retry des webhooks
**Q.** En cas d'indisponibilité de notre serveur webhook, iGO retente-t-il l'envoi des événements ? Si oui, selon quelle politique ?
**R. STILL.** _iGO retente l'envoi mais nous ne savons pas à quelle fréquence. À tester._
**Impact EasyWMS.** Le Listener du Gateway iGO **doit être idempotent** : si iGO renvoie le même event 2 fois, ne pas insérer deux lignes dans AGE. Idempotency-key suggérée :
- `transport.id + transport.status` pour les events de transport ;
- `vehicle.id + vehicle.status` pour les events véhicule (sans timestamp natif — cf. FAQ #3).
À implémenter via une table de déduplication interne au Gateway (cache rolling de quelques minutes), ou un index unique sur (`OrderExtId`, `Phase`) côté `AgeMessages`.
⚠️ **Action de test à mener** : pendant la phase d'intégration, simuler un Gateway iGO en panne pendant 30 s / 1 min / 5 min et observer le comportement réel d'iGO (nombre de retries, intervalles, abandon).
### FAQ #10 — Persistance après redémarrage iGO
**Q.** Les données de transport survivent-elles à un redémarrage iGO, ou sont-elles uniquement en mémoire vive ?
**R. STILL.** _Oui, les données survivent à un redémarrage du système._
**Impact EasyWMS.**
- Bonne nouvelle : pas besoin de re-pousser tous les transports en cours après un reboot iGO.
- Cependant : les **subscriptions webhook** étaient marquées « _all transports that are still in the memory of E'tricc_ » dans la doc PACS § 2.2.1. Donc à clarifier : les **abonnements** survivent-ils aussi ? Si non, le Gateway doit ré-appeler `POST /api/transports/subscription` au redémarrage.
- Recommandation : au boot du **Gateway iGO**, **toujours** :
1. Appeler `GET /api/system` pour voir si la subscription existe.
2. La (re)créer si absente.
3. Faire un `GET /api/transports` pour réconcilier — comparer l'état iGO avec les transports en cours côté tables EAG/AGE et générer les lignes AGE manquantes si nécessaire.
### FAQ #11 — Communication directe ou via iGo Flow
**Q.** Communique-t-on directement avec l'API PACS ou via iGo Flow ?
**R. STILL.** _Directement avec l'API PACS._
**Impact EasyWMS.** Pas de couche d'orchestration intermédiaire « iGo Flow » à intégrer. Le Gateway iGO appelle directement `https://[IP]:7002/api/...` en HTTPS. Architecture simplifiée :
```text
EAG/AGE/AGS ──▶ Gateway iGO ──HTTPS REST──▶ PACS Host API ──▶ iGo / E'tricc ──▶ AGV
▲
│
(pas d'iGo Flow intermédiaire)
```
### Synthèse FAQ — points-clés à intégrer dans le Gateway iGO
|#|Règle dérivée|
|---|---|
|1|N'insérer le `StationNumber` (= Vehicle.id) dans AGE qu'à partir de `Retrieved` (pas dès `Assigned`)|
|2|Pour les `Aborted`, enrichir via `GET /api/vehicles/{id}` (errors[]) avant d'insérer la ligne AGE|
|3|Horodater (`CreationDate`) chaque insertion AGE à la **réception** du callback|
|4|Ignorer `New`|
|5|**Ne PAS** appeler `POST /api/loads` en amont — passer les infos palette dans le transport|
|6|À la lecture d'un EAG `Operation=Delete`, vérifier le `status` iGO courant : refuser si ≥ `Retrieved` et générer un Flags d'erreur dans AGE|
|7|`suspended = false` par défaut dans `POST /transports`|
|8|Header `X-API-Key` (jamais `Bearer`)|
|9|Listener **idempotent** (transport.id + status comme clé de déduplication)|
|10|À chaque boot du Gateway : vérifier subscriptions + réconcilier transports via `GET /transports`|
|11|Pas de couche iGo Flow — appel direct port 7002|
---
## Annexes
### A. Glossaire rapide
|Sigle|Signification|
|---|---|
|**iGO easy**|Fleet manager AGV STILL (KION Group) — variante simple|
|**PACS**|Productized Automated Concept Solutions — offre KION large couvrant iGO et au-delà|
|**E'tricc**|Moteur interne du fleet manager (apparaît dans la doc PACS)|
|**MyMA**|Suite logicielle KION : Vue3 + .NET8 + Postgres — héberge iGO côté admin|
|**EXV CB iGo**|Modèle physique — gerbeur électrique automatisé STILL|
|**TLS 1.2**|Niveau de chiffrement requis sur le port 7002|
|**X-API-Key**|Authentification API officielle (FAQ #8)|
|**iGo Flow**|Couche d'orchestration STILL — **non utilisée** dans l'intégration directe (FAQ #11)|
### B. Pointeurs sources
|Document|Section utile|
|---|---|
|`2510_PACS-2.3-Host-Interface-Technical-Specifications_en-US--STILL-.pdf`|Toute l'API REST (chap. 1-8)|
|`iGo easy 2.3 - Host Interface Specifications.pdf`|Identique à PACS — confirmant que iGO easy = produit dérivé du même moteur|
|`IT requirements R1 20250929.pdf`|Stack, ports, hardware|
|`Technical specification EXV CB iGo.pdf`|Datasheet véhicule|
|`Limagrain-LWM_Std_Interface_Compliance.xlsx`|Matrice de compliance projet — **à exploiter manuellement** (binaire non lu)|
### C. Cross-référence avec le module AGV EasyWMS
Pour le détail du module AGV EasyWMS qui sert de point de comparaison ici, voir le document compagnon `CR_ModuleAGV.md` dans le dossier `CstAGV/`.