# Actions

> L'éditeur d'action : identité et emplacement, la carte du script, ce qu'un script peut lire et écrire, chaque message de validation, le verbe Exécuter et sa journalisation, qui a déclenché la dernière exécution, ce qui exécute l'action, Activer et Désactiver, Copier et Supprimer, et les limites d'une exécution.

Une action est un bloc d'instructions JavaScript qui lit l'espace d'adressage et écrit des variables mémoire et des tags. Les actions sont le chemin d'écriture de Logic : elles s'exécutent à la demande (Exécuter), sur un [déclencheur](logic-triggers), depuis un [Bouton](component-button) de tableau de bord qui leur est associé, ou au démarrage ou à la fin d'un essai de Process, par une [commande d'action Logic](process-actions#logic-action-commands). Sélectionnez une action sous la section Actions de la colonne Logic, ou appuyez sur **Ouvrir** sur sa ligne dans le tableau de son dossier ou de la racine de sa section, pour ouvrir son éditeur (une action créée avec Nouveau… > Nouvelle action ne s'ouvre pas, elle est mise en évidence dans ce tableau, voir [Créer et copier](logic#creating-and-copying)) ; la lecture demande **Voir Logic**, la modification **Configurer Logic**, l'exécution **Piloter Logic**.

L'en-tête montre la marque d'action, le nom, la ligne « Un script que vous exécutez à la demande, ou qu'un déclencheur exécute pour vous, en écrivant des variables et des tags. » et un badge **Désactivée** tant que l'action est éteinte. La colonne du formulaire porte IDENTITÉ, SCRIPT DE L'ACTION et **Utilisé par** ; le rail porte DERNIÈRE EXÉCUTION.

## Identité {#identity}

| Champ | Ce que c'est | Valeurs / défaut | Effet |
| --- | --- | --- | --- |
| Nom | Le nom affiché et le libellé sous lequel une exécution est journalisée. | Texte libre ; une nouvelle action s'appelle `ActionN` ; un nom vide s'enregistre comme `Action`. | Enregistré automatiquement au fil de la frappe. Les actions sont référencées par leur identité, donc un changement de nom ne réécrit rien et ne casse rien. |
| Ligne d'emplacement | Où l'action est classée, en toutes lettres : `Actions / Startup / Walk setpoint`. | Lecture seule. | Une action n'a pas d'adresse ; la ligne dit un endroit, jamais quelque chose à taper dans une expression. Changez le dossier avec **Déplacer vers** dans l'arborescence. |
| Description (facultative) | Texte libre. | Vide par défaut. | Aucun. |

## Script de l'action {#script}

La carte SCRIPT DE L'ACTION est l'[éditeur de script](logic-expressions#the-script-editor) avec l'indication « Instructions JavaScript (let, if, for, fonctions). Écrivez les variables mémoire par affectation (Variables.Setpoint = 21.5;) et les tags avec Connector.Driver.Device.Tag.Write(value). Les lectures ne sont pas filtrées par la qualité : vérifiez .State() et protégez les manques avec ?? avant tout calcul. » Le script s'enregistre automatiquement à chaque frappe et est validé toutes les demi-secondes ; le message apparaît sous l'indication. La validation n'exécute jamais le script.

Un script est du JavaScript ordinaire en mode strict (variables locales, `if`, boucles, fonctions, `Math`, `Date`, `JSON`) sur le même [espace d'adressage](logic-address-space) et la même [surface de lecture](logic-expressions#the-read-surface-of-a-point) que les expressions, à deux différences près. Les lectures ne sont **pas** filtrées par la qualité : un vide se lit `null`, donc vérifiez `.State()` et protégez les calculs avec `??` ; traiter une mauvaise lecture est justement ce à quoi servent les actions. Et un script peut lire les variables d'expression (leur valeur publiée) ; seule une expression n'a pas le droit de les référencer.

Un point en direct (un tag, une variable, une alarme, un canal de Process) est le point, pas sa valeur : lisez donc sa valeur par un membre, `if (Variables.Start.ValueToBool())`, `Variables.Level.ValueToReal() > 80`, `if (Alarms.High.IsActive)`. Écrit nu dans le test de `if`, `while`, `do`, `for` ou `?:`, comme opérande de `!`, `&&` ou `||`, dans une comparaison ou dans l'arithmétique (`+=` et les autres affectations composées compris), il compterait toujours comme vrai ou ne lirait jamais sa valeur : `if (Variables.Start) { … }` exécuterait son bloc à chaque exécution, quelle que soit la valeur de la variable. La validation le refuse et nomme le membre à lire à la place ([Ce que la validation refuse](#what-validation-refuses)). Un point passé à une fonction, et tout membre d'un point, reste tel qu'il est écrit. Il en va de même pour un `switch` sur un point nu ou un `case` qui en nomme un (lisez `.Value`), pour le côté gauche de `??` (lisez `.Value`) et pour un emplacement `${…}` d'un gabarit littéral (lisez `.ValueToString()`). Une affectation composée à une variable (`Variables.Total += 1`, `-=`, `||=` et les autres) lit la variable comme le point avant d'écrire, elle est donc refusée avec l'affectation simple qui fonctionne : `Variables.Total = Variables.Total.ValueToReal() + 1`.

### Ce qu'une action peut écrire {#what-an-action-may-write}

Exactement deux constructions modifient l'installation, et les deux passent par le [canal d'écriture](logic#writing-values) journalisé :

| Écriture | Forme | Cible |
| --- | --- | --- |
| Affectation de mémoire | `Variables.Setpoint = 21.5;` ou `Variables.Forno.Setpoint = 21.5;` | Une variable mémoire, niveaux de dossier compris. La valeur est convertie vers le type de sortie de la variable et marquée pour la rémanence quand la variable est rémanente. Elle s'applique aussitôt : la ligne suivante du même script lit ce qu'elle vient d'écrire. |
| Écriture de tag | `Connector.Simulated.Pump.Cmd.Write(true);` ou `…Cmd.WriteAndWait(true);` | Un tag vivant. La valeur est une valeur physique : elle remonte la [chaîne de conversion](connector-value-pipeline) du tag jusqu'à la valeur brute que l'équipement accepte, et le canal la remet à la file de livraison propre à cet équipement, où les valeurs qui lui sont dues partent une à une, dans leur ordre d'arrivée. |

Un tag porte deux verbes d'écriture, et le script choisit selon qu'il a besoin ou non de la réponse :

| Verbe | Arguments | Omis | Renvoie | Attend l'équipement |
| --- | --- | --- | --- | --- |
| `Write(value)` | La valeur. | Rien à omettre. | Rien. | Non. La valeur est remise à la file de l'équipement et la ligne suivante s'exécute. |
| `WriteAndWait(value, timeoutMs, continueOnFailure)` | La valeur, le nombre de millisecondes pendant lesquelles l'attendre, et s'il faut poursuivre quand elle échoue. | `timeoutMs` : le temps d'attente propre au pilote est pris à la place. `continueOnFailure` : `true`. | `true` uniquement quand l'équipement a pris la valeur ; `false` pour tout le reste, valeur remplacée comprise. | Oui, jusqu'à l'arrivée de l'accusé de réception ou à l'épuisement du temps d'attente. |

L'attente se fait sur la voie des actions, si bien qu'un équipement lent ne retient que les actions placées derrière celle-ci, et elle ne retire jamais l'écriture : une valeur qui n'est pas confirmée à temps est toujours en route, et son accusé de réception est journalisé quand il arrive. C'est ce que dit la réponse, en toutes lettres : « « Cmd » n'a pas été confirmé en 2000 ms ; la valeur est encore en route vers l'équipement. » Avec `continueOnFailure` omis ou à `true`, le verbe répond `false` et le script poursuit ; avec `false`, l'exécution s'arrête à cette ligne, en nommant la cible et la raison.

**Aucune écriture n'interrompt un script d'elle-même.** Une écriture refusée avant tout envoi et une écriture que l'équipement refuse ou à laquelle il ne répond jamais échouent toutes deux seules : le reste du script s'exécute, et l'exécution se termine en exécution réussie avec des écritures en échec. Rien n'est défait non plus : ce qui a été appliqué avant une écriture en échec le reste, et le journal montre chaque écriture de l'ancienne vers la nouvelle valeur sous la source Action. Les raisons dans lesquelles se lit un refus :

- « L'écriture dans « X » a été rejetée : la valeur n'est pas finie. » (NaN ou infini)
- « « Tag » n'est pas matérialisé dans le connecteur. » (un équipement désactivé, ou un tag que le Connector ne porte pas)
- « « valeur » n'a pas pu être converti pour « Tag ». »
- « « X » est protégé en écriture par « `<qui la retient>` ». » (une recette en cours ou un verrouillage retient la source)
- « « Cmd » n'a pas été écrit : l'équipement n'est pas connecté. » (l'équipement est déjà connu comme hors service, rien n'est donc mis en file pour lui)
- « L'écriture dans « Tag » a échoué : le Connector n'est pas en service. »
- « La variable « X » est introuvable. » et « « Variables.X » est une variable d'expression : elle est calculée, jamais écrite. »
- Ce qu'a répondu l'équipement, ou son pilote, avec ses propres mots, quand le refus vient du câble.

Une valeur qu'une écriture plus récente sur le même tag a dépassée dans la file de l'équipement n'est **pas** un échec : « « Cmd » a été remplacé par une valeur plus récente avant l'envoi. » dit exactement ce qui s'est passé, la valeur la plus récente est celle que l'équipement reçoit, et rien n'en est rapporté nulle part, sauf à un `WriteAndWait`, qui répond `false` parce que cette valeur n'est pas celle qui est partie.

Ce qui met fin à une exécution : une erreur du script lui-même (une erreur de syntaxe, une adresse qui ne nomme rien, une exception), un `WriteAndWait(…, false)` dont l'écriture a échoué, et les limites décrites sous [Exécuter](#run).

Les échecs que l'exécution peut rapporter elle-même découlent de qui a attendu. Un `Write` refusé avant tout envoi l'est pendant que le script tourne encore : l'exécution le compte et le journal des actions le dit. Un `Write` que l'équipement refuse ensuite reçoit sa réponse bien après la fin de l'exécution : c'est alors la console [Événements](events) qui le porte, sous le nom de l'action, et jamais l'exécution. Un `WriteAndWait` est resté pour la réponse, il la compte donc dans un sens comme dans l'autre.

## Ce que la validation refuse {#what-validation-refuses}

| Message | Cause |
| --- | --- |
| « Un script est obligatoire. » | L'éditeur est vide. |
| « Erreur de script : … » | Le texte ne s'analyse pas. |
| « Les adresses doivent être statiques : « Root » est atteint par un segment calculé. » | `Variables[name]` ou tout segment qui n'est pas littéral. |
| « Adresse incomplète « … ». » | Une référence trop courte pour nommer un point. |
| « « X » est introuvable. » | Une lecture, ou une cible d'affectation, qui ne résout rien. |
| « « X » est une valeur : lisez-la sans .Value. » | Un membre de point en direct (`.Value`, `.ValueToBool()`, `.State()`, …) lu sur un point qui entre dans le script comme sa valeur : `System.StartTime.Value` ou `Process.Oven_soak.Oven_1.Procedure.Running.ValueToBool()`. Le message nomme le membre écrit. |
| « « X » est le point lui-même, pas sa valeur : comme condition, il compte toujours comme vrai. Lisez sa valeur avec X.ValueToBool(). » | Un point en direct écrit nu là où il est testé comme vrai ou faux : le test de `if`, `while`, `do`, `for` ou `?:`, ou un opérande de `!`, `&&` ou `\|\|` (`if (Variables.Start)`). Sur une alarme, le message nomme `.IsActive`. |
| « « X » est le point lui-même, pas sa valeur : une comparaison ou un calcul ne lit jamais sa valeur. Lisez sa valeur avec X.ValueToReal(). » | Un point en direct écrit nu dans une comparaison ou dans l'arithmétique (`Variables.Level > 80`, `total += Variables.Level`). Pour `==`, `!=`, `===` et `!==`, le message nomme `.Value`, et sur une alarme `.IsActive`. Un `switch` sur un point nu, ou un `case` qui en nomme un, reçoit le message avec `.Value`. |
| « « X » est le point lui-même, pas sa valeur : il n'est jamais vide, donc ?? le garde toujours. Lisez sa valeur avec X.Value. » | Un point en direct écrit nu à gauche de `??` (`Variables.Level ?? 0`). Sur une alarme, le message nomme `.IsActive`. |
| « « X » est le point lui-même, pas sa valeur : écrit dans un texte, il n'y montre jamais sa valeur. Lisez sa valeur avec X.ValueToString(). » | Un point en direct écrit nu dans un emplacement `${…}` d'un gabarit littéral. Sur une alarme, le message nomme `.IsActive`. |
| « « += » lit X comme le point lui-même, pas sa valeur, avant d'écrire. Utilisez X = X.ValueToReal() + 1. » | Une affectation composée à une variable : `+=`, `-=`, `*=`, `/=`, `%=`, `**=`, celles bit à bit, `&&=`, `\|\|=` et `??=`. Le message nomme l'opérateur écrit et l'affectation simple qui fait ce qu'il dit, avec le côté droit tel qu'il est écrit : `.ValueToReal()` pour l'arithmétique, `.ValueToInteger()` pour celles bit à bit, `.ValueToBool()` pour `&&=` et `\|\|=`, `.Value` pour `??=`. |
| « Affectez la variable elle-même : Variables.Name = value. » | La partie gauche du `=` nomme un membre, ou descend plus bas que l'adresse d'une variable. |
| « « X » est une variable d'expression : elle est calculée, jamais écrite. » | Une affectation à une variable d'expression. |
| « Les points « Connector » sont en lecture seule. Écrivez les tags avec Connector.Driver.Device.Tag.Write(value). » | Une affectation à une racine autre que Variables (Process a son propre message). |
| « Les points Process sont en lecture seule dans les actions ; les commandes d'essai et les écritures de préparation ne sont pas prises en charge par les scripts. » | Une affectation à un point Process, ou un appel de `Write` sur lui. |
| « Les commandes d'essai de Process ne peuvent pas être appelées depuis les actions ; les points de commande exposent des valeurs de disponibilité en lecture seule. » | Un appel à `Process.<Model>.<Unit>.Commands.Start()` ou à tout autre point de commande ; un point `Commands` ne fait que dire si la commande pourrait s'exécuter maintenant. |
| « Les opérateurs ++ et -- ne sont pas pris en charge sur les points. Utilisez Variables.Name = Variables.Name.ValueToReal() + 1. » | `Variables.N++`. |
| « Write(value) est l'écriture du tag : Connector.Driver.Device.Tag.Write(value). » | `Write` appelé sur autre chose qu'une adresse de tag. La phrase nomme le verbe employé : `WriteAndWait` reçoit donc la même, avec son propre nom aux deux endroits. |
| « Le temps d'attente de WriteAndWait doit être un nombre de millisecondes : WriteAndWait(value, 2000). » | Le deuxième argument est écrit en toutes lettres et n'est pas un nombre. |
| « Le « continuer en cas d'échec » de WriteAndWait doit être true ou false : WriteAndWait(value, 2000, false). » | Le troisième argument est écrit en toutes lettres et ne vaut ni `true` ni `false`. |
| « WriteAndWait prend au plus trois arguments : la valeur, le temps d'attente en millisecondes et s'il faut continuer en cas d'échec. » | Un quatrième argument. |

Les trois messages de `WriteAndWait` jugent ce que dit le texte lui-même. Un argument que l'éditeur ne peut pas trancher à la lecture (une variable, une expression calculée) est jugé au moment où l'appel a lieu, et la même phrase met alors fin à l'exécution à cette ligne.

Les variables locales JavaScript (`let x = 1; x = x + 1;`) échappent à toutes ces règles.

## Exécuter {#run}

**Exécuter** est le verbe de tête de la barre au-dessus d'une action ouverte ; il n'est dessiné que pour un rôle qui détient **Piloter Logic**, et il est grisé avec « Runtime arrêté » pendant que la station est en lecture seule (le verrou d'ingénierie ne le grise pas : exécuter, c'est piloter, pas configurer). Quand le rôle détient aussi Configurer Logic, le brouillon de l'éditeur est enregistré d'abord et l'exécution porte sur la définition enregistrée ; sans cette permission, l'exécution porte sur la définition durable (les champs étant en lecture seule, les deux ne diffèrent jamais).

Chaque exécution se fait sur la **voie des actions** avec un moteur de script neuf, si bien qu'aucun état ne fuit d'une exécution à l'autre et que deux scripts ne tournent jamais en même temps : ils passent l'un après l'autre, dans l'ordre où ils ont été demandés. Cette voie n'est pas le cycle de 100 ms. Les expressions, les alarmes, les acquittements et les écritures venues d'ailleurs continuent pendant qu'un script tourne, de sorte qu'un cycle peut tomber au milieu de l'un d'eux et que deux lectures du même script peuvent voir des cycles différents ; en échange, une action qui s'arrête pour attendre un équipement ne retarde que les actions placées derrière elle, et rien d'autre dans la station.

Une exécution est interrompue au-delà de 500 ms (« Le script a été interrompu : il a dépassé la limite de temps de 500 ms (boucle infinie ?). »), de 100 000 instructions ou de 4 MB de mémoire, chaque limite ayant son message. Ces 500 ms mesurent le script qui tourne : le compte est suspendu tant qu'un `WriteAndWait` attend un équipement, puis repris ensuite, de sorte qu'un script correct n'est jamais tenu pour responsable d'un équipement lent. Une action désactivée échoue sans s'exécuter (« L'action est désactivée. ») ; un runtime arrêté répond « Le runtime de la logique n'est pas en service. »

Le journal des actions indique « Exécution réussie en 12 ms. » quand toutes les écritures sont passées, « Exécution réussie en 12 ms, 1 écriture en échec : Cmd : « Cmd » n'a pas été écrit : l'équipement n'est pas connecté. » quand le script lui-même est allé au bout et qu'une écriture n'est pas passée (et « Exécution réussie en 12 ms, 3 écritures en échec, la première : Cmd : « Cmd » n'a pas été écrit : l'équipement n'est pas connecté. » à partir du deuxième échec), ou « Échec de l'exécution : `<raison>` » quand l'exécution elle-même s'est mal terminée.

L'exécution en échec parvient à la console [Événements](events), sous le nom de l'action et accompagnée de ce qui l'a invoquée : Manuel pour le verbe, Dashboard pour un Bouton, MCP pour l'agent, le nom du déclencheur pour une exécution planifiée, et `Process · <unit> · <action>` pour un essai de Process. Elle est journalisée en avertissement la première fois, résumée tant qu'elle échoue pour la même cause, puis notée une fois encore au rétablissement, et cette ligne de rétablissement est la seule entrée qu'une exécution réussie y écrira jamais. Une écriture refusée parvient à la console de la même manière et sous le même nom, et pour la même raison c'est l'action qui est nommée et non l'exécution : la réponse de l'équipement peut arriver plusieurs secondes après la fin de l'exécution, et il n'y a alors plus d'exécution à faire échouer. Une exécution qui réussit simplement, et chaque valeur qu'elle écrit, ne va qu'à la [capture détaillée](events-console#detailed-capture) : un script qui écrit à chaque cycle produit des valeurs par milliers, et une console remplie de ces lignes est une console où personne ne lit plus les nouvelles de la station.

Le rail DERNIÈRE EXÉCUTION indique quand l'action s'est exécutée pour la dernière fois (ou « Jamais exécutée. »), « Durée : N ms », qui l'a déclenchée (ci-dessous), la dernière erreur dans un avis de prudence, les écritures en échec de cette exécution dans un avis de prudence à elles (la même phrase que celle portée par le journal des actions, si bien qu'une exécution réussie qui a laissé une consigne là où elle était le dit ici aussi), et le rappel « Les exécutions se font sur la voie des actions, une à la fois. Seule une exécution en échec parvient à la console des événements ; une exécution réussie, et chaque valeur qu'elle écrit, est conservée pour la capture détaillée. Les déclencheurs appellent les actions selon leur planification. » Le rail appartient à l'action vivante : un enregistrement automatique ne l'efface pas ; un redémarrage du runtime, si.

### Déclenchée par {#fired-by}

Sous la durée, la ligne « Déclenchée par : … » dit ce qui a exécuté l'action en dernier :

| Ce qui l'a exécutée | La ligne indique |
| --- | --- |
| **Exécuter** sur cette page | « Déclenchée par : Exécution depuis cette page » |
| Un Bouton de tableau de bord | « Déclenchée par : Un bouton de tableau de bord » |
| L'agent (`action_run`) | « Déclenchée par : Agent logiciel » |
| Un déclencheur | Le nom du déclencheur : « Déclenchée par : Heater off when too hot ». |
| Une commande d'action Logic d'un essai de Process | L'unité et le groupe d'actions : « Déclenchée par : Process · Oven 1 · After soak ». |

## Utilisé par {#used-by}

La carte **Utilisé par** liste tout ce qui, dans la station, exécute l'action (« Tout ce qui, dans la station, exécute cette action. »), lue à l'ouverture de l'action : une ligne par commande de Process qui la déclenche, par déclencheur qui lui est associé et par Bouton de tableau de bord qui l'exécute.

| Nature | Emplacement | Nom |
| --- | --- | --- |
| **Commande Process** | Le modèle. | Le groupe d'actions et le moment : `After soak · Fin`. |
| **Déclencheur** | Vide. | Le déclencheur, sous ses dossiers. |
| **Bouton** | Le tableau de bord. | Le libellé du Bouton. |

Sans rien, elle dit « Rien dans la station n'exécute cette action. » ; quand les références ne peuvent pas être lues, « Cette action est peut-être encore utilisée. Réessayez avant de la supprimer ou de la renommer. » avec **Réessayer**. La carte ne fait que lire, donc chaque rôle la voit, y compris quand la station est verrouillée en ingénierie. Lisez-la avant de désactiver ou de supprimer une action : une commande de Process dont l'action a disparu n'exécute rien et le dit sur sa propre page.

## Lire l'essai qui l'a déclenchée {#reading-the-run-that-fired-it}

Une action qu'un essai de Process déclenche à la fin lit cet essai sous la branche [LastRun](process-addresses#lastrun) de l'unité, qui le porte déjà quand l'action s'exécute ; une action déclenchée au démarrage lit les métadonnées de l'essai qui commence sous `Procedure.Metadata`. Les points de Process se lisent comme des valeurs brutes, sans `.Value` après eux, contrairement aux variables, et un point que l'essai ne porte pas se lit `null`, donc protégez-le :

```js
if (Process.Oven_soak.Oven_1.LastRun.Verdict === "Ok") {
  Variables.Last_serial = Process.Oven_soak.Oven_1.LastRun.Metadata.Serial;
}
const peak = Process.Oven_soak.Oven_1.LastRun.Evaluations.Soak_check.Temperature_in_range.MaxValue;
if (peak !== null) {
  Variables.Last_peak = peak;
}
```

Les adresses s'écrivent en entier, comme toute adresse d'un script : une adresse ne peut pas se construire à partir d'une variable.

## Commandes {#commands}

| Commande | Ce qu'elle fait | Grisée quand | Non dessinée quand |
| --- | --- | --- | --- |
| Exécuter | Enregistre le brouillon (avec Configurer Logic) et met une exécution en file. | Runtime arrêté. | Le rôle n'a pas Piloter Logic. |
| Activer / Désactiver | Bascule Activée ; une action désactivée refuse Exécuter et laisse au repos chaque déclencheur qui lui est associé, et une commande de Process qui la déclenche est refusée à chaque fois et passe À vérifier. | Station verrouillée ou en lecture seule. | Le rôle n'a pas Configurer Logic. |
| Copier | Duplique l'action à la racine Actions sous un nom libre et met la copie en évidence dans le tableau de cette racine au lieu de l'ouvrir ([Créer et copier](logic#creating-and-copying)) ; la copie n'a aucune exécution enregistrée et aucun déclencheur associé. | Station verrouillée ou en lecture seule. | Le rôle n'a pas Configurer Logic. |
| Supprimer l'action | Demande « Supprimer « X » ? Cela retire l'action. Cette action est irréversible. » Un déclencheur qui lui était associé se retrouve sans action, il n'est pas supprimé ; une commande de Process qui l'exécute reste, n'exécute rien et passe À vérifier. | Station verrouillée ou en lecture seule. | Le rôle n'a pas Configurer Logic. |

Le volet de la racine Actions liste chaque action avec son état dans la colonne Valeur : « Activée · dernière exécution `<quand>` », avec « · erreur » quand la dernière exécution a échoué, « · écriture en échec » quand elle a réussi et qu'une de ses écritures n'est pas passée, ou « Désactivée ».

## Ce que les actions ne font pas {#what-actions-do-not-do}

Elles n'envoient pas les commandes d'essai de Process, ne préparent pas une procédure et n'ajoutent pas de commentaire à un essai (ces points sont des valeurs de disponibilité et de préparation en lecture seule) ; elles n'acquittent pas les alarmes ; elles ne s'exécutent pas en parallèle ; elles ne conservent aucun état d'une exécution à l'autre et ne partagent aucune fonction ; et elles ne sont déclenchées ni par un changement de valeur ni par une condition, seulement par Exécuter, par l'intervalle d'un déclencheur, par un bouton de tableau de bord, par l'agent ou par la commande d'un essai de Process. Un essai de Process n'attend jamais l'action qu'il déclenche, et le résultat de l'action n'est jamais écrit dans l'essai.
