Skip to main content

Visión general

El lenguaje de Expresiones (Expressions) (o DSL) es la forma de hacer dinámicos los modelos de ProDex. En cualquier lugar donde esperarías ingresar un número fijo (condiciones de enrutamiento, prioridades de cola, valores de atributos de entidad, disparadores de eventos y los parámetros de una distribución de tiempos de procesamiento y llegadas), puedes ingresar una expresión en su lugar. Las expresiones referencian el estado de simulación, atributos de entidad, constantes, tablas de búsqueda y variables de estado, de modo que tu modelo responde a los datos que fluyen a través de él en lugar de comportarse idénticamente para cada entidad. Un usuario que solo ingresa números fijos está usando una pequeña fracción de lo que el Modelador (Modeler) puede hacer. Aprender el lenguaje de expresiones es lo que convierte un diagrama de flujo estático en un modelo vivo de tus operaciones.

Dónde aparecen las expresiones

Muchos campos numéricos y lógicos en la configuración de un componente aceptan tanto un valor literal como una expresión. En el Modelador, esos son:
  • Lógica de llegada (Arrival Logic) en una Source (un tiempo entre llegadas, no una tasa): puede depender del tiempo de simulación, un calendario o el estado
  • Condiciones de enrutamiento (Routing conditions) en un Router: ramifica según atributos de entidad, disponibilidad de recursos o estado de cola
  • Pesos de enrutamiento (Routing weights) en un Router: el peso relativo de cada rama al enrutar de forma probabilística, calculado por entidad
  • Prioridad de cola (Queue priority) en un Buffer: ordena las entidades en espera por una puntuación calculada
  • Prioridad de asignación de recursos (Resource allocation priority): elige entre demandas en competencia cuando la capacidad está restringida
  • Condición de Event Hook y argumentos de Action: consulta Event Hooks
  • Condición de Event Listener y argumentos de Action: consulta Event Listeners
  • Condición de acción programada: controla si un assign / emit programado se dispara (consulta Calendarios)
  • Asignaciones de atributo de entidad en una salida de Source, Transformer, Combiner o Separator: calcula valores iniciales o actualizados de atributos
  • Expresión de lote del Combiner (Batch Expression): el disparador booleano que cierra un lote de tamaño dinámico
  • Recuento de salidas del Separator: expresión DSL que retorna el número de salidas por entrada
  • Parámetros de distribución (Distribution): cada parámetro de una distribución tiene un interruptor </> que lo cambia a modo de expresión. Consulta Distribuciones vs Expresiones más abajo.
Las cantidades de requisitos de recursos en un Process y los tiempos de changeover en un Resource son valores fijos en el Modelador. Para que cualquiera de los dos dependa de la entidad (por ejemplo, un changeover cuya duración depende de entre qué está cambiando el recurso), pídeselo a Dexter.

Sintaxis básica

Las expresiones siguen una sintaxis tipo Excel: literales numéricos, literales de cadena entre comillas simples O dobles, llamadas a función en forma FUNCTION(args), operadores aritméticos, operadores de comparación y operadores lógicos.
Los literales booleanos no distinguen entre mayúsculas y minúsculas: TRUE, True, true, FALSE, False, false se analizan todos correctamente. Los literales numéricos cubren enteros, decimales, negativos y notación científica (1e-10).

Precedencia de operadores

Precedencia de operadores, de mayor a menor:
  1. - unario
  2. Multiplicación, división: *, /
  3. Suma, resta: +, -
  4. Comparación: <, <=, >, >=, =, ==, !=
  5. NOT
  6. AND
  7. OR
AND, OR y NOT son operadores infijo y prefijo, no funciones: escribe a AND b, a OR b, NOT a, nunca AND(a, b). Tanto = como == se aceptan como operadores de igualdad. Usa paréntesis para agrupar explícitamente cuando quieras anular la precedencia.
NOT se une de forma más laxa que las comparaciones, no más fuerte: NOT a = b se interpreta como NOT (a = b), nunca como (NOT a) = b. Esto suele ser lo que quieres, pero sorprende a quienes vienen de lenguajes donde ! se une de forma fuerte. Usa paréntesis cuando tengas dudas.

Contextos de expresión

El contexto en el que se ejecuta una expresión determina qué identificadores están disponibles para referenciar. Equivocarse de contexto produce errores de validación como “Unknown identifier ‘priority’” que resultan confusos sin entender el modelo. Hay tres contextos.

Contexto sin entidad

Se ejecuta sin ninguna entidad específica en alcance. Identificadores disponibles: Se usa en: lógica de llegada de Source (antes de que exista ninguna entidad), condiciones y acciones de Event Listener (los listeners siempre se ejecutan sin entidad), eventos de Resource, acciones programadas y verificaciones de estado global.

Contexto de entidad única

Se ejecuta con una entidad específica en alcance. Todo lo del contexto sin entidad, más:
  • Atributos de entidad por nombre simple: priority, product_class, weight
  • ENTITY_TYPE, ENTITY_AGE
Se usa en: tiempo de procesamiento de Process, condiciones de Router, asignaciones de atributos de Transformer, Event Hooks por entidad (p. ej., entity created, process started) y lugares similares donde una entidad está claramente en alcance.

Contexto multi-entidad

Se ejecuta con varias entidades en alcance. Las siete superficies multi-entidad son:
  • Asignación de atributo del Combiner: atributos de la entidad de salida derivados de las entradas
  • Expresión de lote del Combiner: el disparador booleano que cierra un lote dinámico
  • Hook batch assembled del Combiner: se dispara una vez por cada lote ensamblado, con todas las entradas en alcance
  • Hook batch created del Separator: se dispara una vez por cada división, con todas las salidas en alcance
  • Cantidad a liberar en una acción de liberación de un Buffer: cuántas entidades retenidas liberar, calculado sobre el contenido del buffer
  • Hook buffer contents changed del Buffer: se dispara con el contenido actual del buffer en alcance
  • Hook station contents changed de la Station: se dispara con el contenido actual de la estación en alcance
En cualquier contexto multi-entidad, las referencias de atributo simples como priority son ambiguas (¿de qué entidad?). Debes envolverlas en una función de agregación: MAX(priority) > 5, ANY(qc_passed).
ENTITY_TYPE y ENTITY_AGE siguen la misma regla en contexto multi-entidad: solo están disponibles dentro del cuerpo de una agregación, MODE(ENTITY_TYPE), SUM(ENTITY_AGE), COUNT(1, filter:=ENTITY_TYPE == "tube"), nunca en el nivel superior de la expresión.

Qué campo se ejecuta en qué contexto

Variables de estado integradas

  • SIM_TIME: tiempo actual de simulación
  • SIM_DURATION: duración configurada de la simulación
  • ENTITY_TYPE: slug de tipo de entidad de la entidad actual (contexto de entidad única, o dentro de cuerpos de agregación en contexto multi-entidad). Retorna el slug (p. ej., widget), no el nombre visible (p. ej., Widget).
  • ENTITY_AGE: cuánto tiempo ha existido la entidad actual, es decir, SIM_TIME − creation_time (contexto de entidad única, o dentro de cuerpos de agregación)
  • SELF: el id del componente actual (consulta Disponibilidad de SELF)

Disponibilidad de SELF

SELF solo es válido dentro de expresiones de event hook y event listener. No está disponible en:
  • Acciones programadas
  • Campos de configuración de componente fuera de hooks/listeners (processing_time, arrival_logic, reglas de enrutamiento, prioridad de cola, prioridad de asignación de recursos, asignaciones de atributos)
Si necesitas referenciar un componente específico desde fuera de un hook, usa el nombre de ese componente explícitamente dentro de una función de consulta de componente.

Identificadores exclusivos de Changeover

La expresión de tiempo de changeover de un Resource (configurada a través de Dexter) recibe cuatro identificadores que no existen en ningún otro lugar del DSL:
  • FROM_ENTITY_TYPE / TO_ENTITY_TYPE: los slugs de tipo de entidad en cada lado del changeover
  • FROM_ENTITY_ATTRIBUTE / TO_ENTITY_ATTRIBUTE: acceso a atributos en las entidades saliente y entrante
Úsalos para hacer que la duración del changeover dependa de entre qué está cambiando el recurso, por ejemplo, una cabina de pintura cuya limpieza tarda más cuando cambia la familia de color.

Funciones de consulta de componentes

Estas funciones leen el estado vivo de la simulación para un componente específico. El primer argumento es una expresión de texto que se evalúa al id del componente (el id del esquema, no el nombre visible): un literal de cadena es el caso común, pero un atributo de entidad que contenga un id, SELF, o un CONCAT(...) también funcionan.
  • BUFFER_LEVEL(buffer_id): número actual de entidades en el buffer nombrado
  • BUFFER_CAPACITY(buffer_id): capacidad configurada del buffer nombrado (retorna infinito para un buffer sin límite)
  • RESOURCE_AVAILABLE(resource_id): unidades actualmente disponibles del recurso nombrado
  • RESOURCE_CAPACITY(resource_id): capacidad configurada del recurso nombrado
  • STATION_WIP(station_id [, entity_type]): trabajo en curso en la estación nombrada; el segundo argumento opcional filtra a un tipo de entidad específico
Las funciones de consulta de componentes toman el id del componente, no el nombre visible mostrado en el Modelador. Si el nombre visible de tu buffer es “WIP Buffer” y su id es wip-buffer, debes escribir BUFFER_LEVEL("wip-buffer"). Pasar el nombre visible retorna silenciosamente 0 y tu modelo se comporta como si el buffer estuviera siempre vacío.

Valor por defecto

Todas las funciones de consulta de componentes aceptan un argumento de palabra clave opcional default:= que sobrescribe el valor de respaldo:
El valor de respaldo se activa cada vez que el id no resuelve a un componente del tipo correcto: un error de tipeo, un nombre visible, o un id válido que pertenece a un tipo de componente distinto (BUFFER_LEVEL apuntando a un recurso). Para STATION_WIP, también se activa cuando el filtro de tipo de entidad nombra un tipo sin entrada de capacidad en esa estación. Sin default:=, el valor de respaldo es el numérico 0. A diferencia de LOOKUP, las consultas de componentes no retornan el cero específico de tipo ("" o FALSE), solo números.

Variables de estado personalizadas

Las variables de estado definidas en el modelo se pueden leer por su nombre simple en SCREAMING_SNAKE_CASE en cualquier contexto del DSL, sin entidad, entidad única o multi-entidad:
Las variables de estado se leen directamente en las expresiones y se escriben mediante la acción assign en event hooks, event listeners o acciones programadas. Persisten a través de las entidades y de toda la ejecución. Cada variable de estado tiene un nombre, un tipo (number, text o boolean) y un valor inicial que coincide con ese tipo. Los nombres están en SCREAMING_SNAKE_CASE (letras mayúsculas, dígitos y guiones bajos, comenzando con una letra) y no deben chocar con identificadores reservados del DSL, constantes, tablas de búsqueda o slugs de tipo de entidad (un tipo de entidad widget bloquea una variable de estado llamada WIDGET). Para la distinción entre variables de estado (a nivel de modelo, mutables) y atributos de entidad (por entidad, se mueven con la entidad), consulta Atributos de entidad más abajo.

Atributos de entidad

Las entidades llevan atributos tipados a través del flujo: boolean, number, text, text_list, number_list. A pesar de sus nombres, text_list y number_list son enums de un solo valor (la entidad tiene un valor de un conjunto declarado), no colecciones, y rechazan la estrategia de asignación DSL Expression en validación. Referencia los atributos de la entidad actual por su nombre simple en cualquier expresión que se ejecute en un contexto de entidad única:
Sin prefijo self.: la entidad en alcance es implícita. Las expresiones referencian los atributos directamente por el nombre definido en el tipo de entidad. En contextos multi-entidad, los nombres simples de atributo se refieren al conjunto de entidades y deben envolverse en una función de agregación como SUM(weight) o MAX(priority). Consulta Funciones de agregación. Los atributos de entidad se establecen en una Source, pueden ser modificados por un Transformer, y pueden leerse desde cualquier expresión aguas abajo. Persisten con la entidad a través de todo el flujo. Consulta Entidades para el sistema completo de tipos de atributo y estrategias de asignación.

Distribuciones vs Expresiones

Una confusión común: las distribuciones no son funciones del DSL. No puedes escribir NORMAL(60, 10) como una expresión. Las distribuciones se eligen desde un menú desplegable en los campos que las admiten: Normal, Uniform, Triangular, Beta y el resto de las diez opciones. Seleccionar una revela los campos de parámetros debajo del selector. Cada campo de parámetro tiene un interruptor </> que lo cambia a modo de expresión: ahí es donde entra el DSL. La forma de la distribución permanece estática; los valores de los parámetros se vuelven dinámicos. El puente en la dirección contraria es la distribución Fixed: cuando un campo espera una distribución pero quieres un valor completamente calculado, elige Fixed y coloca la expresión DSL en su parámetro Value. Esa es la forma de hacer que, por ejemplo, un tiempo de proceso sea exactamente igual a LOOKUP(CYCLE_TIMES, ENTITY_TYPE) sin ninguna aleatoriedad encima. Por ejemplo, en lugar de una media codificada en 60, puedes cambiar el campo Mean (Media) a modo de expresión y escribir:
O hacer que la media dependa de una tabla de búsqueda por tipo de entidad:
El campo Standard Deviation (Desviación Estándar) puede permanecer como un literal 10, o también ser una expresión. Consulta Distribuciones para la lista completa de tipos de distribución admitidos.

Catálogo de funciones

El conjunto completo de funciones integradas, agrupado como lo buscarías.

Flujo de control

  • IF(condition, then, else): ramifica entre dos valores. Ambas ramas deben retornar el mismo tipo.
  • AND, OR, NOT: combinaciones lógicas (operadores infijo/prefijo, no funciones)
  • Operadores de comparación: ==, !=, <, <=, >, >=, =

Matemáticas

  • ABS(x): valor absoluto
  • POW(base, exp): base elevado a exp
  • MOD(a, b): resto de a dividido entre b
  • FLOOR(x): redondea hacia abajo (hacia −∞)
  • CEIL(x): redondea hacia arriba (hacia +∞)
  • ROUND(x, ndigits:=0): redondea a ndigits decimales, al par más cercano (redondeo bancario)
  • MIN_OF(a, b) / MAX_OF(a, b): el menor / mayor de dos números — funciones escalares, distintas de las agregaciones MIN / MAX
  • CLAMP(value, lower, upper): restringe un valor a un rango (el orden de los límites no importa)
  • Aritmética estándar: +, -, *, /

Cadenas

  • LEN(text): longitud de una cadena, como número
  • CONTAINS(text, substring): si una cadena contiene una subcadena (distingue mayúsculas/minúsculas)
  • STARTS_WITH(text, prefix) / ENDS_WITH(text, suffix): pruebas booleanas de prefijo/sufijo
  • UPPER(text) / LOWER(text): conversión de mayúsculas/minúsculas
  • TO_TEXT(number): convierte un número a texto (los números enteros pierden el .0 final)
  • CONCAT(text, text, ...): une dos o más cadenas, en orden. Los números no se convierten automáticamente, envuélvelos con TO_TEXT: CONCAT("sku-", TO_TEXT(part_id))

Búsquedas

  • LOOKUP(table_name, key1, key2, ...): busca un valor en una Tabla de búsqueda. Suministra una clave por dimensión. Acepta un argumento de palabra clave opcional default:=:
Sin default:=, las filas faltantes retornan el cero específico de tipo (0 para tablas numéricas, "" para texto, FALSE para booleano). Consulta Constantes y tablas de búsqueda para el esquema completo.

Funciones de agregación

Válidas solo en contextos multi-entidad (las siete superficies listadas en Contextos de expresión). Las agregaciones operan sobre el conjunto de entidades en alcance:
  • SUM(expr): suma → número
  • MEAN(expr): promedio → número
  • COUNT(expr): cuenta de entidades en alcance → número (el idioma canónico para contar es COUNT(1), o COUNT(1, filter:=...))
  • MAX(expr) / MIN(expr): extremos — polimórficos: la entrada numérica da un número, la entrada de texto compara lexicográficamente y da texto
  • MODE(expr): el valor más común — polimórfico; los empates se resuelven a favor del primer valor visto
  • N_UNIQUE(expr): cuenta de valores distintos → número
  • ANY(expr) / ALL(expr): solo entrada booleana → booleano
En un conjunto de entidades vacío, MEAN retorna 0.0; MAX, MIN y MODE retornan 0.0 sobre números y "" sobre texto; SUM, COUNT y N_UNIQUE retornan 0; ANY retorna FALSE y ALL retorna TRUE (verdad vacua). Cada agregación acepta dos argumentos de palabra clave opcionales:
  • filter:=<expr booleana>: solo agrega entidades donde el filtro evalúa verdadero
  • type:=<slug_de_tipo_de_entidad>: solo agrega entidades del tipo nombrado (slug sin comillas)
type:= toma un slug sin comillas, y los slugs con guión no se admiten en esta posición. Un tipo con un guión en su slug no puede filtrarse con type:=, así que en su lugar usa filter:=ENTITY_TYPE == "my-type".
Ejemplo: un Combiner que produce un ensamblaje, estableciendo los atributos de salida a partir de sus entradas:

Seguridad de tipos

El DSL es estáticamente tipado. Cada expresión se verifica en tiempo de validación del modelo, no en tiempo de ejecución: un error de expresión aparece como un mensaje de validación con la ruta de un campo, nunca como un stack trace a mitad de la ejecución. Un campo de condición espera un booleano; una asignación de atributo para un atributo Number debe producir un número; una expresión de cantidad debe producir un número (el DSL no tiene un tipo entero separado). Mezclar tipos falla la validación con un error específico que apunta a la expresión ofensiva: consulta Validación. Lo que el verificador impone, por operador: No hay coerción de tipo implícita. 5 + "x" no se convierte silenciosamente en "5x", falla la validación. Convierte explícitamente cuando lo necesites: CONCAT("sku-", TO_TEXT(part_id)).
La verificación de tipos ocurre en la validación, pero algunos casos límite aritméticos se manejan en tiempo de ejecución en lugar de ser rechazados: una división entre cero o una expresión que resulta en NaN se resuelve a 0 y registra un aviso a nivel de ejecución en lugar de abortar la corrida. Tu modelo sigue adelante; revisa los avisos si un valor calculado se ve mal.

Patrones comunes

Enrutamiento condicional: cada regla en un Router condicional es una expresión booleana emparejada con una conexión saliente. La primera regla que sea verdadera elige la ruta; las entidades que no coinciden con ninguna regla toman la ruta predeterminada:
Parámetro de distribución impulsado por atributos: cambia el campo Mean (Media) de una distribución Normal a modo de expresión para que se adapte a cada entidad, mientras Standard Deviation (Desviación Estándar) permanece en un valor fijo de 10:
Contrapresión: frena las llegadas cuando aguas abajo está congestionado. La lógica de llegada es el tiempo entre llegadas, así que retorna un intervalo más largo cuando el buffer está lleno:
Puntuación de prioridad: ordena las entidades en espera por importancia calculada:
Salida de ensamblaje: combina varias entradas en una (Combiner, contexto multi-entidad):

Consejos y trampas

  • Las expresiones se evalúan en cada entidad, cada vez. Mantenlas simples cuando sea posible: las expresiones complejas en rutas de alto rendimiento se acumulan.
  • Las consultas de componentes retornan cero numérico. Un nombre de componente faltante produce 0, no nulo. Usa el argumento de palabra clave default:= si necesitas un centinela diferente.
  • LOOKUP retorna el cero específico de tipo. 0, "" o FALSE según el tipo de valor de la tabla. Envuelve con default:= para un respaldo diferente.
  • Los atributos de entidad no son variables de estado. Los atributos pertenecen a una entidad y se mueven con ella. Las Variables de estado pertenecen al modelo y persisten a través de las entidades.
  • El contexto importa. Si te encuentras con un error de validación como “Unknown identifier ‘priority’” y priority es un atributo real, la expresión se está ejecutando en un contexto que no tiene una entidad en alcance. Consulta Contextos de expresión.
  • SELF vs. nombres simples de atributo. SELF se refiere al componente al que está adjunta la expresión (solo hooks y listeners). Los nombres simples se refieren a atributos de entidad. No se superponen.
  • ENTITY_TYPE retorna el slug, no el nombre visible. Compara contra "widget", no "Widget".
  • NOT se une de forma más laxa que las comparaciones. NOT qc_passed AND is_rush es (NOT qc_passed) AND is_rush, pero NOT priority > 5 es NOT (priority > 5). Usa paréntesis en cualquier caso que tengas que pensar dos veces.
  • MIN/MAX escalar vs. agregado. MIN_OF(a, b) compara dos números en cualquier lugar; MIN(expr) agrega sobre entidades y solo funciona en contextos multi-entidad.
  • Prefiere Constantes y tablas de búsqueda sobre valores codificados. Cambia una constante en un solo lugar; no puedes fácilmente buscar-y-reemplazar a través de decenas de expresiones.