Conventions transverses¶
Ces règles s'appliquent à tous les BPM. Chacune correspond à un piège qui a réellement causé un bug.
Vocabulaire : cycle, passe, clôture¶
Les trois termes cycle (entité fille d'OF), passe (tour de fonctionnement d'un équipement) et clôture (fin de vie de l'entité cycle) sont définis sur la page Vocabulaire.
À retenir côté implémentation :
cycle_id/CYCLE_*: l'entité.CC - Choix de fin de passe(étape 45) : la passe.ALL - Clôture de cycle(étapes 100/170) : la clôture de l'entité.
Préfixes des BPM¶
Le préfixe d'un processus métier (exporté comme fichier .bpmn) indique son rôle et sa portée :
| Préfixe | Portée | Rôle |
|---|---|---|
ALL |
Générique | Étape config-driven, valable pour toute séquence (prérequis, présence, appel robot, couvercle, vidage, clôture, moteur). |
CC / EX |
Spécifique séquence | Logique propre à la concentration à chaud / l'extraction (concentration, choix de fin de passe, extraction, transferts…). |
OF |
Cycle de vie | Création / suppression d'un ordre de fabrication. |
TR |
Action physique | Transfert de produit entre équipements (extracteur → concentrateur). |
VR |
Action physique | Vérins : montée / descente du couvercle des cuvons. |
Vi |
Action physique | Vidage d'un cuvon. |
AMR |
Action physique | Missions des robots mobiles autonomes (création / fin de mission). |
TECH |
Transverse (historique) | Fourre-tout d'avant la généralisation. En voie de tri : une partie est archivée (doublons supplantés par ALL), une partie encore vive (TECH - Relance de boucle, TECH - Demande de transfert … pour alimentation). À terme, ce préfixe doit disparaître, ou bien ne servir que dans les sous-processus. |
TEST |
Utilitaire | Stubs et utilitaires (ex. TEST - Passage à la suite, avance sans action). |
Lecture rapide
ALL/CC/EX : niveau orchestration (étapes de séquence).
TR/VR/Vi/AMR : niveau action physique élémentaire, appelés par les blocs ALL.
Un TECH restant est un candidat à la migration.
Présence : ne pas coder le sens en dur¶
Un test de présence n'a pas de sens universel : selon le rôle, « présent » peut signifier « prêt » ou « à remplir ». Le critère vrai/faux et son interprétation viennent de la config (PRESENCE_CONFIG, TRANSFERT_CONFIG), jamais d'une hypothèse en dur. Le cas concret entrée/sortie de l'extraction est détaillé sur Processus EX.
Booléens : deux vocabulaires, ne pas les mélanger¶
Il existe deux représentations du vrai/faux, selon l'origine de la valeur :
| Origine | Valeur | Comparaison en condition |
|---|---|---|
| Booléen JavaScript (calculé dans un script) | true / false |
${ x == true } (minuscules) |
Sélecteur HMI / case à cocher (confirmation) |
"boolean_true" / "boolean_false" |
${ x == TRUE } (le global TRUE) |
Les globaux TRUE / FALSE valent respectivement "boolean_true" / "boolean_false".
Piège classique
Un champ de type confirmation renvoie la chaîne "boolean_true", pas un booléen. Le tester avec == true échoue toujours → la branche n'est jamais prise → « no outgoing sequence flow ». Utiliser == TRUE.
Nombres : une valeur de propriété ou de formulaire est une chaîne¶
Toute valeur issue d'un GetPropertyValue, d'un GetEntityStatusActivity (activité, statut) ou d'un champ de formulaire (même déclaré long) arrive dans le script sous forme de chaîne. Toute comparaison arithmétique ou d'égalité stricte sur ces valeurs doit d'abord les passer par Number(), des deux côtés.
Piège récurrent
activity === CYCLE_ANNULE, new_target > quantity_produced, ep_of == OF_AUCUN_ID… échouent silencieusement si un côté est une chaîne. Un switch ou un === compare "6" === 6 → false → la branche voulue n'est jamais prise (cycle annulé qui démarre, poids valable refusé, etc.). Les globaux numériques peuvent eux-mêmes revenir en chaîne, d'où la coercition des deux côtés.
var act = Number(cycle_activity);
var bloquants = [CYCLE_EN_COURS, CYCLE_CLOTURE, CYCLE_ANNULE];
var ok = bloquants.indexOf(act) === -1;
// comparaison numérique
var acceptable = !isNaN(Number(new_target)) && Number(new_target) > Number(quantity_produced);
Ne pas utiliser .map() et ES6 en règle général
Le moteur utilise du Javascript vanilla, et ne permet pas de coder avec des fonctions ES6
Tracer sans notifier : wiwUtils.log¶
Pour suivre le déroulé d'un BPM sans solliciter l'opérateur, utiliser wiwUtils.log(...) : le message part dans le log serveur (le même flux que PROCHAINE ETAPE : et les erreurs), invisible dans l'IHM.
La règle de partage :
BPMTaskNotifyUser= l'opérateur doit voir ou agir (anomalie, action manuelle).wiwUtils.log= traçabilité pour le développeur / l'ops, silencieuse.
wiwUtils.log("[COUVERCLE] EP=" + ep_id + " étape=" + sequence_number + " : montée");
Deux réflexes : un préfixe constant et greppable ([COUVERCLE], [PRESENCE], [TRANSFERT]…) et l'identité dans le message (ep_id, cycle_id, étape), sans quoi les lignes deviennent ambiguës dès que plusieurs cycles tournent en parallèle.
Tester la présence d'une valeur : estRenseigne¶
Une propriété vide renvoie "" (chaîne vide), pas null. Ne jamais tester != null seul. Utiliser partout le helper :
function estRenseigne(v){ return v != null && v !== "" && v !== "null"; }
Lire une variable non garantie : execution.getVariable¶
Référencer un identifiant nu non défini lève un ReferenceError qui plante tout le script. Pour toute variable susceptible d'être absente, utiliser execution.getVariable("x") (qui renvoie null) plutôt que x.
Le relais des variables entre BPM¶
Un LaunchBPM ne propage pas les variables arbitraires. Un BPM enfant ne reçoit une variable que s'il la déclare en start form property. Pour transporter une donnée à travers une chaîne de BPM, chaque maillon doit la re-déclarer.
Pour éviter de polluer les BPM génériques avec des champs spécifiques, on utilise un payload JSON : un seul champ opaque, déclaré une fois, qui transporte tous les champs spécifiques à une séquence. Empaqueté à l'entrée (JSON.stringify), désempaqueté dans le processus intéressé (JSON.parse).
Ne jamais stocker une entité complète¶
wiwUtils.getEntity(id) renvoie l'objet complet, non sérialisable comme variable de process. L'utiliser uniquement pour lire un champ (.name, .statusValue…). Pour transporter une entité, on transporte son id (chaîne).
Parallélisme et synchronisation¶
Le moteur ne peut pas faire de parallelGateway ni attendre passivement. Deux conséquences :
- Parallélisme = plusieurs BPM lancés séparément (
LaunchBPMsans attente de retour), chacun dans sa propre instance. - Synchronisation = jamais un « join » de jetons, mais un état partagé relu sur l'entité. Chaque branche écrit un marqueur. La dernière qui constate que tous les marqueurs attendus sont posés déclenche la suite.
Relire depuis l'entité, écrire avant de relire
Les instances parallèles ne partagent aucun contexte : toujours relire les marqueurs depuis l'entité (cycle, équipement), jamais depuis les variables locales. Et écrire son marqueur avant de relire, pour réduire la fenêtre de course.
Attendre : par action utilisateur¶
Le moteur ne pouvant pas attendre, une attente (« robot occupé », « conteneur pas encore là ») se modélise par un userTask : l'instance reste suspendue dessus (elle est persistée, ce n'est pas un busy-wait) jusqu'à l'action de l'opérateur, qui reboucle sur la vérification.
Notifications : toujours un entityId et un groupName en plus du message¶
Il est préférable pour un BPMTaskNotifyUser de toujours renseigner entityId et le groupName (en général ep_id et GROUPE_OPERATEUR).