Skip to main content

Visión general

Una Programación es lo que ancla una simulación al tiempo real. Sin una programación, un modelo corre en tiempo abstracto con llegadas sintéticas, útil para trabajo exploratorio, pero no para comparar contra tu piso real. Con una programación, la simulación queda anclada a fechas reales, patrones de turno reales y planes de producción reales. El material entra cuando tu plan dice que entra, las transiciones de turno se disparan en momentos de reloj específicos, y los resultados son directamente comparables con lo que produjo tu piso. Todo uso serio de ProDex involucra al menos una programación. La mayoría de los modelos tienen varias: una para el plan “normal”, algunas para escenarios alternativos que estás comparando. Si te estás orientando primero en el flujo de trabajo de simulación, empieza por Resumen de la simulación: esta página es la referencia profunda de la capa de tiempos. El modelo mental que hace que las programaciones tengan sentido es la división en dos planos: la programación lleva cuándo cambian las cosas (se disparan liberaciones, cambian los turnos), mientras que el modelo lleva qué hace cada componente en cada cambio. Un patrón de tres turnos que afecta a cinco recursos necesita tres emisiones programadas y repetitivas más 15 event listeners definidos una sola vez en el modelo, no cientos de entradas de capacidad individuales.

Elegir una programación

El selector de programación está en el riel del Modelador, junto al selector de modelo. Lista todas las programaciones de la fábrica, más:
  • Predeterminado (sin programación): cuando se selecciona, el modelo corre en tiempo abstracto con las llegadas estocásticas que definan las Fuentes. No hay ningún objeto de programación detrás de esto.
  • Nuevo Programa: crea una programación nueva y vacía y cambia a ella.
  • Editar: abre el Editor de Programación para la programación actualmente seleccionada.
  • Eliminar: elimina la programación actualmente seleccionada, después de confirmar en un diálogo Eliminar Programa.
Editar y Eliminar solo aparecen mientras una programación (no Predeterminado) está seleccionada.

El Editor de Programación

El Editor de Programación es un diálogo titulado Editar {nombre de la programación} donde escribes los tiempos y eventos de una programación. La parte superior del diálogo tiene cuatro campos que definen la programación:
  • Nombre de Programación: corto, descriptivo. Es lo que se muestra en el selector de programación.
  • Descripción: texto libre opcional que explica qué representa esta programación.
  • Hora de Inicio: el momento de reloj en que empieza la simulación. Cada marca de tiempo de evento en esta programación debe caer en o después de la Hora de Inicio.
  • Hora de Finalización: el momento de reloj en que termina la simulación. Si se deja en blanco, la simulación corre durante la Duración configurada del modelo a partir de la Hora de Inicio. Si se define, la Hora de Finalización sobrescribe la Duración.
Las horas de la programación están en UTC. Al motor solo le importan los intervalos: el tiempo de simulación de un evento es su marca de tiempo menos la Hora de Inicio, y la UI, los gráficos, los KPI y las tablas de resultados reportan todos estos tiempos en UTC exactamente como se ingresaron. Ingresa la hora del reloj del piso como si fuera UTC: no la conviertas a UTC real. Una hora sin zona horaria se trata como UTC; un desfase real del mundo como -06:00 no localiza nada, desplaza la hora mostrada respecto al reloj del piso (una liberación a las 09:00-06:00 aparece como 15:00). Esta es la trampa de autoría de programaciones más común, especialmente en datos que le entregas a Dexter.
Debajo de los cuatro campos, las pestañas Línea de Tiempo y Tabla controlan cómo se presentan los eventos. Línea de Tiempo coloca los eventos a lo largo de un eje de reloj, útil para detectar límites de turno y ver el espaciado visualmente. Tabla lista cada evento en una grilla ordenable con columnas Tipo de Acción, Cuándo, Repeticiones, Objetivo y Valor, útil para revisión masiva y para detectar errores de tipeo en marcas de tiempo. El menú Agregar Evento agrega uno de tres tipos de evento:
  • Liberación de Material: entidades que entran al modelo en un momento específico
  • Asignar Variable: define una variable de estado en un momento específico
  • Emitir Evento: publica un tema en un momento específico

Qué contiene una programación

Una programación es una secuencia de liberaciones de material y acciones programadas, cada una marcada con un momento de reloj absoluto específico. La simulación corre desde la Hora de Inicio hasta la Hora de Finalización (o durante la Duración configurada del modelo si no se define la Hora de Finalización), y cada evento se dispara cuando el tiempo de simulación en reloj llega a su marca de tiempo. Cada marca de tiempo de evento es absoluta, no un desfase relativo desde la Hora de Inicio.

Liberaciones de material

Una liberación de material le indica a una Fuente o Búfer que emita una o más entidades en un momento de reloj específico. Cada liberación tiene:
  • Objetivo: la Fuente o Búfer desde la que se libera, y el tipo de Entidad a liberar. Los componentes dentro de un Nodo de modelo se listan después de un divisor; de ellos, solo los Búferes son objetivos válidos, porque una Fuente dentro de un submodelo recibe entidades a través de su Nodo de modelo padre. Las Fuentes y los Búferes solo aceptan liberaciones de su propio tipo de entidad; cualquier otro emparejamiento falla la validación.
  • Valor: la cantidad, un número entero de al menos 1.
  • Cuándo: el momento de reloj en que se dispara la liberación (UTC). Una liberación marcada exactamente en la Hora de Inicio de la programación siembra el WIP inicial: material que ya está en el piso cuando comienza la corrida.
  • Repeticiones (opcional): libera la misma cantidad de nuevo en un intervalo fijo (ver Eventos recurrentes).
  • Atributos: valores para asignar a las entidades recién creadas, mediante Editar Atributos. El Editor de Programación fija un valor por atributo. Dexter también puede usar las demás estrategias de asignación aquí; una Expresión DSL en una liberación corre en contexto sin entidad: puede leer constantes, tablas de búsqueda y variables de estado, pero todavía no hay ninguna entidad en alcance.
Las liberaciones de material son la forma de modelar “la demanda que realmente ocurrió”. En lugar de una Fuente con una tasa de llegada exponencial sintética, le das una secuencia de órdenes reales: el 1 de nov. de 2024 a las 7:00 AM UTC libera 50 unidades del Producto A con priority="rush" y due_priority=5; el 1 de nov. de 2024 a las 9:00 AM UTC libera 30 unidades del Producto B; y así sucesivamente.
La lógica de llegada de una Fuente y las liberaciones de material corren en paralelo. No son mutuamente excluyentes. Una Fuente puede seguir produciendo llegadas estocásticas mientras una programación inyecta simultáneamente lotes específicos en momentos programados, y las acciones release_entity disparadas por eventos pueden dispararse encima de ambas. Si quieres una Fuente que dependa solo de la programación, deja su lógica de llegada vacía.

Acciones programadas

Una acción programada se dispara en un momento de reloj específico (Cuándo) sin estar atada al evento de ciclo de vida de ningún componente. Los dos tipos de acción son: Varias acciones pueden compartir el mismo momento; se disparan juntas. Dexter también puede condicionar una acción programada con una condición: una expresión booleana del DSL que se evalúa cuando la acción vence, de modo que solo se dispara si la condición es verdadera. El Editor de Programación no muestra condiciones; pídele a Dexter que agregue o cambie una. Las acciones ligadas a un componente (Pausar, Reanudar, Establecer Capacidad, Liberar Entidad) no son válidas directamente como acciones programadas. Si necesitas un cambio de capacidad programado, el patrón es:
  1. Un Emitir Evento programado se dispara en el límite del turno: por ejemplo, publica el tema shift_start_night.
  2. Un Event Listener en el recurso se suscribe a ese tema y define la capacidad al valor nocturno.
Esta separación mantiene las programaciones declarativas (“el 1 de nov. de 2024 a las 6:00 PM UTC, anuncia que empezó el turno nocturno”) y deja que los Event Listeners manejen los efectos secundarios a nivel de componente.
Las acciones programadas corren en contexto sin entidad y sin componente. SELF no está disponible (está restringido a event hooks y event listeners), y no hay ninguna entidad en alcance. Las expresiones aquí pueden alcanzar variables de estado, constantes, tablas de búsqueda, el estado de la simulación (SIM_TIME, SIM_DURATION) y las funciones de consulta de componentes con ids de componente explícitos.

Eventos recurrentes

Los límites de turno y las órdenes permanentes se repiten, y no deberías tener que escribir a mano docenas de eventos idénticos. Tanto las acciones programadas como las liberaciones de material pueden repetirse en un intervalo fijo: el evento vence en su momento Cuándo, y luego otra vez en cada intervalo, hasta el momento Hasta (inclusive). Deja Hasta vacío y se repite hasta la hora de finalización de la programación, o hasta la duración del modelo cuando la programación no tiene una. La programación guarda una sola entrada sin importar cuánto dure la corrida. En el Editor de Programación esto es la columna Repeticiones: activa Repetir este evento, define Cada como un número entero y una unidad (Segundos, Minutos, Horas, Días o Semanas), y opcionalmente un momento Hasta.
  • El intervalo es una duración fija, así que un evento diario mantiene su hora UTC todo el año; no sigue los cambios de horario de verano.
  • Un patrón solo de días laborables es un evento por día laborable, cada uno repitiéndose cada 1 semana.
  • Una condición en una acción repetitiva se revisa en cada ocurrencia, y una ocurrencia que cae exactamente en el final de la corrida está planificada pero no se dispara.
Pídele a Dexter un calendario de turnos y lo construye a partir de eventos repetitivos.

Direccionamiento jerárquico

Una liberación puede apuntar a un Búfer dentro de un Nodo de modelo (no a una Fuente; consulta Objetivo más arriba). En los datos de resultados (como la columna element_id de schedule_definition), ese tipo de objetivo se identifica con el separador ::. Ejemplos:
  • source_1: una Fuente de nivel superior
  • model_node_1::buffer_1: un Búfer dentro de un Nodo de modelo
  • outer_node::inner_node::buffer_2: anidado profundamente
Todos los segmentos son ids de componente, no nombres visibles. El separador :: está reservado, así que ningún id de componente puede contenerlo.

El patrón de programación por turnos

La mayoría de las fábricas tienen capacidad basada en turnos que cambia a lo largo del día y la semana. La forma idiomática de modelar esto en ProDex combina programaciones, temas, variables de estado y event listeners:
  1. Una variable de estado de turno: CURRENT_SHIFT, tipo Texto, valor inicial "day".
  2. Acciones Emitir Evento repetitivas en los límites de turno: publica el tema shift_day al inicio del primer turno diurno y shift_night al inicio del primer turno nocturno, cada una repitiéndose cada día.
  3. Un Event Listener en cada recurso afectado suscrito a los temas de turno: cuando se publica shift_night, define la capacidad de la máquina CNC al valor nocturno y actualiza CURRENT_SHIFT a "night".
  4. Las expresiones referencian CURRENT_SHIFT: las tasas de llegada, la puntuación de prioridad y cualquier otra cosa que varíe por turno lee la variable de estado y se ramifica según corresponda.
Esta descomposición mantiene la lógica de turnos en un solo lugar (el Event Listener en el recurso) y los tiempos de turno en la programación. Cambia la programación por un plan de turnos distinto y los cambios de capacidad fluyen automáticamente.

Reutilizar programaciones entre modelos

Las programaciones pertenecen a la fábrica y pueden reutilizarse entre modelos. Cada una es un conjunto completo de liberaciones de material y acciones programadas con su propia Hora de Inicio y Hora de Finalización; eliges cuál usar desde el selector al correr una simulación. Sus referencias a componentes, entidades, variables y temas deben ser válidas para el modelo que ejecutes. Usos típicos:
  • Escenarios base vs. de estrés: una programación “normal” y una programación de “aumento de demanda”, ejecutadas por separado y comparadas.
  • Repetición histórica: la demanda real del mes pasado como una programación, un pronóstico de demanda planificada como otra.
  • Variaciones de turno: una programación estándar de cinco días y una programación alternativa de siete días con cobertura de fin de semana.
Cada ejecución empareja un modelo con la programación contra la que se ejecutó. Comparar ejecuciones que varían la programación te permite preguntar “¿cómo se desempeña esta configuración de modelo bajo distintos perfiles de demanda?” Variar el modelo te permite preguntar “¿cuál configuración es mejor para esta demanda?”

Hora de Inicio y Hora de Finalización

  • La Hora de Inicio es el momento de reloj en que empieza la simulación. Cada marca de tiempo de liberación de material y de acción programada debe caer en o después de la Hora de Inicio.
  • La Hora de Finalización, si se define, es cuándo termina la simulación: sobrescribe la duración configurada del modelo. Útil para correr la misma programación contra una ventana más corta o más larga sin editar los eventos de la programación.
  • Si la Hora de Finalización se deja en blanco, la simulación corre durante la Duración configurada del modelo a partir de la Hora de Inicio.
  • Los eventos marcados después de la Hora de Finalización (o después de Hora de Inicio + Duración si no se define Hora de Finalización) quedan fuera de la ventana de la corrida y nunca se alcanzan.
Por debajo, el motor trabaja en intervalos, no zonas: el tiempo de simulación de un evento es simplemente su fecha y hora menos la Hora de Inicio. Lo que escribes es lo que muestra cada gráfico y KPI, en UTC, así que marca toda la programación con una sola convención (hora del reloj del piso + Z) y los límites de turno se quedan exactamente donde los pusiste.

Formas de crear una programación

Hay dos formas de que una programación llegue a existir, ambas produciendo la misma programación:
  1. El Editor de Programación: el diálogo descrito arriba, mejor para ediciones pequeñas y revisión.
  2. Dexter: el camino más rápido para datos reales. Sube el material crudo (un CSV de órdenes, un calendario de turnos, un extracto de ERP) y pídele a Dexter que construya la programación; perfila los datos y genera las liberaciones y acciones por ti. Así es como conviertes un archivo operativo en una programación sin escribir a mano cada liberación.
Las programaciones son de toda la fábrica: crear o editar una la pone a disposición de todos los modelos de la fábrica. La programación actualmente seleccionada también viaja como contexto cuando chateas con Dexter, así que “agrega un turno de sábado a esta programación” se resuelve sin nombrarla. Antes de cambiar una programación compartida, considera los otros modelos que la usan.

Programaciones en los datos de resultados

Cada corrida contra una programación materializa una tabla schedule_definition en los dataframes de resultados, una fila por entrada atómica:
  • Una acción programada con N acciones internas se expande a N filas, discriminadas por action_index; las liberaciones de material son una fila cada una.
  • entry_type es uno de material_release, scheduled_assign o scheduled_emit.
  • Las columnas están tipadas por entrada: las liberaciones llevan element_id, entity_type_id, quantity y attributes_json (el diccionario de asignación codificado en JSON, poblado solo en liberaciones con sobrescrituras por liberación); las asignaciones programadas llevan variable_name y value_expr; las emisiones programadas llevan topic. condition_expr (la condición DSL sin evaluar, nula cuando no hay condición) aplica solo a acciones, y action_index es nulo en las filas de liberación.
  • Cada entrada se registra por adelantado, así que una acción condicionada aparece en la tabla incluso si su condición nunca evalúa como verdadera en tiempo de ejecución: la fila registra lo que declaró el plan, no lo que se disparó.
Esta es la tabla para unir cuando te preguntas “¿el plan realmente entró al modelo como lo escribí?”

Consejos

  • Todo modelo serio necesita una programación. Las llegadas sintéticas están bien para la primera pasada; las programaciones reales son necesarias para resultados comparables.
  • Usa marcas de tiempo de reloj absolutas de forma consistente. Es tentador pensar en desfases relativos (“libera en +3600s”), pero cada evento necesita una marca de tiempo absoluta. Si estás bosquejando una programación en papel, calcula las marcas de tiempo absolutas a partir de tu Hora de Inicio base antes de ingresarlas.
  • Condiciona las acciones programadas con una condición (pídeselo a Dexter) para depender del estado en tiempo de ejecución, por ejemplo, emitir un tema solo si una variable de estado está en un modo particular.
  • Mantén la lógica a nivel de programación en la programación, y la lógica a nivel de componente en los Event Listeners. La programación emite la señal; el listener maneja los efectos secundarios.
  • Usa varias programaciones para comparar escenarios. Patrones de turno, demanda estacional, casos de carga hipotéticos, planificación de capacidad bajo distintos supuestos: las programaciones son un eje de variación, la configuración del modelo el otro.
  • El orden de eventos simultáneos no está especificado: no confíes en él. Si el orden importa entre dos eventos en la misma marca de tiempo, escalónalos por un segundo o encadénalos mediante un tema.
  • Revisa la procedencia en los números de planificación. Las cantidades de liberación, los pesos de asignación y las condiciones de acción que Dexter escribe llevan una marca Datos / Indicado / Asumido en el panel de Evidencia: una revisión de supuestos hará salir a la superficie cualquier suposición en el plan.