Aller au contenu

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" === 6false → 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 (LaunchBPM sans 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).