Acciones
El editor de acciones: identidad y ubicación, el bloque del script, lo que un script puede leer y escribir, todos los mensajes de validación, el verbo Ejecutar y su diario, quién disparó la última ejecución, qué ejecuta la acción, Habilitar y Deshabilitar, Copiar y Eliminar, y los límites de una ejecución.
Una acción es un bloque de sentencias JavaScript que lee el espacio de direcciones y escribe variables de memoria y tags. Las acciones son el camino de escritura de Logic: corren a petición (Ejecutar), por un disparador, desde un Botón de un panel de control cableado a ellas, o en el Arranque o la Finalización de un ensayo de Process, por un comando de acción de Logic. Seleccione una acción bajo la sección Actions de la columna de Logic, o pulse Abrir en su fila de la tabla de su carpeta o de la raíz de su sección, para abrir su editor (una acción hecha con Nuevo… > Nueva acción no se abre, sino que queda resaltada en esa tabla, véase Crear y copiar); leer necesita Ver Logic, editar Configurar Logic, y ejecutar Operar Logic.
La cabecera muestra la marca de acción, el nombre, la línea "Un script que usted ejecuta a petición, o que un disparador ejecuta por usted, y que escribe variables y tags." y una insignia Deshabilitado mientras la acción está apagada. La columna del formulario lleva IDENTIDAD, SCRIPT DE LA ACCIÓN y Usado por; el carril lleva ÚLTIMA EJECUCIÓN.
Identidad
| Campo | Qué es | Valores / por defecto | Efecto |
|---|---|---|---|
| Nombre | El nombre visible y el rótulo bajo el que se anota una ejecución en el diario. | Texto libre; una acción nueva es ActionN; un nombre en blanco se guarda como Action. |
Se guarda solo con cada pulsación de tecla. Las acciones se referencian por identidad, así que cambiar el nombre no reescribe nada y no rompe nada. |
| Línea de ubicación | Dónde está archivada la acción, en palabras: Actions / Startup / Walk setpoint. |
Solo lectura. | Una acción no tiene dirección; la línea es un lugar, nunca algo que se escriba en una expresión. Se cambia con Mover a en el árbol. |
| Descripción (opcional) | Texto libre. | Vacío por defecto. | Ninguno. |
Script de la acción
La tarjeta SCRIPT DE LA ACCIÓN es el editor de scripts con la indicación "Sentencias JavaScript (let, if, for, funciones). Escriba las variables de memoria por asignación (Variables.Setpoint = 21.5;) y los tags con Connector.Driver.Device.Tag.Write(value). Las lecturas no pasan por un filtro de calidad: compruebe .State() y proteja los huecos con ?? antes de operar." El script se guarda solo con cada pulsación de tecla y se valida cada medio segundo; el mensaje aparece bajo la indicación. La validación nunca ejecuta el script.
Un script es JavaScript corriente en modo estricto (variables locales, if, bucles, funciones, Math, Date, JSON) sobre el mismo espacio de direcciones y la misma superficie de lectura que las expresiones, con dos diferencias. Las lecturas no pasan por el filtro de calidad: un hueco se lee como null, así que compruebe .State() y proteja la aritmética con ??; manejar una lectura mala es para lo que sirven las acciones. Y un script puede leer variables de expresión (su valor publicado); solo una expresión no las puede referenciar.
Un punto en vivo (un tag, una variable, una alarma, un canal de Process) es el punto, no su valor, así que lea su valor por un miembro: if (Variables.Start.ValueToBool()), Variables.Level.ValueToReal() > 80, if (Alarms.High.IsActive). Escrito pelado en la prueba de if, while, do, for o ?:, como operando de !, && o ||, en una comparación o en la aritmética (con += y las demás asignaciones compuestas), siempre contaría como verdadero o nunca leería su valor: if (Variables.Start) { … } ejecutaría su bloque en cada ejecución, tenga la variable el valor que tenga. La validación lo rechaza y nombra el miembro que hay que leer en su lugar (Lo que la validación rechaza). Un punto pasado a una función, y cualquier miembro de un punto, queda como está escrito. Lo mismo vale para un switch sobre un punto pelado o un case que nombra uno (lea .Value), para el lado izquierdo de ?? (lea .Value) y para un marcador ${…} de una plantilla literal (lea .ValueToString()). Una asignación compuesta a una variable (Variables.Total += 1, -=, ||= y las demás) lee la variable como el punto antes de escribir, así que se rechaza con la asignación simple que funciona: Variables.Total = Variables.Total.ValueToReal() + 1.
Lo que una acción puede escribir
Exactamente dos construcciones cambian la planta, y las dos pasan por el punto único de escritura anotado en el diario:
| Escritura | Forma | Destino |
|---|---|---|
| Asignación de memoria | Variables.Setpoint = 21.5; o Variables.Forno.Setpoint = 21.5; |
Una variable de memoria, con sus niveles de carpeta incluidos. El valor se convierte al tipo de salida de la variable y se marca para retención cuando la variable es retentiva. Se aplica de inmediato, así que la línea siguiente del mismo script lee lo que acaba de escribir. |
| Escritura de tag | Connector.Simulated.Pump.Cmd.Write(true); o …Cmd.WriteAndWait(true); |
Un tag en vivo. El valor es un valor de ingeniería: viaja hacia atrás por la cadena de conversión del tag hasta el valor bruto que acepta el dispositivo, y el punto único lo entrega a la cola de entrega propia de ese dispositivo, donde los valores que se le deben se envían de uno en uno en el orden en que llegaron. |
Un tag lleva dos verbos de escritura, y el script elige según necesite o no la respuesta:
| Verbo | Argumentos | Omitidos | Devuelve | Espera al equipo |
|---|---|---|---|---|
Write(value) |
El valor. | No hay nada que omitir. | Nada. | No. El valor se entrega a la cola del dispositivo y la línea siguiente corre. |
WriteAndWait(value, timeoutMs, continueOnFailure) |
El valor, cuántos milisegundos esperarlo, y si continuar cuando falla. | timeoutMs: se usa en su lugar el tiempo de espera propio del driver. continueOnFailure: true. |
true solo cuando el equipo aceptó el valor; false para todo lo demás, incluido un valor sustituido. |
Sí, hasta que llega el acuse o se acaba el tiempo de espera. |
La espera ocurre en la vía de acciones, así que un dispositivo lento solo retiene a las acciones que van detrás de esta, y nunca retira la escritura: un valor que no se confirma a tiempo sigue viajando, y su acuse se anota en el diario cuando llega. Eso es lo que dice la respuesta, con todas sus letras: "'Cmd' no se confirmó en 2000 ms; el valor sigue de camino al equipo." Con continueOnFailure omitido o en true el verbo contesta false y el script continúa; con false la ejecución termina en esa línea, nombrando el destino y el motivo.
Ninguna escritura aborta por sí sola un script. Una escritura rechazada antes de que se envíe nada y una escritura que el equipo rechaza o nunca contesta fallan las dos por su cuenta: el resto del script corre, y la ejecución termina como una ejecución correcta con escrituras fallidas. Tampoco se deshace nada, así que lo que se aplicó antes de una escritura fallida se queda aplicado, y el diario muestra cada escritura como valor antiguo a valor nuevo bajo el origen Acción. Los motivos con los que se lee un rechazo:
- "La escritura en 'X' se rechazó: el valor no es finito." (un NaN o un infinito)
- "'El tag' no está materializado en el connector." (un dispositivo deshabilitado, o un tag que el Connector no lleva)
- "No se pudo convertir 'valor' para 'El tag'."
- "'X' está protegida contra escritura por '
<holder>'." (una receta en marcha o un enclavamiento retienen el origen) - "No se escribió en 'Cmd': el equipo no está conectado." (ya se sabe que el dispositivo está caído, así que no se le encola nada)
- "Falló la escritura en 'El tag': el Connector no está en marcha."
- "No se encontró la variable 'X'." y "'Variables.X' es una variable de expresión: se calcula, nunca se escribe."
- Lo que contestaron el equipo o su driver, con sus propias palabras, cuando el rechazo vino del cable.
Un valor al que una escritura más reciente sobre el mismo tag adelantó en la cola del dispositivo no es un fallo: "'Cmd' se sustituyó por un valor más reciente antes de enviarse." es exactamente lo que pasó, el valor más reciente es el que recibe el equipo, y no se informa de ello en ninguna parte salvo a un WriteAndWait, que contesta false porque este valor no es el que salió.
Lo que sí termina una ejecución: un error del propio script (un error de sintaxis, una dirección que no nombra nada, una excepción), un WriteAndWait(…, false) cuya escritura falló, y los límites de ejecución de Ejecutar.
De quién esperó se sigue qué fallos puede informar la propia ejecución. Un Write que se rechaza antes de que se envíe nada se rechaza mientras el script sigue corriendo, así que la ejecución lo cuenta y el registro de acciones lo dice; un Write que el equipo rechaza después se contesta mucho después de terminada la ejecución, así que es la consola de Eventos, bajo el nombre de la acción, la que lo lleva, y nunca la ejecución. Un WriteAndWait se quedó a por la respuesta, así que la cuenta fuera cual fuera esa respuesta.
Lo que la validación rechaza
| Mensaje | Causa |
|---|---|
| "Se necesita un script." | El editor está vacío. |
| "Error del script: …" | El texto no se puede analizar. |
| "Las direcciones deben ser estáticas: se accede a 'Root' con un segmento calculado." | Variables[name] o cualquier segmento que no sea literal. |
| "Dirección incompleta '…'." | Una referencia demasiado corta para nombrar un punto. |
| "No se encontró 'X'." | Una lectura, o un destino de asignación, que no resuelve nada. |
| "'X' es un valor: léalo sin .Value." | Un miembro de punto en vivo (.Value, .ValueToBool(), .State(), …) leído en un punto que entra al script como su valor: System.StartTime.Value o Process.Oven_soak.Oven_1.Procedure.Running.ValueToBool(). El mensaje nombra el miembro que se escribió. |
| "'X' es el punto en sí, no su valor: como condición siempre cuenta como verdadero. Lea su valor con X.ValueToBool()." | Un punto en vivo escrito pelado donde se prueba si es verdadero: la prueba de if, while, do, for o ?:, o un operando de !, && o \|\| (if (Variables.Start)). En una alarma el mensaje nombra .IsActive. |
| "'X' es el punto en sí, no su valor: una comparación o un cálculo nunca lee su valor. Lea su valor con X.ValueToReal()." | Un punto en vivo escrito pelado en una comparación o en la aritmética (Variables.Level > 80, total += Variables.Level). Para ==, !=, === y !== el mensaje nombra .Value, y en una alarma .IsActive. Un switch sobre un punto pelado, o un case que nombra uno, recibe el mensaje con .Value. |
| "'X' es el punto en sí, no su valor: nunca está vacío, así que ?? siempre se queda con él. Lea su valor con X.Value." | Un punto en vivo escrito pelado a la izquierda de ?? (Variables.Level ?? 0). En una alarma el mensaje nombra .IsActive. |
| "'X' es el punto en sí, no su valor: escrito dentro de un texto nunca muestra su valor. Lea su valor con X.ValueToString()." | Un punto en vivo escrito pelado en un marcador ${…} de una plantilla literal. En una alarma el mensaje nombra .IsActive. |
| "'+=' lee X como el punto en sí, no su valor, antes de escribir. Use X = X.ValueToReal() + 1." | Una asignación compuesta a una variable: +=, -=, *=, /=, %=, **=, las de bits, &&=, \|\|= y ??=. El mensaje nombra el operador escrito y la asignación simple que hace lo que se lee, con el lado derecho tal como se escribió: .ValueToReal() para la aritmética, .ValueToInteger() para las de bits, .ValueToBool() para &&= y \|\|=, .Value para ??=. |
| "Asigne a la propia variable: Variables.Name = value." | El lado izquierdo del = nombra un miembro, o va más adentro de lo que llega la dirección de una variable. |
| "'X' es una variable de expresión: se calcula, nunca se escribe." | Asignar a una variable de expresión. |
| "Los puntos de 'Connector' son de solo lectura. Escriba los tags con Connector.Driver.Device.Tag.Write(value)." | Asignar a cualquier raíz que no sea Variables (Process tiene su propio mensaje). |
| "Los puntos de Process son de solo lectura en las acciones; los comandos de ensayo y las escrituras de preparación no se admiten desde scripts." | Asignar a un punto de Process, o llamar a Write sobre él. |
| "Los comandos de ensayo de Process no se pueden llamar desde las acciones; los puntos de comando exponen valores de disponibilidad de solo lectura." | Llamar a Process.<Model>.<Unit>.Commands.Start() o a cualquier otro punto de comando; un punto Commands solo lee si el comando se podría ejecutar ahora. |
| "Los operadores ++ y -- no se admiten en los puntos. Use Variables.Name = Variables.Name.ValueToReal() + 1." | Variables.N++. |
| "Write(value) es la escritura del tag: Connector.Driver.Device.Tag.Write(value)." | Write llamado sobre algo que no es una dirección de tag. La frase nombra el verbo que se usó, así que WriteAndWait recibe la misma con su propio nombre en los dos sitios. |
| "El tiempo de espera de WriteAndWait tiene que ser un número de milisegundos: WriteAndWait(value, 2000)." | El segundo argumento está escrito y no es un número. |
| "El continuar si falla de WriteAndWait tiene que ser true o false: WriteAndWait(value, 2000, false)." | El tercer argumento está escrito y no es true ni false. |
| "WriteAndWait admite como máximo tres argumentos: el valor, el tiempo de espera en milisegundos y si continúa cuando falla." | Un cuarto argumento. |
Los tres mensajes de WriteAndWait juzgan lo que dice el propio texto. Un argumento que el editor no puede resolver leyéndolo (una variable, una expresión calculada) se juzga cuando la llamada se hace de verdad, y entonces esa misma frase termina la ejecución en esa línea.
Las variables locales de JavaScript (let x = 1; x = x + 1;) no las toca ninguna de estas reglas.
Ejecutar
Ejecutar es el verbo inicial de la barra sobre una acción abierta; se dibuja solo para un rol con Operar Logic y se pone en gris con "Runtime detenido" mientras la estación es de solo lectura (el bloqueo de ingeniería no lo pone en gris: ejecutar es operar, no configurar). Cuando el rol tiene además Configurar Logic, el borrador del editor se guarda primero y la ejecución corre la definición guardada; sin ese permiso, la ejecución corre la definición duradera (los campos son de solo lectura, así que las dos nunca difieren).
Toda ejecución corre en la vía de acciones con un motor de scripts nuevo, así que entre ejecuciones no se filtra ningún estado y dos scripts nunca corren a la vez: corren de uno en uno, en el orden en que se pidieron. La vía no es el ciclo de 100 ms. Las expresiones, las alarmas, los reconocimientos y las escrituras que llegan de otra parte siguen su marcha mientras un script corre, así que un ciclo puede caer en medio de uno y dos lecturas del mismo script pueden ver ciclos distintos; a cambio, una acción que se detiene a esperar a un dispositivo retrasa solo a las acciones encoladas detrás de ella y a nada más de la estación.
Una ejecución se aborta al pasar de 500 ms ("El script se abortó: superó el límite de tiempo de 500 ms (¿bucle infinito?)."), de 100.000 sentencias o de 4 MB de memoria, cada uno con un mensaje que nombra el límite. Los 500 ms miden el script corriendo: la cuenta se detiene mientras un WriteAndWait espera a un dispositivo y se reanuda después, así que a un script correcto nunca se le culpa de un equipo lento. Una acción deshabilitada falla sin llegar a ejecutarse ("La acción está deshabilitada."); un runtime detenido contesta "El runtime de la lógica no está en ejecución."
El registro de acciones informa "Ejecución correcta en 12 ms." cuando todas las escrituras llegaron, "Ejecución correcta en 12 ms, 1 escritura fallida: Cmd: No se escribió en 'Cmd': el equipo no está conectado." cuando el script terminó y una escritura no (con "Ejecución correcta en 12 ms, 3 escrituras fallidas, la primera: Cmd: No se escribió en 'Cmd': el equipo no está conectado." a partir del segundo fallo), o "Error en la ejecución: <reason>" cuando lo que acabó mal fue la ejecución misma.
La ejecución fallida llega a la consola de Eventos, bajo el nombre de la acción y junto a lo que la invocó: Manual para el verbo, Dashboard para un Botón, MCP para el agente, el nombre del disparador para una ejecución programada, y Process · <unit> · <action> para un ensayo de Process. Se anota como advertencia la primera vez, se resume mientras siga fallando por la misma causa, y se anota una vez más cuando se recupera, y esa línea de recuperación es la única entrada que una ejecución correcta escribe ahí. Una escritura rechazada llega a la consola por el mismo camino y bajo el mismo nombre, y por el mismo motivo se nombra a la acción y no a la ejecución: la respuesta del equipo puede llegar segundos después de terminada la ejecución, y ya no queda ejecución que hacer fallar. Una ejecución que simplemente sale bien, y todos los valores que escribe, se escriben solo en la captura detallada: un script que escribe en cada ciclo produce valores por millares, y una consola llena de ellos es una consola en la que nadie puede leer las noticias de la estación.
El carril ÚLTIMA EJECUCIÓN muestra cuándo corrió la acción por última vez (o "Nunca se ha ejecutado."), "Duración: N ms", quién la disparó (abajo), el último error en una nota de precaución, las escrituras fallidas de esa ejecución en una nota de precaución propia (la misma frase que llevó el registro de acciones, así que una ejecución que salió bien y aun así dejó una consigna donde estaba lo dice también aquí), y el recordatorio "Las ejecuciones se hacen en la vía de acciones, de una en una. Solo una ejecución fallida llega a la consola de Eventos; una ejecución correcta, y todos los valores que escribe, se guardan para la captura detallada. Los disparadores invocan las acciones según su programación." El carril pertenece a la acción viva: un guardado automático nunca lo vacía; un reinicio del runtime sí.
Disparada por
Debajo de la duración, la línea "Disparada por: …" dice qué ejecutó la acción por última vez:
| Qué la ejecutó | La línea dice |
|---|---|
| Ejecutar en esta página | "Disparada por: Ejecución en esta página" |
| Un Botón de un panel de control | "Disparada por: Un botón del panel de control" |
El agente (action_run) |
"Disparada por: Agente" |
| Un disparador | El nombre del disparador: "Disparada por: Heater off when too hot". |
| Un comando de acción de Logic de un ensayo de Process | La unidad y el grupo de acciones: "Disparada por: Process · Oven 1 · After soak". |
Usado por
La tarjeta Usado por lista todo lo que en la estación ejecuta la acción ("Todo lo que en la estación ejecuta esta acción."), leído al abrir la acción: una fila por cada comando de Process que la dispara, por cada disparador vinculado a ella y por cada Botón de un panel de control que la ejecuta.
| Tipo | Ubicación | Nombre |
|---|---|---|
| Comando de Process | El modelo. | El grupo de acciones y el momento: After soak · Finalización. |
| Disparador | Vacío. | El disparador, bajo sus carpetas. |
| Botón | El panel de control. | El rótulo del Botón. |
Sin nada, dice "Nada en la estación ejecuta esta acción."; cuando las referencias no se pueden leer, "Puede que esta acción siga en uso. Inténtelo de nuevo antes de eliminarla o renombrarla." con Intentar de nuevo. La tarjeta solo lee, así que todo rol la ve, también mientras la estación está bloqueada en ingeniería. Léala antes de deshabilitar o eliminar una acción: un comando de Process cuya acción desapareció no ejecuta nada y lo dice en su propia página.
Leer el ensayo que la disparó
Una acción que un ensayo de Process dispara en la Finalización lee ese ensayo bajo la rama LastRun de la unidad, que ya lo tiene cuando la acción se ejecuta; una disparada en el Arranque lee los metadatos del ensayo que arranca bajo Procedure.Metadata. Los puntos de Process se leen como valores simples, sin .Value detrás, a diferencia de las variables, y un punto que el ensayo no tiene se lee null, así que protéjalo:
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;
}
Las direcciones se escriben enteras, como toda dirección de un script: una dirección no se puede construir a partir de una variable.
Comandos
| Comando | Qué hace | En gris cuando | No se dibuja cuando |
|---|---|---|---|
| Ejecutar | Guarda el borrador (con Configurar Logic) y encola una ejecución. | El runtime está detenido. | El rol no tiene Operar Logic. |
| Habilitar / Deshabilitar | Cambia Habilitado; una acción deshabilitada rechaza Ejecutar y deja inactivo todo disparador vinculado a ella, y un comando de Process que la dispara se rechaza cada vez y queda en Revisión. | La estación está bloqueada o de solo lectura. | El rol no tiene Configurar Logic. |
| Copiar | Duplica la acción en la raíz Actions con un nombre libre y resalta la copia en la tabla de esa raíz en lugar de abrirla (Crear y copiar); la copia no tiene ninguna ejecución anotada ni ningún disparador vinculado. | La estación está bloqueada o de solo lectura. | El rol no tiene Configurar Logic. |
| Eliminar la acción | Pregunta "¿Eliminar 'X'? Esto quita la acción. Esta acción no se puede deshacer." Un disparador vinculado a ella se queda sin vínculo, no se elimina; un comando de Process que la ejecuta se queda, no ejecuta nada y queda en Revisión. | La estación está bloqueada o de solo lectura. | El rol no tiene Configurar Logic. |
El panel de la raíz Actions lista todas las acciones con su estado en la columna Valor: "Habilitado · última ejecución <when>", con "· error" cuando la última ejecución falló, "· escritura fallida" cuando salió bien y una escritura suya no, o "Deshabilitado".
Lo que las acciones no hacen
No despachan comandos de ensayo de Process, no preparan un procedimiento ni añaden un comentario a un ensayo (esos puntos son valores de disponibilidad y de preparación de solo lectura); no reconocen alarmas; no corren en paralelo; no guardan estado entre ejecuciones y no comparten funciones; y no las dispara ningún cambio de valor ni ninguna condición, solo Ejecutar, el intervalo de un disparador, un botón de un panel de control, el agente o el comando de un ensayo de Process. Un ensayo de Process nunca espera a la acción que dispara, y el resultado de la acción nunca se escribe en el ensayo.