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/emitprogramado 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.
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 formaFUNCTION(args), operadores aritméticos, operadores de comparación y operadores lógicos.
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:-unario- Multiplicación, división:
*,/ - Suma, resta:
+,- - Comparación:
<,<=,>,>=,=,==,!= NOTANDOR
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:- Variables de estado integradas como
SIM_TIME,SIM_DURATION - Funciones de consulta de componentes como
BUFFER_LEVEL(...),RESOURCE_AVAILABLE(...) - Constantes por nombre
- Variables de estado por nombre
SELF: cuando la expresión está en un event hook o event listener (consulta Disponibilidad de SELF más abajo)- Los atributos de entidad no están disponibles
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
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 assembleddel Combiner: se dispara una vez por cada lote ensamblado, con todas las entradas en alcance - Hook
batch createddel 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 changeddel Buffer: se dispara con el contenido actual del buffer en alcance - Hook
station contents changedde la Station: se dispara con el contenido actual de la estación en alcance
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ónSIM_DURATION: duración configurada de la simulaciónENTITY_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: eliddel 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)
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 changeoverFROM_ENTITY_ATTRIBUTE/TO_ENTITY_ATTRIBUTE: acceso a atributos en las entidades saliente y entrante
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 alid 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 nombradoBUFFER_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 nombradoRESOURCE_CAPACITY(resource_id): capacidad configurada del recurso nombradoSTATION_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
Valor por defecto
Todas las funciones de consulta de componentes aceptan un argumento de palabra clave opcionaldefault:= que sobrescribe el valor de respaldo:
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: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:
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 escribirNORMAL(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:
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 absolutoPOW(base, exp):baseelevado aexpMOD(a, b): resto deadividido entrebFLOOR(x): redondea hacia abajo (hacia −∞)CEIL(x): redondea hacia arriba (hacia +∞)ROUND(x, ndigits:=0): redondea andigitsdecimales, 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 agregacionesMIN/MAXCLAMP(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úmeroCONTAINS(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/sufijoUPPER(text)/LOWER(text): conversión de mayúsculas/minúsculasTO_TEXT(number): convierte un número a texto (los números enteros pierden el.0final)CONCAT(text, text, ...): une dos o más cadenas, en orden. Los números no se convierten automáticamente, envuélvelos conTO_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 opcionaldefault:=:
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úmeroMEAN(expr): promedio → númeroCOUNT(expr): cuenta de entidades en alcance → número (el idioma canónico para contar esCOUNT(1), oCOUNT(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 textoMODE(expr): el valor más común — polimórfico; los empates se resuelven a favor del primer valor vistoN_UNIQUE(expr): cuenta de valores distintos → númeroANY(expr)/ALL(expr): solo entrada booleana → booleano
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 verdaderotype:=<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".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:10:
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 clavedefault:=si necesitas un centinela diferente. - LOOKUP retorna el cero específico de tipo.
0,""oFALSEsegún el tipo de valor de la tabla. Envuelve condefault:=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
priorityes un atributo real, la expresión se está ejecutando en un contexto que no tiene una entidad en alcance. Consulta Contextos de expresión. SELFvs. nombres simples de atributo.SELFse 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_TYPEretorna el slug, no el nombre visible. Compara contra"widget", no"Widget".NOTse une de forma más laxa que las comparaciones.NOT qc_passed AND is_rushes(NOT qc_passed) AND is_rush, peroNOT priority > 5esNOT (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.