Cómo se mueve una ejecución
Una ejecución empieza en Inicio, sigue los cables de nodo en nodo y termina cuando todas las ramas han acabado y se ha llegado a Fin. Esta página explica las reglas que sigue; lo que hace cada nodo está en la referencia de los nodos, y los valores que viajan con ella, en datos.
Inicio y Fin
Un experimento tiene exactamente un Inicio y un Fin.
- Inicio no tiene entrada. Se supera de inmediato, y su fila en la línea de tiempo da la semilla de la ejecución. Su salida puede tener varios cables: el experimento empieza entonces con ramas paralelas.
- Toda rama que llega a Fin se detiene ahí. Fin se muestra como en ejecución desde la primera llegada y se supera una vez, después de que haya terminado la última rama — y no lo hace en absoluto si algún paso falló. Esa superación es lo que hace que la ejecución se considere Superado.
- Una ejecución en la que todas las ramas terminaron sin error pero ninguna llegó a Fin falla con
run.no_end.
Salidas y cables
El paso de un nodo termina eligiendo una salida, y la ejecución sigue cada cable de esa salida. La mayoría de los nodos tienen una salida, Salida; algunos eligen entre varias:
| Nodo | Salidas que deben conectarse | Salidas que pueden conectarse |
|---|---|---|
| Fin | — | — |
| Rama paralela | Rama 1, Rama 2 | — |
| Rama por estado, Rama por valor | Sí, No | — |
| Todas las esperas (Esperar OSC, Esperar UDP, Esperar MQTT, Esperar solicitud HTTP, Esperar WebSocket) | Coincide | Tiempo agotado |
| Bucle | Cuerpo, Hecho | Límite |
| Todos los demás nodos | Salida | — |
Para conectar, arrastra desde una salida hasta un nodo; si la sueltas en un lienzo vacío, añade ahí un nodo nuevo. Arrastrar desde una salida que ya tiene un cable añade otro. Agregar siguiente, la tecla A y el + de un cable insertan un nodo en el cable existente.
Un grafo sin terminar es un borrador: se guarda, pero no se ejecuta. Completa el grafo en la barra de herramientas dice qué falta y muestra el nodo. Consulta qué se comprueba antes de una ejecución.
Ramas paralelas
Varios cables desde una salida
Cuando una salida tiene varios cables — los de Inicio incluidos — cada nodo al que llevan se ejecuta a la vez. El primer cable continúa la rama; cada cable adicional inicia una rama paralela. Cada rama lleva su propia copia de las variables y de la última respuesta HTTP, así que lo que una rama define o recibe no lo ven las demás.
Rama paralela y unión
Rama paralela se supera de inmediato y sale por Rama 1 y Rama 2 — lo mismo que dos cables desde una salida, dibujado como un nodo.
Unir ramas espera todos los cables que llegan a él, y luego continúa como una sola rama con las copias fusionadas en el orden de esos cables:
- las variables de todos ellos — en un nombre que definan dos ramas, gana el cable listado después en el experimento;
- la respuesta HTTP del último cable, en ese orden, que traiga una;
- para las esperas posteriores, la acción más temprana de sus últimas acciones.
El orden de los cables decide, nunca qué rama terminó primero por casualidad.
Une solo lo que se ejecuta en paralelo
Una unión cuenta sus cables, sin importar cómo llegaron a ejecutarse en paralelo: desde un Rama paralela, desde varios cables de una salida, desde caminos separados. Detrás de Sí y No de una bifurcación solo se ejecuta un camino, así que una unión alimentada por ambos espera una rama que nunca llega: la ejecución falla con run.join_waiting, indicando cuántos cables nunca se siguieron. Para reunir caminos alternativos, conéctalos directamente al siguiente nodo.
Un nodo que no es una unión, alcanzado por dos ramas paralelas, se ejecuta una vez por cada una de ellas.
Cuando falla un paso
El primer fallo hace fallar la ejecución. Las demás ramas no inician ningún paso nuevo: una repetición o una carga termina antes de tiempo, cualquier otro paso en el que estén se ejecuta hasta su final. Un fallo que encuentran mientras tanto se informa en la línea de tiempo, pero no es el error de la ejecución. Un paso que llegó a un tiempo de espera mientras había un cable Tiempo agotado no falló — consulta esperas.
Bifurcación
| Nodo | Sale por Sí cuando |
|---|---|
| Rama por estado | la última respuesta HTTP en este camino tiene el estado dado |
| Rama por valor | se cumple su comparación — consulta comparar valores |
En caso contrario, cada uno sale por No. Una bifurcación por estado necesita una solicitud HTTP antes en todos los caminos; una bifurcación por valor necesita que los nombres que lee se conozcan allí. Cuando los caminos posteriores a Sí y No se reencuentran, el nodo donde se reencuentran se ejecuta una vez, y allí solo se conocen las variables definidas en ambos caminos (dónde se conoce una variable).
Reintentar
Un paso que envía o escucha puede volver a intentarlo cuando falla: activa reintentar si falla en sus propiedades.
| Ajuste | Qué | Rango | Valor inicial |
|---|---|---|---|
| Intentos | intentos en total, el primero incluido | 1–10 | 3 |
| Pausa, ms | la pausa antes del segundo intento | 0–60 000 ms | 500 |
| Pausas | iguales: todas las pausas iguales; que se duplican: cada pausa el doble que la anterior | — | iguales |
- Reintentar se aplica a Solicitud HTTP, Mensaje TCP, Publicación MQTT, Mensaje OSC, Datagrama UDP, Conectar WebSocket, Enviar por WebSocket y todas las esperas. Los demás nodos lo rechazan (
node.retry_unsupported), y una solicitud HTTP bajo carga no admite Reintentar. - Ninguna pausa dura más de 60 s, diga lo que diga la duplicación.
- Cada intento fallido aparece en la línea de tiempo como Reintentando, con su número y su motivo. El paso pasa entonces, o falla con el motivo del último intento.
- Solo se repite la ejecución. Un campo cuya plantilla no se resuelve falla de inmediato.
- Un envío que espera su respuesta vuelve a enviar. Una espera vuelve a esperar, contando desde la última acción de la rama como antes.
- Una espera con un cable Tiempo agotado no falla por un tiempo de espera, así que no se reintenta: sigue Tiempo agotado.
- Detener termina una pausa de inmediato.
Repetir
Un paso que envía puede enviar una y otra vez — un latido, un sondeo, un flujo constante — sin un bucle en el grafo: activa repetir el envío.
| Ajuste | Qué | Rango | Valor inicial |
|---|---|---|---|
| Repetir | un número de veces o durante un tiempo | — | un número de veces |
| Veces | envíos en total, el primero incluido | 2–10 000 | 10 |
| Durante, ms | cuánto tiempo seguir enviando, desde el primer envío | 1–300 000 ms | 10 000 |
| Cada, ms | la pausa entre dos envíos | 10–60 000 ms | 1000 |
| Jitter, ms | cada pausa es hasta esto más larga, al azar | 0–60 000 ms | 0 |
- Repetir se aplica a Solicitud HTTP, Mensaje TCP, Publicación MQTT, Mensaje OSC, Datagrama UDP y Enviar por WebSocket (
node.repeat_unsupporteden los demás). Una solicitud HTTP tiene Repetir o carga, no ambas. - Cada envío se hace como se haría uno solo: sus plantillas se leen de nuevo —
{{counter}}es el número del envío,{{now}}su hora — y Reintentar, cuando está activado, se aplica a cada envío. Un envío que espera una respuesta espera la suya. - Durante un tiempo, un envío solo se hace si puede empezar antes de que se acabe el tiempo.
- El jitter se sortea a partir de la semilla de la ejecución: la misma semilla da las mismas pausas.
- La línea de tiempo informa del progreso como Repitiendo como máximo una vez por segundo. El paso pasa tras el último envío, con el desenlace de ese envío; un envío que falla definitivamente hace fallar el paso.
- Un fallo en otra rama termina los envíos; Detener termina una pausa de inmediato.
Debe caber en una ejecución: los envíos y sus pausas más largas ((count − 1) × (interval + jitter)) como máximo 300 s (node.repeat_too_long), y una repetición por tiempo como máximo 10 000 envíos (node.repeat_too_many).
Bucle
Bucle ejecuta los pasos de su salida Cuerpo una y otra vez; el último de ellos está conectado de vuelta al Bucle.
| Ajuste | Qué | Rango |
|---|---|---|
| Iteraciones como máximo | el máximo de iteraciones | 1–1000 |
| detener antes si | una condición de salida: Valor, Condición, Esperado, como en Comprobar valor | opcional |
- Alcanzado desde fuera, el Bucle inicia la iteración 1 en Cuerpo.
- Cada vez que el cuerpo vuelve, se lee la condición de salida — después de la iteración, así que el cuerpo siempre se ejecuta al menos una vez y puede definir lo que comprueba.
- Cuando se cumple la condición, el Bucle sale por Hecho.
- Si no, empieza la siguiente iteración, mientras quede alguna.
- Cuando las iteraciones se acaban primero, el Bucle sale por Límite si está conectado, y hace fallar la ejecución con
loop.limitsi no lo está. Sin condición, el cuerpo se ejecuta en cada iteración y el Bucle sale por Hecho.
Dentro del cuerpo, {{counter}} es el número de la iteración, ya que cada nodo cuenta sus propias ejecuciones. La condición y los pasos posteriores a Hecho o Límite pueden usar lo que define cada iteración del cuerpo — por ejemplo, un estado que el cuerpo extrae; el cuerpo en sí solo ve lo que se conocía cuando se alcanzó el Bucle.
La plantilla Consultar hasta que esté listo pregunta a un dispositivo por su estado cada 0,3 s hasta que responde ready, como máximo 10 veces.
Qué puede contener un cuerpo
Un cuerpo se ejecuta como una sola rama, una iteración tras otra. El cable de vuelta al Bucle es el único ciclo que puede tener un experimento; cualquier otro es graph.cycle.
| Regla | Error |
|---|---|
| Algo en Cuerpo vuelve al Bucle | loop.no_return |
| Cada salida del cuerpo tiene un solo cable | loop.body_parallel |
| Cada salida del cuerpo sigue en el cuerpo o vuelve al Bucle | loop.body_leaves |
| Solo la salida Cuerpo del Bucle entra en el cuerpo | loop.body_entered |
| Ningún Inicio, Fin, Rama paralela, Unir ramas ni otro Bucle en el cuerpo | loop.body_unsupported |
Esperas
Una espera pasa cuando llega un mensaje que está esperando: Esperar OSC, Esperar UDP, Esperar MQTT, Esperar solicitud HTTP y Esperar WebSocket. Lo que coincide con cada una está en la referencia de los nodos; aquí se explica cómo escuchan.
Escuchar desde el principio
La ejecución abre aquello en lo que escuchan sus esperas antes de su primer paso, así que una respuesta más rápida que el paso siguiente no se pierde:
| Espera | Se abre antes del primer paso |
|---|---|
| OSC, UDP | un socket UDP por dirección Escuchar en (IP:puerto), compartido por todas las esperas que haya en ella |
| Solicitud HTTP | un escucha por dirección — el emulador HTTP de la ejecución cuando hay uno; si no, uno que responde 204 |
| MQTT | una conexión por bróker y filtro de temas, suscrita; los mensajes retenidos que el bróker reproduce entonces se ignoran |
| WebSocket | nada: lee la conexión que abrió un Conectar WebSocket cuando se ejecutó |
Como se abren primero, estas direcciones quedan fijadas antes de la ejecución: una espera OSC, UDP o HTTP escucha en un IP:port literal con un puerto distinto de 0, y el bróker y el tema de una espera MQTT solo aceptan parámetros. Un puerto que no se puede abrir — ocupado, o que no es una dirección de este equipo — detiene la ejecución antes de cualquier tráfico, en el campo de esa espera. Todo se cierra cuando termina la ejecución, sea como sea.
Qué mensajes cuentan
Una espera considera los mensajes que llegaron después de que empezara la última acción en su rama — la última solicitud, mensaje, publicación, conexión WebSocket o envío — o, antes de cualquier acción, después de que empezara la ejecución. Un mensaje anterior a la solicitud no cuenta, y una Pausa o una Marca de registro entre la solicitud y la espera no oculta su respuesta. Tras una unión, cuenta la acción más temprana de las últimas acciones de las ramas fusionadas.
Una espera toma el primer mensaje que coincide y lo consume: dos esperas nunca coinciden con el mismo mensaje.
Cada socket, suscripción o conexión guarda como máximo 1024 mensajes y 64 MiB para sus esperas; pasado eso, los más antiguos se descartan y se cuentan.
Tiempo de espera
Tiempo de espera, ms es 1–120 000 ms, 2000 al principio. Cuando nada coincide a tiempo:
- con un cable Tiempo agotado, la espera lo sigue;
- sin él, el paso falla con
wait.timeout, que dice cuántos otros mensajes llegaron mientras tanto — un patrón equivocado se ve distinto de un dispositivo en silencio — y, en su detalle, cuántos mensajes más antiguos se descartaron cuando la cola estaba llena.
La variable de la espera — reply, o request para HTTP — solo existe tras Coincide. Cuando el Inspector está capturando, el paso también enlaza la trama con la que coincidió: consulta la línea de tiempo.
Una respuesta en el mismo paso
Un Mensaje OSC o un Datagrama UDP pueden esperar su propia respuesta: activa esperar una respuesta.
| Ajuste | Qué | Valor inicial |
|---|---|---|
| Respuesta en (IP:puerto) | la dirección en la que se espera la respuesta; el puerto 0 es cualquier puerto libre | 0.0.0.0:0 |
| Patrón de dirección de respuesta (OSC), Carga útil de respuesta (UDP) | qué debe ser la respuesta, como en la espera correspondiente | cualquiera |
| Tiempo de espera, ms | 1–120 000 ms | 2000 |
| Variable de respuesta | la variable en la que se escribe la respuesta | reply |
El socket de Respuesta en (IP:puerto) se abre antes del primer paso, como el de una espera, y el mensaje sale desde él: se oye a un dispositivo que responde al propio puerto del emisor, y a uno que responde a un puerto fijo cuando ese puerto es el indicado. El paso pasa con una respuesta que coincide, y la variable existe tras su salida. No hay salida Tiempo agotado: ninguna respuesta a tiempo hace fallar el paso, y Reintentar puede volver a enviar. Para bifurcar por silencio, usa una espera aparte.
Qué se comprueba antes de una ejecución
El editor comprueba el experimento mientras lo editas; el botón Ejecutar lo comprueba una vez más. Un problema nombra el nodo, y el campo cuando lo hay.
| Regla | Error |
|---|---|
| El experimento tiene un nombre | doc.name_required |
| 1–64 nodos, exactamente un Inicio y un Fin | doc.node_count, doc.start_end_count |
| Inicio no tiene entrada | graph.start_input |
| Un cable lleva a otro nodo que existe | doc.connection_invalid |
| El mismo cable no está dos veces | doc.connection_duplicate |
| Cada salida que debe conectarse lo está | graph.outputs_required |
| Un nodo no tiene cable en una salida que no tiene | graph.port_unexpected |
| Se llega a cada nodo desde Inicio | graph.unreachable |
| Ningún ciclo salvo el cable de vuelta de un Bucle | graph.cycle, y las reglas del cuerpo |
| Una comprobación o Extraer tiene una solicitud HTTP antes en todos los caminos; una solicitud bajo carga no cuenta | graph.needs_http |
| Cada plantilla se analiza, y cada nombre que usa se conoce en todos los caminos | template.*, name.* — consulta datos |
| Cada campo está presente y en rango | node.* |
| Un nodo que nombra a otro — Cambiar degradación, Emulador caído/activo, los nodos WebSocket — nombra uno que está ahí, y un nodo WebSocket va después de su conexión | impair.relay_unknown, emulator.node_unknown, ws.connection_unknown, ws.connection_after |
| Dos de los sockets de la ejecución no comparten un puerto | consulta fallos |
La ejecución comprueba después lo que necesita para empezar: cada secreto guardado, cada puerto abierto. Hasta que todo se cumple, no se ejecuta ningún paso y no se envía nada.
Límite de tiempo
Una ejecución dura como máximo 300 s. Una que siga en marcha entonces se detiene y falla con run.timeout. Desde la línea de comandos y la API el límite puede ser más corto, 1–300 s.
Detener
Mientras una ejecución avanza, el botón Ejecutar es Detener. Detener termina la ejecución de inmediato: cada rama, cada pausa de Reintentar o Repetir, cada espera y cada carga — las solicitudes en curso se descartan. Sus sockets, suscripciones, emuladores y relés se cierran, y sus conexiones WebSocket envían una trama de cierre. Detener todo en la cabecera hace lo mismo con todas las tareas. Una ejecución detenida no guarda ningún informe; consulta ejecuciones.