Référence CEL
Référence de syntaxe CEL pour les règles Kilo IoT — Common Expression Language utilisée dans les passerelles, scripts et alarmes.
Le moteur de règles utilise CEL (Common Expression Language) pour les expressions à l’intérieur de l’éditeur visuel de workflow — conditions des passerelles, calculs des tâches de script, messages d’alarme, recherches d’enrichissement et définitions des entrées/sorties. CEL est un langage d’expression rapide et sûr, conçu à l’origine par Google pour évaluer des conditions dans des politiques de sécurité et des systèmes d’infrastructure. La spécification complète du langage est disponible sur GitHub.
CEL n’est pas un langage de programmation à usage général. Il évalue des expressions et renvoie des résultats. Il ne peut pas accéder au système de fichiers, effectuer des appels réseau, créer des boucles ni modifier l’état externe. Cela permet d’exécuter en toute sécurité des expressions définies par l’utilisateur, sans risque pour la plateforme ni pour les autres règles.
La plupart des règles Kilo n’utilisent que quelques expressions courtes. La structure du workflow reste visuelle et basée sur BPMN ; CEL est la couche de précision qui rend ces workflows utiles dans de vrais scénarios de production.
Données disponibles
Chaque expression du moteur de règles a accès aux variables de processus via l’ vars . Les données disponibles dépendent de l’endroit de la règle où l’expression s’exécute.
Les champs dans vars peuvent être accessibles avec la notation par point ou la notation entre crochets:
vars.temperature // notation par point — fonctionne pour les identifiants simples
vars["sensor_id"] // notation entre crochets — fonctionne pour n’importe quel nom
vars["my-sensor"] // notation entre crochets requise — tiret dans le nomLa notation par point est pratique pour la plupart des noms de champ. La notation entre crochets est requise lorsqu’un nom de champ contient des tirets, des espaces ou d’autres caractères spéciaux, ou lorsque le nom du champ est calculé dynamiquement à partir d’une autre expression.
Disponible après l’événement de démarrage
Ce que fournit l’événement de démarrage dépend de sa source de démarrage, donc vérifiez de quel type de règle il s’agit avant d’utiliser une variable.
Source de démarrage : lecture du capteur
vars.value
Varie selon le capteur
La lecture du capteur qui a déclenché la règle. Peut être un nombre (température, humidité), une chaîne (état de la porte) ou un booléen (mouvement détecté).
vars.sensor_id
chaîne
L’identifiant unique du capteur qui a déclenché la règle.
vars.timestamp
horodatage
Le moment où la lecture du capteur a été enregistrée.
Source de démarrage : condition de déclenchement
vars.device_name
chaîne
Le nom de l’appareil qui a satisfait la condition. Sur une déclencheur règle surveillant plusieurs appareils, cela identifie celui qui s’est déclenché.
vars.subject_kind
chaîne
Le type de ressource surveillée. Il s’agit actuellement de appareil.
vars.subject_id
chaîne
L’identifiant de l’appareil surveillé qui a satisfait la condition.
vars.sensor_id
chaîne
L’identifiant du capteur utilisé pour associer l’exécution et toute alarme à l’appareil surveillé.
vars.detector_id
chaîne
L’identifiant du déclencheur qui a démarré la règle.
vars.timestamp
int
L’heure du signal de déclenchement en secondes Unix.
vars.valuen’existe pas dans une règle déclenchée par un déclencheur. Un déclencheur signale une transition de condition plutôt que de fournir à la règle un événement de capteur normalisé. Cela vaut à la fois pour les déclencheurs immédiats et de durée. Une expression faisant référence àvars.valueéchouera à chaque signal de déclenchement — vérifiez cela en premier lors de la conversion d’une règle existante depuis Lecture du capteur.
Disponible après les tâches de script
Si une tâche de script renvoie une map, chaque clé est fusionnée dans vars. Par exemple, après une tâche de script avec l’expression {"level": "critical", "delta": 15.2}, les nœuds en aval peuvent accéder à vars.level et vars.delta.
Les sorties de la tâche de script, de la passerelle, de Définir l’alarme, d’Enrichissement et de l’événement de démarrage peuvent également publier des valeurs nommées dans vars après l’exécution de ce nœud.
Disponible après l’enrichissement
Après qu’un nœud d’enrichissement stocke des données sous un nom de variable (par ex. : outdoor_temp), les données enrichies sont accessibles comme un objet imbriqué :
vars.outdoor_temp.value
Varie
La lecture la plus récente du capteur enrichi.
vars.outdoor_temp.sensor_id
chaîne
L’identifiant du capteur enrichi.
vars.outdoor_temp.type
chaîne
Le type de données du capteur enrichi.
vars.outdoor_temp.timestamp_ms
int
Horodatage de la lecture en millisecondes.
Variables personnalisées
Les expressions d’entrée et de sortie sur les événements de démarrage, les passerelles exclusives, les tâches de script, les nœuds Définir l’alarme et les nœuds d’enrichissement permettent de nommer une valeur calculée. Choisissez entre elles selon la distance que la valeur doit parcourir :
Sorties ajoutent le nom au contexte du workflow. Il est accessible sous la forme
vars.<name>dans chaque nœud exécuté ensuite.Entrées restent sur le nœud sur lequel vous les avez définies. Elles sont calculées avant l’exécution de ce nœud et ses propres expressions peuvent les utiliser — les entrées d’une passerelle sont disponibles pour les conditions de flux de cette passerelle — mais les nœuds suivants ne peuvent pas les lire.
Si une valeur calculée sur un nœud est nécessaire plus loin dans la règle, faites-en une sortie. Voir Référence des nœuds pour un exemple.
Système de types
CEL prend en charge les types suivants. Toute valeur dans une expression se résout en l’un de ceux-ci.
bool
Booléen vrai ou faux
true, vars.value > 30
int
Entier signé
42, -7
uint
Entier non signé
42u
double
Nombre à virgule flottante
30.5, -0.7
chaîne
Texte
"critical", "Alerte de température"
bytes
Séquence d’octets
b"\x00\xff"
list
Collection ordonnée
[1, 2, 3], ["a", "b"]
map
Paires clé-valeur
{"level": "high", "count": 5}
null_type
Valeur nulle
null
horodatage
Un instant précis
vars.timestamp
duration
Une durée
duration("5m")
Conversions de type
Utilisez les fonctions de conversion intégrées pour convertir entre les types :
int()
Convertir en entier
int(vars.value)
uint()
Convertir en entier non signé
uint(42)
double()
Convertir en double
double(vars.value)
string()
Convertir en chaîne
string(vars.value)
type()
Renvoie le type d’une valeur
type(vars.value)
La conversion en chaîne est particulièrement importante lors de la création de messages de motivation d’alarme, car CEL exige une conversion explicite des nombres en chaînes pour la concaténation.
Opérateurs
Opérateurs de comparaison
==
Égal à
vars.value == 0
!=
Différent de
vars.status != "offline"
>
Supérieur à
vars.value > 30.0
>=
Supérieur ou égal à
vars.value >= 100
<
Inférieur à
vars.value < 5
<=
Inférieur ou égal à
vars.value <= 25.0
Opérateurs logiques
&&
ET logique
vars.value > 30 && vars.value < 50
||
OU logique
vars.value < 0 || vars.value > 100
!
NON logique
!has(vars.humidity)
Opérateurs arithmétiques
+
Addition
vars.value + 10
-
Soustraction
vars.value - vars.outdoor_temp.value
*
Multiplication
vars.value * 1.8 + 32
/
Division
vars.value / 100.0
%
Modulo
vars.value % 10
Opérateur ternaire
L’opérateur ternaire ? : renvoie l’une de deux valeurs en fonction d’une condition :
Les expressions ternaires peuvent être imbriquées pour une classification à plusieurs niveaux :
Opérateurs et fonctions de chaîne
Concaténation
+
"Temp : " + string(vars.value)
Longueur
size()
vars.name.size() > 0
Contient
contains()
vars.status.contains("error")
Commence par
startsWith()
vars.zone.startsWith("warehouse")
Se termine par
endsWith()
vars.device_id.endsWith("-prod")
Correspondance regex
matches()
vars.device_id.matches("^WH-[0-9]+$")
Fonctions de collection
x dans la liste
Vérification d’appartenance
vars.zone in ["A", "B", "C"]
clé dans la map
La clé existe dans la map
"humidity" in vars
list.exists(x, expr)
Vrai si un élément satisfait l’expression
[10, 25, 40].exists(t, t > 30)
list.all(x, expr)
Vrai si tous les éléments satisfont l’expression
[10, 25, 40].all(t, t > 0)
list.filter(x, expr)
Renvoie les éléments qui satisfont l’expression
[10, 25, 40].filter(t, t > 20) → [25, 40]
list.map(x, expr)
Transforme chaque élément
[1, 2, 3].map(x, x * 10) → [10, 20, 30]
list.exists_one(x, expr)
Vrai si exactement un élément satisfait
[10, 25, 40].exists_one(t, t > 30) → true
Variables intermédiaires avec cel.bind
Utilisez cel.bind() pour définir une variable temporaire à l’intérieur d’une expression, en évitant un calcul redondant :
Le premier argument nomme la variable, le second calcule sa valeur, et le troisième est l’expression qui l’utilise.
Vérification d’existence
has()
Renvoie true si le champ existe
has(vars.humidity)
Utilisez has() avant d’accéder à une variable qui pourrait ne pas exister. Après un nœud d’enrichissement avec un événement d’erreur de frontière, par exemple, les données enrichies ne sont disponibles que si l’enrichissement a réussi.
Modèles d’expressions courants
Vérification de seuil
Le modèle le plus simple et le plus courant. Utilisé dans les conditions de passerelle pour brancher sur une seule valeur.
Renvoie true si la lecture du capteur dépasse 30. Fonctionne dans les conditions de passerelle exclusive pour orienter le flux.
Vérification d’intervalle
Tester si une lecture se situe dans une plage acceptable.
Utile pour la conformité CVC, la surveillance de la chaîne du froid ou tout scénario où les bornes supérieure et inférieure comptent.
Vérification multi-condition
Combinez les vérifications sur plusieurs variables. Ce modèle apparaît souvent après l’enrichissement, lorsque des données provenant de plus d’un capteur sont disponibles.
Utilisez toujours has() avant de référencer des variables provenant de sources optionnelles (enrichissement, tâches de script précédentes avec sorties conditionnelles).
Classification de la gravité (tâche de script)
Renvoie une map depuis une tâche de script pour classer une lecture en catégories. Chaque clé devient une variable de processus distincte.
Après cette tâche de script, vars.level est disponible pour les conditions de passerelle ou les messages d’alarme.
Message d’alarme dynamique (Définir l’alarme)
Construisez une chaîne lisible par l’humain qui inclut des données de capteur en temps réel. Le champ de message de motivation dans un nœud Définir l’alarme utilise ce modèle.
N’oubliez pas d’utiliser string() pour convertir les valeurs numériques — CEL ne convertit pas implicitement les nombres en chaînes lors de la concaténation.
Vérification des données enrichies (après enrichissement)
Référencez une valeur récupérée par un nœud d’enrichissement. Le nom de variable utilisé dans la configuration d’enrichissement devient la clé sous vars.
Valeurs dérivées calculées (tâche de script)
Calculez de nouvelles valeurs à partir de plusieurs entrées et stockez-les pour une utilisation en aval.
Après cette tâche de script, la passerelle peut vérifier vars.needs_alarm directement, et le message d’alarme peut faire référence à vars.delta pour le contexte.
Combiner la classification avec des valeurs calculées
Une seule tâche de script peut effectuer plusieurs calculs à la fois.
Où CEL est utilisé
Les expressions CEL apparaissent à plusieurs endroits dans le moteur de règles. Le contexte détermine ce que l’expression doit renvoyer.
Événement de démarrage — Entrées
N’importe quel type
Créer des valeurs d’aide locales lorsque l’événement de capteur entre dans la règle.
Événement de démarrage — Sorties
N’importe quel type
Publier des valeurs nommées dans le contexte partagé du workflow.
Tâche de script — Script
N’importe quel type (map de préférence)
Transformer ou classer des données. Les clés de la map fusionnent dans les variables de processus.
Tâche de script — Entrées
N’importe quel type
Créer des valeurs d’aide locales avant l’exécution du script.
Tâche de script — Sorties
N’importe quel type
Publier des valeurs nommées supplémentaires après l’exécution de la tâche.
Passerelle exclusive — Condition
bool
Orienter l’exécution. Doit renvoyer true ou false.
Passerelle exclusive — Entrées
N’importe quel type
Préparer les valeurs locales utilisées par les conditions de branche.
Passerelle exclusive — Sorties
N’importe quel type
Publier des valeurs après la décision d’orientation.
Définir l’alarme — Message de motivation
chaîne
Décrire pourquoi l’alarme a été déclenchée. Affiché aux intervenants.
Définir l’alarme — Entrées
N’importe quel type
Préparer les valeurs avant l’exécution de l’action d’alarme.
Définir l’alarme — Sorties
N’importe quel type
Publier des valeurs après l’exécution du nœud d’alarme.
Enrichissement — ID du capteur
chaîne
Identifier le capteur à partir duquel récupérer les données.
Enrichissement — Entrées
N’importe quel type
Préparer les valeurs avant l’exécution de la recherche.
Enrichissement — Sorties
N’importe quel type
Publier des valeurs après que le résultat de l’enrichissement est disponible.
Portée des entrées et sorties
Les entrées et sorties sur un nœud servent à des fins différentes et ont une visibilité différente :
Entrées créent locales des variables limitées au nœud actuel uniquement. Elles ne modifient pas le contexte partagé du workflow. Les expressions d’entrée sont évaluées par rapport à l’état
varsactuel. Utilisez-les pour préparer des valeurs d’aide ou pré-calculer des résultats intermédiaires avant l’exécution de la logique principale du nœud.Sorties écrivent des valeurs dans le partagé contexte du workflow (
vars). Les expressions de sortie sont évaluées dans la portée locale du nœud — qui inclut à la fois l'originalvarset tous les locaux définis par l'entrée. Les valeurs publiées par les sorties persistent et sont accessibles à tous les nœuds en aval.
Conséquence pratique : Si vous définissez une entrée nommée threshold sur une passerelle exclusive, les nœuds en aval ne peuvent pas voir vars.threshold — elle n'existe que pendant l'évaluation de la condition de cette passerelle. Pour rendre une valeur calculée disponible en aval, définissez-la plutôt comme une sortie.
Sécurité et bac à sable
CEL est isolé par conception. Les expressions s'exécutent dans un environnement restreint sans accès à :
Le système de fichiers
Les ressources réseau
Les horloges système (sauf via les variables d'horodatage fournies)
Les services externes
L'état mutable en dehors de la propre évaluation de l'expression
Une expression ne peut pas créer de boucles infinies, allouer une mémoire illimitée ou affecter d'autres règles. Si une expression échoue (erreur de type, division par zéro, référence à une variable manquante sans has() garde), le nœud qui la contient lève une erreur. Attachez un événement d'erreur de frontière pour gérer ces échecs avec élégance.
Fonctions de la plateforme
En plus de la bibliothèque CEL standard, le moteur de règles fournit deux fonctions spécifiques à la plateforme.
error(message)
Prend un argument de type chaîne et produit toujours une valeur d'erreur. Lorsqu'une expression de tâche de script s'évalue à une erreur, le moteur vérifie la présence d'un événement d'erreur de frontière attaché. S'il en existe un, l'exécution emprunte le chemin d'erreur. Sinon, l'erreur arrête la règle.
Utilisez error() pour une défaillance conditionnelle délibérée — des situations où une condition de données spécifique doit déclencher le chemin de traitement des erreurs plutôt que de poursuivre l'exécution normale.
Dans cet exemple, les températures supérieures à 200 font volontairement échouer la tâche de script. Si un événement d'erreur de frontière est attaché, le chemin d'erreur s'exécute (peut-être en déclenchant une alarme d'urgence). En dessous de 200, la tâche de script produit {"status": "ok"} comme normal.
random()
Renvoie un nombre à virgule flottante pseudo-aléatoire dans l'intervalle [0.0, 1.0). Utile pour l'échantillonnage probabiliste, le routage basé sur un pourcentage ou la génération d'identifiants aléatoires.
Conseils pratiques
Gardez des expressions ciblées. Une seule expression doit faire une seule chose. Si vous devez classifier une mesure, calculer un delta et construire un message, utilisez plusieurs tâches de script plutôt qu'une seule expression complexe. Cela rend la règle plus facile à lire, à déboguer et à maintenir.
Privilégiez d'abord la structure visuelle. Utilisez le canevas pour montrer le workflow, puis utilisez CEL dans les champs pertinents. Un diagramme BPMN lisible avec quelques expressions claires est plus facile à auditer qu'une règle qui cache trop de logique dans une seule immense expression.
Utilisez has() avant les champs facultatifs. Toute variable provenant d'un enrichissement, de tâches de script conditionnelles ou d'entrées facultatives peut ne pas exister. Y accéder sans has() et l'expression lève une erreur.
Convertissez explicitement les types. CEL n'effectue pas de conversion de type implicite. Lorsque vous construisez des messages d'alerte, convertissez les nombres avec string(). Lors d'opérations arithmétiques, assurez-vous que les deux opérandes sont du même type numérique — mélanger int et double peut produire des résultats inattendus.
Utilisez des maps pour les sorties à plusieurs valeurs. Retourner une map depuis une tâche de script est la manière standard de rendre plusieurs valeurs calculées disponibles en aval. Chaque clé devient une variable de processus indépendante.
Testez les expressions sur des cas limites. Réfléchissez à ce qui se passe lorsqu'un capteur renvoie zéro, une valeur négative ou un nombre anormalement élevé. Les conditions de passerelle doivent gérer toute la plage des entrées possibles sans orienter vers une branche non souhaitée.
Nommez les variables de façon descriptive. Lorsque vous définissez des noms de variables d'enrichissement ou des clés de sortie de tâche de script, utilisez des noms qui décrivent les données — outdoor_temp, humidity_reading, severity_level — pas x, val2, ou tmp. Vos collègues les liront lorsqu'ils examineront ou modifieront la règle.
Mis à jour